Compare commits

..

102 Commits

Author SHA1 Message Date
Jesse Hills
a85b7b3f84 Code tidy 2021-12-22 15:58:02 +13:00
Jesse Hills
a207ed08a9 Merge branch 'dev' into pr/ciB89/1424-1 2021-12-22 15:49:07 +13:00
jsuanet
f431c7402f Add shutdown and safe_mode button (#2918)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: Jos Suanet <jos@suanet.net>
2021-12-20 22:25:36 +01:00
Oxan van Leeuwen
4907e6f6d7 Fix MQTT button press action (#2917) 2021-12-21 08:19:20 +13:00
Jonas De Kegel
1ccee86705 Fix tm1637 bootloop (#2929) 2021-12-20 18:06:04 +01:00
Jonas De Kegel
542fb2175b Support inverted tm1637 display (#2878)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-12-20 09:30:35 +01:00
Frank Langtind
6ec9cfb044 Add Tuya Number support (#2765) 2021-12-20 14:35:10 +13:00
Benny de Leeuw
66e0ff8392 Add growatt modbus sensor (#2922)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-12-20 14:30:23 +13:00
Martin
1fb0a7109d Modbus: use multiply for publishing number (#2916) 2021-12-15 22:38:23 +13:00
sveip
192eb49589 ESP32 CAM add Automatic Exposure Control option (#2892)
Co-authored-by: Peter <psv@tsat.net>
Co-authored-by: Carlos Garcia Saura <CarlosGS@users.noreply.github.com>
2021-12-15 07:46:43 +13:00
Petr Vraník
5d70ff702b quantile filter support (#2900)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
Co-authored-by: pvranik <petr.vranik@mgm-tp.com>
2021-12-15 07:43:42 +13:00
jddonovan
a7b05db2a1 Adding Pascal unit to constants (#2914) 2021-12-15 07:39:50 +13:00
wilberforce
45e346cf1b Allow button POST on press from web server (#2913) 2021-12-14 15:08:01 +13:00
dependabot[bot]
80e2bfada3 Bump black from 21.11b1 to 21.12b0 (#2879)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-12-13 19:15:49 +01:00
Martin
16e7bd0388 fix multi-line comment warning/error (#2891) 2021-12-13 19:15:22 +01:00
Oxan van Leeuwen
b3fb35783e Set text sensor state property to filter output (#2893) 2021-12-13 15:21:09 +13:00
myhomeiot
a79c6aa9e0 Added access to ble_scan_result_evt_param as get_scan_result (#2854) 2021-12-13 13:08:18 +13:00
Martin
4bb58b2de9 Add gpio 12 to strapping pin list (#2902) 2021-12-13 11:03:08 +13:00
Jesse Hills
4e10881331 Log the actual value in modbus number (#2901) 2021-12-13 10:28:19 +13:00
Ben Owen
cec4a81e14 Add reset_duration option for waveshare epaper HAT rev 2.1 (#1481)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-12-13 10:27:11 +13:00
Carlos Garcia Saura
da45923d05 Turn verbose a debug statement in bme280 (#2906) 2021-12-13 09:32:37 +13:00
Carlos Garcia Saura
31a61b598b Reduce timing noise in duty_cycle (#2881) 2021-12-13 09:30:47 +13:00
tony
9c0506592b Add light.on_state trigger (#2868) 2021-12-13 09:19:57 +13:00
Oxan van Leeuwen
beeb0c7c5a Introduce hex parsing & formatting helper functions (#2882) 2021-12-13 09:15:23 +13:00
Martin
b2f05faee0 Move i2c scan to setup (#2869)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-12-13 09:12:50 +13:00
Jesse Hills
8375e1d64d Bump esphome-dashboard to 20211211.0 (#2904) 2021-12-11 21:03:41 +13:00
Keith Burzinski
cf5193d3e5 Fix for two points setting when fan_only_cooling is disabled (#2903)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
Co-authored-by: Keith Burzinski <kburzinski@kbx-mbp2021.ad.kbx81.net>
2021-12-11 20:03:22 +13:00
Guillermo Ruffino
c490388e80 Modbus number/output use write single (#2896)
Co-authored-by: Martin <25747549+martgras@users.noreply.github.com>
2021-12-10 09:44:43 +13:00
Jesse Hills
24ec5a6e9d Fix published state for modbus number (#2894) 2021-12-10 09:32:34 +13:00
Oxan van Leeuwen
6df1d5222d Drop unused xSemaphoreWait define (#2888) 2021-12-08 12:46:36 +13:00
Jesse Hills
58fb7a02f6 Bump esphome-dashboard to 20211208.0 (#2887) 2021-12-08 12:42:50 +13:00
Jesse Hills
3d51ac8df0 Use new platform component config blocks for wizard (#2885) 2021-12-08 09:22:03 +13:00
Oxan van Leeuwen
6fe4ff7f85 Drop len parameter from parse_number() (#2883) 2021-12-08 08:46:25 +13:00
Yuval Brik
2253d4bc16 Support different run duration for non-timer wakeup (#2861) 2021-12-06 23:30:27 +01:00
Carlos Garcia Saura
e5cc19de43 Feed watchdog while setting up OTA (#2876) 2021-12-06 23:26:06 +01:00
Jesse Hills
5404617d43 Bump esphome-dashboard to 20211207.0 (#2877) 2021-12-07 07:41:40 +13:00
Oxan van Leeuwen
12467a18e6 Feed watchdog when no component loops (#2857) 2021-12-07 07:24:20 +13:00
Jesse Hills
1db7043a4d Allow wizard to specify secrets (#2875) 2021-12-06 20:58:51 +13:00
Jesse Hills
49932747b3 Adopt using wifi secrets that should exist at this point (#2874) 2021-12-06 20:57:56 +13:00
Jesse Hills
55db190875 Add endpoint to fetch secrets keys (#2873) 2021-12-06 20:15:34 +13:00
Massimiliano Ravelli
71fe2f7ed3 Ignore already stopped dhcp for ethernet (#2862)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-12-06 20:01:50 +13:00
Oxan van Leeuwen
ffc112c9d0 Don't disable idle task WDT when it's not enabled (#2856) 2021-12-06 20:01:14 +13:00
Oxan van Leeuwen
d3e48e296f Fix MCP23x17 not disabling pullup after config change (#2855) 2021-12-06 19:59:50 +13:00
Martin
14f6ae75ea SPS30 : fix i2c read size (#2866) 2021-12-06 19:58:26 +13:00
Carlos Garcia Saura
c84efe64d3 ADC: Turn verbose the debugging "got voltage" (#2863) 2021-12-06 19:56:53 +13:00
Martin
10e89a7dbb tlc59208f : fix compilation error (#2867) 2021-12-06 19:54:46 +13:00
Jesse Hills
ef44acbf10 Bump esphome-dashboard to 20211206.0 (#2870) 2021-12-06 19:42:49 +13:00
dependabot[bot]
06da540ab0 Bump pylint from 2.12.1 to 2.12.2 (#2858)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-05 13:29:08 +01:00
Oxan van Leeuwen
40c017fd54 Update ota_component.cpp (#2852) 2021-12-03 07:52:56 +13:00
Jesse Hills
f0bcf81a98 Add a simple helper to remap values (#2850) 2021-12-02 09:23:11 +01:00
Jesse Hills
6a0b343289 Bump version to 2022.1.0-dev 2021-12-02 19:38:49 +13:00
Martin
9ca4e8f32a modbus_controller: bugfix: enable overriding calculated register size (#2845)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-12-02 15:45:11 +13:00
Alexandre-Jacques St-Jacques
1b88b7a166 Fix wifi not working with manual_ip using esp-idf (#2849) 2021-12-02 15:33:48 +13:00
Paul Nicholls
caf352ff06 Tuya Cover improvements (#2637) 2021-12-02 15:26:56 +13:00
Oxan van Leeuwen
54106179a1 Set ESP32 watchdog to loop task (#2846) 2021-12-02 09:05:42 +13:00
Oxan van Leeuwen
607601b3a4 Enable a bunch of clang-tidy checks (#2149) 2021-12-02 09:03:51 +13:00
Oxan van Leeuwen
f58828cb82 Support setting manual_ip under networks option (#2839) 2021-12-02 08:55:27 +13:00
Leon Loopik
11330af05f Expand uart invert feature to ESP8266 (#1727) 2021-12-01 20:31:04 +01:00
Oxan van Leeuwen
fbe1bca1b9 Fix compilation using subprocesses (#2834) 2021-12-01 17:37:24 +01:00
Mark Dietzer
24a5325db3 Declare arch_get_cpu_cycle_count for esp8266 as IRAM (#2843) 2021-12-01 10:01:15 +01:00
Yuval Brik
1ec3140759 ESP32 Deep Sleep: correct level value (#2812)
Upon registering for ESP32 deep sleep, DeepSleepComponent::begin_sleep
calculates the level value to wake up on.
As part of PR #2303, the level was changed to be based on `inverted`
instead of `!inverted`:
Before:
1e8e471dec/esphome/components/deep_sleep/deep_sleep_component.cpp (L76)
After:
2b04152482/esphome/components/deep_sleep/deep_sleep_component.cpp (L80)

The level argument to `esp_sleep_enable_ext0_wakeup(pin, level)` [0]
should be 0 when the inverted property is true (low triggers wakeup),
and 1 when inverted property is false (high triggers wakeup).

Also revert the changes of #2644.

[0]
https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/sleep_modes.html#_CPPv428esp_sleep_enable_ext0_wakeup10gpio_num_ti
2021-12-01 09:38:58 +01:00
Oxan van Leeuwen
ca8db7696e Don't enable namespace comment clang-tidy check twice (#2830) 2021-12-01 17:21:19 +13:00
Oxan van Leeuwen
c9190574a9 Fix CI check for Windows line endings (#2831) 2021-12-01 17:14:25 +13:00
Oxan van Leeuwen
bfeb0b3639 Add problem matcher for Python formatting errors (#2833) 2021-12-01 17:12:14 +13:00
Oxan van Leeuwen
cbc1334b8d Fix compile warning in Tuya automations (#2837) 2021-12-01 17:11:21 +13:00
mechanarchy
08cbb97ec9 Allow Git credentials to be loaded from secrets (#2825) 2021-12-01 17:10:25 +13:00
Jesse Hills
5719cc1a24 Bump esphome-dashboard to 20211201.0 (#2842) 2021-12-01 16:54:30 +13:00
Jesse Hills
d9513e5ff2 Number mode (#2838) 2021-12-01 08:11:38 +13:00
puuu
b5a0e8b2c0 Implement unit_of_measurement for number component (#2804)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2021-11-30 16:20:59 +01:00
Jesse Hills
b32b918936 Button device class (#2835) 2021-11-30 16:18:21 +01:00
dependabot[bot]
0f47ffd908 Bump aioesphomeapi from 10.2.0 to 10.6.0 (#2840) 2021-11-30 16:17:48 +01:00
Carlos Garcia Saura
cd018ad3a5 Burst read for BME280, to reduce spurious spikes (#2809) 2021-11-30 16:12:52 +01:00
Adrián Panella
24dfecb6f0 cse7766: add energy sensor (#2822) 2021-11-30 16:08:00 +01:00
Oxan van Leeuwen
ab027a6ae2 Fix too-broad matcher for custom CI script (#2829) 2021-11-30 09:35:52 +01:00
Keith Burzinski
556d071e7f Fix 8266 SPI Clock Polarity Setting (#2836) 2021-11-30 19:30:45 +13:00
dentra
939fb313df Tuya text_sensor and raw data usage (#1812) 2021-11-30 08:08:52 +13:00
Jesse Hills
b5639a6472 Add support for button entities (#2824) 2021-11-30 08:00:51 +13:00
definitio
f50e40e0b8 Fix custom mode_state_topic (#2827) 2021-11-29 18:09:09 +01:00
mechanarchy
6f07421911 Optionally show internal components on the web server (#2627)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
2021-11-29 16:52:20 +01:00
Maurice Makaay
adf48246a9 Improve DSMR read timeout handling (#2699) 2021-11-29 16:40:53 +01:00
anatoly-savchenkov
cae283dc86 Fixed data type inside fast_random_8() routine (#2818) 2021-11-29 08:31:15 +13:00
Conclusio
7afcb0fb04 Add delay to improve stability (#2793) 2021-11-29 08:13:42 +13:00
Dave T
10f830c3ef Correct bitmask for third color (blue) scaling. (#2817) 2021-11-29 08:12:40 +13:00
Carlos Garcia Saura
7a5c3aa7ed Fix compilation error for WPA enterprise in ESP-IDF (#2815) 2021-11-29 08:06:53 +13:00
Oxan van Leeuwen
2b50406856 Fix parsing of multiple values in EZO sensor (#2814)
Co-authored-by: Lydia Sevelt <LydiaSevelt@gmail.com>
2021-11-29 08:02:10 +13:00
Oxan van Leeuwen
10a2a7e0fc Fix parsing numbers in Anova (#2816) 2021-11-29 08:00:29 +13:00
Oxan van Leeuwen
7a564b222d Make clang-tidy suggest stdint.h int types (#2820) 2021-11-29 07:59:30 +13:00
Maurice Makaay
671d68bc2c Add missing nvs_flash_init() to ESP32 preferences code (#2805)
Co-authored-by: Maurice Makaay <mmakaay1@xs4all.net>
2021-11-26 21:25:58 +01:00
Oxan van Leeuwen
5946c37925 Fix usage of deprecated climate method in anova (#2801) 2021-11-26 09:16:39 +01:00
Martin
17a37b1de9 Modbus_controller: Add custom command. (#2680) 2021-11-26 12:48:52 +13:00
Adrián Panella
e7827a6997 total_daily_energy: allow to disable restore mode (#2795) 2021-11-25 22:35:36 +01:00
Jesse Hills
2347e043a9 Cancel previous workflows for PRs and branches (#2800) 2021-11-25 22:02:39 +01:00
Oxan van Leeuwen
00965fe19e Consistently format errors in CI scripts (#2762) 2021-11-26 09:54:11 +13:00
Oxan van Leeuwen
9681dfb458 Correct constant for dynamic I2S bus in NeoPixelBus (#2797) 2021-11-26 09:37:27 +13:00
Oxan van Leeuwen
5e631bc6ba Only match GCC warnings from ESPHome source files in CI (#2756) 2021-11-26 09:36:42 +13:00
Oxan van Leeuwen
b5f660398c Add map filter for text sensors (#2761) 2021-11-26 09:35:33 +13:00
Oxan van Leeuwen
d50bdf619f Cache virtualenv instead of pip cache between CI runs (#2759) 2021-11-26 09:29:10 +13:00
Oxan van Leeuwen
4e448b21ff Drop obsolete comment from CI workflow file (#2758) 2021-11-26 09:27:53 +13:00
Oxan van Leeuwen
2a78c2970d Fix CI cache key for test3.yaml compile (#2757) 2021-11-26 09:27:34 +13:00
Oxan van Leeuwen
3637be251e Fix parsing numbers from null-terminated buffers (#2755) 2021-11-26 09:00:49 +13:00
dependabot[bot]
2aea27d272 Bump pylint from 2.11.1 to 2.12.1 (#2798) 2021-11-25 20:34:11 +01:00
Ian
90c3cb62b3 Add tracker for OralB toothbrushes
The OralB toothbrushes expose some of their information in their bluetooth advertisement data.

This data lets us see the state (idle, running), brush mode (daily clean, tongue, whitening, etc.), pressure and some other bits of data.

This component lets you expose that data with config as follows:

```
esp32_ble_tracker:

sensor:
  - platform: oralb_brush
    mac_address: 00:00:00:00:00:00
    state:
      name: "Toothbrush State"
```

Checkout https://github.com/zewelor/bt-mqtt-gateway/blob/master/workers/toothbrush_homeassistant.py and https://esphome.io/components/esp32_ble_tracker.html for more information.
2020-05-03 15:23:21 +01:00
273 changed files with 4096 additions and 1214 deletions

View File

@@ -5,11 +5,8 @@ Checks: >-
-altera-*,
-android-*,
-boost-*,
-bugprone-branch-clone,
-bugprone-easily-swappable-parameters,
-bugprone-narrowing-conversions,
-bugprone-signed-char-misuse,
-bugprone-too-small-loop-variable,
-cert-dcl50-cpp,
-cert-err58-cpp,
-cert-oop57-cpp,
@@ -19,12 +16,10 @@ Checks: >-
-clang-diagnostic-delete-abstract-non-virtual-dtor,
-clang-diagnostic-delete-non-abstract-non-virtual-dtor,
-clang-diagnostic-shadow-field,
-clang-diagnostic-sign-compare,
-clang-diagnostic-unused-variable,
-clang-diagnostic-unused-const-variable,
-clang-diagnostic-unused-parameter,
-concurrency-*,
-cppcoreguidelines-avoid-c-arrays,
-cppcoreguidelines-avoid-goto,
-cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-init-variables,
-cppcoreguidelines-macro-usage,
@@ -41,7 +36,6 @@ Checks: >-
-cppcoreguidelines-pro-type-union-access,
-cppcoreguidelines-pro-type-vararg,
-cppcoreguidelines-special-member-functions,
-fuchsia-default-arguments,
-fuchsia-multiple-inheritance,
-fuchsia-overloaded-operator,
-fuchsia-statically-constructed-objects,
@@ -51,6 +45,7 @@ Checks: >-
-google-explicit-constructor,
-google-readability-braces-around-statements,
-google-readability-casting,
-google-readability-namespace-comments,
-google-readability-todo,
-google-runtime-references,
-hicpp-*,
@@ -97,9 +92,11 @@ CheckOptions:
value: '1'
- key: google-readability-function-size.StatementThreshold
value: '800'
- key: google-readability-namespace-comments.ShortNamespaceLines
- key: google-runtime-int.TypeSuffix
value: '_t'
- key: llvm-namespace-comment.ShortNamespaceLines
value: '10'
- key: google-readability-namespace-comments.SpacesBeforeComments
- key: llvm-namespace-comment.SpacesBeforeComments
value: '2'
- key: modernize-loop-convert.MaxCopySize
value: '16'

View File

@@ -1,5 +1,3 @@
# THESE JOBS ARE COPIED IN release.yml and release-dev.yml
# PLEASE ALSO UPDATE THOSE FILES WHEN CHANGING LINES HERE
name: CI
on:
@@ -11,6 +9,10 @@ on:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
ci:
name: ${{ matrix.name }}
@@ -34,7 +36,7 @@ jobs:
- id: test
file: tests/test3.yaml
name: Test tests/test3.yaml
pio_cache_key: test1
pio_cache_key: test3
- id: test
file: tests/test4.yaml
name: Test tests/test4.yaml
@@ -80,18 +82,23 @@ jobs:
with:
python-version: '3.7'
- name: Cache pip modules
- name: Cache virtualenv
uses: actions/cache@v2
with:
path: ~/.cache/pip
key: pip-${{ steps.python.outputs.python-version }}-${{ hashFiles('requirements*.txt') }}
path: .venv
key: venv-${{ steps.python.outputs.python-version }}-${{ hashFiles('requirements*.txt') }}
restore-keys: |
pip-${{ steps.python.outputs.python-version }}-
venv-${{ steps.python.outputs.python-version }}-
- name: Set up python environment
- name: Set up virtualenv
run: |
pip3 install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt
pip3 install -e .
python -m venv .venv
source .venv/bin/activate
pip install -U pip
pip install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt
pip install -e .
echo "$GITHUB_WORKSPACE/.venv/bin" >> $GITHUB_PATH
echo "VIRTUAL_ENV=$GITHUB_WORKSPACE/.venv" >> $GITHUB_ENV
# Use per check platformio cache because checks use different parts
- name: Cache platformio

View File

@@ -4,7 +4,7 @@
"owner": "ci-custom",
"pattern": [
{
"regexp": "^ERROR (.*):(\\d+):(\\d+) - (.*)$",
"regexp": "^(.*):(\\d+):(\\d+):\\s+lint:\\s+(.*)$",
"file": 1,
"line": 2,
"column": 3,

View File

@@ -5,7 +5,7 @@
"severity": "error",
"pattern": [
{
"regexp": "^(.*):(\\d+):(\\d+):\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$",
"regexp": "^src/(.*):(\\d+):(\\d+):\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$",
"file": 1,
"line": 2,
"column": 3,

View File

@@ -1,11 +1,22 @@
{
"problemMatcher": [
{
"owner": "black",
"severity": "error",
"pattern": [
{
"regexp": "^(.*): (Please format this file with the black formatter)",
"file": 1,
"message": 2
}
]
},
{
"owner": "flake8",
"severity": "error",
"pattern": [
{
"regexp": "^(.*):(\\d+) - ([EFCDNW]\\d{3}.*)$",
"regexp": "^(.*):(\\d+): ([EFCDNW]\\d{3}.*)$",
"file": 1,
"line": 2,
"message": 3
@@ -17,7 +28,7 @@
"severity": "error",
"pattern": [
{
"regexp": "^(.*):(\\d+) - (\\[[EFCRW]\\d{4}\\(.*\\),.*\\].*)$",
"regexp": "^(.*):(\\d+): (\\[[EFCRW]\\d{4}\\(.*\\),.*\\].*)$",
"file": 1,
"line": 2,
"message": 3

View File

@@ -30,6 +30,7 @@ esphome/components/bang_bang/* @OttoWinter
esphome/components/binary_sensor/* @esphome/core
esphome/components/ble_client/* @buxtronix
esphome/components/bme680_bsec/* @trvrnrth
esphome/components/button/* @esphome/core
esphome/components/canbus/* @danielschramm @mvturnho
esphome/components/cap1188/* @MrEditor97
esphome/components/captive_portal/* @OttoWinter
@@ -64,6 +65,7 @@ esphome/components/globals/* @esphome/core
esphome/components/gpio/* @esphome/core
esphome/components/gps/* @coogle
esphome/components/graph/* @synco
esphome/components/growatt_solar/* @leeuwte
esphome/components/havells_solar/* @sourabhjaiswal
esphome/components/hbridge/fan/* @WeekendWarrior
esphome/components/hbridge/light/* @DotNetDann
@@ -131,7 +133,7 @@ esphome/components/restart/* @esphome/core
esphome/components/rf_bridge/* @jesserockz
esphome/components/rgbct/* @jesserockz
esphome/components/rtttl/* @glmnet
esphome/components/safe_mode/* @paulmonigatti
esphome/components/safe_mode/* @jsuanet @paulmonigatti
esphome/components/scd4x/* @sjtrny
esphome/components/script/* @esphome/core
esphome/components/sdm_meter/* @jesserockz @polyfaces
@@ -141,7 +143,7 @@ esphome/components/select/* @esphome/core
esphome/components/sensor/* @esphome/core
esphome/components/sgp40/* @SenexCrenshaw
esphome/components/sht4x/* @sjtrny
esphome/components/shutdown/* @esphome/core
esphome/components/shutdown/* @esphome/core @jsuanet
esphome/components/sim800l/* @glmnet
esphome/components/sm2135/* @BoukeHaarsma23
esphome/components/socket/* @esphome/core
@@ -178,8 +180,10 @@ esphome/components/toshiba/* @kbx81
esphome/components/tsl2591/* @wjcarpenter
esphome/components/tuya/binary_sensor/* @jesserockz
esphome/components/tuya/climate/* @jesserockz
esphome/components/tuya/number/* @frankiboy1
esphome/components/tuya/sensor/* @jesserockz
esphome/components/tuya/switch/* @jesserockz
esphome/components/tuya/text_sensor/* @dentra
esphome/components/uart/* @esphome/core
esphome/components/ultrasonic/* @OttoWinter
esphome/components/version/* @esphome/core

View File

@@ -145,6 +145,8 @@ def wrap_to_code(name, comp):
if comp.config_schema is not None:
conf_str = yaml_util.dump(conf)
conf_str = conf_str.replace("//", "")
# remove tailing \ to avoid multi-line comment warning
conf_str = conf_str.replace("\\\n", "\n")
cg.add(cg.LineComment(indent(conf_str)))
await coro(conf)

View File

@@ -25,7 +25,7 @@ void AdalightLightEffect::stop() {
AddressableLightEffect::stop();
}
int AdalightLightEffect::get_frame_size_(int led_count) const {
unsigned int AdalightLightEffect::get_frame_size_(int led_count) const {
// 3 bytes: Ada
// 2 bytes: LED count
// 1 byte: checksum

View File

@@ -25,7 +25,7 @@ class AdalightLightEffect : public light::AddressableLightEffect, public uart::U
CONSUMED,
};
int get_frame_size_(int led_count) const;
unsigned int get_frame_size_(int led_count) const;
void reset_frame_(light::AddressableLight &it);
void blank_all_leds_(light::AddressableLight &it);
Frame parse_frame_(light::AddressableLight &it);

View File

@@ -91,7 +91,7 @@ void ADCSensor::dump_config() {
float ADCSensor::get_setup_priority() const { return setup_priority::DATA; }
void ADCSensor::update() {
float value_v = this->sample();
ESP_LOGD(TAG, "'%s': Got voltage=%.4fV", this->get_name().c_str(), value_v);
ESP_LOGV(TAG, "'%s': Got voltage=%.4fV", this->get_name().c_str(), value_v);
this->publish_state(value_v);
}

View File

@@ -110,12 +110,12 @@ void AHT10Component::update() {
uint32_t raw_temperature = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];
uint32_t raw_humidity = ((data[1] << 16) | (data[2] << 8) | data[3]) >> 4;
float temperature = ((200.0 * (float) raw_temperature) / 1048576.0) - 50.0;
float temperature = ((200.0f * (float) raw_temperature) / 1048576.0f) - 50.0f;
float humidity;
if (raw_humidity == 0) { // unrealistic value
humidity = NAN;
} else {
humidity = (float) raw_humidity * 100.0 / 1048576.0;
humidity = (float) raw_humidity * 100.0f / 1048576.0f;
}
if (this->temperature_sensor_ != nullptr) {

View File

@@ -38,9 +38,9 @@ void AM2320Component::update() {
return;
}
float temperature = (((data[4] & 0x7F) << 8) + data[5]) / 10.0;
float temperature = (((data[4] & 0x7F) << 8) + data[5]) / 10.0f;
temperature = (data[4] & 0x80) ? -temperature : temperature;
float humidity = ((data[2] << 8) + data[3]) / 10.0;
float humidity = ((data[2] << 8) + data[3]) / 10.0f;
ESP_LOGD(TAG, "Got temperature=%.1f°C humidity=%.1f%%", temperature, humidity);
if (this->temperature_sensor_ != nullptr)

View File

@@ -30,7 +30,7 @@ class Anova : public climate::Climate, public esphome::ble_client::BLEClientNode
climate::ClimateTraits traits() override {
auto traits = climate::ClimateTraits();
traits.set_supports_current_temperature(true);
traits.set_supports_heat_mode(true);
traits.set_supported_modes({climate::CLIMATE_MODE_OFF, climate::ClimateMode::CLIMATE_MODE_HEAT});
traits.set_visual_min_temperature(25.0);
traits.set_visual_max_temperature(100.0);
traits.set_visual_temperature_step(0.1);

View File

@@ -73,51 +73,46 @@ AnovaPacket *AnovaCodec::get_stop_request() {
}
void AnovaCodec::decode(const uint8_t *data, uint16_t length) {
memset(this->buf_, 0, 32);
strncpy(this->buf_, (char *) data, length);
char buf[32];
memset(buf, 0, sizeof(buf));
strncpy(buf, (char *) data, std::min<uint16_t>(length, sizeof(buf) - 1));
this->has_target_temp_ = this->has_current_temp_ = this->has_unit_ = this->has_running_ = false;
switch (this->current_query_) {
case READ_DEVICE_STATUS: {
if (!strncmp(this->buf_, "stopped", 7)) {
if (!strncmp(buf, "stopped", 7)) {
this->has_running_ = true;
this->running_ = false;
}
if (!strncmp(this->buf_, "running", 7)) {
if (!strncmp(buf, "running", 7)) {
this->has_running_ = true;
this->running_ = true;
}
break;
}
case START: {
if (!strncmp(this->buf_, "start", 5)) {
if (!strncmp(buf, "start", 5)) {
this->has_running_ = true;
this->running_ = true;
}
break;
}
case STOP: {
if (!strncmp(this->buf_, "stop", 4)) {
if (!strncmp(buf, "stop", 4)) {
this->has_running_ = true;
this->running_ = false;
}
break;
}
case READ_TARGET_TEMPERATURE: {
this->target_temp_ = parse_number<float>(this->buf_, sizeof(this->buf_)).value_or(0.0f);
if (this->fahrenheit_)
this->target_temp_ = ftoc(this->target_temp_);
this->has_target_temp_ = true;
break;
}
case READ_TARGET_TEMPERATURE:
case SET_TARGET_TEMPERATURE: {
this->target_temp_ = parse_number<float>(this->buf_, sizeof(this->buf_)).value_or(0.0f);
this->target_temp_ = parse_number<float>(str_until(buf, '\r')).value_or(0.0f);
if (this->fahrenheit_)
this->target_temp_ = ftoc(this->target_temp_);
this->has_target_temp_ = true;
break;
}
case READ_CURRENT_TEMPERATURE: {
this->current_temp_ = parse_number<float>(this->buf_, sizeof(this->buf_)).value_or(0.0f);
this->current_temp_ = parse_number<float>(str_until(buf, '\r')).value_or(0.0f);
if (this->fahrenheit_)
this->current_temp_ = ftoc(this->current_temp_);
this->has_current_temp_ = true;
@@ -125,8 +120,8 @@ void AnovaCodec::decode(const uint8_t *data, uint16_t length) {
}
case SET_UNIT:
case READ_UNIT: {
this->unit_ = this->buf_[0];
this->fahrenheit_ = this->buf_[0] == 'f';
this->unit_ = buf[0];
this->fahrenheit_ = buf[0] == 'f';
this->has_unit_ = true;
break;
}

View File

@@ -70,7 +70,6 @@ class AnovaCodec {
bool has_current_temp_;
bool has_unit_;
bool has_running_;
char buf_[32];
bool fahrenheit_;
CurrentQuery current_query_;

View File

@@ -40,6 +40,7 @@ service APIConnection {
rpc climate_command (ClimateCommandRequest) returns (void) {}
rpc number_command (NumberCommandRequest) returns (void) {}
rpc select_command (SelectCommandRequest) returns (void) {}
rpc button_command (ButtonCommandRequest) returns (void) {}
}
@@ -868,6 +869,11 @@ message ClimateCommandRequest {
}
// ==================== NUMBER ====================
enum NumberMode {
NUMBER_MODE_AUTO = 0;
NUMBER_MODE_BOX = 1;
NUMBER_MODE_SLIDER = 2;
}
message ListEntitiesNumberResponse {
option (id) = 49;
option (source) = SOURCE_SERVER;
@@ -884,6 +890,8 @@ message ListEntitiesNumberResponse {
float step = 8;
bool disabled_by_default = 9;
EntityCategory entity_category = 10;
string unit_of_measurement = 11;
NumberMode mode = 12;
}
message NumberStateResponse {
option (id) = 50;
@@ -944,3 +952,28 @@ message SelectCommandRequest {
fixed32 key = 1;
string state = 2;
}
// ==================== BUTTON ====================
message ListEntitiesButtonResponse {
option (id) = 61;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_BUTTON";
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
string icon = 5;
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
string device_class = 8;
}
message ButtonCommandRequest {
option (id) = 62;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_BUTTON";
option (no_delay) = true;
fixed32 key = 1;
}

View File

@@ -132,7 +132,7 @@ void APIConnection::loop() {
if (state_subs_at_ != -1) {
const auto &subs = this->parent_->get_state_subs();
if (state_subs_at_ >= subs.size()) {
if (state_subs_at_ >= (int) subs.size()) {
state_subs_at_ = -1;
} else {
auto &it = subs[state_subs_at_];
@@ -619,6 +619,8 @@ bool APIConnection::send_number_info(number::Number *number) {
msg.icon = number->get_icon();
msg.disabled_by_default = number->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(number->get_entity_category());
msg.unit_of_measurement = number->traits.get_unit_of_measurement();
msg.mode = static_cast<enums::NumberMode>(number->traits.get_mode());
msg.min_value = number->traits.get_min_value();
msg.max_value = number->traits.get_max_value();
@@ -674,6 +676,28 @@ void APIConnection::select_command(const SelectCommandRequest &msg) {
}
#endif
#ifdef USE_BUTTON
bool APIConnection::send_button_info(button::Button *button) {
ListEntitiesButtonResponse msg;
msg.key = button->get_object_id_hash();
msg.object_id = button->get_object_id();
msg.name = button->get_name();
msg.unique_id = get_default_unique_id("button", button);
msg.icon = button->get_icon();
msg.disabled_by_default = button->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(button->get_entity_category());
msg.device_class = button->get_device_class();
return this->send_list_entities_button_response(msg);
}
void APIConnection::button_command(const ButtonCommandRequest &msg) {
button::Button *button = App.get_button_by_key(msg.key);
if (button == nullptr)
return;
button->press();
}
#endif
#ifdef USE_ESP32_CAMERA
void APIConnection::send_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) {
if (!this->state_subscription_)

View File

@@ -73,6 +73,10 @@ class APIConnection : public APIServerConnection {
bool send_select_state(select::Select *select, std::string state);
bool send_select_info(select::Select *select);
void select_command(const SelectCommandRequest &msg) override;
#endif
#ifdef USE_BUTTON
bool send_button_info(button::Button *button);
void button_command(const ButtonCommandRequest &msg) override;
#endif
bool send_log_message(int level, const char *tag, const char *line);
void send_homeassistant_service_call(const HomeassistantServiceResponse &call) {

View File

@@ -174,9 +174,6 @@ APIError APINoiseFrameHelper::loop() {
* errno API_ERROR_HANDSHAKE_PACKET_LEN: Packet too big for this phase.
*/
APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
int err;
APIError aerr;
if (frame == nullptr) {
HELPER_LOG("Bad argument for try_read_frame_");
return APIError::BAD_ARG;
@@ -200,7 +197,7 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
return APIError::CONNECTION_CLOSED;
}
rx_header_buf_len_ += received;
if (received != to_read) {
if ((size_t) received != to_read) {
// not a full read
return APIError::WOULD_BLOCK;
}
@@ -247,7 +244,7 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
return APIError::CONNECTION_CLOSED;
}
rx_buf_len_ += received;
if (received != to_read) {
if ((size_t) received != to_read) {
// not all read
return APIError::WOULD_BLOCK;
}
@@ -255,7 +252,7 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
// uncomment for even more debugging
#ifdef HELPER_LOG_PACKETS
ESP_LOGVV(TAG, "Received frame: %s", hexencode(rx_buf_).c_str());
ESP_LOGVV(TAG, "Received frame: %s", format_hex_pretty(rx_buf_).c_str());
#endif
frame->msg = std::move(rx_buf_);
// consume msg
@@ -544,13 +541,13 @@ APIError APINoiseFrameHelper::try_send_tx_buf_() {
APIError APINoiseFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
if (iovcnt == 0)
return APIError::OK;
int err;
APIError aerr;
size_t total_write_len = 0;
for (int i = 0; i < iovcnt; i++) {
#ifdef HELPER_LOG_PACKETS
ESP_LOGVV(TAG, "Sending raw: %s", hexencode(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
ESP_LOGVV(TAG, "Sending raw: %s",
format_hex_pretty(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
#endif
total_write_len += iov[i].iov_len;
}
@@ -584,7 +581,7 @@ APIError APINoiseFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
state_ = State::FAILED;
HELPER_LOG("Socket write failed with errno %d", errno);
return APIError::SOCKET_WRITE_FAILED;
} else if (sent != total_write_len) {
} else if ((size_t) sent != total_write_len) {
// partially sent, add end to tx_buf
size_t to_consume = sent;
for (int i = 0; i < iovcnt; i++) {
@@ -778,9 +775,6 @@ APIError APIPlaintextFrameHelper::loop() {
* error API_ERROR_BAD_INDICATOR: Bad indicator byte at start of frame.
*/
APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
int err;
APIError aerr;
if (frame == nullptr) {
HELPER_LOG("Bad argument for try_read_frame_");
return APIError::BAD_ARG;
@@ -854,7 +848,7 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
return APIError::CONNECTION_CLOSED;
}
rx_buf_len_ += received;
if (received != to_read) {
if ((size_t) received != to_read) {
// not all read
return APIError::WOULD_BLOCK;
}
@@ -862,7 +856,7 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
// uncomment for even more debugging
#ifdef HELPER_LOG_PACKETS
ESP_LOGVV(TAG, "Received frame: %s", hexencode(rx_buf_).c_str());
ESP_LOGVV(TAG, "Received frame: %s", format_hex_pretty(rx_buf_).c_str());
#endif
frame->msg = std::move(rx_buf_);
// consume msg
@@ -874,7 +868,6 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
}
APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
int err;
APIError aerr;
if (state_ != State::DATA) {
@@ -894,9 +887,6 @@ APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
}
bool APIPlaintextFrameHelper::can_write_without_blocking() { return state_ == State::DATA && tx_buf_.empty(); }
APIError APIPlaintextFrameHelper::write_packet(uint16_t type, const uint8_t *payload, size_t payload_len) {
int err;
APIError aerr;
if (state_ != State::DATA) {
return APIError::BAD_STATE;
}
@@ -940,13 +930,13 @@ APIError APIPlaintextFrameHelper::try_send_tx_buf_() {
APIError APIPlaintextFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
if (iovcnt == 0)
return APIError::OK;
int err;
APIError aerr;
size_t total_write_len = 0;
for (int i = 0; i < iovcnt; i++) {
#ifdef HELPER_LOG_PACKETS
ESP_LOGVV(TAG, "Sending raw: %s", hexencode(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
ESP_LOGVV(TAG, "Sending raw: %s",
format_hex_pretty(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
#endif
total_write_len += iov[i].iov_len;
}
@@ -980,7 +970,7 @@ APIError APIPlaintextFrameHelper::write_raw_(const struct iovec *iov, int iovcnt
state_ = State::FAILED;
HELPER_LOG("Socket write failed with errno %d", errno);
return APIError::SOCKET_WRITE_FAILED;
} else if (sent != total_write_len) {
} else if ((size_t) sent != total_write_len) {
// partially sent, add end to tx_buf
size_t to_consume = sent;
for (int i = 0; i < iovcnt; i++) {

View File

@@ -266,6 +266,18 @@ template<> const char *proto_enum_to_string<enums::ClimatePreset>(enums::Climate
return "UNKNOWN";
}
}
template<> const char *proto_enum_to_string<enums::NumberMode>(enums::NumberMode value) {
switch (value) {
case enums::NUMBER_MODE_AUTO:
return "NUMBER_MODE_AUTO";
case enums::NUMBER_MODE_BOX:
return "NUMBER_MODE_BOX";
case enums::NUMBER_MODE_SLIDER:
return "NUMBER_MODE_SLIDER";
default:
return "UNKNOWN";
}
}
bool HelloRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1: {
@@ -279,7 +291,7 @@ bool HelloRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value)
void HelloRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->client_info); }
#ifdef HAS_PROTO_MESSAGE_DUMP
void HelloRequest::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("HelloRequest {\n");
out.append(" client_info: ");
out.append("'").append(this->client_info).append("'");
@@ -318,7 +330,7 @@ void HelloResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void HelloResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("HelloResponse {\n");
out.append(" api_version_major: ");
sprintf(buffer, "%u", this->api_version_major);
@@ -349,7 +361,7 @@ bool ConnectRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value
void ConnectRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->password); }
#ifdef HAS_PROTO_MESSAGE_DUMP
void ConnectRequest::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ConnectRequest {\n");
out.append(" password: ");
out.append("'").append(this->password).append("'");
@@ -370,7 +382,7 @@ bool ConnectResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
void ConnectResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->invalid_password); }
#ifdef HAS_PROTO_MESSAGE_DUMP
void ConnectResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ConnectResponse {\n");
out.append(" invalid_password: ");
out.append(YESNO(this->invalid_password));
@@ -464,7 +476,7 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void DeviceInfoResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("DeviceInfoResponse {\n");
out.append(" uses_password: ");
out.append(YESNO(this->uses_password));
@@ -588,7 +600,7 @@ void ListEntitiesBinarySensorResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesBinarySensorResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ListEntitiesBinarySensorResponse {\n");
out.append(" object_id: ");
out.append("'").append(this->object_id).append("'");
@@ -660,7 +672,7 @@ void BinarySensorStateResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void BinarySensorStateResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("BinarySensorStateResponse {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -754,7 +766,7 @@ void ListEntitiesCoverResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesCoverResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ListEntitiesCoverResponse {\n");
out.append(" object_id: ");
out.append("'").append(this->object_id).append("'");
@@ -844,7 +856,7 @@ void CoverStateResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void CoverStateResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("CoverStateResponse {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -927,7 +939,7 @@ void CoverCommandRequest::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void CoverCommandRequest::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("CoverCommandRequest {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -1043,7 +1055,7 @@ void ListEntitiesFanResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesFanResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ListEntitiesFanResponse {\n");
out.append(" object_id: ");
out.append("'").append(this->object_id).append("'");
@@ -1139,7 +1151,7 @@ void FanStateResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void FanStateResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("FanStateResponse {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -1240,7 +1252,7 @@ void FanCommandRequest::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void FanCommandRequest::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("FanCommandRequest {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -1391,7 +1403,7 @@ void ListEntitiesLightResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesLightResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ListEntitiesLightResponse {\n");
out.append(" object_id: ");
out.append("'").append(this->object_id).append("'");
@@ -1549,7 +1561,7 @@ void LightStateResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void LightStateResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("LightStateResponse {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -1772,7 +1784,7 @@ void LightCommandRequest::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void LightCommandRequest::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("LightCommandRequest {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -1983,7 +1995,7 @@ void ListEntitiesSensorResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesSensorResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ListEntitiesSensorResponse {\n");
out.append(" object_id: ");
out.append("'").append(this->object_id).append("'");
@@ -2072,7 +2084,7 @@ void SensorStateResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SensorStateResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("SensorStateResponse {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -2152,7 +2164,7 @@ void ListEntitiesSwitchResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesSwitchResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ListEntitiesSwitchResponse {\n");
out.append(" object_id: ");
out.append("'").append(this->object_id).append("'");
@@ -2215,7 +2227,7 @@ void SwitchStateResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SwitchStateResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("SwitchStateResponse {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -2254,7 +2266,7 @@ void SwitchCommandRequest::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SwitchCommandRequest::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("SwitchCommandRequest {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -2324,7 +2336,7 @@ void ListEntitiesTextSensorResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesTextSensorResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ListEntitiesTextSensorResponse {\n");
out.append(" object_id: ");
out.append("'").append(this->object_id).append("'");
@@ -2394,7 +2406,7 @@ void TextSensorStateResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void TextSensorStateResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("TextSensorStateResponse {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -2431,7 +2443,7 @@ void SubscribeLogsRequest::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SubscribeLogsRequest::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("SubscribeLogsRequest {\n");
out.append(" level: ");
out.append(proto_enum_to_string<enums::LogLevel>(this->level));
@@ -2474,7 +2486,7 @@ void SubscribeLogsResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SubscribeLogsResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("SubscribeLogsResponse {\n");
out.append(" level: ");
out.append(proto_enum_to_string<enums::LogLevel>(this->level));
@@ -2516,7 +2528,7 @@ void HomeassistantServiceMap::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void HomeassistantServiceMap::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("HomeassistantServiceMap {\n");
out.append(" key: ");
out.append("'").append(this->key).append("'");
@@ -2575,7 +2587,7 @@ void HomeassistantServiceResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void HomeassistantServiceResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("HomeassistantServiceResponse {\n");
out.append(" service: ");
out.append("'").append(this->service).append("'");
@@ -2631,7 +2643,7 @@ void SubscribeHomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SubscribeHomeAssistantStateResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("SubscribeHomeAssistantStateResponse {\n");
out.append(" entity_id: ");
out.append("'").append(this->entity_id).append("'");
@@ -2668,7 +2680,7 @@ void HomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void HomeAssistantStateResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("HomeAssistantStateResponse {\n");
out.append(" entity_id: ");
out.append("'").append(this->entity_id).append("'");
@@ -2701,7 +2713,7 @@ bool GetTimeResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
void GetTimeResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->epoch_seconds); }
#ifdef HAS_PROTO_MESSAGE_DUMP
void GetTimeResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("GetTimeResponse {\n");
out.append(" epoch_seconds: ");
sprintf(buffer, "%u", this->epoch_seconds);
@@ -2736,7 +2748,7 @@ void ListEntitiesServicesArgument::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesServicesArgument::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ListEntitiesServicesArgument {\n");
out.append(" name: ");
out.append("'").append(this->name).append("'");
@@ -2781,7 +2793,7 @@ void ListEntitiesServicesResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesServicesResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ListEntitiesServicesResponse {\n");
out.append(" name: ");
out.append("'").append(this->name).append("'");
@@ -2875,7 +2887,7 @@ void ExecuteServiceArgument::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ExecuteServiceArgument::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ExecuteServiceArgument {\n");
out.append(" bool_: ");
out.append(YESNO(this->bool_));
@@ -2956,7 +2968,7 @@ void ExecuteServiceRequest::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ExecuteServiceRequest::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ExecuteServiceRequest {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -3028,7 +3040,7 @@ void ListEntitiesCameraResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesCameraResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ListEntitiesCameraResponse {\n");
out.append(" object_id: ");
out.append("'").append(this->object_id).append("'");
@@ -3098,7 +3110,7 @@ void CameraImageResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void CameraImageResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("CameraImageResponse {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -3135,7 +3147,7 @@ void CameraImageRequest::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void CameraImageRequest::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("CameraImageRequest {\n");
out.append(" single: ");
out.append(YESNO(this->single));
@@ -3281,7 +3293,7 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesClimateResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ListEntitiesClimateResponse {\n");
out.append(" object_id: ");
out.append("'").append(this->object_id).append("'");
@@ -3468,7 +3480,7 @@ void ClimateStateResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ClimateStateResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ClimateStateResponse {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -3656,7 +3668,7 @@ void ClimateCommandRequest::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ClimateCommandRequest::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ClimateCommandRequest {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -3758,6 +3770,10 @@ bool ListEntitiesNumberResponse::decode_varint(uint32_t field_id, ProtoVarInt va
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
case 12: {
this->mode = value.as_enum<enums::NumberMode>();
return true;
}
default:
return false;
}
@@ -3780,6 +3796,10 @@ bool ListEntitiesNumberResponse::decode_length(uint32_t field_id, ProtoLengthDel
this->icon = value.as_string();
return true;
}
case 11: {
this->unit_of_measurement = value.as_string();
return true;
}
default:
return false;
}
@@ -3817,10 +3837,12 @@ void ListEntitiesNumberResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_float(8, this->step);
buffer.encode_bool(9, this->disabled_by_default);
buffer.encode_enum<enums::EntityCategory>(10, this->entity_category);
buffer.encode_string(11, this->unit_of_measurement);
buffer.encode_enum<enums::NumberMode>(12, this->mode);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesNumberResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ListEntitiesNumberResponse {\n");
out.append(" object_id: ");
out.append("'").append(this->object_id).append("'");
@@ -3865,6 +3887,14 @@ void ListEntitiesNumberResponse::dump_to(std::string &out) const {
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append(" unit_of_measurement: ");
out.append("'").append(this->unit_of_measurement).append("'");
out.append("\n");
out.append(" mode: ");
out.append(proto_enum_to_string<enums::NumberMode>(this->mode));
out.append("\n");
out.append("}");
}
#endif
@@ -3899,7 +3929,7 @@ void NumberStateResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void NumberStateResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("NumberStateResponse {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -3937,7 +3967,7 @@ void NumberCommandRequest::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void NumberCommandRequest::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("NumberCommandRequest {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -4015,7 +4045,7 @@ void ListEntitiesSelectResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesSelectResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("ListEntitiesSelectResponse {\n");
out.append(" object_id: ");
out.append("'").append(this->object_id).append("'");
@@ -4091,7 +4121,7 @@ void SelectStateResponse::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SelectStateResponse::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("SelectStateResponse {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -4134,7 +4164,7 @@ void SelectCommandRequest::encode(ProtoWriteBuffer buffer) const {
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void SelectCommandRequest::dump_to(std::string &out) const {
char buffer[64];
__attribute__((unused)) char buffer[64];
out.append("SelectCommandRequest {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
@@ -4147,6 +4177,127 @@ void SelectCommandRequest::dump_to(std::string &out) const {
out.append("}");
}
#endif
bool ListEntitiesButtonResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 6: {
this->disabled_by_default = value.as_bool();
return true;
}
case 7: {
this->entity_category = value.as_enum<enums::EntityCategory>();
return true;
}
default:
return false;
}
}
bool ListEntitiesButtonResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1: {
this->object_id = value.as_string();
return true;
}
case 3: {
this->name = value.as_string();
return true;
}
case 4: {
this->unique_id = value.as_string();
return true;
}
case 5: {
this->icon = value.as_string();
return true;
}
case 8: {
this->device_class = value.as_string();
return true;
}
default:
return false;
}
}
bool ListEntitiesButtonResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 2: {
this->key = value.as_fixed32();
return true;
}
default:
return false;
}
}
void ListEntitiesButtonResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(1, this->object_id);
buffer.encode_fixed32(2, this->key);
buffer.encode_string(3, this->name);
buffer.encode_string(4, this->unique_id);
buffer.encode_string(5, this->icon);
buffer.encode_bool(6, this->disabled_by_default);
buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
buffer.encode_string(8, this->device_class);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesButtonResponse::dump_to(std::string &out) const {
char buffer[64];
out.append("ListEntitiesButtonResponse {\n");
out.append(" object_id: ");
out.append("'").append(this->object_id).append("'");
out.append("\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
out.append(buffer);
out.append("\n");
out.append(" name: ");
out.append("'").append(this->name).append("'");
out.append("\n");
out.append(" unique_id: ");
out.append("'").append(this->unique_id).append("'");
out.append("\n");
out.append(" icon: ");
out.append("'").append(this->icon).append("'");
out.append("\n");
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n");
out.append(" device_class: ");
out.append("'").append(this->device_class).append("'");
out.append("\n");
out.append("}");
}
#endif
bool ButtonCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 1: {
this->key = value.as_fixed32();
return true;
}
default:
return false;
}
}
void ButtonCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); }
#ifdef HAS_PROTO_MESSAGE_DUMP
void ButtonCommandRequest::dump_to(std::string &out) const {
char buffer[64];
out.append("ButtonCommandRequest {\n");
out.append(" key: ");
sprintf(buffer, "%u", this->key);
out.append(buffer);
out.append("\n");
out.append("}");
}
#endif
} // namespace api
} // namespace esphome

View File

@@ -123,6 +123,11 @@ enum ClimatePreset : uint32_t {
CLIMATE_PRESET_SLEEP = 6,
CLIMATE_PRESET_ACTIVITY = 7,
};
enum NumberMode : uint32_t {
NUMBER_MODE_AUTO = 0,
NUMBER_MODE_BOX = 1,
NUMBER_MODE_SLIDER = 2,
};
} // namespace enums
@@ -957,6 +962,8 @@ class ListEntitiesNumberResponse : public ProtoMessage {
float step{0.0f};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
std::string unit_of_measurement{};
enums::NumberMode mode{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
@@ -1041,6 +1048,37 @@ class SelectCommandRequest : public ProtoMessage {
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
};
class ListEntitiesButtonResponse : public ProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
std::string icon{};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
std::string device_class{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class ButtonCommandRequest : public ProtoMessage {
public:
uint32_t key{0};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
};
} // namespace api
} // namespace esphome

View File

@@ -282,6 +282,16 @@ bool APIServerConnectionBase::send_select_state_response(const SelectStateRespon
#endif
#ifdef USE_SELECT
#endif
#ifdef USE_BUTTON
bool APIServerConnectionBase::send_list_entities_button_response(const ListEntitiesButtonResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_button_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesButtonResponse>(msg, 61);
}
#endif
#ifdef USE_BUTTON
#endif
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
switch (msg_type) {
case 1: {
@@ -513,6 +523,17 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
ESP_LOGVV(TAG, "on_select_command_request: %s", msg.dump().c_str());
#endif
this->on_select_command_request(msg);
#endif
break;
}
case 62: {
#ifdef USE_BUTTON
ButtonCommandRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_button_command_request: %s", msg.dump().c_str());
#endif
this->on_button_command_request(msg);
#endif
break;
}
@@ -737,6 +758,19 @@ void APIServerConnection::on_select_command_request(const SelectCommandRequest &
this->select_command(msg);
}
#endif
#ifdef USE_BUTTON
void APIServerConnection::on_button_command_request(const ButtonCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->button_command(msg);
}
#endif
} // namespace api
} // namespace esphome

View File

@@ -129,6 +129,12 @@ class APIServerConnectionBase : public ProtoService {
#endif
#ifdef USE_SELECT
virtual void on_select_command_request(const SelectCommandRequest &value){};
#endif
#ifdef USE_BUTTON
bool send_list_entities_button_response(const ListEntitiesButtonResponse &msg);
#endif
#ifdef USE_BUTTON
virtual void on_button_command_request(const ButtonCommandRequest &value){};
#endif
protected:
bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
@@ -171,6 +177,9 @@ class APIServerConnection : public APIServerConnectionBase {
#endif
#ifdef USE_SELECT
virtual void select_command(const SelectCommandRequest &msg) = 0;
#endif
#ifdef USE_BUTTON
virtual void button_command(const ButtonCommandRequest &msg) = 0;
#endif
protected:
void on_hello_request(const HelloRequest &msg) override;
@@ -209,6 +218,9 @@ class APIServerConnection : public APIServerConnectionBase {
#ifdef USE_SELECT
void on_select_command_request(const SelectCommandRequest &msg) override;
#endif
#ifdef USE_BUTTON
void on_button_command_request(const ButtonCommandRequest &msg) override;
#endif
};
} // namespace api

View File

@@ -27,6 +27,9 @@ bool ListEntitiesIterator::on_sensor(sensor::Sensor *sensor) { return this->clie
#ifdef USE_SWITCH
bool ListEntitiesIterator::on_switch(switch_::Switch *a_switch) { return this->client_->send_switch_info(a_switch); }
#endif
#ifdef USE_BUTTON
bool ListEntitiesIterator::on_button(button::Button *button) { return this->client_->send_button_info(button); }
#endif
#ifdef USE_TEXT_SENSOR
bool ListEntitiesIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) {
return this->client_->send_text_sensor_info(text_sensor);

View File

@@ -30,6 +30,9 @@ class ListEntitiesIterator : public ComponentIterator {
#ifdef USE_SWITCH
bool on_switch(switch_::Switch *a_switch) override;
#endif
#ifdef USE_BUTTON
bool on_button(button::Button *button) override;
#endif
#ifdef USE_TEXT_SENSOR
bool on_text_sensor(text_sensor::TextSensor *text_sensor) override;
#endif

View File

@@ -31,6 +31,9 @@ class InitialStateIterator : public ComponentIterator {
#ifdef USE_SWITCH
bool on_switch(switch_::Switch *a_switch) override;
#endif
#ifdef USE_BUTTON
bool on_button(button::Button *button) override { return true; };
#endif
#ifdef USE_TEXT_SENSOR
bool on_text_sensor(text_sensor::TextSensor *text_sensor) override;
#endif

View File

@@ -116,6 +116,21 @@ void ComponentIterator::advance() {
}
break;
#endif
#ifdef USE_BUTTON
case IteratorState::BUTTON:
if (this->at_ >= App.get_buttons().size()) {
advance_platform = true;
} else {
auto *button = App.get_buttons()[this->at_];
if (button->is_internal()) {
success = true;
break;
} else {
success = this->on_button(button);
}
}
break;
#endif
#ifdef USE_TEXT_SENSOR
case IteratorState::TEXT_SENSOR:
if (this->at_ >= App.get_text_sensors().size()) {

View File

@@ -38,6 +38,9 @@ class ComponentIterator {
#ifdef USE_SWITCH
virtual bool on_switch(switch_::Switch *a_switch) = 0;
#endif
#ifdef USE_BUTTON
virtual bool on_button(button::Button *button) = 0;
#endif
#ifdef USE_TEXT_SENSOR
virtual bool on_text_sensor(text_sensor::TextSensor *text_sensor) = 0;
#endif
@@ -78,6 +81,9 @@ class ComponentIterator {
#ifdef USE_SWITCH
SWITCH,
#endif
#ifdef USE_BUTTON
BUTTON,
#endif
#ifdef USE_TEXT_SENSOR
TEXT_SENSOR,
#endif

View File

@@ -33,6 +33,7 @@ static const uint8_t BME280_REGISTER_CONTROLHUMID = 0xF2;
static const uint8_t BME280_REGISTER_STATUS = 0xF3;
static const uint8_t BME280_REGISTER_CONTROL = 0xF4;
static const uint8_t BME280_REGISTER_CONFIG = 0xF5;
static const uint8_t BME280_REGISTER_MEASUREMENTS = 0xF7;
static const uint8_t BME280_REGISTER_PRESSUREDATA = 0xF7;
static const uint8_t BME280_REGISTER_TEMPDATA = 0xFA;
static const uint8_t BME280_REGISTER_HUMIDDATA = 0xFD;
@@ -178,23 +179,29 @@ void BME280Component::update() {
return;
}
float meas_time = 1.5;
float meas_time = 1.5f;
meas_time += 2.3f * oversampling_to_time(this->temperature_oversampling_);
meas_time += 2.3f * oversampling_to_time(this->pressure_oversampling_) + 0.575f;
meas_time += 2.3f * oversampling_to_time(this->humidity_oversampling_) + 0.575f;
this->set_timeout("data", uint32_t(ceilf(meas_time)), [this]() {
uint8_t data[8];
if (!this->read_bytes(BME280_REGISTER_MEASUREMENTS, data, 8)) {
ESP_LOGW(TAG, "Error reading registers.");
this->status_set_warning();
return;
}
int32_t t_fine = 0;
float temperature = this->read_temperature_(&t_fine);
float temperature = this->read_temperature_(data, &t_fine);
if (std::isnan(temperature)) {
ESP_LOGW(TAG, "Invalid temperature, cannot read pressure & humidity values.");
this->status_set_warning();
return;
}
float pressure = this->read_pressure_(t_fine);
float humidity = this->read_humidity_(t_fine);
float pressure = this->read_pressure_(data, t_fine);
float humidity = this->read_humidity_(data, t_fine);
ESP_LOGD(TAG, "Got temperature=%.1f°C pressure=%.1fhPa humidity=%.1f%%", temperature, pressure, humidity);
ESP_LOGV(TAG, "Got temperature=%.1f°C pressure=%.1fhPa humidity=%.1f%%", temperature, pressure, humidity);
if (this->temperature_sensor_ != nullptr)
this->temperature_sensor_->publish_state(temperature);
if (this->pressure_sensor_ != nullptr)
@@ -204,11 +211,8 @@ void BME280Component::update() {
this->status_clear_warning();
});
}
float BME280Component::read_temperature_(int32_t *t_fine) {
uint8_t data[3];
if (!this->read_bytes(BME280_REGISTER_TEMPDATA, data, 3))
return NAN;
int32_t adc = ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF);
float BME280Component::read_temperature_(const uint8_t *data, int32_t *t_fine) {
int32_t adc = ((data[3] & 0xFF) << 16) | ((data[4] & 0xFF) << 8) | (data[5] & 0xFF);
adc >>= 4;
if (adc == 0x80000)
// temperature was disabled
@@ -226,10 +230,7 @@ float BME280Component::read_temperature_(int32_t *t_fine) {
return temperature / 100.0f;
}
float BME280Component::read_pressure_(int32_t t_fine) {
uint8_t data[3];
if (!this->read_bytes(BME280_REGISTER_PRESSUREDATA, data, 3))
return NAN;
float BME280Component::read_pressure_(const uint8_t *data, int32_t t_fine) {
int32_t adc = ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF);
adc >>= 4;
if (adc == 0x80000)
@@ -265,9 +266,9 @@ float BME280Component::read_pressure_(int32_t t_fine) {
return (p / 256.0f) / 100.0f;
}
float BME280Component::read_humidity_(int32_t t_fine) {
uint16_t raw_adc;
if (!this->read_byte_16(BME280_REGISTER_HUMIDDATA, &raw_adc) || raw_adc == 0x8000)
float BME280Component::read_humidity_(const uint8_t *data, int32_t t_fine) {
uint16_t raw_adc = ((data[6] & 0xFF) << 8) | (data[7] & 0xFF);
if (raw_adc == 0x8000)
return NAN;
int32_t adc = raw_adc;

View File

@@ -82,11 +82,11 @@ class BME280Component : public PollingComponent, public i2c::I2CDevice {
protected:
/// Read the temperature value and store the calculated ambient temperature in t_fine.
float read_temperature_(int32_t *t_fine);
float read_temperature_(const uint8_t *data, int32_t *t_fine);
/// Read the pressure value in hPa using the provided t_fine value.
float read_pressure_(int32_t t_fine);
float read_pressure_(const uint8_t *data, int32_t t_fine);
/// Read the humidity value in % using the provided t_fine value.
float read_humidity_(int32_t t_fine);
float read_humidity_(const uint8_t *data, int32_t t_fine);
uint8_t read_u8_(uint8_t a_register);
uint16_t read_u16_le_(uint8_t a_register);
int16_t read_s16_le_(uint8_t a_register);

View File

@@ -0,0 +1,127 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.automation import maybe_simple_id
from esphome.components import mqtt
from esphome.const import (
CONF_DEVICE_CLASS,
CONF_ENTITY_CATEGORY,
CONF_ICON,
CONF_ID,
CONF_ON_PRESS,
CONF_TRIGGER_ID,
CONF_MQTT_ID,
DEVICE_CLASS_RESTART,
DEVICE_CLASS_UPDATE,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@esphome/core"]
IS_PLATFORM_COMPONENT = True
DEVICE_CLASSES = [
DEVICE_CLASS_RESTART,
DEVICE_CLASS_UPDATE,
]
button_ns = cg.esphome_ns.namespace("button")
Button = button_ns.class_("Button", cg.EntityBase)
ButtonPtr = Button.operator("ptr")
PressAction = button_ns.class_("PressAction", automation.Action)
ButtonPressTrigger = button_ns.class_(
"ButtonPressTrigger", automation.Trigger.template()
)
validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
BUTTON_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
{
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTButtonComponent),
cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
cv.Optional(CONF_ON_PRESS): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ButtonPressTrigger),
}
),
}
)
_UNDEF = object()
def button_schema(
icon: str = _UNDEF,
entity_category: str = _UNDEF,
device_class: str = _UNDEF,
) -> cv.Schema:
schema = BUTTON_SCHEMA
if icon is not _UNDEF:
schema = schema.extend({cv.Optional(CONF_ICON, default=icon): cv.icon})
if entity_category is not _UNDEF:
schema = schema.extend(
{
cv.Optional(
CONF_ENTITY_CATEGORY, default=entity_category
): cv.entity_category
}
)
if device_class is not _UNDEF:
schema = schema.extend(
{
cv.Optional(
CONF_DEVICE_CLASS, default=device_class
): validate_device_class
}
)
return schema
async def setup_button_core_(var, config):
await setup_entity(var, config)
for conf in config.get(CONF_ON_PRESS, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
if CONF_DEVICE_CLASS in config:
cg.add(var.set_device_class(config[CONF_DEVICE_CLASS]))
if CONF_MQTT_ID in config:
mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var)
await mqtt.register_mqtt_component(mqtt_, config)
async def register_button(var, config):
if not CORE.has_id(config[CONF_ID]):
var = cg.Pvariable(config[CONF_ID], var)
cg.add(cg.App.register_button(var))
await setup_button_core_(var, config)
async def new_button(config):
var = cg.new_Pvariable(config[CONF_ID])
await register_button(var, config)
return var
BUTTON_PRESS_SCHEMA = maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(Button),
}
)
@automation.register_action("button.press", PressAction, BUTTON_PRESS_SCHEMA)
async def button_press_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, paren)
@coroutine_with_priority(100.0)
async def to_code(config):
cg.add_global(button_ns.using)
cg.add_define("USE_BUTTON")

View File

@@ -0,0 +1,28 @@
#pragma once
#include "esphome/components/button/button.h"
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
namespace esphome {
namespace button {
template<typename... Ts> class PressAction : public Action<Ts...> {
public:
explicit PressAction(Button *button) : button_(button) {}
void play(Ts... x) override { this->button_->press(); }
protected:
Button *button_;
};
class ButtonPressTrigger : public Trigger<> {
public:
ButtonPressTrigger(Button *button) {
button->add_on_press_callback([this]() { this->trigger(); });
}
};
} // namespace button
} // namespace esphome

View File

@@ -0,0 +1,28 @@
#include "button.h"
#include "esphome/core/log.h"
namespace esphome {
namespace button {
static const char *const TAG = "button";
Button::Button(const std::string &name) : EntityBase(name) {}
Button::Button() : Button("") {}
void Button::press() {
ESP_LOGD(TAG, "'%s' Pressed.", this->get_name().c_str());
this->press_action();
this->press_callback_.call();
}
void Button::add_on_press_callback(std::function<void()> &&callback) { this->press_callback_.add(std::move(callback)); }
uint32_t Button::hash_base() { return 1495763804UL; }
void Button::set_device_class(const std::string &device_class) { this->device_class_ = device_class; }
std::string Button::get_device_class() {
if (this->device_class_.has_value())
return *this->device_class_;
return "";
}
} // namespace button
} // namespace esphome

View File

@@ -0,0 +1,57 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/helpers.h"
namespace esphome {
namespace button {
#define LOG_BUTTON(prefix, type, obj) \
if ((obj) != nullptr) { \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
if (!(obj)->get_icon().empty()) { \
ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon().c_str()); \
} \
}
/** Base class for all buttons.
*
* A button is just a momentary switch that does not have a state, only a trigger.
*/
class Button : public EntityBase {
public:
explicit Button();
explicit Button(const std::string &name);
/** Press this button. This is called by the front-end.
*
* For implementing buttons, please override press_action.
*/
void press();
/** Set callback for state changes.
*
* @param callback The void() callback.
*/
void add_on_press_callback(std::function<void()> &&callback);
/// Set the Home Assistant device class (see button::device_class).
void set_device_class(const std::string &device_class);
/// Get the device class for this button.
std::string get_device_class();
protected:
/** You should implement this virtual method if you want to create your own button.
*/
virtual void press_action(){};
uint32_t hash_base() override;
CallbackManager<void()> press_callback_{};
optional<std::string> device_class_{};
};
} // namespace button
} // namespace esphome

View File

@@ -85,14 +85,7 @@ void CaptivePortal::start() {
this->dns_server_->start(53, "*", (uint32_t) ip);
this->base_->get_server()->onNotFound([this](AsyncWebServerRequest *req) {
bool not_found = false;
if (!this->active_) {
not_found = true;
} else if (req->host().c_str() == wifi::global_wifi_component->wifi_soft_ap_ip().str()) {
not_found = true;
}
if (not_found) {
if (!this->active_ || req->host().c_str() == wifi::global_wifi_component->wifi_soft_ap_ip().str()) {
req->send(404, "text/html", "File not found");
return;
}

View File

@@ -213,7 +213,7 @@ async def setup_climate_core_(var, config):
if CONF_MODE_COMMAND_TOPIC in config:
cg.add(mqtt_.set_custom_mode_command_topic(config[CONF_MODE_COMMAND_TOPIC]))
if CONF_MODE_STATE_TOPIC in config:
cg.add(mqtt_.set_custom_state_topic(config[CONF_MODE_STATE_TOPIC]))
cg.add(mqtt_.set_custom_mode_state_topic(config[CONF_MODE_STATE_TOPIC]))
if CONF_SWING_MODE_COMMAND_TOPIC in config:
cg.add(

View File

@@ -102,8 +102,6 @@ void CS5460AComponent::hw_init_() {
/* Doesn't reset the register values etc., just restarts the "computation cycle" */
void CS5460AComponent::restart_() {
int cnt;
this->enable();
/* Stop running conversion, wake up if needed */
this->write_byte(CMD_POWER_UP);

View File

@@ -90,6 +90,7 @@ void CSE7766Component::parse_data_() {
uint32_t power_cycle = this->get_24_bit_uint_(17);
uint8_t adj = this->raw_data_[20];
uint32_t cf_pulses = (this->raw_data_[21] << 8) + this->raw_data_[22];
bool power_ok = true;
bool voltage_ok = true;
@@ -127,6 +128,18 @@ void CSE7766Component::parse_data_() {
power = power_calib / float(power_cycle);
this->power_acc_ += power;
this->power_counts_ += 1;
uint32_t difference;
if (this->cf_pulses_last_ == 0)
this->cf_pulses_last_ = cf_pulses;
if (cf_pulses < this->cf_pulses_last_) {
difference = cf_pulses + (0x10000 - this->cf_pulses_last_);
} else {
difference = cf_pulses - this->cf_pulses_last_;
}
this->cf_pulses_last_ = cf_pulses;
this->energy_total_ += difference * float(power_calib) / 1000000.0 / 3600.0;
}
if ((adj & 0x20) == 0x20 && current_ok && voltage_ok && power != 0.0) {
@@ -136,9 +149,9 @@ void CSE7766Component::parse_data_() {
}
}
void CSE7766Component::update() {
float voltage = this->voltage_counts_ > 0 ? this->voltage_acc_ / this->voltage_counts_ : 0.0;
float current = this->current_counts_ > 0 ? this->current_acc_ / this->current_counts_ : 0.0;
float power = this->power_counts_ > 0 ? this->power_acc_ / this->power_counts_ : 0.0;
float voltage = this->voltage_counts_ > 0 ? this->voltage_acc_ / this->voltage_counts_ : 0.0f;
float current = this->current_counts_ > 0 ? this->current_acc_ / this->current_counts_ : 0.0f;
float power = this->power_counts_ > 0 ? this->power_acc_ / this->power_counts_ : 0.0f;
ESP_LOGV(TAG, "Got voltage_acc=%.2f current_acc=%.2f power_acc=%.2f", this->voltage_acc_, this->current_acc_,
this->power_acc_);
@@ -152,6 +165,8 @@ void CSE7766Component::update() {
this->current_sensor_->publish_state(current);
if (this->power_sensor_ != nullptr)
this->power_sensor_->publish_state(power);
if (this->energy_sensor_ != nullptr)
this->energy_sensor_->publish_state(this->energy_total_);
this->voltage_acc_ = 0.0f;
this->current_acc_ = 0.0f;
@@ -172,6 +187,7 @@ void CSE7766Component::dump_config() {
LOG_SENSOR(" ", "Voltage", this->voltage_sensor_);
LOG_SENSOR(" ", "Current", this->current_sensor_);
LOG_SENSOR(" ", "Power", this->power_sensor_);
LOG_SENSOR(" ", "Energy", this->energy_sensor_);
this->check_uart_settings(4800);
}

View File

@@ -12,6 +12,7 @@ class CSE7766Component : public PollingComponent, public uart::UARTDevice {
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
void set_current_sensor(sensor::Sensor *current_sensor) { current_sensor_ = current_sensor; }
void set_power_sensor(sensor::Sensor *power_sensor) { power_sensor_ = power_sensor; }
void set_energy_sensor(sensor::Sensor *energy_sensor) { energy_sensor_ = energy_sensor; }
void loop() override;
float get_setup_priority() const override;
@@ -29,9 +30,12 @@ class CSE7766Component : public PollingComponent, public uart::UARTDevice {
sensor::Sensor *voltage_sensor_{nullptr};
sensor::Sensor *current_sensor_{nullptr};
sensor::Sensor *power_sensor_{nullptr};
sensor::Sensor *energy_sensor_{nullptr};
float voltage_acc_{0.0f};
float current_acc_{0.0f};
float power_acc_{0.0f};
float energy_total_{0.0f};
uint32_t cf_pulses_last_{0};
uint32_t voltage_counts_{0};
uint32_t current_counts_{0};
uint32_t power_counts_{0};

View File

@@ -3,16 +3,20 @@ import esphome.config_validation as cv
from esphome.components import sensor, uart
from esphome.const import (
CONF_CURRENT,
CONF_ENERGY,
CONF_ID,
CONF_POWER,
CONF_VOLTAGE,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_ENERGY,
DEVICE_CLASS_POWER,
DEVICE_CLASS_VOLTAGE,
STATE_CLASS_MEASUREMENT,
STATE_CLASS_TOTAL_INCREASING,
UNIT_VOLT,
UNIT_AMPERE,
UNIT_WATT,
UNIT_WATT_HOURS,
)
DEPENDENCIES = ["uart"]
@@ -44,6 +48,12 @@ CONFIG_SCHEMA = (
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_ENERGY): sensor.sensor_schema(
unit_of_measurement=UNIT_WATT_HOURS,
accuracy_decimals=3,
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_TOTAL_INCREASING,
),
}
)
.extend(cv.polling_component_schema("60s"))
@@ -71,3 +81,7 @@ async def to_code(config):
conf = config[CONF_POWER]
sens = await sensor.new_sensor(conf)
cg.add(var.set_power_sensor(sens))
if CONF_ENERGY in config:
conf = config[CONF_ENERGY]
sens = await sensor.new_sensor(conf)
cg.add(var.set_energy_sensor(sens))

View File

@@ -231,7 +231,7 @@ bool DaikinClimate::on_receive(remote_base::RemoteReceiveData data) {
// frame header
if (byte != 0x27)
return false;
} else if (pos == 3) {
} else if (pos == 3) { // NOLINT(bugprone-branch-clone)
// frame header
if (byte != 0x00)
return false;

View File

@@ -29,12 +29,11 @@ CONFIG_SCHEMA = cv.Schema(
}
)
WIFI_MESSAGE = """
WIFI_CONFIG = """
# Do not forget to add your own wifi configuration before installing this configuration
# wifi:
# ssid: !secret wifi_ssid
# password: !secret wifi_password
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
"""
@@ -55,6 +54,6 @@ def import_config(path: str, name: str, project_name: str, import_url: str) -> N
"esphome": {"name_add_mac_suffix": False},
}
p.write_text(
dump(config) + WIFI_MESSAGE,
dump(config) + WIFI_CONFIG,
encoding="utf8",
)

View File

@@ -41,15 +41,30 @@ EXT1_WAKEUP_MODES = {
"ALL_LOW": esp_sleep_ext1_wakeup_mode_t.ESP_EXT1_WAKEUP_ALL_LOW,
"ANY_HIGH": esp_sleep_ext1_wakeup_mode_t.ESP_EXT1_WAKEUP_ANY_HIGH,
}
WakeupCauseToRunDuration = deep_sleep_ns.struct("WakeupCauseToRunDuration")
CONF_WAKEUP_PIN_MODE = "wakeup_pin_mode"
CONF_ESP32_EXT1_WAKEUP = "esp32_ext1_wakeup"
CONF_TOUCH_WAKEUP = "touch_wakeup"
CONF_DEFAULT = "default"
CONF_GPIO_WAKEUP_REASON = "gpio_wakeup_reason"
CONF_TOUCH_WAKEUP_REASON = "touch_wakeup_reason"
WAKEUP_CAUSES_SCHEMA = cv.Schema(
{
cv.Required(CONF_DEFAULT): cv.positive_time_period_milliseconds,
cv.Optional(CONF_TOUCH_WAKEUP_REASON): cv.positive_time_period_milliseconds,
cv.Optional(CONF_GPIO_WAKEUP_REASON): cv.positive_time_period_milliseconds,
}
)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(DeepSleepComponent),
cv.Optional(CONF_RUN_DURATION): cv.positive_time_period_milliseconds,
cv.Optional(CONF_RUN_DURATION): cv.Any(
cv.All(cv.only_on_esp32, WAKEUP_CAUSES_SCHEMA),
cv.positive_time_period_milliseconds,
),
cv.Optional(CONF_SLEEP_DURATION): cv.positive_time_period_milliseconds,
cv.Optional(CONF_WAKEUP_PIN): cv.All(
cv.only_on_esp32, pins.internal_gpio_input_pin_schema, validate_pin_number
@@ -85,7 +100,28 @@ async def to_code(config):
if CONF_WAKEUP_PIN_MODE in config:
cg.add(var.set_wakeup_pin_mode(config[CONF_WAKEUP_PIN_MODE]))
if CONF_RUN_DURATION in config:
cg.add(var.set_run_duration(config[CONF_RUN_DURATION]))
run_duration_config = config[CONF_RUN_DURATION]
if not isinstance(run_duration_config, dict):
cg.add(var.set_run_duration(config[CONF_RUN_DURATION]))
else:
default_run_duration = run_duration_config[CONF_DEFAULT]
wakeup_cause_to_run_duration = cg.StructInitializer(
WakeupCauseToRunDuration,
("default_cause", default_run_duration),
(
"touch_cause",
run_duration_config.get(
CONF_TOUCH_WAKEUP_REASON, default_run_duration
),
),
(
"gpio_cause",
run_duration_config.get(
CONF_GPIO_WAKEUP_REASON, default_run_duration
),
),
)
cg.add(var.set_run_duration(wakeup_cause_to_run_duration))
if CONF_ESP32_EXT1_WAKEUP in config:
conf = config[CONF_ESP32_EXT1_WAKEUP]

View File

@@ -13,12 +13,35 @@ static const char *const TAG = "deep_sleep";
bool global_has_deep_sleep = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
optional<uint32_t> DeepSleepComponent::get_run_duration_() const {
#ifdef USE_ESP32
if (this->wakeup_cause_to_run_duration_.has_value()) {
esp_sleep_wakeup_cause_t wakeup_cause = esp_sleep_get_wakeup_cause();
switch (wakeup_cause) {
case ESP_SLEEP_WAKEUP_EXT0:
case ESP_SLEEP_WAKEUP_EXT1:
return this->wakeup_cause_to_run_duration_->gpio_cause;
case ESP_SLEEP_WAKEUP_TOUCHPAD:
return this->wakeup_cause_to_run_duration_->touch_cause;
default:
return this->wakeup_cause_to_run_duration_->default_cause;
}
}
#endif
return this->run_duration_;
}
void DeepSleepComponent::setup() {
ESP_LOGCONFIG(TAG, "Setting up Deep Sleep...");
global_has_deep_sleep = true;
if (this->run_duration_.has_value())
this->set_timeout(*this->run_duration_, [this]() { this->begin_sleep(); });
const optional<uint32_t> run_duration = get_run_duration_();
if (run_duration.has_value()) {
ESP_LOGI(TAG, "Scheduling Deep Sleep to start in %u ms", *run_duration);
this->set_timeout(*run_duration, [this]() { this->begin_sleep(); });
} else {
ESP_LOGD(TAG, "Not scheduling Deep Sleep, as no run duration is configured.");
}
}
void DeepSleepComponent::dump_config() {
ESP_LOGCONFIG(TAG, "Setting up Deep Sleep...");
@@ -33,6 +56,11 @@ void DeepSleepComponent::dump_config() {
if (wakeup_pin_ != nullptr) {
LOG_PIN(" Wakeup Pin: ", this->wakeup_pin_);
}
if (this->wakeup_cause_to_run_duration_.has_value()) {
ESP_LOGCONFIG(TAG, " Default Wakeup Run Duration: %u ms", this->wakeup_cause_to_run_duration_->default_cause);
ESP_LOGCONFIG(TAG, " Touch Wakeup Run Duration: %u ms", this->wakeup_cause_to_run_duration_->touch_cause);
ESP_LOGCONFIG(TAG, " GPIO Wakeup Run Duration: %u ms", this->wakeup_cause_to_run_duration_->gpio_cause);
}
#endif
}
void DeepSleepComponent::loop() {
@@ -49,6 +77,9 @@ void DeepSleepComponent::set_wakeup_pin_mode(WakeupPinMode wakeup_pin_mode) {
}
void DeepSleepComponent::set_ext1_wakeup(Ext1Wakeup ext1_wakeup) { this->ext1_wakeup_ = ext1_wakeup; }
void DeepSleepComponent::set_touch_wakeup(bool touch_wakeup) { this->touch_wakeup_ = touch_wakeup; }
void DeepSleepComponent::set_run_duration(WakeupCauseToRunDuration wakeup_cause_to_run_duration) {
wakeup_cause_to_run_duration_ = wakeup_cause_to_run_duration;
}
#endif
void DeepSleepComponent::set_run_duration(uint32_t time_ms) { this->run_duration_ = time_ms; }
void DeepSleepComponent::begin_sleep(bool manual) {
@@ -77,8 +108,8 @@ void DeepSleepComponent::begin_sleep(bool manual) {
if (this->sleep_duration_.has_value())
esp_sleep_enable_timer_wakeup(*this->sleep_duration_);
if (this->wakeup_pin_ != nullptr) {
bool level = this->wakeup_pin_->is_inverted();
if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_INVERT_WAKEUP && !this->wakeup_pin_->digital_read()) {
bool level = !this->wakeup_pin_->is_inverted();
if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_INVERT_WAKEUP && this->wakeup_pin_->digital_read()) {
level = !level;
}
esp_sleep_enable_ext0_wakeup(gpio_num_t(this->wakeup_pin_->get_pin()), level);

View File

@@ -32,6 +32,15 @@ struct Ext1Wakeup {
esp_sleep_ext1_wakeup_mode_t wakeup_mode;
};
struct WakeupCauseToRunDuration {
// Run duration if woken up by timer or any other reason besides those below.
uint32_t default_cause;
// Run duration if woken up by touch pads.
uint32_t touch_cause;
// Run duration if woken up by GPIO pins.
uint32_t gpio_cause;
};
#endif
template<typename... Ts> class EnterDeepSleepAction;
@@ -59,6 +68,11 @@ class DeepSleepComponent : public Component {
void set_ext1_wakeup(Ext1Wakeup ext1_wakeup);
void set_touch_wakeup(bool touch_wakeup);
// Set the duration in ms for how long the code should run before entering
// deep sleep mode, according to the cause the ESP32 has woken.
void set_run_duration(WakeupCauseToRunDuration wakeup_cause_to_run_duration);
#endif
/// Set a duration in ms for how long the code should run before entering deep sleep mode.
void set_run_duration(uint32_t time_ms);
@@ -75,12 +89,17 @@ class DeepSleepComponent : public Component {
void prevent_deep_sleep();
protected:
// Returns nullopt if no run duration is set. Otherwise, returns the run
// duration before entering deep sleep.
optional<uint32_t> get_run_duration_() const;
optional<uint64_t> sleep_duration_;
#ifdef USE_ESP32
InternalGPIOPin *wakeup_pin_;
WakeupPinMode wakeup_pin_mode_{WAKEUP_PIN_MODE_IGNORE};
optional<Ext1Wakeup> ext1_wakeup_;
optional<bool> touch_wakeup_;
optional<WakeupCauseToRunDuration> wakeup_cause_to_run_duration_;
#endif
optional<uint32_t> run_duration_;
bool next_enter_deep_sleep_{false};

View File

@@ -496,7 +496,7 @@ bool Animation::get_pixel(int x, int y) const {
return false;
const uint32_t width_8 = ((this->width_ + 7u) / 8u) * 8u;
const uint32_t frame_index = this->height_ * width_8 * this->current_frame_;
if (frame_index >= this->width_ * this->height_ * this->animation_frame_count_)
if (frame_index >= (uint32_t)(this->width_ * this->height_ * this->animation_frame_count_))
return false;
const uint32_t pos = x + y * width_8 + frame_index;
return progmem_read_byte(this->data_start_ + (pos / 8u)) & (0x80 >> (pos % 8u));
@@ -505,7 +505,7 @@ Color Animation::get_color_pixel(int x, int y) const {
if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_)
return Color::BLACK;
const uint32_t frame_index = this->width_ * this->height_ * this->current_frame_;
if (frame_index >= this->width_ * this->height_ * this->animation_frame_count_)
if (frame_index >= (uint32_t)(this->width_ * this->height_ * this->animation_frame_count_))
return Color::BLACK;
const uint32_t pos = (x + y * this->width_ + frame_index) * 3;
const uint32_t color32 = (progmem_read_byte(this->data_start_ + pos + 2) << 0) |
@@ -517,7 +517,7 @@ Color Animation::get_grayscale_pixel(int x, int y) const {
if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_)
return Color::BLACK;
const uint32_t frame_index = this->width_ * this->height_ * this->current_frame_;
if (frame_index >= this->width_ * this->height_ * this->animation_frame_count_)
if (frame_index >= (uint32_t)(this->width_ * this->height_ * this->animation_frame_count_))
return Color::BLACK;
const uint32_t pos = (x + y * this->width_ + frame_index);
const uint8_t gray = progmem_read_byte(this->data_start_ + pos);

View File

@@ -42,7 +42,7 @@ class ColorUtil {
? esp_scale(((colorcode >> third_bits) & ((1 << second_bits) - 1)), ((1 << second_bits) - 1))
: esp_scale(((colorcode >> 8) & 0xFF), ((1 << second_bits) - 1));
third_color = (right_bit_aligned ? esp_scale(((colorcode >> 0) & 0xFF), ((1 << third_bits) - 1))
third_color = (right_bit_aligned ? esp_scale(((colorcode >> 0) & ((1 << third_bits) - 1)), ((1 << third_bits) - 1))
: esp_scale(((colorcode >> 0) & 0xFF), (1 << third_bits) - 1));
Color color_return;

View File

@@ -5,6 +5,7 @@ from esphome.components import uart
from esphome.const import (
CONF_ID,
CONF_UART_ID,
CONF_RECEIVE_TIMEOUT,
)
CODEOWNERS = ["@glmnet", "@zuidwijk"]
@@ -52,7 +53,12 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_GAS_MBUS_ID, default=1): cv.int_,
cv.Optional(CONF_MAX_TELEGRAM_LENGTH, default=1500): cv.int_,
cv.Optional(CONF_REQUEST_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_REQUEST_INTERVAL): cv.positive_time_period_milliseconds,
cv.Optional(
CONF_REQUEST_INTERVAL, default="0ms"
): cv.positive_time_period_milliseconds,
cv.Optional(
CONF_RECEIVE_TIMEOUT, default="200ms"
): cv.positive_time_period_milliseconds,
}
).extend(uart.UART_DEVICE_SCHEMA),
cv.only_with_arduino,
@@ -70,10 +76,8 @@ async def to_code(config):
if CONF_REQUEST_PIN in config:
request_pin = await cg.gpio_pin_expression(config[CONF_REQUEST_PIN])
cg.add(var.set_request_pin(request_pin))
if CONF_REQUEST_INTERVAL in config:
cg.add(
var.set_request_interval(config[CONF_REQUEST_INTERVAL].total_milliseconds)
)
cg.add(var.set_request_interval(config[CONF_REQUEST_INTERVAL].total_milliseconds))
cg.add(var.set_receive_timeout(config[CONF_RECEIVE_TIMEOUT].total_milliseconds))
cg.add_define("DSMR_GAS_MBUS_ID", config[CONF_GAS_MBUS_ID])

View File

@@ -24,7 +24,7 @@ void Dsmr::loop() {
if (this->decryption_key_.empty()) {
this->receive_telegram_();
} else {
this->receive_encrypted_();
this->receive_encrypted_telegram_();
}
}
}
@@ -57,14 +57,42 @@ bool Dsmr::request_interval_reached_() {
return millis() - this->last_request_time_ > this->request_interval_;
}
bool Dsmr::receive_timeout_reached_() { return millis() - this->last_read_time_ > this->receive_timeout_; }
bool Dsmr::available_within_timeout_() {
uint8_t tries = READ_TIMEOUT_MS / 5;
while (tries--) {
delay(5);
if (this->available()) {
return true;
// Data are available for reading on the UART bus?
// Then we can start reading right away.
if (this->available()) {
this->last_read_time_ = millis();
return true;
}
// When we're not in the process of reading a telegram, then there is
// no need to actively wait for new data to come in.
if (!header_found_) {
return false;
}
// A telegram is being read. The smart meter might not deliver a telegram
// in one go, but instead send it in chunks with small pauses in between.
// When the UART RX buffer cannot hold a full telegram, then make sure
// that the UART read buffer does not overflow while other components
// perform their work in their loop. Do this by not returning control to
// the main loop, until the read timeout is reached.
if (this->parent_->get_rx_buffer_size() < this->max_telegram_len_) {
while (!this->receive_timeout_reached_()) {
delay(5);
if (this->available()) {
this->last_read_time_ = millis();
return true;
}
}
}
// No new data has come in during the read timeout? Then stop reading the
// telegram and start waiting for the next one to arrive.
if (this->receive_timeout_reached_()) {
ESP_LOGW(TAG, "Timeout while reading data for telegram");
this->reset_telegram_();
}
return false;
}
@@ -96,30 +124,31 @@ void Dsmr::stop_requesting_data_() {
}
}
void Dsmr::receive_telegram_() {
while (true) {
if (!this->available()) {
if (!this->header_found_ || !this->available_within_timeout_()) {
return;
}
}
void Dsmr::reset_telegram_() {
this->header_found_ = false;
this->footer_found_ = false;
this->bytes_read_ = 0;
this->crypt_bytes_read_ = 0;
this->crypt_telegram_len_ = 0;
this->last_read_time_ = 0;
}
void Dsmr::receive_telegram_() {
while (this->available_within_timeout_()) {
const char c = this->read();
// Find a new telegram header, i.e. forward slash.
if (c == '/') {
ESP_LOGV(TAG, "Header of telegram found");
this->reset_telegram_();
this->header_found_ = true;
this->footer_found_ = false;
this->telegram_len_ = 0;
}
if (!this->header_found_)
continue;
// Check for buffer overflow.
if (this->telegram_len_ >= this->max_telegram_len_) {
this->header_found_ = false;
this->footer_found_ = false;
if (this->bytes_read_ >= this->max_telegram_len_) {
this->reset_telegram_();
ESP_LOGE(TAG, "Error: telegram larger than buffer (%d bytes)", this->max_telegram_len_);
return;
}
@@ -129,9 +158,9 @@ void Dsmr::receive_telegram_() {
// proper parsing, remove these new line characters.
if (c == '(') {
while (true) {
auto previous_char = this->telegram_[this->telegram_len_ - 1];
auto previous_char = this->telegram_[this->bytes_read_ - 1];
if (previous_char == '\n' || previous_char == '\r') {
this->telegram_len_--;
this->bytes_read_--;
} else {
break;
}
@@ -139,8 +168,8 @@ void Dsmr::receive_telegram_() {
}
// Store the byte in the buffer.
this->telegram_[this->telegram_len_] = c;
this->telegram_len_++;
this->telegram_[this->bytes_read_] = c;
this->bytes_read_++;
// Check for a footer, i.e. exlamation mark, followed by a hex checksum.
if (c == '!') {
@@ -152,28 +181,14 @@ void Dsmr::receive_telegram_() {
if (this->footer_found_ && c == '\n') {
// Parse the telegram and publish sensor values.
this->parse_telegram();
this->header_found_ = false;
this->reset_telegram_();
return;
}
}
}
void Dsmr::receive_encrypted_() {
this->encrypted_telegram_len_ = 0;
size_t packet_size = 0;
while (true) {
if (!this->available()) {
if (!this->header_found_) {
return;
}
if (!this->available_within_timeout_()) {
ESP_LOGW(TAG, "Timeout while reading data for encrypted telegram");
return;
}
}
void Dsmr::receive_encrypted_telegram_() {
while (this->available_within_timeout_()) {
const char c = this->read();
// Find a new telegram start byte.
@@ -182,50 +197,58 @@ void Dsmr::receive_encrypted_() {
continue;
}
ESP_LOGV(TAG, "Start byte 0xDB of encrypted telegram found");
this->reset_telegram_();
this->header_found_ = true;
}
// Check for buffer overflow.
if (this->encrypted_telegram_len_ >= this->max_telegram_len_) {
this->header_found_ = false;
if (this->crypt_bytes_read_ >= this->max_telegram_len_) {
this->reset_telegram_();
ESP_LOGE(TAG, "Error: encrypted telegram larger than buffer (%d bytes)", this->max_telegram_len_);
return;
}
this->encrypted_telegram_[this->encrypted_telegram_len_++] = c;
// Store the byte in the buffer.
this->crypt_telegram_[this->crypt_bytes_read_] = c;
this->crypt_bytes_read_++;
if (packet_size == 0 && this->encrypted_telegram_len_ > 20) {
// Read the length of the incoming encrypted telegram.
if (this->crypt_telegram_len_ == 0 && this->crypt_bytes_read_ > 20) {
// Complete header + data bytes
packet_size = 13 + (this->encrypted_telegram_[11] << 8 | this->encrypted_telegram_[12]);
ESP_LOGV(TAG, "Encrypted telegram size: %d bytes", packet_size);
this->crypt_telegram_len_ = 13 + (this->crypt_telegram_[11] << 8 | this->crypt_telegram_[12]);
ESP_LOGV(TAG, "Encrypted telegram length: %d bytes", this->crypt_telegram_len_);
}
if (this->encrypted_telegram_len_ == packet_size && packet_size > 0) {
ESP_LOGV(TAG, "End of encrypted telegram found");
GCM<AES128> *gcmaes128{new GCM<AES128>()};
gcmaes128->setKey(this->decryption_key_.data(), gcmaes128->keySize());
// the iv is 8 bytes of the system title + 4 bytes frame counter
// system title is at byte 2 and frame counter at byte 15
for (int i = 10; i < 14; i++)
this->encrypted_telegram_[i] = this->encrypted_telegram_[i + 4];
constexpr uint16_t iv_size{12};
gcmaes128->setIV(&this->encrypted_telegram_[2], iv_size);
gcmaes128->decrypt(reinterpret_cast<uint8_t *>(this->telegram_),
// the ciphertext start at byte 18
&this->encrypted_telegram_[18],
// cipher size
this->encrypted_telegram_len_ - 17);
delete gcmaes128; // NOLINT(cppcoreguidelines-owning-memory)
this->telegram_len_ = strnlen(this->telegram_, this->max_telegram_len_);
ESP_LOGV(TAG, "Decrypted telegram size: %d bytes", this->telegram_len_);
ESP_LOGVV(TAG, "Decrypted telegram: %s", this->telegram_);
this->parse_telegram();
this->header_found_ = false;
this->telegram_len_ = 0;
return;
// Check for the end of the encrypted telegram.
if (this->crypt_telegram_len_ == 0 || this->crypt_bytes_read_ != this->crypt_telegram_len_) {
continue;
}
ESP_LOGV(TAG, "End of encrypted telegram found");
// Decrypt the encrypted telegram.
GCM<AES128> *gcmaes128{new GCM<AES128>()};
gcmaes128->setKey(this->decryption_key_.data(), gcmaes128->keySize());
// the iv is 8 bytes of the system title + 4 bytes frame counter
// system title is at byte 2 and frame counter at byte 15
for (int i = 10; i < 14; i++)
this->crypt_telegram_[i] = this->crypt_telegram_[i + 4];
constexpr uint16_t iv_size{12};
gcmaes128->setIV(&this->crypt_telegram_[2], iv_size);
gcmaes128->decrypt(reinterpret_cast<uint8_t *>(this->telegram_),
// the ciphertext start at byte 18
&this->crypt_telegram_[18],
// cipher size
this->crypt_bytes_read_ - 17);
delete gcmaes128; // NOLINT(cppcoreguidelines-owning-memory)
this->bytes_read_ = strnlen(this->telegram_, this->max_telegram_len_);
ESP_LOGV(TAG, "Decrypted telegram size: %d bytes", this->bytes_read_);
ESP_LOGVV(TAG, "Decrypted telegram: %s", this->telegram_);
// Parse the decrypted telegram and publish sensor values.
this->parse_telegram();
this->reset_telegram_();
return;
}
}
@@ -234,11 +257,11 @@ bool Dsmr::parse_telegram() {
ESP_LOGV(TAG, "Trying to parse telegram");
this->stop_requesting_data_();
::dsmr::ParseResult<void> res =
::dsmr::P1Parser::parse(&data, this->telegram_, this->telegram_len_, false,
::dsmr::P1Parser::parse(&data, this->telegram_, this->bytes_read_, false,
this->crc_check_); // Parse telegram according to data definition. Ignore unknown values.
if (res.err) {
// Parsing error, show it
auto err_str = res.fullError(this->telegram_, this->telegram_ + this->telegram_len_);
auto err_str = res.fullError(this->telegram_, this->telegram_ + this->bytes_read_);
ESP_LOGE(TAG, "%s", err_str.c_str());
return false;
} else {
@@ -251,7 +274,7 @@ bool Dsmr::parse_telegram() {
void Dsmr::dump_config() {
ESP_LOGCONFIG(TAG, "DSMR:");
ESP_LOGCONFIG(TAG, " Max telegram length: %d", this->max_telegram_len_);
ESP_LOGCONFIG(TAG, " Receive timeout: %.1fs", this->receive_timeout_ / 1e3f);
if (this->request_pin_ != nullptr) {
LOG_PIN(" Request Pin: ", this->request_pin_);
}
@@ -270,9 +293,9 @@ void Dsmr::set_decryption_key(const std::string &decryption_key) {
if (decryption_key.length() == 0) {
ESP_LOGI(TAG, "Disabling decryption");
this->decryption_key_.clear();
if (this->encrypted_telegram_ != nullptr) {
delete[] this->encrypted_telegram_;
this->encrypted_telegram_ = nullptr;
if (this->crypt_telegram_ != nullptr) {
delete[] this->crypt_telegram_;
this->crypt_telegram_ = nullptr;
}
return;
}
@@ -293,13 +316,11 @@ void Dsmr::set_decryption_key(const std::string &decryption_key) {
this->decryption_key_.push_back(std::strtoul(temp, nullptr, 16));
}
if (this->encrypted_telegram_ == nullptr) {
this->encrypted_telegram_ = new uint8_t[this->max_telegram_len_]; // NOLINT
if (this->crypt_telegram_ == nullptr) {
this->crypt_telegram_ = new uint8_t[this->max_telegram_len_]; // NOLINT
}
}
void Dsmr::set_max_telegram_length(size_t length) { max_telegram_len_ = length; }
} // namespace dsmr
} // namespace esphome

View File

@@ -16,8 +16,6 @@
namespace esphome {
namespace dsmr {
static constexpr uint32_t READ_TIMEOUT_MS = 200;
using namespace ::dsmr::fields;
// DSMR_**_LIST generated by ESPHome and written in esphome/core/defines
@@ -71,11 +69,10 @@ class Dsmr : public Component, public uart::UARTDevice {
void dump_config() override;
void set_decryption_key(const std::string &decryption_key);
void set_max_telegram_length(size_t length);
void set_max_telegram_length(size_t length) { this->max_telegram_len_ = length; }
void set_request_pin(GPIOPin *request_pin) { this->request_pin_ = request_pin; }
void set_request_interval(uint32_t interval) { this->request_interval_ = interval; }
void set_receive_timeout(uint32_t timeout) { this->receive_timeout_ = timeout; }
// Sensor setters
#define DSMR_SET_SENSOR(s) \
@@ -88,7 +85,8 @@ class Dsmr : public Component, public uart::UARTDevice {
protected:
void receive_telegram_();
void receive_encrypted_();
void receive_encrypted_telegram_();
void reset_telegram_();
/// Wait for UART data to become available within the read timeout.
///
@@ -101,24 +99,26 @@ class Dsmr : public Component, public uart::UARTDevice {
/// lost in the process.
bool available_within_timeout_();
// Data request
// Request telegram
uint32_t request_interval_;
bool request_interval_reached_();
GPIOPin *request_pin_{nullptr};
uint32_t request_interval_{0};
uint32_t last_request_time_{0};
bool requesting_data_{false};
bool ready_to_request_data_();
bool request_interval_reached_();
void start_requesting_data_();
void stop_requesting_data_();
// Telegram buffer
// Read telegram
uint32_t receive_timeout_;
bool receive_timeout_reached_();
size_t max_telegram_len_;
char *telegram_{nullptr};
int telegram_len_{0};
uint8_t *encrypted_telegram_{nullptr};
int encrypted_telegram_len_{0};
// Serial parser
size_t bytes_read_{0};
uint8_t *crypt_telegram_{nullptr};
size_t crypt_telegram_len_{0};
size_t crypt_bytes_read_{0};
uint32_t last_read_time_{0};
bool header_found_{false};
bool footer_found_{false};

View File

@@ -23,22 +23,24 @@ void DutyCycleSensor::dump_config() {
}
void DutyCycleSensor::update() {
const uint32_t now = micros();
const uint32_t last_interrupt = this->store_.last_interrupt; // Read the measurement taken by the interrupt
uint32_t on_time = this->store_.on_time;
this->store_.on_time = 0; // Start new measurement, exactly aligned with the micros() reading
this->store_.last_interrupt = now;
if (this->last_update_ != 0) {
const bool level = this->store_.last_level;
const uint32_t last_interrupt = this->store_.last_interrupt;
uint32_t on_time = this->store_.on_time;
if (level)
on_time += now - last_interrupt;
const float total_time = float(now - this->last_update_);
const float value = (on_time / total_time) * 100.0f;
const float value = (on_time * 100.0f) / total_time;
ESP_LOGD(TAG, "'%s' Got duty cycle=%.1f%%", this->get_name().c_str(), value);
this->publish_state(value);
}
this->store_.on_time = 0;
this->store_.last_interrupt = now;
this->last_update_ = now;
}

View File

@@ -311,9 +311,16 @@ async def to_code(config):
)
add_idf_sdkconfig_option("CONFIG_COMPILER_OPTIMIZATION_DEFAULT", False)
add_idf_sdkconfig_option("CONFIG_COMPILER_OPTIMIZATION_SIZE", True)
# Increase freertos tick speed from 100Hz to 1kHz so that delay() resolution is 1ms
add_idf_sdkconfig_option("CONFIG_FREERTOS_HZ", 1000)
# Setup watchdog
add_idf_sdkconfig_option("CONFIG_ESP_TASK_WDT", True)
add_idf_sdkconfig_option("CONFIG_ESP_TASK_WDT_PANIC", True)
add_idf_sdkconfig_option("CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0", False)
add_idf_sdkconfig_option("CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1", False)
cg.add_platformio_option("board_build.partitions", "partitions.csv")
for name, value in conf[CONF_SDKCONFIG_OPTIONS].items():

View File

@@ -6,12 +6,17 @@
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_idf_version.h>
#include <esp_task_wdt.h>
#include <soc/rtc.h>
#if ESP_IDF_VERSION_MAJOR >= 4
#include <hal/cpu_hal.h>
#endif
#ifdef USE_ARDUINO
#include <esp32-hal.h>
#endif
void setup();
void loop();
@@ -29,24 +34,24 @@ void arch_restart() {
yield();
}
}
void IRAM_ATTR HOT arch_feed_wdt() {
#ifdef USE_ARDUINO
#if CONFIG_ARDUINO_RUNNING_CORE == 0
#ifdef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0
// ESP32 uses "Task Watchdog" which is hooked to the FreeRTOS idle task.
// To cause the Watchdog to be triggered we need to put the current task
// to sleep to get the idle task scheduled.
delay(1);
#endif
#endif
#endif // USE_ARDUINO
#ifdef USE_ESP_IDF
#ifdef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0
delay(1);
void arch_init() {
// Enable the task watchdog only on the loop task (from which we're currently running)
#if defined(USE_ESP_IDF)
esp_task_wdt_add(nullptr);
// Idle task watchdog is disabled on ESP-IDF
#elif defined(USE_ARDUINO)
enableLoopWDT();
// Disable idle task watchdog on the core we're using (Arduino pins the task to a core)
#if defined(CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0) && CONFIG_ARDUINO_RUNNING_CORE == 0
disableCore0WDT();
#endif
#if defined(CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1) && CONFIG_ARDUINO_RUNNING_CORE == 1
disableCore1WDT();
#endif
#endif
#endif // USE_ESP_IDF
}
void IRAM_ATTR HOT arch_feed_wdt() { esp_task_wdt_reset(); }
uint8_t progmem_read_byte(const uint8_t *addr) { return *addr; }
uint32_t arch_get_cpu_cycle_count() {

View File

@@ -18,7 +18,7 @@ _ESP_SDIO_PINS = {
11: "Flash Command",
}
_ESP32_STRAPPING_PINS = {0, 2, 4, 15}
_ESP32_STRAPPING_PINS = {0, 2, 4, 12, 15}
_LOGGER = logging.getLogger(__name__)

View File

@@ -76,6 +76,7 @@ class ESP32Preferences : public ESPPreferences {
uint32_t current_offset = 0;
void open() {
nvs_flash_init();
esp_err_t err = nvs_open("esphome", NVS_READWRITE, &nvs_handle);
if (err == 0)
return;

View File

@@ -483,6 +483,7 @@ optional<ESPBLEiBeacon> ESPBLEiBeacon::from_manufacturer_data(const ServiceData
}
void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &param) {
this->scan_result_ = param;
for (uint8_t i = 0; i < ESP_BD_ADDR_LEN; i++)
this->address_[i] = param.bda[i];
this->address_type_ = param.ble_addr_type;
@@ -524,7 +525,7 @@ void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_e
ESP_LOGVV(TAG, " Service UUID: %s", uuid.to_string().c_str());
}
for (auto &data : this->manufacturer_datas_) {
ESP_LOGVV(TAG, " Manufacturer data: %s", hexencode(data.data).c_str());
ESP_LOGVV(TAG, " Manufacturer data: %s", format_hex_pretty(data.data).c_str());
if (this->get_ibeacon().has_value()) {
auto ibeacon = this->get_ibeacon().value();
ESP_LOGVV(TAG, " iBeacon data:");
@@ -537,10 +538,10 @@ void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_e
for (auto &data : this->service_datas_) {
ESP_LOGVV(TAG, " Service data:");
ESP_LOGVV(TAG, " UUID: %s", data.uuid.to_string().c_str());
ESP_LOGVV(TAG, " Data: %s", hexencode(data.data).c_str());
ESP_LOGVV(TAG, " Data: %s", format_hex_pretty(data.data).c_str());
}
ESP_LOGVV(TAG, "Adv data: %s", hexencode(param.ble_adv, param.adv_data_len + param.scan_rsp_len).c_str());
ESP_LOGVV(TAG, "Adv data: %s", format_hex_pretty(param.ble_adv, param.adv_data_len + param.scan_rsp_len).c_str());
#endif
}
void ESPBTDevice::parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &param) {

View File

@@ -97,6 +97,8 @@ class ESPBTDevice {
const std::vector<ServiceData> &get_service_datas() const { return service_datas_; }
const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &get_scan_result() const { return scan_result_; }
optional<ESPBLEiBeacon> get_ibeacon() const {
for (auto &it : this->manufacturer_datas_) {
auto res = ESPBLEiBeacon::from_manufacturer_data(it);
@@ -121,6 +123,7 @@ class ESPBTDevice {
std::vector<ESPBTUUID> service_uuids_;
std::vector<ServiceData> manufacturer_datas_{};
std::vector<ServiceData> service_datas_{};
esp_ble_gap_cb_param_t::ble_scan_result_evt_param scan_result_{};
};
class ESP32BLETracker;

View File

@@ -57,6 +57,9 @@ CONF_IDLE_FRAMERATE = "idle_framerate"
CONF_JPEG_QUALITY = "jpeg_quality"
CONF_VERTICAL_FLIP = "vertical_flip"
CONF_HORIZONTAL_MIRROR = "horizontal_mirror"
CONF_AEC2 = "aec2"
CONF_AE_LEVEL = "ae_level"
CONF_AEC_VALUE = "aec_value"
CONF_SATURATION = "saturation"
CONF_TEST_PATTERN = "test_pattern"
@@ -102,6 +105,9 @@ CONFIG_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
cv.Optional(CONF_SATURATION, default=0): camera_range_param,
cv.Optional(CONF_VERTICAL_FLIP, default=True): cv.boolean,
cv.Optional(CONF_HORIZONTAL_MIRROR, default=True): cv.boolean,
cv.Optional(CONF_AEC2, default=False): cv.boolean,
cv.Optional(CONF_AE_LEVEL, default=0): camera_range_param,
cv.Optional(CONF_AEC_VALUE, default=300): cv.int_range(min=0, max=1200),
cv.Optional(CONF_TEST_PATTERN, default=False): cv.boolean,
}
).extend(cv.COMPONENT_SCHEMA)
@@ -116,6 +122,9 @@ SETTERS = {
CONF_JPEG_QUALITY: "set_jpeg_quality",
CONF_VERTICAL_FLIP: "set_vertical_flip",
CONF_HORIZONTAL_MIRROR: "set_horizontal_mirror",
CONF_AEC2: "set_aec2",
CONF_AE_LEVEL: "set_ae_level",
CONF_AEC_VALUE: "set_aec_value",
CONF_CONTRAST: "set_contrast",
CONF_BRIGHTNESS: "set_brightness",
CONF_SATURATION: "set_saturation",

View File

@@ -26,6 +26,9 @@ void ESP32Camera::setup() {
sensor_t *s = esp_camera_sensor_get();
s->set_vflip(s, this->vertical_flip_);
s->set_hmirror(s, this->horizontal_mirror_);
s->set_aec2(s, this->aec2_); // 0 = disable , 1 = enable
s->set_ae_level(s, this->ae_level_); // -2 to 2
s->set_aec_value(s, this->aec_value_); // 0 to 1200
s->set_contrast(s, this->contrast_);
s->set_brightness(s, this->brightness_);
s->set_saturation(s, this->saturation_);
@@ -111,9 +114,9 @@ void ESP32Camera::dump_config() {
// ESP_LOGCONFIG(TAG, " Auto White Balance: %u", st.awb);
// ESP_LOGCONFIG(TAG, " Auto White Balance Gain: %u", st.awb_gain);
// ESP_LOGCONFIG(TAG, " Auto Exposure Control: %u", st.aec);
// ESP_LOGCONFIG(TAG, " Auto Exposure Control 2: %u", st.aec2);
// ESP_LOGCONFIG(TAG, " Auto Exposure Level: %d", st.ae_level);
// ESP_LOGCONFIG(TAG, " Auto Exposure Value: %u", st.aec_value);
ESP_LOGCONFIG(TAG, " Auto Exposure Control 2: %u", st.aec2);
ESP_LOGCONFIG(TAG, " Auto Exposure Level: %d", st.ae_level);
ESP_LOGCONFIG(TAG, " Auto Exposure Value: %u", st.aec_value);
// ESP_LOGCONFIG(TAG, " AGC: %u", st.agc);
// ESP_LOGCONFIG(TAG, " AGC Gain: %u", st.agc_gain);
// ESP_LOGCONFIG(TAG, " Gain Ceiling: %u", st.gainceiling);
@@ -250,6 +253,9 @@ void ESP32Camera::add_image_callback(std::function<void(std::shared_ptr<CameraIm
}
void ESP32Camera::set_vertical_flip(bool vertical_flip) { this->vertical_flip_ = vertical_flip; }
void ESP32Camera::set_horizontal_mirror(bool horizontal_mirror) { this->horizontal_mirror_ = horizontal_mirror; }
void ESP32Camera::set_aec2(bool aec2) { this->aec2_ = aec2; }
void ESP32Camera::set_ae_level(int ae_level) { this->ae_level_ = ae_level; }
void ESP32Camera::set_aec_value(uint32_t aec_value) { this->aec_value_ = aec_value; }
void ESP32Camera::set_contrast(int contrast) { this->contrast_ = contrast; }
void ESP32Camera::set_brightness(int brightness) { this->brightness_ = brightness; }
void ESP32Camera::set_saturation(int saturation) { this->saturation_ = saturation; }

View File

@@ -67,6 +67,9 @@ class ESP32Camera : public Component, public EntityBase {
void set_power_down_pin(uint8_t pin);
void set_vertical_flip(bool vertical_flip);
void set_horizontal_mirror(bool horizontal_mirror);
void set_aec2(bool aec2);
void set_ae_level(int ae_level);
void set_aec_value(uint32_t aec_value);
void set_contrast(int contrast);
void set_brightness(int brightness);
void set_saturation(int saturation);
@@ -91,6 +94,9 @@ class ESP32Camera : public Component, public EntityBase {
camera_config_t config_{};
bool vertical_flip_{true};
bool horizontal_mirror_{true};
bool aec2_{false};
int ae_level_{0};
uint32_t aec_value_{300};
int contrast_{0};
int brightness_{0};
int saturation_{0};

View File

@@ -219,7 +219,7 @@ void ESP32ImprovComponent::dump_config() {
void ESP32ImprovComponent::process_incoming_data_() {
uint8_t length = this->incoming_data_[1];
ESP_LOGD(TAG, "Processing bytes - %s", hexencode(this->incoming_data_).c_str());
ESP_LOGD(TAG, "Processing bytes - %s", format_hex_pretty(this->incoming_data_).c_str());
if (this->incoming_data_.size() - 3 == length) {
this->set_error_(improv::ERROR_NONE);
improv::ImprovCommand command = improv::parse_improv_data(this->incoming_data_);

View File

@@ -20,6 +20,7 @@ void arch_restart() {
yield();
}
}
void arch_init() {}
void IRAM_ATTR HOT arch_feed_wdt() {
ESP.wdtFeed(); // NOLINT(readability-static-accessed-through-instance)
}
@@ -27,7 +28,7 @@ void IRAM_ATTR HOT arch_feed_wdt() {
uint8_t progmem_read_byte(const uint8_t *addr) {
return pgm_read_byte(addr); // NOLINT
}
uint32_t arch_get_cpu_cycle_count() {
uint32_t IRAM_ATTR HOT arch_get_cpu_cycle_count() {
return ESP.getCycleCount(); // NOLINT(readability-static-accessed-through-instance)
}
uint32_t arch_get_cpu_freq_hz() { return F_CPU; }

View File

@@ -9,7 +9,7 @@ namespace esp8266 {
static const char *const TAG = "esp8266";
static int IRAM_ATTR flags_to_mode(gpio::Flags flags, uint8_t pin) {
if (flags == gpio::FLAG_INPUT) {
if (flags == gpio::FLAG_INPUT) { // NOLINT(bugprone-branch-clone)
return INPUT;
} else if (flags == gpio::FLAG_OUTPUT) {
return OUTPUT;

View File

@@ -55,7 +55,7 @@ static inline bool esp_rtc_user_mem_write(uint32_t index, uint32_t value) {
extern "C" uint32_t _SPIFFS_end; // NOLINT
static const uint32_t get_esp8266_flash_sector() {
static uint32_t get_esp8266_flash_sector() {
union {
uint32_t *ptr;
uint32_t uint;
@@ -63,7 +63,7 @@ static const uint32_t get_esp8266_flash_sector() {
data.ptr = &_SPIFFS_end;
return (data.uint - 0x40200000) / SPI_FLASH_SEC_SIZE;
}
static const uint32_t get_esp8266_flash_address() { return get_esp8266_flash_sector() * SPI_FLASH_SEC_SIZE; }
static uint32_t get_esp8266_flash_address() { return get_esp8266_flash_sector() * SPI_FLASH_SEC_SIZE; }
template<class It> uint32_t calculate_crc(It first, It last, uint32_t type) {
uint32_t crc = type;

View File

@@ -184,7 +184,9 @@ void EthernetComponent::start_connect_() {
}
err = tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_ETH);
ESPHL_ERROR_CHECK(err, "DHCPC stop error");
if (err != ESP_ERR_TCPIP_ADAPTER_DHCP_ALREADY_STOPPED) {
ESPHL_ERROR_CHECK(err, "DHCPC stop error");
}
err = tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_ETH, &info);
ESPHL_ERROR_CHECK(err, "DHCPC set IP info error");

View File

@@ -12,6 +12,8 @@ from esphome.const import (
CONF_TYPE,
CONF_EXTERNAL_COMPONENTS,
CONF_PATH,
CONF_USERNAME,
CONF_PASSWORD,
)
from esphome.core import CORE
from esphome import git, loader
@@ -27,6 +29,8 @@ TYPE_LOCAL = "local"
GIT_SCHEMA = {
cv.Required(CONF_URL): cv.url,
cv.Optional(CONF_REF): cv.git_ref,
cv.Optional(CONF_USERNAME): cv.string,
cv.Optional(CONF_PASSWORD): cv.string,
}
LOCAL_SCHEMA = {
cv.Required(CONF_PATH): cv.directory,
@@ -99,6 +103,8 @@ def _process_git_config(config: dict, refresh) -> str:
ref=config.get(CONF_REF),
refresh=refresh,
domain=DOMAIN,
username=config.get(CONF_USERNAME),
password=config.get(CONF_PASSWORD),
)
if (repo_dir / "esphome" / "components").is_dir():

View File

@@ -32,7 +32,7 @@ void EZOSensor::update() {
}
void EZOSensor::loop() {
uint8_t buf[20];
uint8_t buf[21];
if (!(this->state_ & EZO_STATE_WAIT)) {
if (this->state_ & EZO_STATE_SEND_TEMP) {
int len = sprintf((char *) buf, "T,%0.3f", this->tempcomp_);
@@ -74,7 +74,12 @@ void EZOSensor::loop() {
if (buf[0] != 1)
return;
float val = parse_number<float>((char *) &buf[1], sizeof(buf) - 1).value_or(0);
// some sensors return multiple comma-separated values, terminate string after first one
for (size_t i = 1; i < sizeof(buf) - 1; i++)
if (buf[i] == ',')
buf[i] = '\0';
float val = parse_number<float>((char *) &buf[1]).value_or(0);
this->publish_state(val);
}

View File

@@ -86,7 +86,7 @@ void Graph::draw(DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Colo
// Look back in trace data to best-fit into local range
float mx = NAN;
float mn = NAN;
for (int16_t i = 0; i < this->width_; i++) {
for (uint32_t i = 0; i < this->width_; i++) {
for (auto *trace : traces_) {
float v = trace->get_tracedata()->get_value(i);
if (!std::isnan(v)) {
@@ -132,7 +132,7 @@ void Graph::draw(DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Colo
if (!std::isnan(this->gridspacing_y_)) {
for (int y = yn; y <= ym; y++) {
int16_t py = (int16_t) roundf((this->height_ - 1) * (1.0 - (float) (y - yn) / (ym - yn)));
for (int x = 0; x < this->width_; x += 2) {
for (uint32_t x = 0; x < this->width_; x += 2) {
buff->draw_pixel_at(x_offset + x, y_offset + py, color);
}
}
@@ -147,7 +147,7 @@ void Graph::draw(DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Colo
ESP_LOGW(TAG, "Graphing reducing x-scale to prevent too many gridlines");
}
for (int i = 0; i <= n; i++) {
for (int y = 0; y < this->height_; y += 2) {
for (uint32_t y = 0; y < this->height_; y += 2) {
buff->draw_pixel_at(x_offset + i * (this->width_ - 1) / n, y_offset + y, color);
}
}
@@ -158,14 +158,14 @@ void Graph::draw(DisplayBuffer *buff, uint16_t x_offset, uint16_t y_offset, Colo
for (auto *trace : traces_) {
Color c = trace->get_line_color();
uint16_t thick = trace->get_line_thickness();
for (int16_t i = 0; i < this->width_; i++) {
for (uint32_t i = 0; i < this->width_; i++) {
float v = (trace->get_tracedata()->get_value(i) - ymin) / yrange;
if (!std::isnan(v) && (thick > 0)) {
int16_t x = this->width_ - 1 - i;
uint8_t b = (i % (thick * LineType::PATTERN_LENGTH)) / thick;
if (((uint8_t) trace->get_line_type() & (1 << b)) == (1 << b)) {
int16_t y = (int16_t) roundf((this->height_ - 1) * (1.0 - v)) - thick / 2;
for (int16_t t = 0; t < thick; t++) {
for (uint16_t t = 0; t < thick; t++) {
buff->draw_pixel_at(x_offset + x, y_offset + y + t, c);
}
}
@@ -179,8 +179,8 @@ void GraphLegend::init(Graph *g) {
parent_ = g;
// Determine maximum expected text and value width / height
int txtw = 0, txtos = 0, txtbl = 0, txth = 0;
int valw = 0, valos = 0, valbl = 0, valh = 0;
int txtw = 0, txth = 0;
int valw = 0, valh = 0;
int lt = 0;
for (auto *trace : g->traces_) {
std::string txtstr = trace->get_name();
@@ -320,7 +320,7 @@ void Graph::draw_legend(display::DisplayBuffer *buff, uint16_t x_offset, uint16_
if (legend_->lines_) {
uint16_t thick = trace->get_line_thickness();
for (int16_t i = 0; i < legend_->x0_ * 4 / 3; i++) {
for (int i = 0; i < legend_->x0_ * 4 / 3; i++) {
uint8_t b = (i % (thick * LineType::PATTERN_LENGTH)) / thick;
if (((uint8_t) trace->get_line_type() & (1 << b)) == (1 << b)) {
buff->vertical_line(x - legend_->x0_ * 2 / 3 + i, y + legend_->yl_ - thick / 2, thick,

View File

@@ -0,0 +1,69 @@
#include "growatt_solar.h"
#include "esphome/core/log.h"
namespace esphome {
namespace growatt_solar {
static const char *const TAG = "growatt_solar";
static const uint8_t MODBUS_CMD_READ_IN_REGISTERS = 0x04;
static const uint8_t MODBUS_REGISTER_COUNT = 33;
void GrowattSolar::update() { this->send(MODBUS_CMD_READ_IN_REGISTERS, 0, MODBUS_REGISTER_COUNT); }
void GrowattSolar::on_modbus_data(const std::vector<uint8_t> &data) {
auto publish_1_reg_sensor_state = [&](sensor::Sensor *sensor, size_t i, float unit) -> void {
if (sensor == nullptr)
return;
float value = encode_uint16(data[i * 2], data[i * 2 + 1]) * unit;
sensor->publish_state(value);
};
auto publish_2_reg_sensor_state = [&](sensor::Sensor *sensor, size_t reg1, size_t reg2, float unit) -> void {
float value = ((encode_uint16(data[reg1 * 2], data[reg1 * 2 + 1]) << 16) +
encode_uint16(data[reg2 * 2], data[reg2 * 2 + 1])) *
unit;
if (sensor != nullptr)
sensor->publish_state(value);
};
publish_1_reg_sensor_state(this->inverter_status_, 0, 1);
publish_2_reg_sensor_state(this->pv_active_power_sensor_, 1, 2, ONE_DEC_UNIT);
publish_1_reg_sensor_state(this->pvs_[0].voltage_sensor_, 3, ONE_DEC_UNIT);
publish_1_reg_sensor_state(this->pvs_[0].current_sensor_, 4, ONE_DEC_UNIT);
publish_2_reg_sensor_state(this->pvs_[0].active_power_sensor_, 5, 6, ONE_DEC_UNIT);
publish_1_reg_sensor_state(this->pvs_[1].voltage_sensor_, 7, ONE_DEC_UNIT);
publish_1_reg_sensor_state(this->pvs_[1].current_sensor_, 8, ONE_DEC_UNIT);
publish_2_reg_sensor_state(this->pvs_[1].active_power_sensor_, 9, 10, ONE_DEC_UNIT);
publish_2_reg_sensor_state(this->grid_active_power_sensor_, 11, 12, ONE_DEC_UNIT);
publish_1_reg_sensor_state(this->grid_frequency_sensor_, 13, TWO_DEC_UNIT);
publish_1_reg_sensor_state(this->phases_[0].voltage_sensor_, 14, ONE_DEC_UNIT);
publish_1_reg_sensor_state(this->phases_[0].current_sensor_, 15, ONE_DEC_UNIT);
publish_2_reg_sensor_state(this->phases_[0].active_power_sensor_, 16, 17, ONE_DEC_UNIT);
publish_1_reg_sensor_state(this->phases_[1].voltage_sensor_, 18, ONE_DEC_UNIT);
publish_1_reg_sensor_state(this->phases_[1].current_sensor_, 19, ONE_DEC_UNIT);
publish_2_reg_sensor_state(this->phases_[1].active_power_sensor_, 20, 21, ONE_DEC_UNIT);
publish_1_reg_sensor_state(this->phases_[2].voltage_sensor_, 22, ONE_DEC_UNIT);
publish_1_reg_sensor_state(this->phases_[2].current_sensor_, 23, ONE_DEC_UNIT);
publish_2_reg_sensor_state(this->phases_[2].active_power_sensor_, 24, 25, ONE_DEC_UNIT);
publish_2_reg_sensor_state(this->today_production_, 26, 27, ONE_DEC_UNIT);
publish_2_reg_sensor_state(this->total_energy_production_, 28, 29, ONE_DEC_UNIT);
publish_1_reg_sensor_state(this->inverter_module_temp_, 32, ONE_DEC_UNIT);
}
void GrowattSolar::dump_config() {
ESP_LOGCONFIG(TAG, "GROWATT Solar:");
ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_);
}
} // namespace growatt_solar
} // namespace esphome

View File

@@ -0,0 +1,73 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/modbus/modbus.h"
namespace esphome {
namespace growatt_solar {
static const float TWO_DEC_UNIT = 0.01;
static const float ONE_DEC_UNIT = 0.1;
class GrowattSolar : public PollingComponent, public modbus::ModbusDevice {
public:
void update() override;
void on_modbus_data(const std::vector<uint8_t> &data) override;
void dump_config() override;
void set_inverter_status_sensor(sensor::Sensor *sensor) { this->inverter_status_ = sensor; }
void set_grid_frequency_sensor(sensor::Sensor *sensor) { this->grid_frequency_sensor_ = sensor; }
void set_grid_active_power_sensor(sensor::Sensor *sensor) { this->grid_active_power_sensor_ = sensor; }
void set_pv_active_power_sensor(sensor::Sensor *sensor) { this->pv_active_power_sensor_ = sensor; }
void set_today_production_sensor(sensor::Sensor *sensor) { this->today_production_ = sensor; }
void set_total_energy_production_sensor(sensor::Sensor *sensor) { this->total_energy_production_ = sensor; }
void set_inverter_module_temp_sensor(sensor::Sensor *sensor) { this->inverter_module_temp_ = sensor; }
void set_voltage_sensor(uint8_t phase, sensor::Sensor *voltage_sensor) {
this->phases_[phase].voltage_sensor_ = voltage_sensor;
}
void set_current_sensor(uint8_t phase, sensor::Sensor *current_sensor) {
this->phases_[phase].current_sensor_ = current_sensor;
}
void set_active_power_sensor(uint8_t phase, sensor::Sensor *active_power_sensor) {
this->phases_[phase].active_power_sensor_ = active_power_sensor;
}
void set_voltage_sensor_pv(uint8_t pv, sensor::Sensor *voltage_sensor) {
this->pvs_[pv].voltage_sensor_ = voltage_sensor;
}
void set_current_sensor_pv(uint8_t pv, sensor::Sensor *current_sensor) {
this->pvs_[pv].current_sensor_ = current_sensor;
}
void set_active_power_sensor_pv(uint8_t pv, sensor::Sensor *active_power_sensor) {
this->pvs_[pv].active_power_sensor_ = active_power_sensor;
}
protected:
struct GrowattPhase {
sensor::Sensor *voltage_sensor_{nullptr};
sensor::Sensor *current_sensor_{nullptr};
sensor::Sensor *active_power_sensor_{nullptr};
} phases_[3];
struct GrowattPV {
sensor::Sensor *voltage_sensor_{nullptr};
sensor::Sensor *current_sensor_{nullptr};
sensor::Sensor *active_power_sensor_{nullptr};
} pvs_[2];
sensor::Sensor *inverter_status_{nullptr};
sensor::Sensor *grid_frequency_sensor_{nullptr};
sensor::Sensor *grid_active_power_sensor_{nullptr};
sensor::Sensor *pv_active_power_sensor_{nullptr};
sensor::Sensor *today_production_{nullptr};
sensor::Sensor *total_energy_production_{nullptr};
sensor::Sensor *inverter_module_temp_{nullptr};
};
} // namespace growatt_solar
} // namespace esphome

View File

@@ -0,0 +1,201 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, modbus
from esphome.const import (
CONF_ACTIVE_POWER,
CONF_CURRENT,
CONF_FREQUENCY,
CONF_ID,
CONF_VOLTAGE,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_ENERGY,
DEVICE_CLASS_POWER,
DEVICE_CLASS_VOLTAGE,
ICON_CURRENT_AC,
STATE_CLASS_MEASUREMENT,
STATE_CLASS_TOTAL_INCREASING,
UNIT_AMPERE,
UNIT_CELSIUS,
UNIT_HERTZ,
UNIT_VOLT,
UNIT_WATT,
)
CONF_PHASE_A = "phase_a"
CONF_PHASE_B = "phase_b"
CONF_PHASE_C = "phase_c"
CONF_ENERGY_PRODUCTION_DAY = "energy_production_day"
CONF_TOTAL_ENERGY_PRODUCTION = "total_energy_production"
CONF_TOTAL_GENERATION_TIME = "total_generation_time"
CONF_TODAY_GENERATION_TIME = "today_generation_time"
CONF_PV1 = "pv1"
CONF_PV2 = "pv2"
UNIT_KILOWATT_HOURS = "kWh"
UNIT_HOURS = "h"
UNIT_KOHM = ""
UNIT_MILLIAMPERE = "mA"
CONF_INVERTER_STATUS = "inverter_status"
CONF_PV_ACTIVE_POWER = "pv_active_power"
CONF_INVERTER_MODULE_TEMP = "inverter_module_temp"
AUTO_LOAD = ["modbus"]
CODEOWNERS = ["@leeuwte"]
growatt_solar_ns = cg.esphome_ns.namespace("growatt_solar")
GrowattSolar = growatt_solar_ns.class_(
"GrowattSolar", cg.PollingComponent, modbus.ModbusDevice
)
PHASE_SENSORS = {
CONF_VOLTAGE: sensor.sensor_schema(
unit_of_measurement=UNIT_VOLT,
accuracy_decimals=2,
device_class=DEVICE_CLASS_VOLTAGE,
),
CONF_CURRENT: sensor.sensor_schema(
unit_of_measurement=UNIT_AMPERE,
accuracy_decimals=2,
device_class=DEVICE_CLASS_CURRENT,
state_class=STATE_CLASS_MEASUREMENT,
),
CONF_ACTIVE_POWER: sensor.sensor_schema(
unit_of_measurement=UNIT_WATT,
accuracy_decimals=0,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
}
PV_SENSORS = {
CONF_VOLTAGE: sensor.sensor_schema(
unit_of_measurement=UNIT_VOLT,
accuracy_decimals=2,
device_class=DEVICE_CLASS_VOLTAGE,
),
CONF_CURRENT: sensor.sensor_schema(
unit_of_measurement=UNIT_AMPERE,
accuracy_decimals=2,
device_class=DEVICE_CLASS_CURRENT,
state_class=STATE_CLASS_MEASUREMENT,
),
CONF_ACTIVE_POWER: sensor.sensor_schema(
unit_of_measurement=UNIT_WATT,
accuracy_decimals=0,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
}
PHASE_SCHEMA = cv.Schema(
{cv.Optional(sensor): schema for sensor, schema in PHASE_SENSORS.items()}
)
PV_SCHEMA = cv.Schema(
{cv.Optional(sensor): schema for sensor, schema in PV_SENSORS.items()}
)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(GrowattSolar),
cv.Optional(CONF_PHASE_A): PHASE_SCHEMA,
cv.Optional(CONF_PHASE_B): PHASE_SCHEMA,
cv.Optional(CONF_PHASE_C): PHASE_SCHEMA,
cv.Optional(CONF_PV1): PV_SCHEMA,
cv.Optional(CONF_PV2): PV_SCHEMA,
cv.Optional(CONF_INVERTER_STATUS): sensor.sensor_schema(),
cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(
unit_of_measurement=UNIT_HERTZ,
icon=ICON_CURRENT_AC,
accuracy_decimals=2,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_ACTIVE_POWER): sensor.sensor_schema(
unit_of_measurement=UNIT_WATT,
accuracy_decimals=0,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_PV_ACTIVE_POWER): sensor.sensor_schema(
unit_of_measurement=UNIT_WATT,
accuracy_decimals=0,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_ENERGY_PRODUCTION_DAY): sensor.sensor_schema(
unit_of_measurement=UNIT_KILOWATT_HOURS,
accuracy_decimals=2,
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_TOTAL_INCREASING,
),
cv.Optional(CONF_TOTAL_ENERGY_PRODUCTION): sensor.sensor_schema(
unit_of_measurement=UNIT_KILOWATT_HOURS,
accuracy_decimals=0,
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_TOTAL_INCREASING,
),
cv.Optional(CONF_INVERTER_MODULE_TEMP): sensor.sensor_schema(
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)
.extend(cv.polling_component_schema("10s"))
.extend(modbus.modbus_device_schema(0x01))
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await modbus.register_modbus_device(var, config)
if CONF_INVERTER_STATUS in config:
sens = await sensor.new_sensor(config[CONF_INVERTER_STATUS])
cg.add(var.set_inverter_status_sensor(sens))
if CONF_FREQUENCY in config:
sens = await sensor.new_sensor(config[CONF_FREQUENCY])
cg.add(var.set_grid_frequency_sensor(sens))
if CONF_ACTIVE_POWER in config:
sens = await sensor.new_sensor(config[CONF_ACTIVE_POWER])
cg.add(var.set_grid_active_power_sensor(sens))
if CONF_PV_ACTIVE_POWER in config:
sens = await sensor.new_sensor(config[CONF_PV_ACTIVE_POWER])
cg.add(var.set_pv_active_power_sensor(sens))
if CONF_ENERGY_PRODUCTION_DAY in config:
sens = await sensor.new_sensor(config[CONF_ENERGY_PRODUCTION_DAY])
cg.add(var.set_today_production_sensor(sens))
if CONF_TOTAL_ENERGY_PRODUCTION in config:
sens = await sensor.new_sensor(config[CONF_TOTAL_ENERGY_PRODUCTION])
cg.add(var.set_total_energy_production_sensor(sens))
if CONF_INVERTER_MODULE_TEMP in config:
sens = await sensor.new_sensor(config[CONF_INVERTER_MODULE_TEMP])
cg.add(var.set_inverter_module_temp_sensor(sens))
for i, phase in enumerate([CONF_PHASE_A, CONF_PHASE_B, CONF_PHASE_C]):
if phase not in config:
continue
phase_config = config[phase]
for sensor_type in PHASE_SENSORS:
if sensor_type in phase_config:
sens = await sensor.new_sensor(phase_config[sensor_type])
cg.add(getattr(var, f"set_{sensor_type}_sensor")(i, sens))
for i, pv in enumerate([CONF_PV1, CONF_PV2]):
if pv not in config:
continue
pv_config = config[pv]
for sensor_type in pv_config:
if sensor_type in pv_config:
sens = await sensor.new_sensor(pv_config[sensor_type])
cg.add(getattr(var, f"set_{sensor_type}_sensor_pv")(i, sens))

View File

@@ -299,9 +299,7 @@ bool HitachiClimate::parse_swing_(const uint8_t remote_state[]) {
GETBITS8(remote_state[HITACHI_AC344_SWINGH_BYTE], HITACHI_AC344_SWINGH_OFFSET, HITACHI_AC344_SWINGH_SIZE);
ESP_LOGV(TAG, "SwingH: %02X %02X", remote_state[HITACHI_AC344_SWINGH_BYTE], swing_modeh);
if ((swing_modeh & 0x7) == 0x0) {
this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL;
} else if ((swing_modeh & 0x3) == 0x3) {
if ((swing_modeh & 0x3) == 0x3) {
this->swing_mode = climate::CLIMATE_SWING_OFF;
} else {
this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL;

View File

@@ -300,9 +300,7 @@ bool HitachiClimate::parse_swing_(const uint8_t remote_state[]) {
HITACHI_AC424_SWINGH_SIZE);
ESP_LOGV(TAG, "SwingH: %02X %02X", remote_state[HITACHI_AC424_SWINGH_BYTE], swing_modeh);
if ((swing_modeh & 0x7) == 0x0) {
this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL;
} else if ((swing_modeh & 0x3) == 0x3) {
if ((swing_modeh & 0x3) == 0x3) {
this->swing_mode = climate::CLIMATE_SWING_OFF;
} else {
this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL;

View File

@@ -1,6 +1,8 @@
#pragma once
#include <cstdint>
#include <cstddef>
#include <utility>
#include <vector>
namespace esphome {
namespace i2c {
@@ -40,6 +42,20 @@ class I2CBus {
return writev(address, &buf, 1);
}
virtual ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t cnt) = 0;
protected:
void i2c_scan_() {
for (uint8_t address = 8; address < 120; address++) {
auto err = writev(address, nullptr, 0);
if (err == ERROR_OK) {
scan_results_.emplace_back(address, true);
} else if (err == ERROR_UNKNOWN) {
scan_results_.emplace_back(address, false);
}
}
}
std::vector<std::pair<uint8_t, bool>> scan_results_;
bool scan_{false};
};
} // namespace i2c

View File

@@ -2,6 +2,7 @@
#include "i2c_bus_arduino.h"
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
#include <Arduino.h>
#include <cstring>
@@ -27,6 +28,10 @@ void ArduinoI2CBus::setup() {
wire_->begin(sda_pin_, scl_pin_);
wire_->setClock(frequency_);
initialized_ = true;
if (this->scan_) {
ESP_LOGV(TAG, "Scanning i2c bus for active devices...");
this->i2c_scan_();
}
}
void ArduinoI2CBus::dump_config() {
ESP_LOGCONFIG(TAG, "I2C Bus:");
@@ -45,22 +50,20 @@ void ArduinoI2CBus::dump_config() {
break;
}
if (this->scan_) {
ESP_LOGI(TAG, "Scanning i2c bus for active devices...");
uint8_t found = 0;
for (uint8_t address = 8; address < 120; address++) {
auto err = writev(address, nullptr, 0);
if (err == ERROR_OK) {
ESP_LOGI(TAG, "Found i2c device at address 0x%02X", address);
found++;
} else if (err == ERROR_UNKNOWN) {
ESP_LOGI(TAG, "Unknown error at address 0x%02X", address);
}
}
if (found == 0) {
ESP_LOGI(TAG, "Results from i2c bus scan:");
if (scan_results_.empty()) {
ESP_LOGI(TAG, "Found no i2c devices!");
} else {
for (const auto &s : scan_results_) {
if (s.second)
ESP_LOGI(TAG, "Found i2c device at address 0x%02X", s.first);
else
ESP_LOGE(TAG, "Unknown error at address 0x%02X", s.first);
}
}
}
}
ErrorCode ArduinoI2CBus::readv(uint8_t address, ReadBuffer *buffers, size_t cnt) {
// logging is only enabled with vv level, if warnings are shown the caller
// should log them

View File

@@ -34,7 +34,6 @@ class ArduinoI2CBus : public I2CBus, public Component {
protected:
TwoWire *wire_;
bool scan_;
uint8_t sda_pin_;
uint8_t scl_pin_;
uint32_t frequency_;

View File

@@ -3,6 +3,7 @@
#include "i2c_bus_esp_idf.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
#include <cstring>
namespace esphome {
@@ -37,6 +38,10 @@ void IDFI2CBus::setup() {
return;
}
initialized_ = true;
if (this->scan_) {
ESP_LOGV(TAG, "Scanning i2c bus for active devices...");
this->i2c_scan_();
}
}
void IDFI2CBus::dump_config() {
ESP_LOGCONFIG(TAG, "I2C Bus:");
@@ -55,23 +60,20 @@ void IDFI2CBus::dump_config() {
break;
}
if (this->scan_) {
ESP_LOGI(TAG, "Scanning i2c bus for active devices...");
uint8_t found = 0;
for (uint8_t address = 8; address < 120; address++) {
auto err = writev(address, nullptr, 0);
if (err == ERROR_OK) {
ESP_LOGI(TAG, "Found i2c device at address 0x%02X", address);
found++;
} else if (err == ERROR_UNKNOWN) {
ESP_LOGI(TAG, "Unknown error at address 0x%02X", address);
}
}
if (found == 0) {
ESP_LOGI(TAG, "Results from i2c bus scan:");
if (scan_results_.empty()) {
ESP_LOGI(TAG, "Found no i2c devices!");
} else {
for (const auto &s : scan_results_) {
if (s.second)
ESP_LOGI(TAG, "Found i2c device at address 0x%02X", s.first);
else
ESP_LOGE(TAG, "Unknown error at address 0x%02X", s.first);
}
}
}
}
ErrorCode IDFI2CBus::readv(uint8_t address, ReadBuffer *buffers, size_t cnt) {
// logging is only enabled with vv level, if warnings are shown the caller
// should log them

View File

@@ -36,7 +36,6 @@ class IDFI2CBus : public I2CBus, public Component {
protected:
i2c_port_t port_;
bool scan_;
uint8_t sda_pin_;
bool sda_pullup_enabled_;
uint8_t scl_pin_;

View File

@@ -86,8 +86,8 @@ void ILI9341Display::update() {
void ILI9341Display::display_() {
// we will only update the changed window to the display
int w = this->x_high_ - this->x_low_ + 1;
int h = this->y_high_ - this->y_low_ + 1;
uint16_t w = this->x_high_ - this->x_low_ + 1;
uint16_t h = this->y_high_ - this->y_low_ + 1;
set_addr_window_(this->x_low_, this->y_low_, w, h);
this->start_data_();

View File

@@ -111,7 +111,7 @@ std::vector<uint8_t> ImprovSerialComponent::build_version_info_() {
bool ImprovSerialComponent::parse_improv_serial_byte_(uint8_t byte) {
size_t at = this->rx_buffer_.size();
this->rx_buffer_.push_back(byte);
ESP_LOGV(TAG, "Improv Serial byte: 0x%02X", byte);
ESP_LOGD(TAG, "Improv Serial byte: 0x%02X", byte);
const uint8_t *raw = &this->rx_buffer_[0];
if (at == 0)
return byte == 'I';
@@ -145,7 +145,7 @@ bool ImprovSerialComponent::parse_improv_serial_byte_(uint8_t byte) {
if (at == 8 + data_len + 1) {
uint8_t checksum = 0x00;
for (uint8_t i = 0; i < at; i++)
for (size_t i = 0; i < at; i++)
checksum += raw[i];
if (checksum != byte) {
@@ -176,7 +176,7 @@ bool ImprovSerialComponent::parse_improv_payload_(improv::ImprovCommand &command
wifi::global_wifi_component->set_sta(sta);
wifi::global_wifi_component->start_scanning();
this->set_state_(improv::STATE_PROVISIONING);
ESP_LOGI(TAG, "Received Improv wifi settings ssid=%s, password=" LOG_SECRET("%s"), command.ssid.c_str(),
ESP_LOGD(TAG, "Received Improv wifi settings ssid=%s, password=" LOG_SECRET("%s"), command.ssid.c_str(),
command.password.c_str());
auto f = std::bind(&ImprovSerialComponent::on_wifi_connect_timeout_, this);

View File

@@ -17,7 +17,7 @@ void GPIOLCDDisplay::setup() {
this->enable_pin_->setup(); // OUTPUT
this->enable_pin_->digital_write(false);
for (uint8_t i = 0; i < (this->is_four_bit_mode() ? 4 : 8); i++) {
for (uint8_t i = 0; i < (uint8_t)(this->is_four_bit_mode() ? 4u : 8u); i++) {
this->data_pins_[i]->setup(); // OUTPUT
this->data_pins_[i]->digital_write(false);
}

View File

@@ -14,6 +14,7 @@ from esphome.const import (
CONF_RESTORE_MODE,
CONF_ON_TURN_OFF,
CONF_ON_TURN_ON,
CONF_ON_STATE,
CONF_TRIGGER_ID,
CONF_COLD_WHITE_COLOR_TEMPERATURE,
CONF_WARM_WHITE_COLOR_TEMPERATURE,
@@ -37,6 +38,7 @@ from .types import ( # noqa
AddressableLight,
LightTurnOnTrigger,
LightTurnOffTrigger,
LightStateTrigger,
)
CODEOWNERS = ["@esphome/core"]
@@ -69,6 +71,11 @@ LIGHT_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).ex
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LightTurnOffTrigger),
}
),
cv.Optional(CONF_ON_STATE): auto.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LightStateTrigger),
}
),
}
)
@@ -151,6 +158,9 @@ async def setup_light_core_(light_var, output_var, config):
for conf in config.get(CONF_ON_TURN_OFF, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], light_var)
await auto.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_STATE, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], light_var)
await auto.build_automation(trigger, [], conf)
if CONF_COLOR_CORRECT in config:
cg.add(output_var.set_correction(*config[CONF_COLOR_CORRECT]))

View File

@@ -167,7 +167,7 @@ class AddressableScanEffect : public AddressableLightEffect {
this->last_move_ = now;
it.all() = Color::BLACK;
for (auto i = 0; i < this->scan_width_; i++) {
for (uint32_t i = 0; i < this->scan_width_; i++) {
it[this->at_led_ + i] = current_color;
}
@@ -178,7 +178,7 @@ class AddressableScanEffect : public AddressableLightEffect {
uint32_t move_interval_{};
uint32_t scan_width_{1};
uint32_t last_move_{0};
int at_led_{0};
uint32_t at_led_{0};
bool direction_{true};
};

View File

@@ -141,6 +141,13 @@ class LightTurnOffTrigger : public Trigger<> {
}
};
class LightStateTrigger : public Trigger<> {
public:
LightStateTrigger(LightState *a_light) {
a_light->add_new_remote_values_callback([this]() { this->trigger(); });
}
};
// This is slightly ugly, but we can't log in headers, and can't make this a static method on AddressableSet
// due to the template. It's just a temporary warning anyway.
void addressableset_warn_about_scale(const char *field);

View File

@@ -98,7 +98,7 @@ void LightCall::perform() {
// EFFECT
auto effect = this->effect_;
const char *effect_s;
if (effect == 0)
if (effect == 0u)
effect_s = "None";
else
effect_s = this->parent_->effects_[*this->effect_ - 1]->get_name().c_str();

View File

@@ -41,6 +41,7 @@ LightTurnOnTrigger = light_ns.class_(
LightTurnOffTrigger = light_ns.class_(
"LightTurnOffTrigger", automation.Trigger.template()
)
LightStateTrigger = light_ns.class_("LightStateTrigger", automation.Trigger.template())
# Effects
LightEffect = light_ns.class_("LightEffect")

View File

@@ -97,7 +97,7 @@ void LTR390Component::read_mode_(int mode_index) {
// If there are more modes to read then begin the next
// otherwise stop
if (mode_index + 1 < this->mode_funcs_.size()) {
if (mode_index + 1 < (int) this->mode_funcs_.size()) {
this->read_mode_(mode_index + 1);
} else {
this->reading_ = false;

View File

@@ -203,16 +203,16 @@ float MAX31865Sensor::calc_temperature_(float rtd_ratio) {
rtd_resistance *= 100;
}
float rpoly = rtd_resistance;
float neg_temp = -242.02;
neg_temp += 2.2228 * rpoly;
float neg_temp = -242.02f;
neg_temp += 2.2228f * rpoly;
rpoly *= rtd_resistance; // square
neg_temp += 2.5859e-3 * rpoly;
neg_temp += 2.5859e-3f * rpoly;
rpoly *= rtd_resistance; // ^3
neg_temp -= 4.8260e-6 * rpoly;
neg_temp -= 4.8260e-6f * rpoly;
rpoly *= rtd_resistance; // ^4
neg_temp -= 2.8183e-8 * rpoly;
neg_temp -= 2.8183e-8f * rpoly;
rpoly *= rtd_resistance; // ^5
neg_temp += 1.5243e-10 * rpoly;
neg_temp += 1.5243e-10f * rpoly;
return neg_temp;
}

View File

@@ -76,7 +76,7 @@ void MAX7219Component::loop() {
this->stepsleft_ = 0;
// Return if there is no need to scroll or scroll is off
if (!this->scroll_ || (this->max_displaybuffer_[0].size() <= get_width_internal())) {
if (!this->scroll_ || (this->max_displaybuffer_[0].size() <= (size_t) get_width_internal())) {
this->display();
return;
}
@@ -88,7 +88,7 @@ void MAX7219Component::loop() {
// Dwell time at end of string in case of stop at end
if (this->scroll_mode_ == ScrollMode::STOP) {
if (this->stepsleft_ >= this->max_displaybuffer_[0].size() - get_width_internal() + 1) {
if (this->stepsleft_ >= this->max_displaybuffer_[0].size() - (size_t) get_width_internal() + 1) {
if (now - this->last_scroll_ >= this->scroll_dwell_) {
this->stepsleft_ = 0;
this->last_scroll_ = now;
@@ -155,7 +155,7 @@ int MAX7219Component::get_height_internal() {
int MAX7219Component::get_width_internal() { return this->num_chips_ / this->num_chip_lines_ * 8; }
void HOT MAX7219Component::draw_absolute_pixel_internal(int x, int y, Color color) {
if (x + 1 > this->max_displaybuffer_[0].size()) { // Extend the display buffer in case required
if (x + 1 > (int) this->max_displaybuffer_[0].size()) { // Extend the display buffer in case required
for (int chip_line = 0; chip_line < this->num_chip_lines_; chip_line++) {
this->max_displaybuffer_[chip_line].resize(x + 1, this->bckgrnd_);
}

View File

@@ -35,7 +35,6 @@ void MCP23S08::dump_config() {
}
bool MCP23S08::read_reg(uint8_t reg, uint8_t *value) {
uint8_t data;
this->enable();
this->transfer_byte(this->device_opcode_ | 1);
this->transfer_byte(reg);

View File

@@ -24,6 +24,7 @@ void MCP23X17Base::pin_mode(uint8_t pin, gpio::Flags flags) {
uint8_t gppu = pin < 8 ? mcp23x17_base::MCP23X17_GPPUA : mcp23x17_base::MCP23X17_GPPUB;
if (flags == gpio::FLAG_INPUT) {
this->update_reg(pin, true, iodir);
this->update_reg(pin, false, gppu);
} else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_PULLUP)) {
this->update_reg(pin, true, iodir);
this->update_reg(pin, true, gppu);

View File

@@ -127,9 +127,6 @@ canbus::Error MCP2515::set_mode_(const CanctrlReqopMode mode) {
}
canbus::Error MCP2515::set_clk_out_(const CanClkOut divisor) {
canbus::Error res;
uint8_t cfg3;
if (divisor == CLKOUT_DISABLE) {
/* Turn off CLKEN */
modify_register_(MCP_CANCTRL, CANCTRL_CLKEN, 0x00);

View File

@@ -23,7 +23,7 @@ void MD5Digest::get_hex(char *output) {
}
}
bool MD5Digest::equals_bytes(const char *expected) {
bool MD5Digest::equals_bytes(const uint8_t *expected) {
for (size_t i = 0; i < 16; i++) {
if (expected[i] != this->digest_[i]) {
return false;
@@ -33,18 +33,10 @@ bool MD5Digest::equals_bytes(const char *expected) {
}
bool MD5Digest::equals_hex(const char *expected) {
for (size_t i = 0; i < 16; i++) {
auto high = parse_hex(expected[i * 2]);
auto low = parse_hex(expected[i * 2 + 1]);
if (!high.has_value() || !low.has_value()) {
return false;
}
auto value = (*high << 4) | *low;
if (value != this->digest_[i]) {
return false;
}
}
return true;
uint8_t parsed[16];
if (!parse_hex(expected, parsed, 16))
return false;
return equals_bytes(parsed);
}
} // namespace md5

View File

@@ -44,7 +44,7 @@ class MD5Digest {
void get_hex(char *output);
/// Compare the digest against a provided byte-encoded digest (16 bytes).
bool equals_bytes(const char *expected);
bool equals_bytes(const uint8_t *expected);
/// Compare the digest against a provided hex-encoded digest (32 bytes).
bool equals_hex(const char *expected);

View File

@@ -181,7 +181,7 @@ void Modbus::send(uint8_t address, uint8_t function_code, uint16_t start_address
this->flow_control_pin_->digital_write(false);
waiting_for_response = address;
last_send_ = millis();
ESP_LOGV(TAG, "Modbus write: %s", hexencode(data).c_str());
ESP_LOGV(TAG, "Modbus write: %s", format_hex_pretty(data).c_str());
}
// Helper function for lambdas
@@ -202,7 +202,7 @@ void Modbus::send_raw(const std::vector<uint8_t> &payload) {
if (this->flow_control_pin_ != nullptr)
this->flow_control_pin_->digital_write(false);
waiting_for_response = payload[0];
ESP_LOGV(TAG, "Modbus write raw: %s", hexencode(payload).c_str());
ESP_LOGV(TAG, "Modbus write raw: %s", format_hex_pretty(payload).c_str());
last_send_ = millis();
}

View File

@@ -1,10 +1,21 @@
import binascii
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import modbus
from esphome.const import CONF_ID, CONF_ADDRESS
from esphome.const import CONF_ADDRESS, CONF_ID, CONF_NAME, CONF_LAMBDA, CONF_OFFSET
from esphome.cpp_helpers import logging
from .const import (
CONF_BITMASK,
CONF_BYTE_OFFSET,
CONF_COMMAND_THROTTLE,
CONF_CUSTOM_COMMAND,
CONF_FORCE_NEW_RANGE,
CONF_MODBUS_CONTROLLER_ID,
CONF_REGISTER_COUNT,
CONF_REGISTER_TYPE,
CONF_RESPONSE_SIZE,
CONF_SKIP_UPDATES,
CONF_VALUE_TYPE,
)
CODEOWNERS = ["@martgras"]
@@ -37,6 +48,7 @@ MODBUS_FUNCTION_CODE = {
ModbusRegisterType_ns = modbus_controller_ns.namespace("ModbusRegisterType")
ModbusRegisterType = ModbusRegisterType_ns.enum("ModbusRegisterType")
MODBUS_REGISTER_TYPE = {
"custom": ModbusRegisterType.CUSTOM,
"coil": ModbusRegisterType.COIL,
"discrete_input": ModbusRegisterType.DISCRETE_INPUT,
"holding": ModbusRegisterType.HOLDING,
@@ -95,6 +107,100 @@ CONFIG_SCHEMA = cv.All(
)
ModbusItemBaseSchema = cv.Schema(
{
cv.GenerateID(CONF_MODBUS_CONTROLLER_ID): cv.use_id(ModbusController),
cv.Optional(CONF_ADDRESS): cv.positive_int,
cv.Optional(CONF_CUSTOM_COMMAND): cv.ensure_list(cv.hex_uint8_t),
cv.Exclusive(
CONF_OFFSET,
"offset",
f"{CONF_OFFSET} and {CONF_BYTE_OFFSET} can't be used together",
): cv.positive_int,
cv.Exclusive(
CONF_BYTE_OFFSET,
"offset",
f"{CONF_OFFSET} and {CONF_BYTE_OFFSET} can't be used together",
): cv.positive_int,
cv.Optional(CONF_BITMASK, default=0xFFFFFFFF): cv.hex_uint32_t,
cv.Optional(CONF_SKIP_UPDATES, default=0): cv.positive_int,
cv.Optional(CONF_FORCE_NEW_RANGE, default=False): cv.boolean,
cv.Optional(CONF_LAMBDA): cv.returning_lambda,
cv.Optional(CONF_RESPONSE_SIZE, default=0): cv.positive_int,
},
)
def validate_modbus_register(config):
if CONF_CUSTOM_COMMAND not in config and CONF_ADDRESS not in config:
raise cv.Invalid(
f" {CONF_ADDRESS} is a required property if '{CONF_CUSTOM_COMMAND}:' isn't used"
)
if CONF_CUSTOM_COMMAND in config and CONF_REGISTER_TYPE in config:
raise cv.Invalid(
f"can't use '{CONF_REGISTER_TYPE}:' together with '{CONF_CUSTOM_COMMAND}:'",
)
if CONF_CUSTOM_COMMAND not in config and CONF_REGISTER_TYPE not in config:
raise cv.Invalid(
f" {CONF_REGISTER_TYPE} is a required property if '{CONF_CUSTOM_COMMAND}:' isn't used"
)
return config
def modbus_calc_properties(config):
byte_offset = 0
reg_count = 0
if CONF_OFFSET in config:
byte_offset = config[CONF_OFFSET]
# A CONF_BYTE_OFFSET setting overrides CONF_OFFSET
if CONF_BYTE_OFFSET in config:
byte_offset = config[CONF_BYTE_OFFSET]
if CONF_REGISTER_COUNT in config:
reg_count = config[CONF_REGISTER_COUNT]
if CONF_VALUE_TYPE in config:
value_type = config[CONF_VALUE_TYPE]
if reg_count == 0:
reg_count = TYPE_REGISTER_MAP[value_type]
if CONF_CUSTOM_COMMAND in config:
if CONF_ADDRESS not in config:
# generate a unique modbus address using the hash of the name
# CONF_NAME set even if only CONF_ID is used.
# a modbus register address is required to add the item to sensormap
value = config[CONF_NAME]
if isinstance(value, str):
value = value.encode()
config[CONF_ADDRESS] = binascii.crc_hqx(value, 0)
config[CONF_REGISTER_TYPE] = ModbusRegisterType.CUSTOM
config[CONF_FORCE_NEW_RANGE] = True
return byte_offset, reg_count
async def add_modbus_base_properties(
var, config, sensor_type, lamdba_param_type=cg.float_, lamdba_return_type=float
):
if CONF_CUSTOM_COMMAND in config:
cg.add(var.set_custom_data(config[CONF_CUSTOM_COMMAND]))
if config[CONF_RESPONSE_SIZE] > 0:
cg.add(var.set_register_size(config[CONF_RESPONSE_SIZE]))
if CONF_LAMBDA in config:
template_ = await cg.process_lambda(
config[CONF_LAMBDA],
[
(sensor_type.operator("ptr"), "item"),
(lamdba_param_type, "x"),
(
cg.std_vector.template(cg.uint8).operator("const").operator("ref"),
"data",
),
],
return_type=cg.optional.template(lamdba_return_type),
)
cg.add(var.set_template(template_))
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID], config[CONF_COMMAND_THROTTLE])
cg.add(var.set_command_throttle(config[CONF_COMMAND_THROTTLE]))
@@ -119,11 +225,3 @@ def function_code_to_register(function_code):
"write_multiple_registers": ModbusRegisterType.HOLDING,
}
return FUNCTION_CODE_TYPE_MAP[function_code]
def find_by_value(dict, find_value):
for (key, value) in MODBUS_REGISTER_TYPE.items():
print(find_value, value)
if find_value == value:
return key
return "not found"

View File

@@ -2,16 +2,18 @@ from esphome.components import binary_sensor
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.const import CONF_ADDRESS, CONF_ID, CONF_LAMBDA, CONF_OFFSET
from esphome.const import CONF_ADDRESS, CONF_ID
from .. import (
SensorItem,
add_modbus_base_properties,
modbus_controller_ns,
ModbusController,
modbus_calc_properties,
validate_modbus_register,
ModbusItemBaseSchema,
SensorItem,
MODBUS_REGISTER_TYPE,
)
from ..const import (
CONF_BITMASK,
CONF_BYTE_OFFSET,
CONF_FORCE_NEW_RANGE,
CONF_MODBUS_CONTROLLER_ID,
CONF_REGISTER_TYPE,
@@ -27,30 +29,20 @@ ModbusBinarySensor = modbus_controller_ns.class_(
)
CONFIG_SCHEMA = cv.All(
binary_sensor.BINARY_SENSOR_SCHEMA.extend(
binary_sensor.BINARY_SENSOR_SCHEMA.extend(cv.COMPONENT_SCHEMA)
.extend(ModbusItemBaseSchema)
.extend(
{
cv.GenerateID(): cv.declare_id(ModbusBinarySensor),
cv.GenerateID(CONF_MODBUS_CONTROLLER_ID): cv.use_id(ModbusController),
cv.Required(CONF_ADDRESS): cv.positive_int,
cv.Required(CONF_REGISTER_TYPE): cv.enum(MODBUS_REGISTER_TYPE),
cv.Optional(CONF_OFFSET, default=0): cv.positive_int,
cv.Optional(CONF_BYTE_OFFSET): cv.positive_int,
cv.Optional(CONF_BITMASK, default=0x1): cv.hex_uint32_t,
cv.Optional(CONF_SKIP_UPDATES, default=0): cv.positive_int,
cv.Optional(CONF_FORCE_NEW_RANGE, default=False): cv.boolean,
cv.Optional(CONF_LAMBDA): cv.returning_lambda,
cv.Optional(CONF_REGISTER_TYPE): cv.enum(MODBUS_REGISTER_TYPE),
}
).extend(cv.COMPONENT_SCHEMA),
),
validate_modbus_register,
)
async def to_code(config):
byte_offset = 0
if CONF_OFFSET in config:
byte_offset = config[CONF_OFFSET]
# A CONF_BYTE_OFFSET setting overrides CONF_OFFSET
if CONF_BYTE_OFFSET in config:
byte_offset = config[CONF_BYTE_OFFSET]
byte_offset, _ = modbus_calc_properties(config)
var = cg.new_Pvariable(
config[CONF_ID],
config[CONF_REGISTER_TYPE],
@@ -65,17 +57,4 @@ async def to_code(config):
paren = await cg.get_variable(config[CONF_MODBUS_CONTROLLER_ID])
cg.add(paren.add_sensor_item(var))
if CONF_LAMBDA in config:
template_ = await cg.process_lambda(
config[CONF_LAMBDA],
[
(ModbusBinarySensor.operator("ptr"), "item"),
(cg.float_, "x"),
(
cg.std_vector.template(cg.uint8).operator("const").operator("ref"),
"data",
),
],
return_type=cg.optional.template(bool),
)
cg.add(var.set_template(template_))
await add_modbus_base_properties(var, config, ModbusBinarySensor, cg.float_, bool)

View File

@@ -13,8 +13,6 @@ void ModbusBinarySensor::parse_and_publish(const std::vector<uint8_t> &data) {
switch (this->register_type) {
case ModbusRegisterType::DISCRETE_INPUT:
value = coil_from_vector(this->offset, data);
break;
case ModbusRegisterType::COIL:
// offset for coil is the actual number of the coil not the byte offset
value = coil_from_vector(this->offset, data);

View File

@@ -1,6 +1,7 @@
CONF_BITMASK = "bitmask"
CONF_BYTE_OFFSET = "byte_offset"
CONF_COMMAND_THROTTLE = "command_throttle"
CONF_CUSTOM_COMMAND = "custom_command"
CONF_FORCE_NEW_RANGE = "force_new_range"
CONF_MODBUS_CONTROLLER_ID = "modbus_controller_id"
CONF_MODBUS_FUNCTIONCODE = "modbus_functioncode"
@@ -9,5 +10,6 @@ CONF_REGISTER_COUNT = "register_count"
CONF_REGISTER_TYPE = "register_type"
CONF_RESPONSE_SIZE = "response_size"
CONF_SKIP_UPDATES = "skip_updates"
CONF_USE_WRITE_MULTIPLE = "use_write_multiple"
CONF_VALUE_TYPE = "value_type"
CONF_WRITE_LAMBDA = "write_lambda"

Some files were not shown because too many files have changed in this diff Show More