Compare commits

...
Author SHA1 Message Date
J. Nick Koston e671c8ebdf Merge remote-tracking branch 'origin/esp8266-entity-names-flash' into esp8266-entity-names-components 2026-10-14 16:15:42 -10:00
J. Nick Koston fe2637356b Merge remote-tracking branch 'origin/api-action-strings-progmem' into esp8266-entity-names-flash 2026-10-14 16:15:04 -10:00
J. Nick Koston 6f7b875844 Merge remote-tracking branch 'origin/dev' into api-action-strings-progmem 2026-10-14 16:14:40 -10:00
J. Nick Koston 677516efcc Merge remote-tracking branch 'origin/esp8266-entity-names-flash' into esp8266-entity-names-components 2026-10-14 16:09:46 -10:00
Jonathan Swoboda f3eaf698d6 [mipi_rgb] Rotate pure RGB panels in software (#20757) 2026-10-14 22:05:01 -04:00
J. Nick Koston 52ec1c7ccf Merge remote-tracking branch 'origin/api-action-strings-progmem' into esp8266-entity-names-flash
# Conflicts:
#	esphome/core/string_ref.h
#	tests/components/core/test_progmem_string_ref.cpp
2026-10-14 16:01:49 -10:00
J. Nick Koston f7057e1270 [tests] Reset saved preferences before every C++ unit test (#20771) 2026-10-14 16:01:45 -10:00
J. Nick Koston 6edfbb594d [bluetooth_connection] Value-initialize the connection parameters for ESP-IDF 6.1 (#20774) 2026-10-14 22:00:07 -04:00
J. Nick Koston 22defcd704 [esp32_ble_client] Value-initialize the connection parameters for ESP-IDF 6.1 (#20776) 2026-10-14 21:59:56 -04:00
J. Nick Koston ef5b0861f1 Encode Home Assistant action names and data from flash 2026-10-14 15:59:41 -10:00
J. Nick Koston da561e08d6 Keep the RAM short string encode on memcpy; the ESP8266 shared copy moves to #20423 2026-10-14 15:59:41 -10:00
Jonathan Swoboda 0f408234d0 [core] Add ksz8851snl to the static analysis manifest for IDF 6 (#20788) 2026-10-14 21:53:17 -04:00
Jonathan Swoboda 92e0e6db6b [wifi] Call esp_supp_dpp_init without a callback on ESP-IDF 6 (#20789) 2026-10-15 01:40:07 +00:00
908c2f6778 [wifi] Add Wi-Fi Easy Connect (DPP) provisioning on the ESP32 (#17934)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-14 14:29:47 -10:00
Keith BurzinskiandClaude Opus 5.5 af709ac9a5 [openthread] Fail instead of aborting when NVS is unavailable (#20717)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 13:30:36 -10:00
Panoplian fe57eef3f6 [core] Test that includes inside a package resolve next to the package (#20739) 2026-10-14 13:28:01 -10:00
d0b9f195b0 [wifi] Apply reboot_timeout with a fallback AP unless a station is connected to it (#9958)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-14 13:23:37 -10:00
eafeef7cb8 [fusb302b] Add FUSB302B driver (#20760)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-14 13:18:20 -10:00
471774cdbf [ecocomfort2] Fix the clock sync and read boost from the unit state (#20782)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-10-14 13:14:04 -10:00
Kevin AhrendtandJ. Nick Koston 4df6291feb [api][sendspin] Add the Sendspin client id to the capabilities message (#20775)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-14 22:56:51 +00:00
d8ca4a9460 [audio_dac] Add ES9038Q2M Katana platform (#15017)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-14 22:50:41 +00:00
48a0a41cce [wifi] Implement access point WISP mode (#13633)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-14 12:49:37 -10:00
esphome[bot] 0e2c4cf5c2 Bump aioesphomeapi from 46.11.0 to 46.12.0 (#20781)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-10-14 12:43:07 -10:00
ea76073d86 [micro_wake_word] Allow selecting the inference task core (#20126)
Co-authored-by: n-IA-hane <n-IA-hane@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-14 12:42:24 -10:00
J. Nick Koston dcb943ded3 [noise] Initialize the protocol id's reserved field for ESP-IDF 6.1 (#20779) 2026-10-14 11:45:54 -10:00
dependabot[bot] 2c6f59738c Bump filelock from 4.1.0 to 4.1.1 (#20778)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-14 21:41:28 +00:00
puddlyandJ. Nick Koston 04715fb32b [usb_uart] Expose interface_class and port_number (#20659)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-14 21:41:12 +00:00
nullable.ethandJ. Nick Koston 3df9360876 [i2s_audio] Add keep_alive option to speaker to prevent pop/click on audio start (#15565)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-14 11:39:30 -10:00
dependabot[bot] 61f3de0bc0 Bump pylint from 4.1.2 to 4.1.3 (#20773)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-14 21:30:26 +00:00
dependabot[bot] cd97c9a9cb Bump platformdirs from 4.12.4 to 4.13.0 (#20772)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-14 21:29:17 +00:00
J. Nick Koston d3668a7186 [mqtt] Match subscription topics in a loop instead of recursing per character (#20767) 2026-10-14 11:13:32 -10:00
J. Nick Koston ac74796263 [friedrich] Clear saved preferences in the test fixture (#20770) 2026-10-14 10:52:13 -10:00
c56bb14037 [mqtt] Print crash info over MQTT (#17935)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-14 10:39:55 -10:00
J. Nick Koston a4278b8dec [ci] Run benchmarks when a component the Python import benchmark imports changes (#20763) 2026-10-14 10:35:33 -10:00
Panoplian 6d173c7371 [bq25186] Set i2c_id in the component tests (#20742) 2026-10-14 10:35:01 -10:00
esphome[bot] 1779b011c9 Bump aioesphomeapi from 46.10.0 to 46.11.0 (#20766)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-10-14 20:21:45 +00:00
PanoplianandJ. Nick Koston 3c4cf50cb6 [core] Point compile errors after a lambda at the right line (#20740)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-14 10:02:51 -10:00
J. Nick Koston e869d1f7ab [ci] Give the BK72xx clang-tidy environment the static allocation flag the real build has (#20764) 2026-10-14 09:56:09 -10:00
e1a996435d [ld2450] Add polygon zones evaluated on the device (#20154)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-14 19:29:16 +00:00
Jonathan Swoboda c5a624a1f2 [tests] Count OTA key probe retries on a fake clock (#20761) 2026-10-14 15:21:35 -04:00
Remco van Essen 15fb7f775e [tmc2209] Add chopper and PWM tuning actions (#20753) 2026-10-14 08:59:21 -10:00
J. Nick Koston 3fa72e61c6 [mqtt] Build the mqtt triggers with build_trigger_callback (#20433) 2026-10-14 08:55:39 -10:00
Kevin Ahrendt 163b09a473 [microphone] Speed up MicrophoneSource audio conversion (#20755) 2026-10-14 08:47:25 -10:00
J. Nick Koston 7afc2768f8 [esp8266] Check that every component's saved values fit together (#20686) 2026-10-14 08:45:51 -10:00
56f1a2bf46 [midea_portasplit] Add Midea PortaSplit climate component (#18033)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-14 08:45:21 -10:00
Panoplian fee47fe2f1 [pcf85063] Convert the weekday to and from the chip's 0-6 range (#20736) 2026-10-14 08:43:23 -10:00
Panoplian b994e081c3 [bm8563] Convert the weekday to and from the chip's 0-6 range (#20737) 2026-10-14 08:42:47 -10:00
Jonathan Swoboda a1f740b71b [lvgl] Keep LV_CONF_PATH one argument when the build path has a space (#20758) 2026-10-14 08:36:17 -10:00
Remco van EssenandClaude Opus 5.5 7f144365ba [fusb302b] Add USB PD sink protocol (#20632)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 08:35:29 -10:00
Marcus VoßandJ. Nick Koston 928459d16f [cse7766] Keep last power reading between measurements at low load (#20631)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-14 08:33:11 -10:00
Jonathan Swoboda 9163359445 [core] Analyze the ethernet component under ESP-IDF (#20745) 2026-10-14 08:31:39 -10:00
Jonathan Swoboda 67307f0eeb [ci] Select the feature-gated files the platform clang-tidy jobs were missing (#20743) 2026-10-14 08:31:06 -10:00
91e0edc11d [pn532] Fix NDEF parsing greater than 255 bytes (#18188)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-14 08:15:48 -10:00
Jonathan Swoboda 9fe75e262f [ci] Add a host clang-tidy environment and job (#20747) 2026-10-14 08:09:53 -10:00
Kevin Ahrendt 0132f722a3 [audio] Bump esp-audio-libs to v4.1.0 (#20754) 2026-10-14 13:24:36 -04:00
Jonathan Swoboda 7d7a78308d [ci] Test tinyusb_keyboard in isolation (#20749) 2026-10-14 10:05:02 -04:00
J. Nick Koston 9c568525b0 [core] Add CodSpeed benchmarks for importing entity components (#20648) 2026-10-14 09:08:41 -04:00
J. Nick Koston 10f42e3e7c [shelly_dimmer] Build the STM32 firmware update path in CI (#20677) 2026-10-14 09:03:34 -04:00
J. Nick Koston a70f565a22 Default the autotuner id to empty, wrap the sonoff_d1 fallback and align the deprecation wording 2026-10-12 17:31:19 -10:00
J. Nick Koston 1e45d1893c Merge commit 'c4c19a00e07874d2a7a4a98df588bad3c971d806' into esp8266-entity-names-components 2026-10-12 17:27:46 -10:00
J. Nick Koston c4c19a00e0 Move the entity domain and MQTT log conversions to the stacked PR 2026-10-12 17:27:40 -10:00
J. Nick Koston 0f8d26e1dd Convert components to the flash-safe name accessors and deprecate get_name() 2026-10-12 17:19:06 -10:00
J. Nick Koston 522395de11 Read entity names flash-safely in web_server, MQTT and prometheus 2026-10-12 17:19:03 -10:00
J. Nick Koston 39d63a3166 Keep the entity domain log and dump helpers on the flash-safe name accessors 2026-10-12 17:10:33 -10:00
J. Nick Koston 36cfe5c785 Move the component conversions and the get_name() deprecation to a stacked PR 2026-10-12 17:02:29 -10:00
J. Nick Koston d8dd8cf36c Keep the flash object id hash in entity_base and read it with progmem_read_byte 2026-10-12 16:57:20 -10:00
J. Nick Koston 12c35c1545 Compute the object id hash when an external caller passes 0 2026-10-12 16:53:49 -10:00
J. Nick Koston 0e8a3dfb6f Fold dev's ProgmemStringRef struct into the class and share the progmem short string body 2026-10-12 16:53:49 -10:00
J. Nick Koston 65b32731d4 Keep the ESP8266 short string encode on the shared progmem copy 2026-10-12 16:53:23 -10:00
J. Nick Koston 00f8e89f28 Merge remote-tracking branch 'origin/dev' into esp8266-entity-names-flash 2026-10-12 16:49:42 -10:00
J. Nick Koston 9b873e2179 Keep the RAM short string encode on memcpy and drop unused ProgmemStringRef helpers 2026-10-12 16:47:08 -10:00
J. Nick Koston dd495be861 Fold dev's ProgmemStringRef struct into the class 2026-10-12 16:28:37 -10:00
J. Nick Koston a7fdb143cd Merge branch 'dev' into api-action-strings-progmem 2026-10-12 16:02:23 -10:00
J. Nick Koston 5326d1dfe9 Merge remote-tracking branch 'origin/dev' into esp8266-entity-names-flash
# Conflicts:
#	esphome/components/web_server/web_server.cpp
2026-10-11 16:06:33 -10:00
J. Nick Koston ba9705e19b Construct the get_name() copy with placement new 2026-10-11 15:39:22 -10:00
J. Nick Koston 543a818307 Merge remote-tracking branch 'origin/dev' into esp8266-entity-names-flash
# Conflicts:
#	esphome/components/uart/button/uart_button.cpp
#	esphome/components/uart/switch/uart_switch.cpp
2026-10-11 15:36:07 -10:00
J. Nick Koston 229aa22006 Merge remote-tracking branch 'origin/dev' into api-action-strings-progmem
# Conflicts:
#	esphome/components/api/__init__.py
#	tests/component_tests/api/test_action_metadata.py
2026-10-11 15:26:13 -10:00
J. Nick Koston b31bedd464 Merge remote-tracking branch 'origin/dev' into HEAD
# Conflicts:
#	esphome/components/mqtt/mqtt_button.cpp
#	esphome/components/mqtt/mqtt_lock.cpp
#	esphome/components/mqtt/mqtt_switch.cpp
#	esphome/components/mqtt/mqtt_update.cpp
2026-10-09 18:28:52 -10:00
J. Nick Koston 491a2e58d5 Merge remote-tracking branch 'origin/dev' into HEAD 2026-10-09 18:28:25 -10:00
J. Nick Koston c8d331d44b Merge remote-tracking branch 'origin/dev' into esp8266-entity-names-flash
# Conflicts:
#	esphome/components/current_based/current_based_cover.cpp
2026-10-09 09:37:51 -10:00
J. Nick Koston 40ec3067b6 Merge remote-tracking branch 'origin/dev' into api-action-strings-progmem 2026-10-08 22:14:06 -10:00
J. Nick Koston 62f79e007a Merge remote-tracking branch 'origin/dev' into esp8266-entity-names-flash 2026-10-08 22:14:05 -10:00
J. Nick Koston 4497dad959 Keep the inline short string encode off ESP8266 2026-10-08 22:07:35 -10:00
J. Nick Koston b989221c71 Print prometheus names from flash, keep the inline short string encode off ESP8266 2026-10-08 22:07:33 -10:00
J. Nick Koston 096921cddb Keep get_name() working on ESP8266 with a RAM copy made on first use 2026-10-08 22:00:40 -10:00
J. Nick Koston e3d33df379 Share the outlined PROGMEM append for the progmem string dump 2026-10-08 21:44:09 -10:00
J. Nick Koston 47655129e1 Share the outlined PROGMEM append for the progmem string dump 2026-10-08 21:44:06 -10:00
J. Nick Koston c263852df9 Dump progmem strings straight into the dump buffer 2026-10-08 21:34:34 -10:00
J. Nick Koston 40460f3e01 Dump progmem strings straight into the dump buffer, assert the scratch define is gone 2026-10-08 21:34:31 -10:00
J. Nick Koston 8378c935d6 Drop the scratch define assertion from the action metadata test 2026-10-08 20:17:56 -10:00
J. Nick Koston 7c58d26d13 [api] Add encode_progmem_string for progmem string fields
Generated code passes the ProgmemStringRef instead of spelling out the cast.
2026-10-08 20:17:55 -10:00
J. Nick Koston 1fa844a5d2 [api] Add encode_progmem_string for progmem string fields
Generated code passes the ProgmemStringRef instead of spelling out the cast.
2026-10-08 20:17:55 -10:00
J. Nick Koston 7b5a1f9de9 [api] Encode action strings straight from flash
The service name, description and argument strings are progmem fields, so
list entities no longer copies them into a stack scratch buffer, and the
ESP8266 per action string budget goes away.
2026-10-08 20:10:41 -10:00
J. Nick Koston b63b74f236 [api] Allow the progmem option on string fields
Mirrors the helper from the entity names change, so either PR can merge first.
2026-10-08 20:10:41 -10:00
J. Nick Koston 88f7fc7454 Allow progmem on any string field and split the ProgmemStringRef tests
Long or unforced progmem strings encode through encode_progmem_bytes, which
API action strings need. The ProgmemStringRef tests move to their own file.
2026-10-08 20:04:18 -10:00
J. Nick Koston 140488723d Keep get_name() returning a reference off ESP8266
ProgmemStringRef now holds a StringRef, so get_name() and friendly_name_() can
keep returning const StringRef & on other platforms. External components that
return get_name() from a const StringRef & getter would otherwise return a
reference to a temporary. friendly_name_() is deprecated there, not removed.
2026-10-08 19:55:29 -10:00
J. Nick Koston b626711d50 Build the list entities benchmark with the ProgmemStringRef name field 2026-10-08 19:36:01 -10:00
pre-commit-ci-lite[bot] a6cb560e90 apply automatic formatting fixes 2026-10-09 04:36:31 +00:00
J. Nick Koston a721574706 Hash the RAM fallback name directly and share the short string encoder
Only unnamed entities hash at runtime, and their fallback name is in RAM, so
configure_entity_ hashes it before wrapping and named entities keep the codegen
hash; fnv1_hash_object_id_P is no longer needed. On ESP8266 the short string
encoder uses the outlined flash safe copy for RAM strings too.
2026-10-08 18:32:39 -10:00
J. Nick Koston 568934cc91 Drop the remaining entity name copies in mqtt, pid and prometheus
mqtt puts the name into the discovery JSON straight from flash, the pid
autotuner keeps the entity's log name instead of a std::string, and
prometheus prints names and ids without building a std::string per scrape.
2026-10-08 18:22:29 -10:00
J. Nick Koston 13057b7ae3 Type the API progmem name field as ProgmemStringRef
Progmem string fields must take the short encode path, the outlined encode
helper takes a ProgmemStringRef, and the hasher test checks the Python value.
2026-10-08 18:22:29 -10:00
J. Nick Koston b4289aee59 Add a flash safe fnv1_hash_object_id_P()
calc_object_id_() hashes the name in place instead of copying it into a
stack buffer first.
2026-10-08 18:11:36 -10:00
J. Nick Koston 93aecdd435 Deprecate Sprinkler::valve_name() and guard ProgmemStringRef::write_to
valve_name() returned a flash pointer on ESP8266; it now follows get_name():
static_assert there, deprecated elsewhere, with valve_log_name() for logs.
2026-10-08 17:58:37 -10:00
J. Nick Koston afac871938 [api] Encode entity names straight from flash
Extend the progmem proto option to string fields and mark the entity info
name field with it, so ListEntities copies the name once instead of through
a stack buffer.
2026-10-08 17:58:37 -10:00
J. Nick Koston 4c6e6b5447 Move flash copy and compare into ProgmemStringRef
ProgmemStringRef owns write_to() and equals() with the platform split, so the
EntityBase accessors are inline forwarders. Adds ESPHOME_ENTITY_NAME_MAX_LEN
mirroring NAME_MAX_LENGTH and tidies the codegen and tests.
2026-10-08 17:44:53 -10:00
J. Nick Koston b5c06efb2f Store entity names as ProgmemStringRef and deprecate its c_str()
ProgmemStringRef has no byte-reading operations, so new comparisons or copies
of name_ fail to compile. c_str() stays for external log-only users and is
deprecated until 2027.5.0; in-tree logs now use get_log_name().
2026-10-08 17:37:28 -10:00
J. Nick Koston 5cbdf26993 Tidy comments and always wrap entity names in ESPHOME_PSTR 2026-10-08 17:21:11 -10:00
J. Nick Koston 6f5108eb70 Apply review cleanups and deprecate get_name() off ESP8266
Inline get_name_to() off ESP8266, add write_name_to() and name_equals() so
web_server avoids copies, always emit ESPHOME_PSTR, and follow the get_icon()
pattern: get_name() is a static_assert stub on ESP8266 and ESPDEPRECATED
elsewhere until 2027.5.0.
2026-10-08 17:18:00 -10:00
J. Nick Koston 4ef86fe534 [core] Keep entity names in flash on ESP8266
On ESP8266 codegen emits entity names with PSTR and EntityBase::get_name()
is removed there. get_name_to() copies the name into a caller buffer and
get_log_name() returns it for %s log arguments; both exist on every platform.
2026-10-08 16:57:25 -10:00
375 changed files with 9873 additions and 1573 deletions
+25 -11
View File
@@ -81,8 +81,8 @@ jobs:
- name: Install apt packages (cached)
uses: awalsh128/cache-apt-pkgs-action@553a35bb8ebd9fcabcb1c9451aa4c98e1b4ca8a9 # v1.6.3
with:
packages: libsdl2-dev ccache
version: 1.1
packages: libsdl2-dev libmbedtls-dev ccache
version: 1.2
determine-jobs:
name: Determine which jobs to run
@@ -415,8 +415,8 @@ jobs:
timeout-minutes: 10
uses: awalsh128/cache-apt-pkgs-action@553a35bb8ebd9fcabcb1c9451aa4c98e1b4ca8a9 # v1.6.3
with:
packages: libsdl2-dev ccache
version: 1.1
packages: libsdl2-dev libmbedtls-dev ccache
version: 1.2
- name: Set up Python 3.13
id: python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
@@ -680,7 +680,7 @@ jobs:
include:
- id: clang-tidy
name: Run script/clang-tidy for ESP8266
options: --environment esp8266-arduino-tidy --grep USE_ESP8266
options: --environment esp8266-arduino-tidy --grep USE_ESP8266 --grep USE_SOCKET_IMPL_LWIP_TCP --grep USE_SHD_FIRMWARE_DATA
pio_cache_key: tidyesp8266
- id: clang-tidy
name: Run script/clang-tidy for ESP32 Arduino
@@ -693,12 +693,16 @@ jobs:
ignore_errors: false
- id: clang-tidy
name: Run script/clang-tidy for RP2
options: --environment rp2-tidy --grep USE_RP2
options: --environment rp2-tidy --grep USE_RP2 --grep USE_BLE_SCAN_RESPONSE_MERGER
pio_cache_key: tidyrp2
- id: clang-tidy
name: Run script/clang-tidy for host
options: --environment host-tidy --grep USE_HOST
pio_cache_key: tidyhost
- id: clang-tidy
name: Run script/clang-tidy for LibreTiny
environments: bk72xx-tidy ln882h-tidy rtl87xxb-tidy rtl87xxc-tidy
options: --grep USE_LIBRETINY --grep USE_BK72XX --grep USE_RTL87XX --grep USE_LN882X
options: --grep USE_LIBRETINY --grep USE_BK72XX --grep USE_RTL87XX --grep USE_LN882X --grep USE_LN882H_BLE --grep USE_SOCKET_IMPL_LWIP_SOCKETS
pio_cache_key: tidylibretiny
steps:
@@ -707,6 +711,16 @@ jobs:
with:
# Need history for HEAD~1 to work for checking changed files
fetch-depth: 2
- name: Install apt packages (cached)
if: matrix.pio_cache_key == 'tidyhost'
# libsdl2-dev is needed by the sdl component on the host, libmbedtls-dev
# by dsmr and dlms_meter. Packages and version must match seed-apt-cache
# exactly.
timeout-minutes: 10
uses: awalsh128/cache-apt-pkgs-action@553a35bb8ebd9fcabcb1c9451aa4c98e1b4ca8a9 # v1.6.3
with:
packages: libsdl2-dev libmbedtls-dev ccache
version: 1.2
- name: Restore Python
id: restore-python
@@ -989,14 +1003,14 @@ jobs:
- id: clang-tidy
name: Run script/clang-tidy for ESP32 S3
# yamllint disable-line rule:line-length
options: --environment esp32s3-idf-tidy --grep SOC_TEMP_SENSOR_SUPPORTED --grep USE_ESP32_VARIANT_ESP32S3 --grep USE_LOGGER_USB_CDC
options: --environment esp32s3-idf-tidy --grep SOC_TEMP_SENSOR_SUPPORTED --grep USE_ESP32_VARIANT_ESP32S3 --grep USE_LOGGER_USB_CDC --grep SOC_I2S_SUPPORTS_TDM
tidy_environment: esp32s3-idf-tidy
- id: clang-tidy
name: Run script/clang-tidy for ESP32 P4
# P4 has no native Wi-Fi/BLE; those run over the hosted co-processor,
# so their code paths differ -- lint them under the P4 build too.
# yamllint disable-line rule:line-length
options: --environment esp32p4-idf-tidy --grep USE_ESP32_VARIANT_ESP32P4 --grep USE_ESP32_HOSTED --grep USE_WIFI --grep USE_BLE
options: --environment esp32p4-idf-tidy --grep USE_ESP32_VARIANT_ESP32P4 --grep CONFIG_IDF_TARGET_ESP32P4 --grep USE_ESP32_HOSTED --grep USE_WIFI --grep USE_BLE
tidy_environment: esp32p4-idf-tidy
- id: clang-tidy
name: Run script/clang-tidy for ESP32 C6
@@ -1102,8 +1116,8 @@ jobs:
timeout-minutes: 10
uses: awalsh128/cache-apt-pkgs-action@553a35bb8ebd9fcabcb1c9451aa4c98e1b4ca8a9 # v1.6.3
with:
packages: libsdl2-dev ccache
version: 1.1
packages: libsdl2-dev libmbedtls-dev ccache
version: 1.2
- name: Check out code from GitHub
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
+3
View File
@@ -183,6 +183,7 @@ esphome/components/es7243e/* @kbx81
esphome/components/es8156/* @kbx81
esphome/components/es8311/* @kahrendt @kroimon
esphome/components/es8388/* @P4uLT
esphome/components/es9038q2m_katana/* @GioMez
esphome/components/esp32/* @esphome/core
esphome/components/esp32_ble/* @bdraco @jesserockz @Rapsssito
esphome/components/esp32_ble_client/* @bdraco @jesserockz
@@ -219,6 +220,7 @@ esphome/components/friedrich/* @rwrozelle
esphome/components/fs3000/* @kahrendt
esphome/components/ft5x06/* @clydebarrow
esphome/components/ft63x6/* @gpambrozio
esphome/components/fusb302b/* @remcom
esphome/components/gcja5/* @gcormier
esphome/components/gdk101/* @Szewcson
esphome/components/gl_r01_i2c/* @pkejval
@@ -386,6 +388,7 @@ esphome/components/microphone/* @jesserockz @kahrendt
esphome/components/mics_4514/* @jesserockz
esphome/components/midea/* @dudanov
esphome/components/midea_ir/* @dudanov
esphome/components/midea_portasplit/* @Fexiven
esphome/components/mipi_dsi/* @clydebarrow
esphome/components/mipi_rgb/* @clydebarrow
esphome/components/mipi_spi/* @clydebarrow
@@ -10,7 +10,7 @@ void AbsoluteHumidityComponent::setup() {
this->temperature_ = state;
this->enable_loop();
});
ESP_LOGD(TAG, " Added callback for temperature '%s'", this->temperature_sensor_->get_name().c_str());
ESP_LOGD(TAG, " Added callback for temperature '%s'", LOG_STR_ARG(this->temperature_sensor_->get_log_name()));
// Get initial value
if (this->temperature_sensor_->has_state()) {
this->temperature_ = this->temperature_sensor_->get_state();
@@ -20,7 +20,7 @@ void AbsoluteHumidityComponent::setup() {
this->humidity_ = state;
this->enable_loop();
});
ESP_LOGD(TAG, " Added callback for relative humidity '%s'", this->humidity_sensor_->get_name().c_str());
ESP_LOGD(TAG, " Added callback for relative humidity '%s'", LOG_STR_ARG(this->humidity_sensor_->get_log_name()));
// Get initial value
if (this->humidity_sensor_->has_state()) {
this->humidity_ = this->humidity_sensor_->get_state();
@@ -49,7 +49,8 @@ void AbsoluteHumidityComponent::dump_config() {
"Sources\n"
" Temperature: '%s'\n"
" Relative Humidity: '%s'",
this->temperature_sensor_->get_name().c_str(), this->humidity_sensor_->get_name().c_str());
LOG_STR_ARG(this->temperature_sensor_->get_log_name()),
LOG_STR_ARG(this->humidity_sensor_->get_log_name()));
}
void AbsoluteHumidityComponent::loop() {
+1 -1
View File
@@ -66,7 +66,7 @@ template class Aggregator<uint32_t>;
void ADCSensor::update() {
float value_v = this->sample();
ESP_LOGV(TAG, "'%s': Voltage=%.4fV", this->get_name().c_str(), value_v);
ESP_LOGV(TAG, "'%s': Voltage=%.4fV", LOG_STR_ARG(this->get_log_name()), value_v);
this->publish_state(value_v);
}
@@ -13,7 +13,7 @@ float ADS1115Sensor::sample() {
void ADS1115Sensor::update() {
float v = this->sample();
if (!std::isnan(v)) {
ESP_LOGD(TAG, "'%s': Got Voltage=%fV", this->get_name().c_str(), v);
ESP_LOGD(TAG, "'%s': Got Voltage=%fV", LOG_STR_ARG(this->get_log_name()), v);
this->publish_state(v);
}
}
@@ -21,7 +21,7 @@ float ADS1118Sensor::sample() {
void ADS1118Sensor::update() {
float v = this->sample();
if (!std::isnan(v)) {
ESP_LOGD(TAG, "'%s': Got Voltage=%fV", this->get_name().c_str(), v);
ESP_LOGD(TAG, "'%s': Got Voltage=%fV", LOG_STR_ARG(this->get_log_name()), v);
this->publish_state(v);
}
}
@@ -31,7 +31,7 @@ void AlarmControlPanel::publish_state(AlarmControlPanelState state) {
this->last_update_ = millis();
if (state != this->current_state_) {
auto prev_state = this->current_state_;
ESP_LOGV(TAG, "'%s' >> %s (was %s)", this->get_name().c_str(),
ESP_LOGV(TAG, "'%s' >> %s (was %s)", LOG_STR_ARG(this->get_log_name()),
LOG_STR_ARG(alarm_control_panel_state_to_string(state)),
LOG_STR_ARG(alarm_control_panel_state_to_string(prev_state)));
this->current_state_ = state;
+16 -13
View File
@@ -30,9 +30,9 @@ void Am43Component::loop() {
auto status =
esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_,
packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
ESP_LOGI(TAG, "[%s] Logging into AM43", this->get_name().c_str());
ESP_LOGI(TAG, "[%s] Logging into AM43", LOG_STR_ARG(this->get_log_name()));
if (status) {
ESP_LOGW(TAG, "[%s] Error writing set_pin to device, error = %d", this->get_name().c_str(), status);
ESP_LOGW(TAG, "[%s] Error writing set_pin to device, error = %d", LOG_STR_ARG(this->get_log_name()), status);
} else {
this->logged_in_ = true;
}
@@ -50,7 +50,7 @@ CoverTraits Am43Component::get_traits() {
void Am43Component::control(const CoverCall &call) {
if (this->node_state != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "[%s] Cannot send cover control, not connected", this->get_name().c_str());
ESP_LOGW(TAG, "[%s] Cannot send cover control, not connected", LOG_STR_ARG(this->get_log_name()));
return;
}
if (call.get_stop()) {
@@ -59,7 +59,7 @@ void Am43Component::control(const CoverCall &call) {
esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_,
packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
if (status) {
ESP_LOGW(TAG, "[%s] Error writing stop command to device, error = %d", this->get_name().c_str(), status);
ESP_LOGW(TAG, "[%s] Error writing stop command to device, error = %d", LOG_STR_ARG(this->get_log_name()), status);
}
}
auto opt_pos = call.get_position();
@@ -73,7 +73,8 @@ void Am43Component::control(const CoverCall &call) {
esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_,
packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
if (status) {
ESP_LOGW(TAG, "[%s] Error writing set_position command to device, error = %d", this->get_name().c_str(), status);
ESP_LOGW(TAG, "[%s] Error writing set_position command to device, error = %d", LOG_STR_ARG(this->get_log_name()),
status);
}
}
}
@@ -89,9 +90,9 @@ void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
auto *chr = this->parent_->get_characteristic(AM43_SERVICE_UUID, AM43_CHARACTERISTIC_UUID);
if (chr == nullptr) {
if (this->parent_->get_characteristic(AM43_TUYA_SERVICE_UUID, AM43_TUYA_CHARACTERISTIC_UUID) != nullptr) {
ESP_LOGE(TAG, "[%s] Detected a Tuya AM43 which is not supported, sorry.", this->get_name().c_str());
ESP_LOGE(TAG, "[%s] Detected a Tuya AM43 which is not supported, sorry.", LOG_STR_ARG(this->get_log_name()));
} else {
ESP_LOGE(TAG, "[%s] No control service found at device, not an AM43..?", this->get_name().c_str());
ESP_LOGE(TAG, "[%s] No control service found at device, not an AM43..?", LOG_STR_ARG(this->get_log_name()));
}
break;
}
@@ -100,7 +101,8 @@ void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
auto status = esp_ble_gattc_register_for_notify(this->parent_->get_gattc_if(), this->parent_->get_remote_bda(),
chr->handle);
if (status) {
ESP_LOGW(TAG, "[%s] esp_ble_gattc_register_for_notify failed, status=%d", this->get_name().c_str(), status);
ESP_LOGW(TAG, "[%s] esp_ble_gattc_register_for_notify failed, status=%d", LOG_STR_ARG(this->get_log_name()),
status);
}
break;
}
@@ -126,25 +128,26 @@ void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
if (this->decoder_->has_pin_response()) {
if (this->decoder_->pin_ok_) {
ESP_LOGI(TAG, "[%s] AM43 pin accepted.", this->get_name().c_str());
ESP_LOGI(TAG, "[%s] AM43 pin accepted.", LOG_STR_ARG(this->get_log_name()));
auto *packet = this->encoder_->get_position_request();
auto status = esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(),
this->char_handle_, packet->length, packet->data,
ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
if (status) {
ESP_LOGW(TAG, "[%s] Error writing set_position to device, error = %d", this->get_name().c_str(), status);
ESP_LOGW(TAG, "[%s] Error writing set_position to device, error = %d", LOG_STR_ARG(this->get_log_name()),
status);
}
} else {
ESP_LOGW(TAG, "[%s] AM43 pin rejected!", this->get_name().c_str());
ESP_LOGW(TAG, "[%s] AM43 pin rejected!", LOG_STR_ARG(this->get_log_name()));
}
}
if (this->decoder_->has_set_position_response() && !this->decoder_->set_position_ok_) {
ESP_LOGW(TAG, "[%s] Got nack after sending set_position. Bad pin?", this->get_name().c_str());
ESP_LOGW(TAG, "[%s] Got nack after sending set_position. Bad pin?", LOG_STR_ARG(this->get_log_name()));
}
if (this->decoder_->has_set_state_response() && !this->decoder_->set_state_ok_) {
ESP_LOGW(TAG, "[%s] Got nack after sending set_state. Bad pin?", this->get_name().c_str());
ESP_LOGW(TAG, "[%s] Got nack after sending set_state. Bad pin?", LOG_STR_ARG(this->get_log_name()));
}
break;
}
+5 -3
View File
@@ -67,8 +67,9 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
case ESP_GATTC_SEARCH_CMPL_EVT: {
auto *chr = this->parent_->get_characteristic(ANOVA_SERVICE_UUID, ANOVA_CHARACTERISTIC_UUID);
if (chr == nullptr) {
ESP_LOGW(TAG, "[%s] No control service found at device, not an Anova..?", this->get_name().c_str());
ESP_LOGW(TAG, "[%s] Note, this component does not currently support Anova Nano.", this->get_name().c_str());
ESP_LOGW(TAG, "[%s] No control service found at device, not an Anova..?", LOG_STR_ARG(this->get_log_name()));
ESP_LOGW(TAG, "[%s] Note, this component does not currently support Anova Nano.",
LOG_STR_ARG(this->get_log_name()));
break;
}
this->char_handle_ = chr->handle;
@@ -76,7 +77,8 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
auto status = esp_ble_gattc_register_for_notify(this->parent_->get_gattc_if(), this->parent_->get_remote_bda(),
chr->handle);
if (status) {
ESP_LOGW(TAG, "[%s] esp_ble_gattc_register_for_notify failed, status=%d", this->get_name().c_str(), status);
ESP_LOGW(TAG, "[%s] esp_ble_gattc_register_for_notify failed, status=%d", LOG_STR_ARG(this->get_log_name()),
status);
}
break;
}
-33
View File
@@ -251,9 +251,6 @@ def _validate_supports_response(value: Any) -> str:
return cv.enum(SUPPORTS_RESPONSE_OPTIONS, lower=True)(value)
# ESP8266 copies every string of an action into a stack buffer sized by codegen; keep it small
ESP8266_ACTION_STRINGS_MAX_TOTAL = 384
VARIABLE_SCHEMA = cv.Schema(
{
cv.Required(CONF_TYPE): cv.one_of(*SERVICE_ARG_NATIVE_TYPES, lower=True),
@@ -484,29 +481,6 @@ def _action_strings(conf: ConfigType, has_metadata: bool) -> list[str | None]:
return strings
def _action_strings_size(strings: list[str | None]) -> int:
"""Bytes needed to copy every string out of flash, each with its terminator."""
return sum(
len(string.encode("utf-8")) + 1 for string in strings if string is not None
)
def _validate_esp8266_action_strings(config: ConfigType) -> ConfigType:
if not CORE.is_esp8266:
return config
actions = config.get(CONF_ACTIONS, [])
has_metadata = _has_action_metadata(actions)
for conf in actions:
size = _action_strings_size(_action_strings(conf, has_metadata))
if size > ESP8266_ACTION_STRINGS_MAX_TOTAL:
raise cv.Invalid(
f"Action '{conf[CONF_ACTION]}' has {size} bytes of name, variable name, "
f"description and example text; ESP8266 allows at most "
f"{ESP8266_ACTION_STRINGS_MAX_TOTAL} bytes per action"
)
return config
def _validate_outgoing_host_ipv6(config: ConfigType) -> ConfigType:
"""An IPv6 host can never be parsed, so never dialed, without IPv6."""
if (
@@ -531,7 +505,6 @@ def _validate_wizard(config: ConfigType) -> ConfigType:
FINAL_VALIDATE_SCHEMA = cv.All(
_validate_esp8266_action_strings,
_validate_outgoing_host_ipv6,
_validate_wizard,
)
@@ -587,7 +560,6 @@ async def to_code(config: ConfigType) -> None:
if config[CONF_HOMEASSISTANT_STATES]:
cg.add_define("USE_API_HOMEASSISTANT_STATES")
scratch_size = 0
if actions:
# Metadata is compiled in for every action once any action declares it, because the
# string table layout is fixed by the define rather than per action
@@ -639,8 +611,6 @@ async def to_code(config: ConfigType) -> None:
func_args.append((native, name))
strings = _action_strings(conf, has_metadata)
table = _add_action_strings(index, strings)
if CORE.is_esp8266:
scratch_size = max(scratch_size, _action_strings_size(strings))
# Template args: supports_response mode, then user service arg types
templ = cg.TemplateArguments(supports_response, *service_template_args)
# Key is hashed here because the name is not readable at runtime on ESP8266
@@ -667,9 +637,6 @@ async def to_code(config: ConfigType) -> None:
cg.add(auto.add_action(unregister_action))
# Register all services at once - single allocation, no reallocations
cg.add(var.initialize_user_services(triggers))
if CORE.is_esp8266 and has_user_actions:
# Stack buffer that list-entities copies PROGMEM strings into, sized for the largest action
cg.add_define("API_USER_ACTION_STRINGS_SCRATCH_SIZE", max(scratch_size, 1))
if (wizard_config := config.get(wizard.CONF_WIZARD)) is not None:
await wizard.to_code(wizard_config)
+42 -36
View File
@@ -403,6 +403,9 @@ message WizardCapabilities {
message SendspinCapabilities {
// Bitmask of the Sendspin features this device supports
uint32 feature_flags = 1;
// The Sendspin client_id: base64url of the client's static public key, 43 characters.
// Empty until the Sendspin client has started once since boot.
string client_id = 2 [(max_data_length) = 43];
}
message DeviceCapabilitiesResponse {
@@ -552,7 +555,7 @@ message ListEntitiesBinarySensorResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string device_class = 5 [(max_data_length) = 47];
@@ -586,7 +589,7 @@ message ListEntitiesCoverResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
bool assumed_state = 5;
@@ -670,7 +673,7 @@ message ListEntitiesFanResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
bool supports_oscillation = 5;
@@ -758,7 +761,7 @@ message ListEntitiesLightResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
repeated ColorMode supported_color_modes = 12 [(container_pointer_no_template) = "light::ColorModeMask"];
@@ -863,7 +866,7 @@ message ListEntitiesSensorResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -904,7 +907,7 @@ message ListEntitiesSwitchResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -949,7 +952,7 @@ message ListEntitiesTextSensorResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -1026,8 +1029,8 @@ message SubscribeHomeassistantServicesRequest {
}
message HomeassistantServiceMap {
string key = 1;
string value = 2;
string key = 1 [(progmem) = true];
string value = 2 [(progmem) = true];
}
message HomeassistantActionRequest {
@@ -1036,7 +1039,7 @@ message HomeassistantActionRequest {
option (no_delay) = true;
option (ifdef) = "USE_API_HOMEASSISTANT_SERVICES";
string service = 1;
string service = 1 [(progmem) = true];
repeated HomeassistantServiceMap data = 2 [(fixed_vector) = true];
repeated HomeassistantServiceMap data_template = 3 [(fixed_vector) = true];
repeated HomeassistantServiceMap variables = 4 [(fixed_vector) = true];
@@ -1157,21 +1160,21 @@ enum SupportsResponseType {
}
message ListEntitiesServicesArgument {
option (ifdef) = "USE_API_USER_DEFINED_ACTIONS";
string name = 1;
string name = 1 [(progmem) = true];
ServiceArgType type = 2;
string description = 3 [(field_ifdef) = "USE_API_USER_DEFINED_ACTION_METADATA"];
string example = 4 [(field_ifdef) = "USE_API_USER_DEFINED_ACTION_METADATA"];
string description = 3 [(field_ifdef) = "USE_API_USER_DEFINED_ACTION_METADATA", (progmem) = true];
string example = 4 [(field_ifdef) = "USE_API_USER_DEFINED_ACTION_METADATA", (progmem) = true];
}
message ListEntitiesServicesResponse {
option (id) = 41;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_API_USER_DEFINED_ACTIONS";
string name = 1;
string name = 1 [(progmem) = true];
fixed32 key = 2 [(force) = true];
repeated ListEntitiesServicesArgument args = 3 [(fixed_vector) = true];
SupportsResponseType supports_response = 4;
string description = 5 [(field_ifdef) = "USE_API_USER_DEFINED_ACTION_METADATA"];
string description = 5 [(field_ifdef) = "USE_API_USER_DEFINED_ACTION_METADATA", (progmem) = true];
}
message ExecuteServiceArgument {
option (ifdef) = "USE_API_USER_DEFINED_ACTIONS";
@@ -1220,7 +1223,7 @@ message ListEntitiesCameraResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
bool disabled_by_default = 5;
string icon = 6 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -1312,7 +1315,7 @@ message ListEntitiesClimateResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
bool supports_current_temperature = 5; // Deprecated: use feature_flags
@@ -1427,7 +1430,7 @@ message ListEntitiesWaterHeaterResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string icon = 4 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
bool disabled_by_default = 5;
EntityCategory entity_category = 6;
@@ -1507,7 +1510,7 @@ message ListEntitiesNumberResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -1556,7 +1559,7 @@ message ListEntitiesSelectResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -1600,7 +1603,7 @@ message ListEntitiesSirenResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -1665,7 +1668,7 @@ message ListEntitiesLockResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -1714,7 +1717,7 @@ message ListEntitiesButtonResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -1781,7 +1784,7 @@ message ListEntitiesMediaPlayerResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -2373,7 +2376,7 @@ message ListEntitiesAlarmControlPanelResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
bool disabled_by_default = 6;
@@ -2420,7 +2423,7 @@ message ListEntitiesTextResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
bool disabled_by_default = 6;
@@ -2468,7 +2471,7 @@ message ListEntitiesDateResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -2515,7 +2518,7 @@ message ListEntitiesTimeResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -2562,7 +2565,7 @@ message ListEntitiesEventResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -2593,7 +2596,7 @@ message ListEntitiesValveResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -2648,7 +2651,7 @@ message ListEntitiesDateTimeResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -2691,7 +2694,7 @@ message ListEntitiesUpdateResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
@@ -2791,7 +2794,7 @@ message ListEntitiesInfraredResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string icon = 4 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
bool disabled_by_default = 5;
EntityCategory entity_category = 6;
@@ -2860,7 +2863,7 @@ message ListEntitiesRadioFrequencyResponse {
string object_id = 1 [(max_data_length) = 120, (force) = true];
fixed32 key = 2 [(force) = true];
string name = 3 [(max_data_length) = 120, (force) = true];
string name = 3 [(max_data_length) = 120, (force) = true, (progmem) = true];
string icon = 4 [(field_ifdef) = "USE_ENTITY_ICON", (max_data_length) = 63];
bool disabled_by_default = 5;
EntityCategory entity_category = 6;
@@ -3043,12 +3046,15 @@ enum SerialProxyIdentityFlag {
// IDs below are empty
}
// The descriptor fields of a USB device as seen by the host stack
// The descriptor fields of a USB device as seen by the host stack, and the port's index
// among those a vendor driver exposes on the interface
message UsbDeviceDescriptor {
uint32 vendor_id = 1;
uint32 product_id = 2;
uint32 bcd_device = 3;
uint32 interface_number = 4; // bInterfaceNumber the host driver binds to
uint32 interface_number = 4; // bInterfaceNumber the port is bound through
uint32 interface_class = 5; // bInterfaceClass of that interface
uint32 port_number = 6;
}
// The identity of the device behind a port. Identifies one physical device among others of
+14 -1
View File
@@ -1,4 +1,7 @@
#include "api_connection.h"
#if defined(USE_LOGGER) && defined(USE_CRASH_HANDLER)
#include "esphome/components/logger/logger.h"
#endif
#ifdef USE_API
#include "api_connection_buffer.h" // for the APIServer-dependent APIConnection inlines
#ifdef USE_API_NOISE
@@ -451,6 +454,15 @@ bool APIConnection::send_disconnect_response_() {
DisconnectResponse resp;
return this->send_message(resp);
}
void APIConnection::on_subscribe_logs_request(const SubscribeLogsRequest &msg) {
this->flags_.log_subscription = msg.level;
if (msg.dump_config)
App.schedule_dump_config();
#if defined(USE_LOGGER) && defined(USE_CRASH_HANDLER)
logger::log_crash_to_client();
#endif
}
void APIConnection::on_disconnect_response() {
// Don't close socket here, let APIServer::loop() do it
// so getpeername() still works for the disconnect trigger
@@ -474,7 +486,7 @@ uint16_t APIConnection::fill_and_encode_entity_info(EntityBase *entity, InfoResp
msg.key = entity->get_object_id_hash();
if (entity->has_own_name()) {
msg.name = entity->get_name();
msg.name = entity->get_name_progmem();
}
// Set common EntityBase properties
@@ -2119,6 +2131,7 @@ bool APIConnection::send_device_capabilities_response_() {
#endif
#if defined(USE_SENDSPIN) && defined(USE_ESP_IDF)
resp.sendspin.feature_flags = sendspin_::global_sendspin_hub->get_feature_flags();
resp.sendspin.client_id = sendspin_::global_sendspin_hub->get_client_id();
#endif
return this->send_message(resp);
}
+1 -24
View File
@@ -15,15 +15,6 @@
#include "subscribe_state.h"
#include "esphome/core/application.h"
#include "esphome/core/component.h"
#ifdef USE_ESP32_CRASH_HANDLER
#include "esphome/components/esp32/crash_handler.h"
#endif
#ifdef USE_RP2_CRASH_HANDLER
#include "esphome/components/rp2/crash_handler.h"
#endif
#ifdef USE_ESP8266_CRASH_HANDLER
#include "esphome/components/esp8266/crash_handler.h"
#endif
#include "esphome/core/entity_base.h"
#include "esphome/core/log.h"
#include "esphome/core/string_ref.h"
@@ -298,21 +289,7 @@ class APIConnection final : public APIServerConnectionBase {
this->begin_iterator_(ActiveIterator::INITIAL_STATE);
}
}
void on_subscribe_logs_request(const SubscribeLogsRequest &msg) {
this->flags_.log_subscription = msg.level;
if (msg.dump_config)
App.schedule_dump_config();
#ifdef USE_ESP32_CRASH_HANDLER
esp32::crash_handler_log();
esp32::crash_handler_clear();
#endif
#ifdef USE_RP2_CRASH_HANDLER
rp2::crash_handler_log();
#endif
#ifdef USE_ESP8266_CRASH_HANDLER
esp8266::crash_handler_log();
#endif
}
void on_subscribe_logs_request(const SubscribeLogsRequest &msg);
#ifdef USE_API_HOMEASSISTANT_SERVICES
void on_subscribe_homeassistant_services_request() { this->flags_.service_call_subscription = true; }
#endif
+1 -1
View File
@@ -123,7 +123,7 @@ extend google.protobuf.FieldOptions {
// from an absent one (e.g. a UTC ParsedTimezone, which is all zeros).
optional bool track_presence = 50020 [default=false];
// progmem: The pointer_to_buffer bytes field points into flash (PROGMEM).
// progmem: The string field, or pointer_to_buffer bytes field, points into flash (PROGMEM).
// The generated encode and dump copy it with progmem_memcpy, as ESP8266
// cannot read flash with ordinary loads.
optional bool progmem = 50021 [default=false];
+40 -34
View File
@@ -310,12 +310,14 @@ uint32_t WizardCapabilities::calc_size_msg(const void *self) {
uint8_t *SendspinCapabilities::encode_msg(const void *self, uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM) {
const auto &msg = *static_cast<const SendspinCapabilities *>(self);
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 1, msg.feature_flags);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 2, msg.client_id);
return pos;
}
uint32_t SendspinCapabilities::calc_size_msg(const void *self) {
const auto &msg = *static_cast<const SendspinCapabilities *>(self);
uint32_t size = 0;
size += ProtoSize::calc_uint32(1, msg.feature_flags);
size += !msg.client_id.empty() ? 2 + msg.client_id.size() : 0;
return size;
}
#endif
@@ -401,7 +403,7 @@ uint8_t *ListEntitiesBinarySensorResponse::encode_msg(const void *self,
const auto &msg = *static_cast<const ListEntitiesBinarySensorResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.device_class);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.is_status_binary_sensor);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 7, msg.disabled_by_default);
@@ -459,7 +461,7 @@ uint8_t *ListEntitiesCoverResponse::encode_msg(const void *self, uint8_t *__rest
const auto &msg = *static_cast<const ListEntitiesCoverResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.assumed_state);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.supports_position);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 7, msg.supports_tilt);
@@ -558,7 +560,7 @@ uint8_t *ListEntitiesFanResponse::encode_msg(const void *self, uint8_t *__restri
const auto &msg = *static_cast<const ListEntitiesFanResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.supports_oscillation);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.supports_speed);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 7, msg.supports_direction);
@@ -678,7 +680,7 @@ uint8_t *ListEntitiesLightResponse::encode_msg(const void *self, uint8_t *__rest
const auto &msg = *static_cast<const ListEntitiesLightResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
for (const auto &it : *msg.supported_color_modes) {
pos = ProtoEncode::encode_uint32_force(pos PROTO_ENCODE_DEBUG_ARG, 12, static_cast<uint32_t>(it));
}
@@ -884,7 +886,7 @@ uint8_t *ListEntitiesSensorResponse::encode_msg(const void *self, uint8_t *__res
const auto &msg = *static_cast<const ListEntitiesSensorResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -954,7 +956,7 @@ uint8_t *ListEntitiesSwitchResponse::encode_msg(const void *self, uint8_t *__res
const auto &msg = *static_cast<const ListEntitiesSwitchResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -1030,7 +1032,7 @@ uint8_t *ListEntitiesTextSensorResponse::encode_msg(const void *self,
const auto &msg = *static_cast<const ListEntitiesTextSensorResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -1140,8 +1142,8 @@ uint32_t NoiseEncryptionSetKeyResponse::calc_size_msg(const void *self) {
#ifdef USE_API_HOMEASSISTANT_SERVICES
uint8_t *HomeassistantServiceMap::encode_msg(const void *self, uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM) {
const auto &msg = *static_cast<const HomeassistantServiceMap *>(self);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 1, msg.key);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 2, msg.value);
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 1, msg.key);
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 2, msg.value);
return pos;
}
uint32_t HomeassistantServiceMap::calc_size_msg(const void *self) {
@@ -1153,7 +1155,7 @@ uint32_t HomeassistantServiceMap::calc_size_msg(const void *self) {
}
uint8_t *HomeassistantActionRequest::encode_msg(const void *self, uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM) {
const auto &msg = *static_cast<const HomeassistantActionRequest *>(self);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 1, msg.service);
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 1, msg.service);
for (auto &it : msg.data) {
pos = ProtoEncode::encode_sub_message(pos PROTO_ENCODE_DEBUG_ARG, 2, it);
}
@@ -1324,13 +1326,13 @@ void GetTimeResponse::decode_field(void *self, uint32_t tag, const uint8_t *data
uint8_t *ListEntitiesServicesArgument::encode_msg(const void *self,
uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM) {
const auto &msg = *static_cast<const ListEntitiesServicesArgument *>(self);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 1, msg.name);
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 1, msg.name);
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 2, static_cast<uint32_t>(msg.type));
#ifdef USE_API_USER_DEFINED_ACTION_METADATA
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 3, msg.description);
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 3, msg.description);
#endif
#ifdef USE_API_USER_DEFINED_ACTION_METADATA
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 4, msg.example);
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 4, msg.example);
#endif
return pos;
}
@@ -1350,14 +1352,14 @@ uint32_t ListEntitiesServicesArgument::calc_size_msg(const void *self) {
uint8_t *ListEntitiesServicesResponse::encode_msg(const void *self,
uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM) {
const auto &msg = *static_cast<const ListEntitiesServicesResponse *>(self);
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 1, msg.name);
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 1, msg.name);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
for (auto &it : msg.args) {
pos = ProtoEncode::encode_sub_message(pos PROTO_ENCODE_DEBUG_ARG, 3, it);
}
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 4, static_cast<uint32_t>(msg.supports_response));
#ifdef USE_API_USER_DEFINED_ACTION_METADATA
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.description);
pos = ProtoEncode::encode_progmem_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.description);
#endif
return pos;
}
@@ -1478,7 +1480,7 @@ uint8_t *ListEntitiesCameraResponse::encode_msg(const void *self, uint8_t *__res
const auto &msg = *static_cast<const ListEntitiesCameraResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.disabled_by_default);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.icon);
@@ -1544,7 +1546,7 @@ uint8_t *ListEntitiesClimateResponse::encode_msg(const void *self, uint8_t *__re
const auto &msg = *static_cast<const ListEntitiesClimateResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.supports_current_temperature);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.supports_two_point_target_temperature);
for (const auto &it : *msg.supported_modes) {
@@ -1781,7 +1783,7 @@ uint8_t *ListEntitiesWaterHeaterResponse::encode_msg(const void *self,
const auto &msg = *static_cast<const ListEntitiesWaterHeaterResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 4, msg.icon);
#endif
@@ -1908,7 +1910,7 @@ uint8_t *ListEntitiesNumberResponse::encode_msg(const void *self, uint8_t *__res
const auto &msg = *static_cast<const ListEntitiesNumberResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -1999,7 +2001,7 @@ uint8_t *ListEntitiesSelectResponse::encode_msg(const void *self, uint8_t *__res
const auto &msg = *static_cast<const ListEntitiesSelectResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -2078,7 +2080,7 @@ uint8_t *ListEntitiesSirenResponse::encode_msg(const void *self, uint8_t *__rest
const auto &msg = *static_cast<const ListEntitiesSirenResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -2180,7 +2182,7 @@ uint8_t *ListEntitiesLockResponse::encode_msg(const void *self, uint8_t *__restr
const auto &msg = *static_cast<const ListEntitiesLockResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -2263,7 +2265,7 @@ uint8_t *ListEntitiesButtonResponse::encode_msg(const void *self, uint8_t *__res
const auto &msg = *static_cast<const ListEntitiesButtonResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -2332,7 +2334,7 @@ uint8_t *ListEntitiesMediaPlayerResponse::encode_msg(const void *self,
const auto &msg = *static_cast<const ListEntitiesMediaPlayerResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -3178,7 +3180,7 @@ uint8_t *ListEntitiesAlarmControlPanelResponse::encode_msg(const void *self,
const auto &msg = *static_cast<const ListEntitiesAlarmControlPanelResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -3258,7 +3260,7 @@ uint8_t *ListEntitiesTextResponse::encode_msg(const void *self, uint8_t *__restr
const auto &msg = *static_cast<const ListEntitiesTextResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -3337,7 +3339,7 @@ uint8_t *ListEntitiesDateResponse::encode_msg(const void *self, uint8_t *__restr
const auto &msg = *static_cast<const ListEntitiesDateResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -3418,7 +3420,7 @@ uint8_t *ListEntitiesTimeResponse::encode_msg(const void *self, uint8_t *__restr
const auto &msg = *static_cast<const ListEntitiesTimeResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -3499,7 +3501,7 @@ uint8_t *ListEntitiesEventResponse::encode_msg(const void *self, uint8_t *__rest
const auto &msg = *static_cast<const ListEntitiesEventResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -3561,7 +3563,7 @@ uint8_t *ListEntitiesValveResponse::encode_msg(const void *self, uint8_t *__rest
const auto &msg = *static_cast<const ListEntitiesValveResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -3649,7 +3651,7 @@ uint8_t *ListEntitiesDateTimeResponse::encode_msg(const void *self,
const auto &msg = *static_cast<const ListEntitiesDateTimeResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -3722,7 +3724,7 @@ uint8_t *ListEntitiesUpdateResponse::encode_msg(const void *self, uint8_t *__res
const auto &msg = *static_cast<const ListEntitiesUpdateResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.icon);
#endif
@@ -3878,7 +3880,7 @@ uint8_t *ListEntitiesInfraredResponse::encode_msg(const void *self,
const auto &msg = *static_cast<const ListEntitiesInfraredResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 4, msg.icon);
#endif
@@ -3996,7 +3998,7 @@ uint8_t *ListEntitiesRadioFrequencyResponse::encode_msg(const void *self,
const auto &msg = *static_cast<const ListEntitiesRadioFrequencyResponse *>(self);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 10, msg.object_id);
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 21, msg.key);
pos = ProtoEncode::encode_short_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
pos = ProtoEncode::encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, 26, msg.name);
#ifdef USE_ENTITY_ICON
pos = ProtoEncode::encode_string(pos PROTO_ENCODE_DEBUG_ARG, 4, msg.icon);
#endif
@@ -4175,6 +4177,8 @@ uint8_t *UsbDeviceDescriptor::encode_msg(const void *self, uint8_t *__restrict__
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 2, msg.product_id);
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 3, msg.bcd_device);
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 4, msg.interface_number);
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 5, msg.interface_class);
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 6, msg.port_number);
return pos;
}
uint32_t UsbDeviceDescriptor::calc_size_msg(const void *self) {
@@ -4184,6 +4188,8 @@ uint32_t UsbDeviceDescriptor::calc_size_msg(const void *self) {
size += ProtoSize::calc_uint32(1, msg.product_id);
size += ProtoSize::calc_uint32(1, msg.bcd_device);
size += ProtoSize::calc_uint32(1, msg.interface_number);
size += ProtoSize::calc_uint32(1, msg.interface_class);
size += ProtoSize::calc_uint32(1, msg.port_number);
return size;
}
uint8_t *SerialProxyIdentity::encode_msg(const void *self, uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM) {
+12 -9
View File
@@ -399,7 +399,7 @@ class InfoResponseProtoMessage : public ProtoMessage {
public:
StringRef object_id{};
uint32_t key{0};
StringRef name{};
ProgmemStringRef name{};
bool disabled_by_default{false};
#ifdef USE_ENTITY_ICON
StringRef icon{nullptr, 0}; // null until set, encode only
@@ -750,6 +750,7 @@ class WizardCapabilities final : public ProtoMessage {
class SendspinCapabilities final : public ProtoMessage {
public:
uint32_t feature_flags{0};
StringRef client_id{nullptr, 0}; // null until set, encode only
static uint8_t *encode_msg(const void *self, uint8_t *pos PROTO_ENCODE_DEBUG_PARAM);
uint8_t *encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const {
return encode_msg(this, buffer.get_pos() PROTO_ENCODE_DEBUG_ARG);
@@ -1388,8 +1389,8 @@ class NoiseEncryptionSetKeyResponse final : public ProtoMessage {
#ifdef USE_API_HOMEASSISTANT_SERVICES
class HomeassistantServiceMap final : public ProtoMessage {
public:
StringRef key{nullptr, 0}; // null until set, encode only
StringRef value{nullptr, 0}; // null until set, encode only
ProgmemStringRef key{};
ProgmemStringRef value{};
static uint8_t *encode_msg(const void *self, uint8_t *pos PROTO_ENCODE_DEBUG_PARAM);
uint8_t *encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const {
return encode_msg(this, buffer.get_pos() PROTO_ENCODE_DEBUG_ARG);
@@ -1409,7 +1410,7 @@ class HomeassistantActionRequest final : public ProtoMessage {
#ifdef HAS_PROTO_MESSAGE_DUMP
const LogString *message_name() const override { return LOG_STR("homeassistant_action_request"); }
#endif
StringRef service{nullptr, 0}; // null until set, encode only
ProgmemStringRef service{};
FixedVector<HomeassistantServiceMap> data{};
FixedVector<HomeassistantServiceMap> data_template{};
FixedVector<HomeassistantServiceMap> variables{};
@@ -1576,13 +1577,13 @@ class GetTimeResponse final : public ProtoDecodableMessage {
#ifdef USE_API_USER_DEFINED_ACTIONS
class ListEntitiesServicesArgument final : public ProtoMessage {
public:
StringRef name{nullptr, 0}; // null until set, encode only
ProgmemStringRef name{};
enums::ServiceArgType type{};
#ifdef USE_API_USER_DEFINED_ACTION_METADATA
StringRef description{nullptr, 0}; // null until set, encode only
ProgmemStringRef description{};
#endif
#ifdef USE_API_USER_DEFINED_ACTION_METADATA
StringRef example{nullptr, 0}; // null until set, encode only
ProgmemStringRef example{};
#endif
static uint8_t *encode_msg(const void *self, uint8_t *pos PROTO_ENCODE_DEBUG_PARAM);
uint8_t *encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const {
@@ -1603,12 +1604,12 @@ class ListEntitiesServicesResponse final : public ProtoMessage {
#ifdef HAS_PROTO_MESSAGE_DUMP
const LogString *message_name() const override { return LOG_STR("list_entities_services_response"); }
#endif
StringRef name{nullptr, 0}; // null until set, encode only
ProgmemStringRef name{};
uint32_t key{0};
FixedVector<ListEntitiesServicesArgument> args{};
enums::SupportsResponseType supports_response{};
#ifdef USE_API_USER_DEFINED_ACTION_METADATA
StringRef description{nullptr, 0}; // null until set, encode only
ProgmemStringRef description{};
#endif
static uint8_t *encode_msg(const void *self, uint8_t *pos PROTO_ENCODE_DEBUG_PARAM);
uint8_t *encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const {
@@ -4083,6 +4084,8 @@ class UsbDeviceDescriptor final : public ProtoMessage {
uint32_t product_id{0};
uint32_t bcd_device{0};
uint32_t interface_number{0};
uint32_t interface_class{0};
uint32_t port_number{0};
static uint8_t *encode_msg(const void *self, uint8_t *pos PROTO_ENCODE_DEBUG_PARAM);
uint8_t *encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) const {
return encode_msg(this, buffer.get_pos() PROTO_ENCODE_DEBUG_ARG);
+47 -36
View File
@@ -12,8 +12,9 @@ namespace esphome::api {
#ifdef USE_ESP8266
// Out-of-line to avoid inlining strlen_P/memcpy_P at every call site
void DumpBuffer::append_p_esp8266(const char *str) {
size_t len = strlen_P(str);
void DumpBuffer::append_p_esp8266(const char *str) { this->append_p_esp8266(str, strlen_P(str)); }
void DumpBuffer::append_p_esp8266(const char *str, size_t len) {
size_t space = CAPACITY - 1 - pos_;
if (len > space)
len = space;
@@ -134,6 +135,13 @@ static void dump_progmem_bytes_field(DumpBuffer &out, const char *field_name, co
progmem_memcpy(data_buf, data, len);
dump_bytes_field(out, field_name, data_buf, len, indent);
}
// Helper for string fields in flash
static void dump_progmem_string_field(DumpBuffer &out, const char *field_name, const ProgmemStringRef &value,
int indent = 2) {
append_field_prefix(out, field_name, indent);
out.append("'").append_p(value.progmem_ptr(), value.size()).append("'\n");
}
#pragma GCC diagnostic pop
template<> const char *proto_enum_to_string<enums::DisconnectReason>(enums::DisconnectReason value) {
@@ -1119,6 +1127,7 @@ const char *WizardCapabilities::dump_to(DumpBuffer &out) const {
const char *SendspinCapabilities::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("SendspinCapabilities"));
dump_field(out, ESPHOME_PSTR("feature_flags"), this->feature_flags);
dump_field(out, ESPHOME_PSTR("client_id"), this->client_id);
return out.c_str();
}
#endif
@@ -1182,7 +1191,7 @@ const char *ListEntitiesBinarySensorResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesBinarySensorResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("device_class"), this->device_class);
dump_field(out, ESPHOME_PSTR("is_status_binary_sensor"), this->is_status_binary_sensor);
dump_field(out, ESPHOME_PSTR("disabled_by_default"), this->disabled_by_default);
@@ -1211,7 +1220,7 @@ const char *ListEntitiesCoverResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesCoverResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("assumed_state"), this->assumed_state);
dump_field(out, ESPHOME_PSTR("supports_position"), this->supports_position);
dump_field(out, ESPHOME_PSTR("supports_tilt"), this->supports_tilt);
@@ -1257,7 +1266,7 @@ const char *ListEntitiesFanResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesFanResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("supports_oscillation"), this->supports_oscillation);
dump_field(out, ESPHOME_PSTR("supports_speed"), this->supports_speed);
dump_field(out, ESPHOME_PSTR("supports_direction"), this->supports_direction);
@@ -1312,7 +1321,7 @@ const char *ListEntitiesLightResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesLightResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
for (const auto &it : *this->supported_color_modes) {
dump_field(out, ESPHOME_PSTR("supported_color_modes"), static_cast<enums::ColorMode>(it), 4);
}
@@ -1391,7 +1400,7 @@ const char *ListEntitiesSensorResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesSensorResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -1423,7 +1432,7 @@ const char *ListEntitiesSwitchResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesSwitchResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -1461,7 +1470,7 @@ const char *ListEntitiesTextSensorResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesTextSensorResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -1511,13 +1520,13 @@ const char *NoiseEncryptionSetKeyResponse::dump_to(DumpBuffer &out) const {
#ifdef USE_API_HOMEASSISTANT_SERVICES
const char *HomeassistantServiceMap::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("HomeassistantServiceMap"));
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("value"), this->value);
dump_progmem_string_field(out, ESPHOME_PSTR("key"), this->key);
dump_progmem_string_field(out, ESPHOME_PSTR("value"), this->value);
return out.c_str();
}
const char *HomeassistantActionRequest::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("HomeassistantActionRequest"));
dump_field(out, ESPHOME_PSTR("service"), this->service);
dump_progmem_string_field(out, ESPHOME_PSTR("service"), this->service);
for (const auto &it : this->data) {
out.append(4, ' ').append_p(ESPHOME_PSTR("data")).append(": ");
it.dump_to(out);
@@ -1612,19 +1621,19 @@ const char *GetTimeResponse::dump_to(DumpBuffer &out) const {
#ifdef USE_API_USER_DEFINED_ACTIONS
const char *ListEntitiesServicesArgument::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesServicesArgument"));
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("type"), static_cast<enums::ServiceArgType>(this->type));
#ifdef USE_API_USER_DEFINED_ACTION_METADATA
dump_field(out, ESPHOME_PSTR("description"), this->description);
dump_progmem_string_field(out, ESPHOME_PSTR("description"), this->description);
#endif
#ifdef USE_API_USER_DEFINED_ACTION_METADATA
dump_field(out, ESPHOME_PSTR("example"), this->example);
dump_progmem_string_field(out, ESPHOME_PSTR("example"), this->example);
#endif
return out.c_str();
}
const char *ListEntitiesServicesResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesServicesResponse"));
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("key"), this->key);
for (const auto &it : this->args) {
out.append(4, ' ').append_p(ESPHOME_PSTR("args")).append(": ");
@@ -1633,7 +1642,7 @@ const char *ListEntitiesServicesResponse::dump_to(DumpBuffer &out) const {
}
dump_field(out, ESPHOME_PSTR("supports_response"), static_cast<enums::SupportsResponseType>(this->supports_response));
#ifdef USE_API_USER_DEFINED_ACTION_METADATA
dump_field(out, ESPHOME_PSTR("description"), this->description);
dump_progmem_string_field(out, ESPHOME_PSTR("description"), this->description);
#endif
return out.c_str();
}
@@ -1692,7 +1701,7 @@ const char *ListEntitiesCameraResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesCameraResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("disabled_by_default"), this->disabled_by_default);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
@@ -1725,7 +1734,7 @@ const char *ListEntitiesClimateResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesClimateResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
dump_field(out, ESPHOME_PSTR("supports_current_temperature"), this->supports_current_temperature);
dump_field(out, ESPHOME_PSTR("supports_two_point_target_temperature"), this->supports_two_point_target_temperature);
for (const auto &it : *this->supported_modes) {
@@ -1823,7 +1832,7 @@ const char *ListEntitiesWaterHeaterResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesWaterHeaterResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -1877,7 +1886,7 @@ const char *ListEntitiesNumberResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesNumberResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -1919,7 +1928,7 @@ const char *ListEntitiesSelectResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesSelectResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -1958,7 +1967,7 @@ const char *ListEntitiesSirenResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesSirenResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -2005,7 +2014,7 @@ const char *ListEntitiesLockResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesLockResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -2046,7 +2055,7 @@ const char *ListEntitiesButtonResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesButtonResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -2081,7 +2090,7 @@ const char *ListEntitiesMediaPlayerResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesMediaPlayerResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -2471,7 +2480,7 @@ const char *ListEntitiesAlarmControlPanelResponse::dump_to(DumpBuffer &out) cons
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesAlarmControlPanelResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -2510,7 +2519,7 @@ const char *ListEntitiesTextResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesTextResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -2550,7 +2559,7 @@ const char *ListEntitiesDateResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesDateResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -2590,7 +2599,7 @@ const char *ListEntitiesTimeResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesTimeResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -2630,7 +2639,7 @@ const char *ListEntitiesEventResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesEventResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -2660,7 +2669,7 @@ const char *ListEntitiesValveResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesValveResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -2702,7 +2711,7 @@ const char *ListEntitiesDateTimeResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesDateTimeResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -2738,7 +2747,7 @@ const char *ListEntitiesUpdateResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesUpdateResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -2801,7 +2810,7 @@ const char *ListEntitiesInfraredResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesInfraredResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -2859,7 +2868,7 @@ const char *ListEntitiesRadioFrequencyResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, ESPHOME_PSTR("ListEntitiesRadioFrequencyResponse"));
dump_field(out, ESPHOME_PSTR("object_id"), this->object_id);
dump_field(out, ESPHOME_PSTR("key"), this->key);
dump_field(out, ESPHOME_PSTR("name"), this->name);
dump_progmem_string_field(out, ESPHOME_PSTR("name"), this->name);
#ifdef USE_ENTITY_ICON
dump_field(out, ESPHOME_PSTR("icon"), this->icon);
#endif
@@ -2942,6 +2951,8 @@ const char *UsbDeviceDescriptor::dump_to(DumpBuffer &out) const {
dump_field(out, ESPHOME_PSTR("product_id"), this->product_id);
dump_field(out, ESPHOME_PSTR("bcd_device"), this->bcd_device);
dump_field(out, ESPHOME_PSTR("interface_number"), this->interface_number);
dump_field(out, ESPHOME_PSTR("interface_class"), this->interface_class);
dump_field(out, ESPHOME_PSTR("port_number"), this->port_number);
return out.c_str();
}
const char *SerialProxyIdentity::dump_to(DumpBuffer &out) const {
+1 -1
View File
@@ -547,7 +547,7 @@ void APIServer::send_homeassistant_action(const HomeassistantActionRequest &call
// arrive before the subscription and are lost - warn instead of failing silently.
ESP_LOGW(TAG, "Home Assistant %s '%.*s' dropped; %s",
call.is_event ? LOG_STR_LITERAL("event") : LOG_STR_LITERAL("action"),
static_cast<int>(call.service.size()), call.service.empty() ? "" : call.service.c_str(),
static_cast<int>(call.service.size()), call.service.empty() ? "" : call.service.progmem_ptr(),
this->is_connected() ? LOG_STR_LITERAL("client has not subscribed to actions (yet)")
: LOG_STR_LITERAL("no client connected"));
}
+8 -8
View File
@@ -247,7 +247,7 @@ class CustomAPIDevice {
*/
void call_homeassistant_service(const std::string &service_name) {
HomeassistantActionRequest resp;
resp.service = StringRef(service_name);
resp.service = ProgmemStringRef(StringRef(service_name));
global_api_server->send_homeassistant_action(resp);
}
@@ -267,12 +267,12 @@ class CustomAPIDevice {
*/
void call_homeassistant_service(const std::string &service_name, const std::map<std::string, std::string> &data) {
HomeassistantActionRequest resp;
resp.service = StringRef(service_name);
resp.service = ProgmemStringRef(StringRef(service_name));
resp.data.init(data.size());
for (auto &it : data) {
auto &kv = resp.data.emplace_back();
kv.key = StringRef(it.first);
kv.value = StringRef(it.second); // data map lives until send completes
kv.key = ProgmemStringRef(StringRef(it.first));
kv.value = ProgmemStringRef(StringRef(it.second)); // data map lives until send completes
}
global_api_server->send_homeassistant_action(resp);
}
@@ -289,7 +289,7 @@ class CustomAPIDevice {
*/
void fire_homeassistant_event(const std::string &event_name) {
HomeassistantActionRequest resp;
resp.service = StringRef(event_name);
resp.service = ProgmemStringRef(StringRef(event_name));
resp.is_event = true;
global_api_server->send_homeassistant_action(resp);
}
@@ -309,13 +309,13 @@ class CustomAPIDevice {
*/
void fire_homeassistant_event(const std::string &service_name, const std::map<std::string, std::string> &data) {
HomeassistantActionRequest resp;
resp.service = StringRef(service_name);
resp.service = ProgmemStringRef(StringRef(service_name));
resp.is_event = true;
resp.data.init(data.size());
for (auto &it : data) {
auto &kv = resp.data.emplace_back();
kv.key = StringRef(it.first);
kv.value = StringRef(it.second); // data map lives until send completes
kv.key = ProgmemStringRef(StringRef(it.first));
kv.value = ProgmemStringRef(StringRef(it.second)); // data map lives until send completes
}
global_api_server->send_homeassistant_action(resp);
}
+4 -28
View File
@@ -116,42 +116,18 @@ template<typename... Ts> class HomeAssistantServiceCallAction final : public Act
const Field *fields = this->fields_;
const size_t total = 1 + this->data_count_ + this->data_template_count_ + this->variables_count_;
// Lambda results, and on ESP8266 the RAM copies of the flash strings, must live until the send
// Lambda results must live until the send; the constant strings are encoded from flash where they are
size_t lambda_count = 0;
#ifdef USE_ESP8266
size_t flash_len = 0;
#endif
for (size_t i = 0; i < total; i++) {
for (size_t i = 0; i < total; i++)
lambda_count += fields[i].fn != nullptr;
#ifdef USE_ESP8266
if (fields[i].fn == nullptr)
flash_len += ESPHOME_strlen_P(fields[i].value);
if (fields[i].key != nullptr)
flash_len += ESPHOME_strlen_P(fields[i].key);
#endif
}
FixedVector<std::string> results;
results.init(lambda_count);
#ifdef USE_ESP8266
SmallBufferWithHeapFallback<128, char> flash_copy(flash_len);
char *cursor = flash_copy.get();
#endif
auto string_ref = [&](const char *str) {
#ifdef USE_ESP8266
size_t len = ESPHOME_strlen_P(str);
memcpy_P(cursor, str, len);
StringRef ref(cursor, len);
cursor += len;
return ref;
#else
return StringRef(str);
#endif
};
auto string_ref = [](const char *str) { return ProgmemStringRef(str, ESPHOME_strlen_P(str)); };
auto value_ref = [&](const Field &field) {
if (field.fn == nullptr)
return string_ref(field.value);
results.push_back(field.fn(x...));
return StringRef(results.back());
return ProgmemStringRef(StringRef(results.back()));
};
auto fill = [&](FixedVector<HomeassistantServiceMap> &dest, uint8_t count) {
dest.init(count);
+1 -2
View File
@@ -99,8 +99,7 @@ ListEntitiesIterator::ListEntitiesIterator(APIConnection *client) : client_(clie
static constexpr uint8_t SERVICE_YIELD_INTERVAL = 3;
bool ListEntitiesIterator::on_service(UserServiceDescriptor *service) {
UserActionScratch scratch;
auto resp = service->encode_list_service_response(scratch);
auto resp = service->encode_list_service_response();
if (!this->client_->send_message(resp))
return false;
// at_ is this service's index
+39
View File
@@ -409,6 +409,10 @@ class ProtoEncode {
/// Tag must be a single-byte varint (< 128). Always encodes (no zero check).
[[nodiscard]] static inline uint8_t *encode_short_string_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
uint8_t tag, const StringRef &ref) {
#ifdef USE_ESP8266
// memcpy_P reads RAM too, and sharing the progmem copy is smaller than an inline memcpy
return encode_short_progmem_string_force(pos PROTO_ENCODE_DEBUG_ARG, tag, ProgmemStringRef(ref));
#else
#ifdef ESPHOME_DEBUG_API
assert(ref.size() < 128 && "encode_short_string_force: string exceeds max_data_length < 128");
#endif
@@ -417,6 +421,19 @@ class ProtoEncode {
pos[1] = static_cast<uint8_t>(ref.size());
std::memcpy(pos + 2, ref.c_str(), ref.size());
return pos + 2 + ref.size();
#endif
}
/// encode_short_string_force for a string that may be in flash (PROGMEM).
[[nodiscard]] static inline uint8_t *encode_short_progmem_string_force(
uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint8_t tag, const ProgmemStringRef &ref) {
#ifdef ESPHOME_DEBUG_API
assert(ref.size() < 128 && "encode_short_progmem_string_force: string exceeds max_data_length < 128");
#endif
PROTO_ENCODE_CHECK_BOUNDS(pos, 2 + ref.size());
pos[0] = tag;
pos[1] = static_cast<uint8_t>(ref.size());
progmem_memcpy(pos + 2, ref.progmem_ptr(), ref.size());
return pos + 2 + ref.size();
}
/// Write a precomputed tag byte + 32-bit value. Outlined on embedded: one copy beats inline stores per field.
[[nodiscard]] static PROTO_OUTLINE_FOR_SIZE uint8_t *write_tag_and_fixed32(
@@ -481,6 +498,17 @@ class ProtoEncode {
progmem_memcpy(pos, data, len);
return pos + len;
}
/// encode_progmem_bytes for a string field that may be in flash.
[[nodiscard]] static inline uint8_t *encode_progmem_string(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
uint32_t field_id, const ProgmemStringRef &ref) {
return encode_progmem_bytes(pos PROTO_ENCODE_DEBUG_ARG, field_id,
reinterpret_cast<const uint8_t *>(ref.progmem_ptr()), ref.size());
}
[[nodiscard]] static inline uint8_t *encode_progmem_string_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
uint32_t field_id, const ProgmemStringRef &ref) {
return encode_progmem_bytes_force(pos PROTO_ENCODE_DEBUG_ARG, field_id,
reinterpret_cast<const uint8_t *>(ref.progmem_ptr()), ref.size());
}
[[nodiscard]] static inline uint8_t *encode_uint32_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
uint32_t field_id, uint32_t value) {
pos = encode_field_raw(pos PROTO_ENCODE_DEBUG_ARG, field_id, 0);
@@ -680,9 +708,20 @@ class DumpBuffer {
return *this;
}
/// Append len bytes of a PROGMEM string
DumpBuffer &append_p(const char *str, size_t len) {
#ifdef USE_ESP8266
append_p_esp8266(str, len);
#else
append_impl_(str, len);
#endif
return *this;
}
#ifdef USE_ESP8266
/// Out-of-line ESP8266 PROGMEM append to avoid inlining strlen_P/memcpy_P at every call site
void append_p_esp8266(const char *str);
void append_p_esp8266(const char *str, size_t len);
#endif
const char *c_str() const { return buf_; }
+9 -20
View File
@@ -1,47 +1,36 @@
#include "user_services.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
#include "esphome/core/progmem.h"
#include "esphome/core/string_ref.h"
namespace esphome::api {
StringRef UserServiceStatic::str_(size_t idx, std::span<char> &scratch) const {
ProgmemStringRef UserServiceStatic::str_(size_t idx) const {
const char *s = progmem_read_ptr(&this->strings_[idx]);
if (s == nullptr)
return {};
#ifdef USE_ESP8266
// Codegen sizes the scratch buffer for the largest service; the bound only guards other callers
if (scratch.empty())
return {};
size_t len = strnlen_P(s, scratch.size() - 1);
progmem_memcpy(scratch.data(), s, len);
scratch[len] = '\0';
StringRef ref(scratch.data(), len);
scratch = scratch.subspan(len + 1);
return ref;
#else
return StringRef(s);
#endif
return ProgmemStringRef(s, ESPHOME_strlen_P(s));
}
ListEntitiesServicesResponse UserServiceStatic::encode_list_service_response_(
std::span<const enums::ServiceArgType> arg_types, std::span<char> scratch) const {
std::span<const enums::ServiceArgType> arg_types) const {
ListEntitiesServicesResponse msg;
msg.name = this->str_(0, scratch);
msg.name = this->str_(0);
msg.key = this->key_;
msg.supports_response = this->supports_response_;
#ifdef USE_API_USER_DEFINED_ACTION_METADATA
msg.description = this->str_(1, scratch);
msg.description = this->str_(1);
#endif
msg.args.init(arg_types.size());
for (size_t i = 0; i < arg_types.size(); i++) {
size_t base = USER_ACTION_HEADER_STRINGS + i * USER_ACTION_ARG_STRINGS;
auto &arg = msg.args.emplace_back();
arg.type = arg_types[i];
arg.name = this->str_(base, scratch);
arg.name = this->str_(base);
#ifdef USE_API_USER_DEFINED_ACTION_METADATA
arg.description = this->str_(base + 1, scratch);
arg.example = this->str_(base + 2, scratch);
arg.description = this->str_(base + 1);
arg.example = this->str_(base + 2);
#endif
}
return msg;
+10 -21
View File
@@ -20,9 +20,8 @@ class APIServer;
class UserServiceDescriptor {
public:
/// Build the list-entities message. On ESP8266 the strings live in PROGMEM and are copied into
/// `scratch`, so the returned message is only valid while `scratch` is; other platforms ignore it.
virtual ListEntitiesServicesResponse encode_list_service_response(std::span<char> scratch) = 0;
/// Build the list-entities message; its strings may point into flash (PROGMEM).
virtual ListEntitiesServicesResponse encode_list_service_response() = 0;
virtual bool execute_service(const ExecuteServiceRequest &req) = 0;
#ifdef USE_API_USER_DEFINED_ACTION_RESPONSES
@@ -37,13 +36,6 @@ template<typename T> T get_execute_arg_value(const ExecuteServiceArgument &arg);
template<typename T> enums::ServiceArgType to_service_arg_type();
// Scratch buffer list-entities hands to encode_list_service_response(); only ESP8266 copies into it
#ifdef USE_ESP8266
using UserActionScratch = std::array<char, API_USER_ACTION_STRINGS_SCRATCH_SIZE>;
#else
using UserActionScratch = std::array<char, 0>;
#endif
// Non-template base for YAML-defined services so the list-entities encoder is compiled once.
// All strings live in one PROGMEM pointer table emitted by codegen (see _action_strings in
// __init__.py), so each service costs a single pointer of RAM. Layout: the action name, then
@@ -63,12 +55,9 @@ class UserServiceStatic : public UserServiceDescriptor {
: strings_(strings), key_(key), supports_response_(supports_response) {}
protected:
ListEntitiesServicesResponse encode_list_service_response_(std::span<const enums::ServiceArgType> arg_types,
std::span<char> scratch) const;
/// Reference table entry `idx`; nullptr gives an empty StringRef.
/// On ESP8266 the bytes are copied out of PROGMEM into `scratch` with a terminator, and the span
/// is advanced past the copy.
StringRef str_(size_t idx, std::span<char> &scratch) const;
ListEntitiesServicesResponse encode_list_service_response_(std::span<const enums::ServiceArgType> arg_types) const;
/// Table entry `idx` as it sits in flash; nullptr gives an empty string.
ProgmemStringRef str_(size_t idx) const;
const char *const *strings_; // PROGMEM pointer table, read with progmem_read_ptr()
uint32_t key_;
@@ -79,9 +68,9 @@ template<typename... Ts> class UserServiceBase : public UserServiceStatic {
public:
using UserServiceStatic::UserServiceStatic;
ListEntitiesServicesResponse encode_list_service_response(std::span<char> scratch) override {
ListEntitiesServicesResponse encode_list_service_response() override {
std::array<enums::ServiceArgType, sizeof...(Ts)> arg_types = {to_service_arg_type<Ts>()...};
return this->encode_list_service_response_(arg_types, scratch);
return this->encode_list_service_response_(arg_types);
}
bool execute_service(const ExecuteServiceRequest &req) override {
@@ -125,9 +114,9 @@ template<typename... Ts> class UserServiceDynamic : public UserServiceDescriptor
this->key_ = fnv1_hash(this->name_.c_str());
}
ListEntitiesServicesResponse encode_list_service_response(std::span<char> /*scratch*/) override {
ListEntitiesServicesResponse encode_list_service_response() override {
ListEntitiesServicesResponse msg;
msg.name = StringRef(this->name_);
msg.name = ProgmemStringRef(StringRef(this->name_));
msg.key = this->key_;
msg.supports_response = enums::SUPPORTS_RESPONSE_NONE; // Dynamic services don't support responses yet
std::array<enums::ServiceArgType, sizeof...(Ts)> arg_types = {to_service_arg_type<Ts>()...};
@@ -135,7 +124,7 @@ template<typename... Ts> class UserServiceDynamic : public UserServiceDescriptor
for (size_t i = 0; i < sizeof...(Ts); i++) {
auto &arg = msg.args.emplace_back();
arg.type = arg_types[i];
arg.name = StringRef(this->arg_names_[i]);
arg.name = ProgmemStringRef(StringRef(this->arg_names_[i]));
}
return msg;
}
+2 -2
View File
@@ -30,10 +30,10 @@ void AQISensor::dump_config() {
ESP_LOGCONFIG(TAG, " Extended Range: %s",
this->extended_range_ ? LOG_STR_LITERAL("enabled") : LOG_STR_LITERAL("disabled"));
if (this->pm_2_5_sensor_ != nullptr) {
ESP_LOGCONFIG(TAG, " PM2.5 Sensor: '%s'", this->pm_2_5_sensor_->get_name().c_str());
ESP_LOGCONFIG(TAG, " PM2.5 Sensor: '%s'", LOG_STR_ARG(this->pm_2_5_sensor_->get_log_name()));
}
if (this->pm_10_0_sensor_ != nullptr) {
ESP_LOGCONFIG(TAG, " PM10 Sensor: '%s'", this->pm_10_0_sensor_->get_name().c_str());
ESP_LOGCONFIG(TAG, " PM10 Sensor: '%s'", LOG_STR_ARG(this->pm_10_0_sensor_->get_log_name()));
}
LOG_SENSOR(" ", "AQI", this);
}
+1 -1
View File
@@ -1289,7 +1289,7 @@ void ATM90E32Component::check_phase_status() {
if (!status.empty()) {
status.pop_back(); // remove space
status.pop_back(); // remove semicolon
ESP_LOGW(TAG, "%s: %s", sensor->get_name().c_str(), status.c_str());
ESP_LOGW(TAG, "%s: %s", LOG_STR_ARG(sensor->get_log_name()), status.c_str());
sensor->publish_state(status);
} else {
sensor->publish_state("Okay");
@@ -8,11 +8,12 @@ ESPHOME_LOG_TAG(TAG, "atm90e32.button");
void ATM90E32GainCalibrationButton::press_action() {
if (this->parent_ == nullptr) {
ESP_LOGW(TAG, "[CALIBRATION] No meters assigned to Gain Calibration button [%s]", this->get_name().c_str());
ESP_LOGW(TAG, "[CALIBRATION] No meters assigned to Gain Calibration button [%s]",
LOG_STR_ARG(this->get_log_name()));
return;
}
ESP_LOGI(TAG, "%s", this->get_name().c_str());
ESP_LOGI(TAG, "%s", LOG_STR_ARG(this->get_log_name()));
ESP_LOGI(TAG,
"[CALIBRATION] Use gain_ct: & gain_voltage: under each phase_x: in your config file to save these values");
this->parent_->run_gain_calibrations();
@@ -20,21 +21,22 @@ void ATM90E32GainCalibrationButton::press_action() {
void ATM90E32ClearGainCalibrationButton::press_action() {
if (this->parent_ == nullptr) {
ESP_LOGW(TAG, "[CALIBRATION] No meters assigned to Clear Gain button [%s]", this->get_name().c_str());
ESP_LOGW(TAG, "[CALIBRATION] No meters assigned to Clear Gain button [%s]", LOG_STR_ARG(this->get_log_name()));
return;
}
ESP_LOGI(TAG, "%s", this->get_name().c_str());
ESP_LOGI(TAG, "%s", LOG_STR_ARG(this->get_log_name()));
this->parent_->clear_gain_calibrations();
}
void ATM90E32OffsetCalibrationButton::press_action() {
if (this->parent_ == nullptr) {
ESP_LOGW(TAG, "[CALIBRATION] No meters assigned to Offset Calibration button [%s]", this->get_name().c_str());
ESP_LOGW(TAG, "[CALIBRATION] No meters assigned to Offset Calibration button [%s]",
LOG_STR_ARG(this->get_log_name()));
return;
}
ESP_LOGI(TAG, "%s", this->get_name().c_str());
ESP_LOGI(TAG, "%s", LOG_STR_ARG(this->get_log_name()));
ESP_LOGI(TAG, "[CALIBRATION] **NOTE: CTs and ACVs must be 0 during this process. USB power only**");
ESP_LOGI(TAG, "[CALIBRATION] Use offset_voltage: & offset_current: under each phase_x: in your config file to save "
"these values");
@@ -43,21 +45,22 @@ void ATM90E32OffsetCalibrationButton::press_action() {
void ATM90E32ClearOffsetCalibrationButton::press_action() {
if (this->parent_ == nullptr) {
ESP_LOGW(TAG, "[CALIBRATION] No meters assigned to Clear Offset button [%s]", this->get_name().c_str());
ESP_LOGW(TAG, "[CALIBRATION] No meters assigned to Clear Offset button [%s]", LOG_STR_ARG(this->get_log_name()));
return;
}
ESP_LOGI(TAG, "%s", this->get_name().c_str());
ESP_LOGI(TAG, "%s", LOG_STR_ARG(this->get_log_name()));
this->parent_->clear_offset_calibrations();
}
void ATM90E32PowerOffsetCalibrationButton::press_action() {
if (this->parent_ == nullptr) {
ESP_LOGW(TAG, "[CALIBRATION] No meters assigned to Power Calibration button [%s]", this->get_name().c_str());
ESP_LOGW(TAG, "[CALIBRATION] No meters assigned to Power Calibration button [%s]",
LOG_STR_ARG(this->get_log_name()));
return;
}
ESP_LOGI(TAG, "%s", this->get_name().c_str());
ESP_LOGI(TAG, "%s", LOG_STR_ARG(this->get_log_name()));
ESP_LOGI(TAG, "[CALIBRATION] **NOTE: CTs must be 0 during this process. Voltage reference should be present**");
ESP_LOGI(TAG, "[CALIBRATION] Use offset_active_power: & offset_reactive_power: under each phase_x: in your config "
"file to save these values");
@@ -66,11 +69,11 @@ void ATM90E32PowerOffsetCalibrationButton::press_action() {
void ATM90E32ClearPowerOffsetCalibrationButton::press_action() {
if (this->parent_ == nullptr) {
ESP_LOGW(TAG, "[CALIBRATION] No meters assigned to Clear Power button [%s]", this->get_name().c_str());
ESP_LOGW(TAG, "[CALIBRATION] No meters assigned to Clear Power button [%s]", LOG_STR_ARG(this->get_log_name()));
return;
}
ESP_LOGI(TAG, "%s", this->get_name().c_str());
ESP_LOGI(TAG, "%s", LOG_STR_ARG(this->get_log_name()));
this->parent_->clear_power_offset_calibrations();
}
+1 -1
View File
@@ -338,7 +338,7 @@ async def to_code(config: ConfigType) -> None:
# HTTPS streams verify the server against the root certificate bundle
require_certificate_bundle()
add_idf_component(name="esphome/esp-audio-libs", ref="4.0.1")
add_idf_component(name="esphome/esp-audio-libs", ref="4.1.0")
data = _get_data()
@@ -211,7 +211,7 @@ void BedJetClimate::control(const ClimateCall &call) {
} else if (fan_mode == CLIMATE_FAN_HIGH) {
result = this->parent_->set_fan_speed(75);
} else {
ESP_LOGW(TAG, "[%s] Unsupported fan mode: %s", this->get_name().c_str(),
ESP_LOGW(TAG, "[%s] Unsupported fan mode: %s", LOG_STR_ARG(this->get_log_name()),
LOG_STR_ARG(climate_fan_mode_to_string(fan_mode)));
return;
}
@@ -223,7 +223,7 @@ void BedJetClimate::control(const ClimateCall &call) {
auto fan_mode = call.get_custom_fan_mode();
auto fan_index = bedjet_fan_speed_to_step(fan_mode.c_str());
if (fan_index <= 19) {
ESP_LOGV(TAG, "[%s] Converted fan mode %.*s to bedjet fan step %d", this->get_name().c_str(),
ESP_LOGV(TAG, "[%s] Converted fan mode %.*s to bedjet fan step %d", LOG_STR_ARG(this->get_log_name()),
(int) fan_mode.size(), fan_mode.c_str(), fan_index);
bool result = this->parent_->set_fan_index(fan_index);
if (result) {
@@ -236,7 +236,7 @@ void BedJetClimate::control(const ClimateCall &call) {
void BedJetClimate::on_bedjet_state(bool is_ready) {}
void BedJetClimate::on_status(const BedjetStatusPacket *data) {
ESP_LOGV(TAG, "[%s] Handling on_status with data=%p", this->get_name().c_str(), (void *) data);
ESP_LOGV(TAG, "[%s] Handling on_status with data=%p", LOG_STR_ARG(this->get_log_name()), (void *) data);
auto converted_temp = bedjet_temp_to_c(data->target_temp_step);
if (converted_temp > 0)
@@ -310,11 +310,11 @@ void BedJetClimate::on_status(const BedjetStatusPacket *data) {
break;
default:
ESP_LOGW(TAG, "[%s] Unexpected mode: 0x%02X", this->get_name().c_str(), data->mode);
ESP_LOGW(TAG, "[%s] Unexpected mode: 0x%02X", LOG_STR_ARG(this->get_log_name()), data->mode);
break;
}
ESP_LOGV(TAG, "[%s] After on_status, new mode=%s", this->get_name().c_str(),
ESP_LOGV(TAG, "[%s] After on_status, new mode=%s", LOG_STR_ARG(this->get_log_name()),
LOG_STR_ARG(climate_mode_to_string(this->mode)));
// FIXME: compare new state to previous state.
this->publish_state();
@@ -351,11 +351,11 @@ bool BedJetClimate::update_status_() {
}
void BedJetClimate::update() {
ESP_LOGD(TAG, "[%s] update()", this->get_name().c_str());
ESP_LOGD(TAG, "[%s] update()", LOG_STR_ARG(this->get_log_name()));
// TODO: if the hub component is already polling, do we also need to include polling?
// We're already going to get on_status() at the hub's polling interval.
auto result = this->update_status_();
ESP_LOGD(TAG, "[%s] update_status result=%s", this->get_name().c_str(), result ? "true" : "false");
ESP_LOGD(TAG, "[%s] update_status result=%s", LOG_STR_ARG(this->get_log_name()), result ? "true" : "false");
}
} // namespace esphome::bedjet
+3 -3
View File
@@ -53,7 +53,7 @@ void BedJetFan::control(const fan::FanCall &call) {
}
void BedJetFan::on_status(const BedjetStatusPacket *data) {
ESP_LOGVV(TAG, "[%s] Handling on_status with data=%p", this->get_name().c_str(), (void *) data);
ESP_LOGVV(TAG, "[%s] Handling on_status with data=%p", LOG_STR_ARG(this->get_log_name()), (void *) data);
bool did_change = false;
bool new_state = data->mode != MODE_STANDBY && data->mode != MODE_WAIT;
@@ -96,11 +96,11 @@ bool BedJetFan::update_status_() {
}
void BedJetFan::update() {
ESP_LOGD(TAG, "[%s] update()", this->get_name().c_str());
ESP_LOGD(TAG, "[%s] update()", LOG_STR_ARG(this->get_log_name()));
// TODO: if the hub component is already polling, do we also need to include polling?
// We're already going to get on_status() at the hub's polling interval.
auto result = this->update_status_();
ESP_LOGD(TAG, "[%s] update_status result=%s", this->get_name().c_str(), result ? "true" : "false");
ESP_LOGD(TAG, "[%s] update_status result=%s", LOG_STR_ARG(this->get_log_name()), result ? "true" : "false");
}
/** Resets states to defaults. */
+2 -2
View File
@@ -75,7 +75,7 @@ void BH1750Sensor::dump_config() {
LOG_SENSOR("", "BH1750", this);
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL_FOR, this->get_name().c_str());
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL_FOR, LOG_STR_ARG(this->get_log_name()));
}
LOG_UPDATE_INTERVAL(this);
@@ -154,7 +154,7 @@ void BH1750Sensor::loop() {
break;
}
ESP_LOGV(TAG, "'%s': Illuminance=%.1flx", this->get_name().c_str(), lx);
ESP_LOGV(TAG, "'%s': Illuminance=%.1flx", LOG_STR_ARG(this->get_log_name()), lx);
this->status_clear_warning();
this->publish_state(lx);
this->state_ = IDLE;
@@ -13,7 +13,7 @@ void log_binary_sensor(const char *tag, const char *prefix, const char *type, Bi
return;
}
ESP_LOGCONFIG(tag, "%s%s '%s'", prefix, type, obj->get_name().c_str());
ESP_LOGCONFIG(tag, "%s%s '%s'", prefix, type, LOG_STR_ARG(obj->get_log_name()));
LOG_ENTITY_DEVICE_CLASS(tag, prefix, *obj);
}
@@ -38,7 +38,7 @@ bool BinarySensor::set_new_state(const optional<bool> &new_state) {
#if defined(USE_BINARY_SENSOR) && defined(USE_CONTROLLER_REGISTRY)
ControllerRegistry::notify_binary_sensor_update(this);
#endif
ESP_LOGV(TAG, "'%s' >> %s", this->get_name().c_str(), ONOFFMAYBE(new_state));
ESP_LOGV(TAG, "'%s' >> %s", LOG_STR_ARG(this->get_log_name()), ONOFFMAYBE(new_state));
return true;
}
return false;
@@ -47,7 +47,7 @@ void BinarySensorMap::process_group_() {
}
} else if (this->last_mask_ != 0ULL) {
// no buttons are pressed and the states have changed since last run, so publish NAN
ESP_LOGV(TAG, "'%s' - No binary sensor active, publishing NAN", this->name_.c_str());
ESP_LOGV(TAG, "'%s' - No binary sensor active, publishing NAN", LOG_STR_ARG(this->get_log_name()));
this->publish_state(NAN);
}
+1 -1
View File
@@ -191,7 +191,7 @@ float BL0940::update_temp_(sensor::Sensor *sensor, uint16_le_t temperature) cons
if (sensor != nullptr) {
if (sensor->has_state() && std::abs(converted_temp - sensor->get_state()) > max_temperature_diff_) {
ESP_LOGD(TAG, "Invalid temperature change. Sensor: '%s', Old temperature: %f, New temperature: %f",
sensor->get_name().c_str(), sensor->get_state(), converted_temp);
LOG_STR_ARG(sensor->get_log_name()), sensor->get_state(), converted_temp);
return 0.0f;
}
sensor->publish_state(converted_temp);
@@ -63,7 +63,7 @@ void BLEClientRSSISensor::gap_event_handler(esp_gap_ble_cb_event_t event, esp_bl
void BLEClientRSSISensor::update() {
if (this->node_state != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->get_name().c_str());
ESP_LOGW(TAG, "[%s] Cannot poll, not connected", LOG_STR_ARG(this->get_log_name()));
this->should_update_ = true;
return;
}
@@ -37,13 +37,13 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
switch (event) {
case ESP_GATTC_OPEN_EVT: {
if (param->open.status == ESP_GATT_OK) {
ESP_LOGI(TAG, "[%s] Connected successfully!", this->get_name().c_str());
ESP_LOGI(TAG, "[%s] Connected successfully!", LOG_STR_ARG(this->get_log_name()));
break;
}
break;
}
case ESP_GATTC_CLOSE_EVT: {
ESP_LOGW(TAG, "[%s] Disconnected!", this->get_name().c_str());
ESP_LOGW(TAG, "[%s] Disconnected!", LOG_STR_ARG(this->get_log_name()));
this->status_set_warning();
this->publish_state(NAN);
break;
@@ -102,10 +102,10 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
}
case ESP_GATTC_NOTIFY_EVT: {
if (param->notify.value_len == 0) {
ESP_LOGW(TAG, "[%s] ESP_GATTC_NOTIFY_EVT: empty value", this->get_name().c_str());
ESP_LOGW(TAG, "[%s] ESP_GATTC_NOTIFY_EVT: empty value", LOG_STR_ARG(this->get_log_name()));
break;
}
ESP_LOGD(TAG, "[%s] ESP_GATTC_NOTIFY_EVT: handle=0x%x, value=0x%x", this->get_name().c_str(),
ESP_LOGD(TAG, "[%s] ESP_GATTC_NOTIFY_EVT: handle=0x%x, value=0x%x", LOG_STR_ARG(this->get_log_name()),
param->notify.handle, param->notify.value[0]);
if (param->notify.handle != this->handle)
break;
@@ -143,11 +143,11 @@ float BLESensor::parse_data_(uint8_t *value, uint16_t value_len) {
void BLESensor::update() {
if (this->node_state != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->get_name().c_str());
ESP_LOGW(TAG, "[%s] Cannot poll, not connected", LOG_STR_ARG(this->get_log_name()));
return;
}
if (this->handle == 0) {
ESP_LOGW(TAG, "[%s] Cannot poll, no service or characteristic found", this->get_name().c_str());
ESP_LOGW(TAG, "[%s] Cannot poll, no service or characteristic found", LOG_STR_ARG(this->get_log_name()));
return;
}
@@ -156,7 +156,7 @@ void BLESensor::update() {
if (status) {
this->status_set_warning();
this->publish_state(NAN);
ESP_LOGW(TAG, "[%s] Error sending read request for sensor, status=%d", this->get_name().c_str(), status);
ESP_LOGW(TAG, "[%s] Error sending read request for sensor, status=%d", LOG_STR_ARG(this->get_log_name()), status);
}
}
@@ -38,7 +38,7 @@ void BLETextSensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
switch (event) {
case ESP_GATTC_OPEN_EVT: {
if (param->open.status == ESP_GATT_OK) {
ESP_LOGI(TAG, "[%s] Connected successfully!", this->get_name().c_str());
ESP_LOGI(TAG, "[%s] Connected successfully!", LOG_STR_ARG(this->get_log_name()));
break;
}
break;
@@ -104,10 +104,10 @@ void BLETextSensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
if (param->notify.handle != this->handle)
break;
if (param->notify.value_len == 0) {
ESP_LOGW(TAG, "[%s] ESP_GATTC_NOTIFY_EVT: empty value", this->get_name().c_str());
ESP_LOGW(TAG, "[%s] ESP_GATTC_NOTIFY_EVT: empty value", LOG_STR_ARG(this->get_log_name()));
break;
}
ESP_LOGV(TAG, "[%s] ESP_GATTC_NOTIFY_EVT: handle=0x%x, value=0x%x", this->get_name().c_str(),
ESP_LOGV(TAG, "[%s] ESP_GATTC_NOTIFY_EVT: handle=0x%x, value=0x%x", LOG_STR_ARG(this->get_log_name()),
param->notify.handle, param->notify.value[0]);
this->publish_state(reinterpret_cast<const char *>(param->notify.value), param->notify.value_len);
break;
@@ -124,11 +124,11 @@ void BLETextSensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
void BLETextSensor::update() {
if (this->node_state != espbt::ClientState::ESTABLISHED) {
ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->get_name().c_str());
ESP_LOGW(TAG, "[%s] Cannot poll, not connected", LOG_STR_ARG(this->get_log_name()));
return;
}
if (this->handle == 0) {
ESP_LOGW(TAG, "[%s] Cannot poll, no service or characteristic found", this->get_name().c_str());
ESP_LOGW(TAG, "[%s] Cannot poll, no service or characteristic found", LOG_STR_ARG(this->get_log_name()));
return;
}
@@ -137,7 +137,7 @@ void BLETextSensor::update() {
if (status) {
this->status_set_warning();
this->publish_state("");
ESP_LOGW(TAG, "[%s] Error sending read request for sensor, status=%d", this->get_name().c_str(), status);
ESP_LOGW(TAG, "[%s] Error sending read request for sensor, status=%d", LOG_STR_ARG(this->get_log_name()), status);
}
}
@@ -382,7 +382,7 @@ void BluedroidGattClient::set_disconnecting_() {
esp_err_t BluedroidGattClient::update_conn_params_(uint16_t min_interval, uint16_t max_interval, uint16_t latency,
uint16_t timeout, const char *param_type) {
esp_ble_conn_update_params_t conn_params = {{0}};
esp_ble_conn_update_params_t conn_params = {};
memcpy(conn_params.bda, this->remote_bda_, sizeof(esp_bd_addr_t));
conn_params.min_int = min_interval;
conn_params.max_int = max_interval;
+3 -2
View File
@@ -100,7 +100,8 @@ void BM8563::get_date_(ESPTime &time) {
this->read_register(DATE_FIRST_REG, buf, sizeof(buf));
time.day_of_month = this->bcd2_to_byte_(buf[0] & 0x3f);
time.day_of_week = this->bcd2_to_byte_(buf[1] & 0x07);
// The chip counts weekdays 0-6 from Sunday (not BCD), ESPTime counts 1-7
time.day_of_week = (buf[1] & 0x07) + 1;
time.month = this->bcd2_to_byte_(buf[2] & 0x1f);
uint8_t year_byte = this->bcd2_to_byte_(buf[3] & 0xff);
@@ -115,7 +116,7 @@ void BM8563::get_date_(ESPTime &time) {
void BM8563::set_date_(const ESPTime &time) {
uint8_t buf[4] = {
this->byte_to_bcd2_(time.day_of_month),
this->byte_to_bcd2_(time.day_of_week),
static_cast<uint8_t>(time.day_of_week - 1),
this->byte_to_bcd2_(time.month),
this->byte_to_bcd2_(time.year % 100),
};
+2 -2
View File
@@ -11,12 +11,12 @@ void log_button(const char *tag, const char *prefix, const char *type, Button *o
return;
}
ESP_LOGCONFIG(tag, "%s%s '%s'", prefix, type, obj->get_name().c_str());
ESP_LOGCONFIG(tag, "%s%s '%s'", prefix, type, LOG_STR_ARG(obj->get_log_name()));
LOG_ENTITY_ICON(tag, prefix, *obj);
}
void Button::press() {
ESP_LOGV(TAG, "'%s' Pressed.", this->get_name().c_str());
ESP_LOGV(TAG, "'%s' Pressed.", LOG_STR_ARG(this->get_log_name()));
this->press_action();
this->press_callback_.call();
}
+10 -7
View File
@@ -46,7 +46,7 @@ constexpr StringToUint8 CLIMATE_SWING_MODES_BY_STR[] = {
void ClimateCall::perform() {
this->parent_->control_callback_.call(*this);
ESP_LOGV(TAG, "'%s' - Setting", this->parent_->get_name().c_str());
ESP_LOGV(TAG, "'%s' - Setting", LOG_STR_ARG(this->parent_->get_log_name()));
this->validate_();
if (this->mode_.has_value()) {
const LogString *mode_s = climate_mode_to_string(*this->mode_);
@@ -183,7 +183,7 @@ ClimateCall &ClimateCall::set_mode(const char *mode, size_t len) {
return *this;
}
}
ESP_LOGW(TAG, "'%s' - Unrecognized mode %.*s", this->parent_->get_name().c_str(), (int) len, mode);
ESP_LOGW(TAG, "'%s' - Unrecognized mode %.*s", LOG_STR_ARG(this->parent_->get_log_name()), (int) len, mode);
return *this;
}
@@ -214,7 +214,8 @@ ClimateCall &ClimateCall::set_fan_mode(const char *custom_fan_mode, size_t len)
this->fan_mode_.reset();
return *this;
}
ESP_LOGW(TAG, "'%s' - Unrecognized fan mode %.*s", this->parent_->get_name().c_str(), (int) len, custom_fan_mode);
ESP_LOGW(TAG, "'%s' - Unrecognized fan mode %.*s", LOG_STR_ARG(this->parent_->get_log_name()), (int) len,
custom_fan_mode);
return *this;
}
@@ -252,7 +253,8 @@ ClimateCall &ClimateCall::set_preset(const char *custom_preset, size_t len) {
this->preset_.reset();
return *this;
}
ESP_LOGW(TAG, "'%s' - Unrecognized preset %.*s", this->parent_->get_name().c_str(), (int) len, custom_preset);
ESP_LOGW(TAG, "'%s' - Unrecognized preset %.*s", LOG_STR_ARG(this->parent_->get_log_name()), (int) len,
custom_preset);
return *this;
}
@@ -280,7 +282,8 @@ ClimateCall &ClimateCall::set_swing_mode(const char *swing_mode, size_t len) {
return *this;
}
}
ESP_LOGW(TAG, "'%s' - Unrecognized swing mode %.*s", this->parent_->get_name().c_str(), (int) len, swing_mode);
ESP_LOGW(TAG, "'%s' - Unrecognized swing mode %.*s", LOG_STR_ARG(this->parent_->get_log_name()), (int) len,
swing_mode);
return *this;
}
@@ -428,7 +431,7 @@ void Climate::save_state_(const ClimateTraits &traits) {
void Climate::publish_state() {
this->set_has_state(true);
ESP_LOGV(TAG, "'%s' >>", this->name_.c_str());
ESP_LOGV(TAG, "'%s' >>", LOG_STR_ARG(this->get_log_name()));
auto traits = this->get_traits();
ESP_LOGV(TAG, " Mode: %s", LOG_STR_ARG(climate_mode_to_string(this->mode)));
@@ -549,7 +552,7 @@ void ClimateDeviceRestoreState::apply(Climate *climate) {
if (traits.supports_mode(this->mode)) {
climate->mode = this->mode;
} else {
ESP_LOGW(TAG, "'%s' - Saved mode %s is no longer supported, keeping %s", climate->get_name().c_str(),
ESP_LOGW(TAG, "'%s' - Saved mode %s is no longer supported, keeping %s", LOG_STR_ARG(climate->get_log_name()),
LOG_STR_ARG(climate_mode_to_string(this->mode)), LOG_STR_ARG(climate_mode_to_string(climate->mode)));
}
if (traits.has_feature_flags(CLIMATE_SUPPORTS_TWO_POINT_TARGET_TEMPERATURE |
+1 -1
View File
@@ -14,7 +14,7 @@ namespace esphome::climate {
#define LOG_CLIMATE(prefix, type, obj) \
if ((obj) != nullptr) { \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), LOG_STR_ARG((obj)->get_log_name())); \
}
class Climate;
@@ -30,14 +30,14 @@ void CombinationOneParameterComponent::add_source(Sensor *sensor, float value) {
void CombinationNoParameterComponent::log_source_sensors() {
ESP_LOGCONFIG(TAG, " Source Sensors:");
for (const auto &sensor : this->sensors_) {
ESP_LOGCONFIG(TAG, " - %s", sensor->get_name().c_str());
ESP_LOGCONFIG(TAG, " - %s", LOG_STR_ARG(sensor->get_log_name()));
}
}
void CombinationOneParameterComponent::log_source_sensors() {
ESP_LOGCONFIG(TAG, " Source Sensors:");
for (const auto &source : this->sensor_sources_) {
ESP_LOGCONFIG(TAG, " - %s", source.sensor->get_name().c_str());
ESP_LOGCONFIG(TAG, " - %s", LOG_STR_ARG(source.sensor->get_log_name()));
}
}
+4 -4
View File
@@ -39,7 +39,7 @@ CoverCall &CoverCall::set_command(const char *command) {
} else if (ESPHOME_strcasecmp_P(command, ESPHOME_PSTR("TOGGLE")) == 0) {
this->set_command_toggle();
} else {
ESP_LOGW(TAG, "'%s' - Unrecognized command %s", this->parent_->get_name().c_str(), command);
ESP_LOGW(TAG, "'%s' - Unrecognized command %s", LOG_STR_ARG(this->parent_->get_log_name()), command);
}
return *this;
}
@@ -68,7 +68,7 @@ CoverCall &CoverCall::set_tilt(float tilt) {
return *this;
}
void CoverCall::perform() {
ESP_LOGV(TAG, "'%s' - Setting", this->parent_->get_name().c_str());
ESP_LOGV(TAG, "'%s' - Setting", LOG_STR_ARG(this->parent_->get_log_name()));
auto traits = this->parent_->get_traits();
this->validate_();
if (this->stop_) {
@@ -94,7 +94,7 @@ const optional<float> &CoverCall::get_tilt() const { return this->tilt_; }
const optional<bool> &CoverCall::get_toggle() const { return this->toggle_; }
void CoverCall::validate_() {
auto traits = this->parent_->get_traits();
const char *name = this->parent_->get_name().c_str();
const char *name = LOG_STR_ARG(this->parent_->get_log_name());
if (this->position_.has_value()) {
auto pos = *this->position_;
@@ -139,7 +139,7 @@ void Cover::publish_state(bool save) {
this->position = clamp(this->position, 0.0f, 1.0f);
this->tilt = clamp(this->tilt, 0.0f, 1.0f);
ESP_LOGV(TAG, "'%s' >>", this->name_.c_str());
ESP_LOGV(TAG, "'%s' >>", LOG_STR_ARG(this->get_log_name()));
auto traits = this->get_traits();
if (traits.get_supports_position()) {
ESP_LOGV(TAG, " Position: %.0f%%", this->position * 100.0f);
+1 -1
View File
@@ -15,7 +15,7 @@ static constexpr float COVER_CLOSED = 0.0f;
#define LOG_COVER(prefix, type, obj) \
if ((obj) != nullptr) { \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), LOG_STR_ARG((obj)->get_log_name())); \
auto traits_ = (obj)->get_traits(); \
if (traits_.get_is_assumed_state()) { \
ESP_LOGCONFIG(TAG, "%s Assumed State: YES", prefix); \
+22 -4
View File
@@ -8,6 +8,9 @@ namespace esphome::cse7766 {
ESPHOME_LOG_TAG(TAG, "cse7766");
// The chip only sends the time since the last pulse once a cycle is longer than 1 s (in us)
static constexpr uint32_t MIN_HELD_POWER_CYCLE = 1000000;
void CSE7766Component::loop() {
const uint32_t now = App.get_loop_component_start_time();
if (now - this->last_transmission_ >= 500) {
@@ -154,18 +157,33 @@ void CSE7766Component::parse_data_() {
if (power_cycle_exceeds_range) {
// Datasheet: power cycle exceeding range means active power is 0
have_power = true;
this->last_power_cycle_ = 0;
if (this->power_sensor_ != nullptr) {
this->power_sensor_->publish_state(0.0f);
}
} else if (have_power) {
power = power_coeff / float(power_cycle);
this->last_power_cycle_ = power_cycle;
if (this->power_sensor_ != nullptr) {
this->power_sensor_->publish_state(power);
}
} else if (this->power_sensor_ != nullptr) {
// No valid power measurement from chip - publish 0W to avoid stale readings
// This typically happens when current is below the measurable threshold (~50mA)
this->power_sensor_->publish_state(0.0f);
} else if (this->last_power_cycle_ > MIN_HELD_POWER_CYCLE && power_cycle <= 2 * this->last_power_cycle_) {
// No new measurement in this frame. With cycles over 1 s the register holds the last
// cycle or the time elapsed since the last pulse. While that time is within twice the
// last cycle, the next pulse is not overdue, so the last measurement still applies.
// Shorter cycles never get here in normal operation and keep the previous behaviour.
have_power = true;
power = power_coeff / float(this->last_power_cycle_);
if (this->power_sensor_ != nullptr) {
this->power_sensor_->publish_state(power);
}
} else {
// The next pulse is overdue: the load has dropped, report 0 W. A load that drops to less
// than half also reads 0 W here until its first new pulse arrives.
this->last_power_cycle_ = 0;
if (this->power_sensor_ != nullptr) {
this->power_sensor_->publish_state(0.0f);
}
}
float current = 0.0f;
+3 -1
View File
@@ -36,6 +36,7 @@ class CSE7766Component final : public Component, public uart::UARTDevice {
uint8_t raw_data_[CSE7766_RAW_DATA_SIZE];
uint8_t raw_data_index_{0};
uint16_t cf_pulses_last_{0};
uint32_t last_transmission_{0};
sensor::Sensor *voltage_sensor_{nullptr};
sensor::Sensor *current_sensor_{nullptr};
@@ -45,7 +46,8 @@ class CSE7766Component final : public Component, public uart::UARTDevice {
sensor::Sensor *reactive_power_sensor_{nullptr};
sensor::Sensor *power_factor_sensor_{nullptr};
uint32_t cf_pulses_total_{0};
uint16_t cf_pulses_last_{0};
// Power cycle of the last frame that carried a new power measurement, 0 if none
uint32_t last_power_cycle_{0};
};
} // namespace esphome::cse7766
@@ -40,7 +40,8 @@ void CTClampSensor::update() {
if (rms_ac_squared > 0)
rms_ac = std::sqrt(rms_ac_squared);
ESP_LOGD(TAG, "'%s' - Raw AC Value: %.3fA after %" PRIu32 " different samples (%" PRIu32 " SPS)",
this->name_.c_str(), rms_ac, this->num_samples_, 1000 * this->num_samples_ / this->sample_duration_);
LOG_STR_ARG(this->get_log_name()), rms_ac, this->num_samples_,
1000 * this->num_samples_ / this->sample_duration_);
this->publish_state(rms_ac);
});
@@ -70,20 +70,20 @@ void CurrentBasedCover::loop() {
this->direction_idle_();
this->malfunction_trigger_.trigger();
ESP_LOGI(TAG, "'%s' - Malfunction detected during opening. Current flow detected in close circuit",
this->name_.c_str());
LOG_STR_ARG(this->get_log_name()));
} else if (this->is_opening_blocked_()) { // Blocked
ESP_LOGD(TAG, "'%s' - Obstacle detected during opening.", this->name_.c_str());
ESP_LOGD(TAG, "'%s' - Obstacle detected during opening.", LOG_STR_ARG(this->get_log_name()));
this->direction_idle_();
if (this->obstacle_rollback_ != 0) {
this->set_timeout(ROLLBACK_TIMEOUT_ID, 300, [this]() {
ESP_LOGD(TAG, "'%s' - Rollback.", this->name_.c_str());
ESP_LOGD(TAG, "'%s' - Rollback.", LOG_STR_ARG(this->get_log_name()));
this->target_position_ = clamp(this->position - this->obstacle_rollback_, 0.0F, 1.0F);
this->start_direction_(COVER_OPERATION_CLOSING);
});
}
} else if (this->is_initial_delay_finished_() && !this->is_opening_()) { // End reached
auto dur = (now - this->start_dir_time_) / 1e3f;
ESP_LOGD(TAG, "'%s' - Open position reached. Took %.1fs.", this->name_.c_str(), dur);
ESP_LOGD(TAG, "'%s' - Open position reached. Took %.1fs.", LOG_STR_ARG(this->get_log_name()), dur);
this->direction_idle_(COVER_OPEN);
}
} else if (this->current_operation == COVER_OPERATION_CLOSING) {
@@ -91,25 +91,25 @@ void CurrentBasedCover::loop() {
this->direction_idle_();
this->malfunction_trigger_.trigger();
ESP_LOGI(TAG, "'%s' - Malfunction detected during closing. Current flow detected in open circuit",
this->name_.c_str());
LOG_STR_ARG(this->get_log_name()));
} else if (this->is_closing_blocked_()) { // Blocked
ESP_LOGD(TAG, "'%s' - Obstacle detected during closing.", this->name_.c_str());
ESP_LOGD(TAG, "'%s' - Obstacle detected during closing.", LOG_STR_ARG(this->get_log_name()));
this->direction_idle_();
if (this->obstacle_rollback_ != 0) {
this->set_timeout(ROLLBACK_TIMEOUT_ID, 300, [this]() {
ESP_LOGD(TAG, "'%s' - Rollback.", this->name_.c_str());
ESP_LOGD(TAG, "'%s' - Rollback.", LOG_STR_ARG(this->get_log_name()));
this->target_position_ = clamp(this->position + this->obstacle_rollback_, 0.0F, 1.0F);
this->start_direction_(COVER_OPERATION_OPENING);
});
}
} else if (this->is_initial_delay_finished_() && !this->is_closing_()) { // End reached
auto dur = (now - this->start_dir_time_) / 1e3f;
ESP_LOGD(TAG, "'%s' - Close position reached. Took %.1fs.", this->name_.c_str(), dur);
ESP_LOGD(TAG, "'%s' - Close position reached. Took %.1fs.", LOG_STR_ARG(this->get_log_name()), dur);
this->direction_idle_(COVER_CLOSED);
}
}
if (now - this->start_dir_time_ > this->max_duration_) {
ESP_LOGD(TAG, "'%s' - Max duration reached. Stopping cover.", this->name_.c_str());
ESP_LOGD(TAG, "'%s' - Max duration reached. Stopping cover.", LOG_STR_ARG(this->get_log_name()));
this->direction_idle_();
}
@@ -53,7 +53,7 @@ void DallasTemperatureSensor::update() {
}
float tempc = this->get_temp_c_();
ESP_LOGD(TAG, "'%s': Got Temperature=%f°C", this->get_name().c_str(), tempc);
ESP_LOGD(TAG, "'%s': Got Temperature=%f°C", LOG_STR_ARG(this->get_log_name()), tempc);
this->publish_state(tempc);
});
}
@@ -65,7 +65,7 @@ bool DallasTemperatureSensor::read_scratch_pad_() {
i = this->bus_->read8();
}
} else {
ESP_LOGW(TAG, "'%s' - reading scratch pad failed bus reset", this->get_name().c_str());
ESP_LOGW(TAG, "'%s' - reading scratch pad failed bus reset", LOG_STR_ARG(this->get_log_name()));
this->status_set_warning(LOG_STR("bus reset failed"));
}
return success;
+2 -2
View File
@@ -30,7 +30,7 @@ void DateEntity::publish_state() {
return;
}
this->set_has_state(true);
ESP_LOGV(TAG, "'%s' >> %d-%d-%d", this->get_name().c_str(), this->year_, this->month_, this->day_);
ESP_LOGV(TAG, "'%s' >> %d-%d-%d", LOG_STR_ARG(this->get_log_name()), this->year_, this->month_, this->day_);
this->state_callback_.call();
#if defined(USE_DATETIME_DATE) && defined(USE_CONTROLLER_REGISTRY)
ControllerRegistry::notify_date_update(this);
@@ -81,7 +81,7 @@ void DateCall::validate_() {
void DateCall::perform() {
this->validate_();
ESP_LOGV(TAG, "'%s' - Setting", this->parent_->get_name().c_str());
ESP_LOGV(TAG, "'%s' - Setting", LOG_STR_ARG(this->parent_->get_log_name()));
if (this->year_.has_value()) {
ESP_LOGV(TAG, " Year: %d", *this->year_);
+1 -1
View File
@@ -14,7 +14,7 @@ namespace esphome::datetime {
#define LOG_DATETIME_DATE(prefix, type, obj) \
if ((obj) != nullptr) { \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), LOG_STR_ARG((obj)->get_log_name())); \
LOG_ENTITY_ICON(TAG, prefix, *(obj)); \
}
@@ -45,7 +45,7 @@ void DateTimeEntity::publish_state() {
return;
}
this->set_has_state(true);
ESP_LOGV(TAG, "'%s' >> %04u-%02u-%02u %02d:%02d:%02d", this->get_name().c_str(), this->year_, this->month_,
ESP_LOGV(TAG, "'%s' >> %04u-%02u-%02u %02d:%02d:%02d", LOG_STR_ARG(this->get_log_name()), this->year_, this->month_,
this->day_, this->hour_, this->minute_, this->second_);
this->state_callback_.call();
#if defined(USE_DATETIME_DATETIME) && defined(USE_CONTROLLER_REGISTRY)
@@ -125,7 +125,7 @@ void DateTimeCall::validate_() {
void DateTimeCall::perform() {
this->validate_();
ESP_LOGV(TAG, "'%s' - Setting", this->parent_->get_name().c_str());
ESP_LOGV(TAG, "'%s' - Setting", LOG_STR_ARG(this->parent_->get_log_name()));
if (this->year_.has_value()) {
ESP_LOGV(TAG, " Year: %d", *this->year_);
@@ -14,7 +14,7 @@ namespace esphome::datetime {
#define LOG_DATETIME_DATETIME(prefix, type, obj) \
if ((obj) != nullptr) { \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), LOG_STR_ARG((obj)->get_log_name())); \
LOG_ENTITY_ICON(TAG, prefix, *(obj)); \
}
+2 -2
View File
@@ -26,7 +26,7 @@ void TimeEntity::publish_state() {
return;
}
this->set_has_state(true);
ESP_LOGV(TAG, "'%s' >> %02d:%02d:%02d", this->get_name().c_str(), this->hour_, this->minute_, this->second_);
ESP_LOGV(TAG, "'%s' >> %02d:%02d:%02d", LOG_STR_ARG(this->get_log_name()), this->hour_, this->minute_, this->second_);
this->state_callback_.call();
#if defined(USE_DATETIME_TIME) && defined(USE_CONTROLLER_REGISTRY)
ControllerRegistry::notify_time_update(this);
@@ -50,7 +50,7 @@ void TimeCall::validate_() {
void TimeCall::perform() {
this->validate_();
ESP_LOGV(TAG, "'%s' - Setting", this->parent_->get_name().c_str());
ESP_LOGV(TAG, "'%s' - Setting", LOG_STR_ARG(this->parent_->get_log_name()));
if (this->hour_.has_value()) {
ESP_LOGV(TAG, " Hour: %d", *this->hour_);
}
+1 -1
View File
@@ -14,7 +14,7 @@ namespace esphome::datetime {
#define LOG_DATETIME_TIME(prefix, type, obj) \
if ((obj) != nullptr) { \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), LOG_STR_ARG((obj)->get_log_name())); \
LOG_ENTITY_ICON(TAG, prefix, *(obj)); \
}
+4 -3
View File
@@ -36,7 +36,8 @@ void DewPointComponent::dump_config() {
"Sources\n"
" Temperature: '%s'\n"
" Humidity: '%s'",
this->temperature_sensor_->get_name().c_str(), this->humidity_sensor_->get_name().c_str());
LOG_STR_ARG(this->temperature_sensor_->get_log_name()),
LOG_STR_ARG(this->humidity_sensor_->get_log_name()));
}
float DewPointComponent::get_setup_priority() const { return setup_priority::DATA; }
@@ -75,8 +76,8 @@ void DewPointComponent::loop() {
// Publish the calculated dew point
this->publish_state(dew_point);
ESP_LOGD(TAG, "'%s' >> %.1f°C (T: %.1f°C, RH: %.1f%%)", this->get_name().c_str(), dew_point, this->temperature_value_,
this->humidity_value_);
ESP_LOGD(TAG, "'%s' >> %.1f°C (T: %.1f°C, RH: %.1f%%)", LOG_STR_ARG(this->get_log_name()), dew_point,
this->temperature_value_, this->humidity_value_);
}
} // namespace esphome::dew_point
@@ -36,7 +36,7 @@ void DutyCycleSensor::update() {
const float total_time = float(now - this->last_update_);
const float value = (on_time * 100.0f) / total_time;
ESP_LOGD(TAG, "'%s' Got duty cycle=%.1f%%", this->get_name().c_str(), value);
ESP_LOGD(TAG, "'%s' Got duty cycle=%.1f%%", LOG_STR_ARG(this->get_log_name()), value);
this->publish_state(value);
}
this->last_update_ = now;
@@ -118,13 +118,8 @@ void Ecocomfort2Fan::on_status() {
int new_speed = this->speed;
uint8_t base_speed = raw_speed & SPEED_MASK;
if (raw_speed & FLAG_BOOST) {
new_speed = SPEED_COUNT;
} else if (raw_speed & FLAG_NIGHT) {
new_speed = SPEED_SLEEP;
} else if (base_speed >= SPEED_SLEEP && base_speed <= SPEED_VEL3) {
if (base_speed >= SPEED_SLEEP && base_speed <= SPEED_VEL3)
new_speed = base_speed;
}
const Preset *preset = find_preset(mode, (raw_speed & FLAG_AUTO) != 0);
const char *new_preset = preset != nullptr ? preset->name : nullptr;
bool same_preset = new_preset == nullptr ? !this->has_preset_mode() : this->get_preset_mode().c_str() == new_preset;
@@ -205,6 +205,10 @@ void Ecocomfort2Hub::publish_unknown_() {
sensor->publish_state(NAN);
}
#endif
#ifdef USE_BINARY_SENSOR
if (this->boost_binary_sensor_ != nullptr)
this->boost_binary_sensor_->invalidate_state();
#endif
}
// C_OPER: mode byte, speed/flags byte
@@ -216,10 +220,6 @@ void Ecocomfort2Hub::on_oper_(const uint8_t *data, uint16_t len) {
this->mode_ = data[0];
this->speed_ = data[1];
ESP_LOGD(TAG, "[%s] C_OPER: mode=0x%02x, speed=0x%02x", this->parent_->address_str(), this->mode_, this->speed_);
#ifdef USE_BINARY_SENSOR
if (this->boost_binary_sensor_ != nullptr)
this->boost_binary_sensor_->publish_state((this->speed_ & FLAG_BOOST) != 0);
#endif
#ifdef ECOCOMFORT2_CHILD_COUNT
for (auto *child : this->children_) {
child->on_status();
@@ -227,7 +227,9 @@ void Ecocomfort2Hub::on_oper_(const uint8_t *data, uint16_t len) {
#endif
}
// C_STATE: bytes 3-4 temperature and 5-6 humidity in hundredths, 7-8 VOC in ppb, byte 2 the air flow direction
// C_STATE: bytes 0-1 the mode and speed the unit runs; byte 1 also has the boost flag (0x40) and the advanced
// control step (0x20). Byte 2 the air flow direction, bytes 3-4 temperature and 5-6 humidity in hundredths,
// 7-8 VOC in ppb
void Ecocomfort2Hub::on_state_(const uint8_t *data, uint16_t len) {
if (len < 9) {
ESP_LOGW(TAG, "[%s] C_STATE too short: %u bytes", this->parent_->address_str(), len);
@@ -236,8 +238,12 @@ void Ecocomfort2Hub::on_state_(const uint8_t *data, uint16_t len) {
const float temperature = static_cast<int16_t>(encode_uint16(data[3], data[4])) / 100.0f;
const float humidity = encode_uint16(data[5], data[6]) / 100.0f;
const uint16_t voc = encode_uint16(data[7], data[8]);
ESP_LOGD(TAG, "[%s] C_STATE: %.2f °C, %.2f %%, %u ppb, direction=%u", this->parent_->address_str(), temperature,
humidity, voc, data[2]);
ESP_LOGD(TAG, "[%s] C_STATE: mode=0x%02x, speed=0x%02x, %.2f °C, %.2f %%, %u ppb, direction=%u",
this->parent_->address_str(), data[0], data[1], temperature, humidity, voc, data[2]);
#ifdef USE_BINARY_SENSOR
if (this->boost_binary_sensor_ != nullptr)
this->boost_binary_sensor_->publish_state((data[1] & FLAG_BOOST) != 0);
#endif
#ifdef USE_SENSOR
if (this->temperature_sensor_ != nullptr)
this->temperature_sensor_->publish_state(temperature);
@@ -267,7 +273,7 @@ void Ecocomfort2Hub::on_info_(const uint8_t *data, uint16_t len) {
}
#ifdef USE_TIME
// C_CLOCK: hour, minute, second, weekday with Monday as 0, day, month, year as two bytes
// C_CLOCK: two-digit year, month, day, weekday with Monday as 0, hour, minute, second, automatic DST flag
void Ecocomfort2Hub::sync_clock_() {
if (!this->is_ready() || this->char_handle_clock_ == 0)
return;
@@ -276,15 +282,11 @@ void Ecocomfort2Hub::sync_clock_() {
return;
// ESPTime counts Sunday=1 .. Saturday=7, the unit Monday=0 .. Sunday=6
const uint8_t weekday = (now.day_of_week + 5) % 7;
uint8_t data[8]{now.hour,
now.minute,
now.second,
weekday,
now.day_of_month,
now.month,
static_cast<uint8_t>(now.year >> 8),
static_cast<uint8_t>(now.year)};
ESP_LOGD(TAG, "[%s] Syncing clock to %02u:%02u:%02u", this->parent_->address_str(), now.hour, now.minute, now.second);
// The time source already applies DST and every sync rewrites the clock, so the unit must not shift it again
uint8_t data[8]{
static_cast<uint8_t>(now.year % 100), now.month, now.day_of_month, weekday, now.hour, now.minute, now.second, 0};
ESP_LOGD(TAG, "[%s] Syncing clock to %04u-%02u-%02u %02u:%02u:%02u", this->parent_->address_str(), now.year,
now.month, now.day_of_month, now.hour, now.minute, now.second);
auto status =
esp_ble_gattc_write_char(this->parent_->get_gattc_if(), this->parent_->get_conn_id(), this->char_handle_clock_,
sizeof(data), data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
@@ -32,7 +32,8 @@ static constexpr uint8_t OPER_OUT = 0x02;
static constexpr uint8_t OPER_IN_OUT = 0x03;
static constexpr uint8_t OPER_SENSOR_OR_AUTO = 0x04;
// C_OPER byte 1: speed in the low nibble, flags in the high nibble
// Speed byte: speed in the low nibble, flags in the high nibble.
// C_OPER carries FLAG_AUTO; the unit reports FLAG_BOOST in byte 1 of C_STATE.
static constexpr uint8_t SPEED_OFF = 0x00;
static constexpr uint8_t SPEED_SLEEP = 0x01;
static constexpr uint8_t SPEED_VEL1 = 0x02;
@@ -41,7 +42,6 @@ static constexpr uint8_t SPEED_VEL3 = 0x04;
static constexpr uint8_t SPEED_MASK = 0x0F;
static constexpr uint8_t FLAG_AUTO = 0x10;
static constexpr uint8_t FLAG_BOOST = 0x40;
static constexpr uint8_t FLAG_NIGHT = 0x80;
/** Hub component for Ecocomfort 2.0 VMC via BLE. */
class Ecocomfort2Hub : public esphome::ble_client::BLEClientNode, public PollingComponent {
+3 -3
View File
@@ -70,20 +70,20 @@ void EndstopCover::loop() {
if (this->current_operation == COVER_OPERATION_OPENING && this->is_open_()) {
float dur = (now - this->start_dir_time_) / 1e3f;
ESP_LOGD(TAG, "'%s' - Open endstop reached. Took %.1fs.", this->name_.c_str(), dur);
ESP_LOGD(TAG, "'%s' - Open endstop reached. Took %.1fs.", LOG_STR_ARG(this->get_log_name()), dur);
this->start_direction_(COVER_OPERATION_IDLE);
this->position = COVER_OPEN;
this->publish_state();
} else if (this->current_operation == COVER_OPERATION_CLOSING && this->is_closed_()) {
float dur = (now - this->start_dir_time_) / 1e3f;
ESP_LOGD(TAG, "'%s' - Close endstop reached. Took %.1fs.", this->name_.c_str(), dur);
ESP_LOGD(TAG, "'%s' - Close endstop reached. Took %.1fs.", LOG_STR_ARG(this->get_log_name()), dur);
this->start_direction_(COVER_OPERATION_IDLE);
this->position = COVER_CLOSED;
this->publish_state();
} else if (now - this->start_dir_time_ > this->max_duration_) {
ESP_LOGD(TAG, "'%s' - Max duration reached. Stopping cover.", this->name_.c_str());
ESP_LOGD(TAG, "'%s' - Max duration reached. Stopping cover.", LOG_STR_ARG(this->get_log_name()));
this->start_direction_(COVER_OPERATION_IDLE);
this->publish_state();
}
@@ -0,0 +1,3 @@
CODEOWNERS = ["@GioMez"]
DEPENDENCIES = ["i2c"]
DOMAIN = "es9038q2m_katana"
@@ -0,0 +1,92 @@
import esphome.codegen as cg
from esphome.components import audio_dac, i2c
import esphome.config_validation as cv
from esphome.const import CONF_BITS_PER_SAMPLE, CONF_ID, CONF_SAMPLE_RATE
from esphome.types import ConfigType
CONF_DEEMPHASIS = "deemphasis"
CONF_DOP = "dop"
CONF_FILTER_SHAPE = "filter_shape"
# Fields of the Katana format register: channel layout, word length and rate code.
FORMAT_STEREO = 0x80
BITS_PER_SAMPLE_CODES = {16: 0x10, 24: 0x20, 32: 0x30}
SAMPLE_RATE_CODES = {
44100: 0x04,
48000: 0x05,
88200: 0x06,
96000: 0x07,
176400: 0x08,
192000: 0x09,
352800: 0x0A,
384000: 0x0B,
}
es9038q2m_katana_ns = cg.esphome_ns.namespace("es9038q2m_katana")
ES9038Q2MKatana = es9038q2m_katana_ns.class_(
"ES9038Q2MKatana",
audio_dac.AudioDac,
cg.Component,
i2c.I2CDevice,
)
FilterShape = es9038q2m_katana_ns.enum("FilterShape")
FILTER_SHAPES = {
"linear_phase_fast": FilterShape.FILTER_SHAPE_LINEAR_PHASE_FAST,
"linear_phase_slow": FilterShape.FILTER_SHAPE_LINEAR_PHASE_SLOW,
"minimum_phase_fast": FilterShape.FILTER_SHAPE_MIN_PHASE_FAST,
"minimum_phase_slow": FilterShape.FILTER_SHAPE_MIN_PHASE_SLOW,
"apodizing": FilterShape.FILTER_SHAPE_APODIZING,
"hybrid": FilterShape.FILTER_SHAPE_HYBRID,
"brick_wall": FilterShape.FILTER_SHAPE_BRICK_WALL,
}
DeemphasisMode = es9038q2m_katana_ns.enum("DeemphasisMode")
DEEMPHASIS_MODES = {
"bypass": DeemphasisMode.DEEMPHASIS_BYPASS,
"32khz": DeemphasisMode.DEEMPHASIS_32KHZ,
"44_1khz": DeemphasisMode.DEEMPHASIS_44_1KHZ,
"48khz": DeemphasisMode.DEEMPHASIS_48KHZ,
}
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(ES9038Q2MKatana),
cv.Optional(CONF_FILTER_SHAPE, default="apodizing"): cv.enum(
FILTER_SHAPES, lower=True
),
cv.Optional(CONF_BITS_PER_SAMPLE, default="16bit"): cv.All(
cv.float_with_unit("bits", "bit"),
cv.one_of(*BITS_PER_SAMPLE_CODES, int=True),
),
cv.Optional(CONF_SAMPLE_RATE, default=48000): cv.one_of(
*SAMPLE_RATE_CODES, int=True
),
cv.Optional(CONF_DEEMPHASIS, default="bypass"): cv.enum(
DEEMPHASIS_MODES, lower=True
),
# Only switches the control logic; the source must already send DoP frames.
cv.Optional(CONF_DOP, default=False): cv.boolean,
}
)
.extend(cv.COMPONENT_SCHEMA)
.extend(i2c.i2c_device_schema(0x30))
)
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
cg.add(var.set_filter_shape(config[CONF_FILTER_SHAPE]))
cg.add(
var.set_format(
FORMAT_STEREO
| BITS_PER_SAMPLE_CODES[config[CONF_BITS_PER_SAMPLE]]
| SAMPLE_RATE_CODES[config[CONF_SAMPLE_RATE]]
)
)
cg.add(var.set_deemphasis_mode(config[CONF_DEEMPHASIS]))
cg.add(var.set_dop_enabled(config[CONF_DOP]))
@@ -0,0 +1,176 @@
#include "es9038q2m_katana.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
#include <cmath>
namespace esphome::es9038q2m_katana {
ESPHOME_LOG_TAG(TAG, "es9038q2m_katana");
namespace {
constexpr uint8_t KATANA_CHIP_ID = 0x30;
constexpr uint8_t KATANA_REG_CHIP_ID = 0x00;
constexpr uint8_t KATANA_REG_RESET = 0x01;
constexpr uint8_t KATANA_REG_VOLUME_1 = 0x02;
constexpr uint8_t KATANA_REG_VOLUME_2 = 0x03;
constexpr uint8_t KATANA_REG_MUTE = 0x04;
constexpr uint8_t KATANA_REG_DSP_PROGRAM = 0x05;
constexpr uint8_t KATANA_REG_DEEMPHASIS = 0x06;
constexpr uint8_t KATANA_REG_DOP = 0x07;
constexpr uint8_t KATANA_REG_FORMAT = 0x08;
constexpr uint8_t KATANA_REG_COMMAND = 0x09;
constexpr uint8_t KATANA_REG_MUTE_STREAM = 0x0A;
constexpr float KATANA_VOLUME_MIDPOINT = 0.5f;
constexpr float KATANA_VOLUME_MIDPOINT_STEPS = 50.0f; // -25 dB at 50% volume.
// Cubic amplitude gain: -20 * log10(volume^3) / 0.5 attenuation steps.
constexpr float KATANA_VOLUME_CUBIC_SCALE = 120.0f;
constexpr uint8_t KATANA_VOLUME_FULL_ATTENUATION = 255;
// The control logic ignores writes for this long after a reset.
constexpr uint32_t RESET_SETTLE_DELAY_MS = 10;
const LogString *filter_shape_to_str(FilterShape shape) {
switch (shape) {
case FILTER_SHAPE_LINEAR_PHASE_FAST:
return LOG_STR("linear_phase_fast");
case FILTER_SHAPE_LINEAR_PHASE_SLOW:
return LOG_STR("linear_phase_slow");
case FILTER_SHAPE_MIN_PHASE_FAST:
return LOG_STR("minimum_phase_fast");
case FILTER_SHAPE_MIN_PHASE_SLOW:
return LOG_STR("minimum_phase_slow");
case FILTER_SHAPE_APODIZING:
return LOG_STR("apodizing");
case FILTER_SHAPE_HYBRID:
return LOG_STR("hybrid");
default:
return LOG_STR("brick_wall");
}
}
const LogString *deemphasis_to_str(DeemphasisMode mode) {
switch (mode) {
case DEEMPHASIS_32KHZ:
return LOG_STR("32khz");
case DEEMPHASIS_44_1KHZ:
return LOG_STR("44_1khz");
case DEEMPHASIS_48KHZ:
return LOG_STR("48khz");
default:
return LOG_STR("bypass");
}
}
} // namespace
void ES9038Q2MKatana::setup() {
uint8_t chip_id = 0;
if (!this->read_byte(KATANA_REG_CHIP_ID, &chip_id)) {
this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
return;
}
if (chip_id != KATANA_CHIP_ID) {
ESP_LOGE(TAG, "Unexpected chip ID 0x%02X", chip_id);
this->mark_failed(LOG_STR("Unexpected chip ID"));
return;
}
if (!this->write_byte(KATANA_REG_RESET, 0x01)) {
this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
return;
}
delay(RESET_SETTLE_DELAY_MS);
// MUTE_STREAM is the gate the Linux driver closes between streams; ESPHome has no such
// notion, so it stays open and mute is only ever driven through MUTE.
if (!this->write_byte(KATANA_REG_FORMAT, this->format_) || !this->write_byte(KATANA_REG_COMMAND, 0x00) ||
!this->write_byte(KATANA_REG_DSP_PROGRAM, this->filter_shape_) ||
!this->write_byte(KATANA_REG_DEEMPHASIS, this->deemphasis_mode_) ||
!this->write_byte(KATANA_REG_DOP, this->dop_enabled_ ? 1 : 0) || !this->write_volume_() ||
!this->write_byte(KATANA_REG_MUTE, this->is_muted_ ? 1 : 0) || !this->write_byte(KATANA_REG_MUTE_STREAM, 0x00)) {
this->mark_failed(LOG_STR("Failed to configure the control logic"));
}
}
void ES9038Q2MKatana::dump_config() {
ESP_LOGCONFIG(TAG,
"ES9038Q2M Katana:\n"
" Filter shape: %s\n"
" De-emphasis: %s\n"
" Format register: 0x%02X\n"
" DoP: %s",
LOG_STR_ARG(filter_shape_to_str(this->filter_shape_)),
LOG_STR_ARG(deemphasis_to_str(this->deemphasis_mode_)), this->format_, ONOFF(this->dop_enabled_));
LOG_I2C_DEVICE(this);
if (this->is_failed())
return;
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
for (uint8_t reg = KATANA_REG_CHIP_ID; reg <= KATANA_REG_MUTE_STREAM; reg++) {
uint8_t value = 0;
if (this->read_byte(reg, &value)) {
ESP_LOGV(TAG, " Register 0x%02X: 0x%02X", reg, value);
} else {
ESP_LOGV(TAG, " Register 0x%02X: read failed", reg);
}
}
#endif
}
// Both channel registers follow ESPHome's single master volume.
bool ES9038Q2MKatana::write_volume_() {
return this->write_byte(KATANA_REG_VOLUME_1, this->volume_reg_) &&
this->write_byte(KATANA_REG_VOLUME_2, this->volume_reg_);
}
// Records the outcome of a runtime write; the cached state only changes on success.
bool ES9038Q2MKatana::check_write_(bool ok) {
if (ok) {
this->status_clear_warning();
} else {
this->status_set_warning(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
}
return ok;
}
bool ES9038Q2MKatana::set_volume(float volume) {
if (this->is_failed())
return false;
volume = clamp(volume, 0.0f, 1.0f);
const uint8_t previous = this->volume_reg_;
float attenuation = KATANA_VOLUME_FULL_ATTENUATION;
if (volume >= KATANA_VOLUME_MIDPOINT) {
attenuation = KATANA_VOLUME_MIDPOINT_STEPS * (1.0f - volume) / (1.0f - KATANA_VOLUME_MIDPOINT);
} else if (volume > 0.0f) {
attenuation = KATANA_VOLUME_MIDPOINT_STEPS - KATANA_VOLUME_CUBIC_SCALE * log10f(volume / KATANA_VOLUME_MIDPOINT);
}
this->volume_reg_ =
static_cast<uint8_t>(lroundf(clamp(attenuation, 0.0f, static_cast<float>(KATANA_VOLUME_FULL_ATTENUATION))));
if (this->check_write_(this->write_volume_()))
return true;
this->volume_reg_ = previous;
return false;
}
float ES9038Q2MKatana::volume() {
if (this->volume_reg_ == KATANA_VOLUME_FULL_ATTENUATION)
return 0.0f;
if (this->volume_reg_ <= KATANA_VOLUME_MIDPOINT_STEPS)
return 1.0f - this->volume_reg_ * (1.0f - KATANA_VOLUME_MIDPOINT) / KATANA_VOLUME_MIDPOINT_STEPS;
return KATANA_VOLUME_MIDPOINT *
powf(10.0f, (KATANA_VOLUME_MIDPOINT_STEPS - this->volume_reg_) / KATANA_VOLUME_CUBIC_SCALE);
}
bool ES9038Q2MKatana::set_mute_state_(bool mute_state) {
if (this->is_failed())
return false;
if (!this->check_write_(this->write_byte(KATANA_REG_MUTE, mute_state ? 1 : 0)))
return false;
this->is_muted_ = mute_state;
return true;
}
} // namespace esphome::es9038q2m_katana
@@ -0,0 +1,62 @@
#pragma once
#include "esphome/components/audio_dac/audio_dac.h"
#include "esphome/components/i2c/i2c.h"
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
namespace esphome::es9038q2m_katana {
// DSP program numbers of the Katana control interface; there is no program 5.
enum FilterShape : uint8_t {
FILTER_SHAPE_LINEAR_PHASE_FAST = 0,
FILTER_SHAPE_LINEAR_PHASE_SLOW = 1,
FILTER_SHAPE_MIN_PHASE_FAST = 2,
FILTER_SHAPE_MIN_PHASE_SLOW = 3,
FILTER_SHAPE_APODIZING = 4,
FILTER_SHAPE_HYBRID = 6,
FILTER_SHAPE_BRICK_WALL = 7,
};
enum DeemphasisMode : uint8_t {
DEEMPHASIS_BYPASS = 0,
DEEMPHASIS_32KHZ = 1,
DEEMPHASIS_44_1KHZ = 2,
DEEMPHASIS_48KHZ = 3,
};
// Boards that expose an ES9038Q2M through the Allo Katana control logic at I2C address 0x30.
// Ported from the Linux driver sound/soc/bcm/allo-katana-codec.c; the DAC's own registers
// are never written, the control logic applies the configuration itself.
class ES9038Q2MKatana : public audio_dac::AudioDac, public Component, public i2c::I2CDevice {
public:
void setup() override;
void dump_config() override;
// Cubic below 50%, then 0.5 dB per 1% above, meeting at -25 dB.
// Zero selects full attenuation (-127.5 dB).
bool set_volume(float volume) override;
float volume() override;
bool set_mute_off() override { return this->set_mute_state_(false); }
bool set_mute_on() override { return this->set_mute_state_(true); }
bool is_muted() override { return this->is_muted_; }
void set_filter_shape(FilterShape shape) { this->filter_shape_ = shape; }
void set_format(uint8_t format) { this->format_ = format; }
void set_deemphasis_mode(DeemphasisMode mode) { this->deemphasis_mode_ = mode; }
void set_dop_enabled(bool enabled) { this->dop_enabled_ = enabled; }
protected:
bool set_mute_state_(bool mute_state);
bool write_volume_();
bool check_write_(bool ok);
FilterShape filter_shape_{FILTER_SHAPE_APODIZING};
DeemphasisMode deemphasis_mode_{DEEMPHASIS_BYPASS};
uint8_t format_{0};
// Attenuation in 0.5 dB steps: 0 is loudest, 255 quietest. Starts 20 dB down.
uint8_t volume_reg_{0x28};
bool dop_enabled_{false};
};
} // namespace esphome::es9038q2m_katana
+1
View File
@@ -2920,6 +2920,7 @@ async def _finalize_arduino_aware_flags():
if not arduino_linked:
cg.add_build_flag("-Wl,--wrap=esp_panic_handler")
cg.add_define("USE_ESP32_CRASH_HANDLER")
cg.add_define("USE_CRASH_HANDLER")
elif not CORE.using_arduino:
cg.add_build_flag("-DUSE_ARDUINO")
+3 -2
View File
@@ -161,7 +161,7 @@ static bool s_crash_data_valid = false; // NOLINT(cppcoreguidelines-avoid-non-c
namespace esphome::esp32 {
ESPHOME_LOG_TAG(TAG, "esp32.crash");
ESPHOME_LOG_TAG(TAG, CRASH_LOG_TAG);
// RAM copy of the build timestamp. The generated constant lives in flash,
// which the panic handler must not read (cache may be disabled during
@@ -175,7 +175,8 @@ static uint32_t s_current_build_time = static_cast<uint32_t>(ESPHOME_BUILD_TIME)
// Validate the NOINIT record. Runs on every has_data() call; re-running is
// harmless and the magic is left alone so the record survives an OTA
// rollback reboot, crash_handler_clear() drops it once an API client has it.
// rollback reboot, crash_handler_clear() drops it once a log client has it
// (logger::log_crash_to_client(), for the API and MQTT).
static void read_crash_data() {
if (s_raw_crash_data.magic == CRASH_MAGIC && s_raw_crash_data.version == CRASH_DATA_VERSION) {
s_crash_data_valid = true;
+3
View File
@@ -4,6 +4,9 @@
namespace esphome::esp32 {
/// The tag the crash record is logged under, so a client can check the level in force for it
inline constexpr const char *CRASH_LOG_TAG = "esp32.crash";
/// Log crash data if a crash was detected on previous boot.
void crash_handler_log();
@@ -300,7 +300,7 @@ void BLEClientBase::log_warning_(const char *message) {
esp_err_t BLEClientBase::update_conn_params_(uint16_t min_interval, uint16_t max_interval, uint16_t latency,
uint16_t timeout, const char *param_type) {
esp_ble_conn_update_params_t conn_params = {{0}};
esp_ble_conn_update_params_t conn_params = {};
memcpy(conn_params.bda, this->remote_bda_, sizeof(esp_bd_addr_t));
conn_params.min_int = min_interval;
conn_params.max_int = max_interval;
@@ -132,9 +132,9 @@ void ESP32Camera::dump_config() {
" External Clock: Pin:%d Frequency:%u\n"
" I2C Pins: SDA:%d SCL:%d\n"
" Reset Pin: %d",
this->name_.c_str(), YESNO(this->is_internal()), conf.pin_d0, conf.pin_d1, conf.pin_d2, conf.pin_d3,
conf.pin_d4, conf.pin_d5, conf.pin_d6, conf.pin_d7, conf.pin_vsync, conf.pin_href, conf.pin_pclk,
conf.pin_xclk, conf.xclk_freq_hz, conf.pin_sccb_sda, conf.pin_sccb_scl, conf.pin_reset);
LOG_STR_ARG(this->get_log_name()), YESNO(this->is_internal()), conf.pin_d0, conf.pin_d1, conf.pin_d2,
conf.pin_d3, conf.pin_d4, conf.pin_d5, conf.pin_d6, conf.pin_d7, conf.pin_vsync, conf.pin_href,
conf.pin_pclk, conf.pin_xclk, conf.xclk_freq_hz, conf.pin_sccb_sda, conf.pin_sccb_scl, conf.pin_reset);
ESP_LOGCONFIG(TAG, " Resolution: %s", frame_size_to_str(this->config_.frame_size));
ESP_LOGCONFIG(TAG, " Pixel Format: %s", pixel_format_to_str(this->config_.pixel_format));
@@ -42,6 +42,7 @@ extern "C" {
}
#include "esp_now_hosted_rpc.h"
#include "esphome/core/log.h"
namespace {
@@ -52,6 +53,8 @@ ESPHOME_LOG_TAG(TAG, "esp_now_hosted");
// single esp-hosted RPC RX thread. Serializing requests keeps the shared
// response slot race-free; a sequence number stops a late/stale response from
// being mistaken for ours.
// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables): the shim is
// C-ABI state shared between the main loop and the esp-hosted RX thread.
SemaphoreHandle_t g_req_mutex = nullptr;
SemaphoreHandle_t g_resp_sem = nullptr; // given when the matching RESP lands
bool g_setup_done = false; // set only after setup fully succeeds
@@ -85,6 +88,7 @@ constexpr size_t ESP_NOW_HOSTED_MAX_PEERS = 20;
uint8_t g_peer_cache[ESP_NOW_HOSTED_MAX_PEERS][6];
size_t g_peer_count = 0;
portMUX_TYPE g_peer_lock = portMUX_INITIALIZER_UNLOCKED;
// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
// Caller must hold g_peer_lock.
int peer_cache_find_locked(const uint8_t *mac) {
@@ -231,12 +235,14 @@ esp_err_t ensure_setup() {
g_resp_sem = xSemaphoreCreateBinary();
if (g_req_mutex == nullptr || g_resp_sem == nullptr)
return ESP_ERR_NO_MEM;
esp_err_t err;
if ((err = esp_hosted_register_custom_callback(ESP_NOW_HOSTED_MSG_RESP, on_resp, nullptr)) != ESP_OK)
esp_err_t err = esp_hosted_register_custom_callback(ESP_NOW_HOSTED_MSG_RESP, on_resp, nullptr);
if (err != ESP_OK)
return err;
if ((err = esp_hosted_register_custom_callback(ESP_NOW_HOSTED_MSG_RECV, on_recv, nullptr)) != ESP_OK)
err = esp_hosted_register_custom_callback(ESP_NOW_HOSTED_MSG_RECV, on_recv, nullptr);
if (err != ESP_OK)
return err;
if ((err = esp_hosted_register_custom_callback(ESP_NOW_HOSTED_MSG_SEND, on_send, nullptr)) != ESP_OK)
err = esp_hosted_register_custom_callback(ESP_NOW_HOSTED_MSG_SEND, on_send, nullptr);
if (err != ESP_OK)
return err;
g_setup_done = true;
return ESP_OK;
@@ -422,10 +428,11 @@ esp_err_t esp_now_send(const uint8_t *peer_addr, const uint8_t *data, size_t len
uint8_t hdr[sizeof(esp_now_hosted_send_req_t)];
auto *s = reinterpret_cast<esp_now_hosted_send_req_t *>(hdr);
s->has_addr = peer_addr != nullptr ? 1 : 0;
if (peer_addr != nullptr)
if (peer_addr != nullptr) {
memcpy(s->peer_addr, peer_addr, 6);
else
} else {
memset(s->peer_addr, 0, 6);
}
s->data_len = static_cast<uint16_t>(len);
// Fire-and-forget (wait=false): native esp_now_send returns once the frame is
// queued, with the real TX result delivered later through the send callback.
@@ -346,13 +346,13 @@ void ESP32TouchComponent::loop() {
child->publish_state(new_state);
#ifdef USE_ESP32_VARIANT_ESP32
ESP_LOGV(TAG, "Touch Pad '%s' state: %s (value: %" PRIu32 ", threshold: %" PRIu32 ")",
child->get_name().c_str(), ONOFF(new_state), value, child->get_threshold());
LOG_STR_ARG(child->get_log_name()), ONOFF(new_state), value, child->get_threshold());
#else
if (new_state) {
ESP_LOGV(TAG, "Touch Pad '%s' state: ON (value: %" PRIu32 ", benchmark: %" PRIu32 ", threshold: %" PRIu32 ")",
child->get_name().c_str(), value, benchmark, child->get_threshold());
LOG_STR_ARG(child->get_log_name()), value, benchmark, child->get_threshold());
} else {
ESP_LOGV(TAG, "Touch Pad '%s' state: OFF", child->get_name().c_str());
ESP_LOGV(TAG, "Touch Pad '%s' state: OFF", LOG_STR_ARG(child->get_log_name()));
}
#endif
}
@@ -475,7 +475,8 @@ void ESP32TouchComponent::process_setup_mode_logging_(uint32_t now) {
child->value_ = smooth_value;
#ifdef USE_ESP32_VARIANT_ESP32
ESP_LOGD(TAG, "Touch Pad '%s' (Ch%d): %" PRIu32, child->get_name().c_str(), child->channel_id_, smooth_value);
ESP_LOGD(TAG, "Touch Pad '%s' (Ch%d): %" PRIu32, LOG_STR_ARG(child->get_log_name()), child->channel_id_,
smooth_value);
#else
uint32_t benchmark = 0;
touch_channel_read_data(child->chan_handle_, TOUCH_CHAN_DATA_TYPE_BENCHMARK, &benchmark);
@@ -484,7 +485,7 @@ void ESP32TouchComponent::process_setup_mode_logging_(uint32_t now) {
ESP_LOGD(TAG,
"Touch Pad '%s' (Ch%d): value=%" PRIu32 ", benchmark=%" PRIu32 ", difference=%" PRId32
" (set threshold < %" PRId32 " to detect touch)",
child->get_name().c_str(), child->channel_id_, smooth_value, benchmark, difference, difference);
LOG_STR_ARG(child->get_log_name()), child->channel_id_, smooth_value, benchmark, difference, difference);
#endif
}
this->setup_mode_last_log_print_ = now;
@@ -496,7 +497,7 @@ void ESP32TouchComponent::publish_initial_state_if_needed_(ESP32TouchBinarySenso
if (now > INITIAL_STATE_DELAY_MS) {
child->publish_initial_state(false);
child->initial_state_published_ = true;
ESP_LOGV(TAG, "Touch Pad '%s' state: OFF (initial)", child->get_name().c_str());
ESP_LOGV(TAG, "Touch Pad '%s' state: OFF (initial)", LOG_STR_ARG(child->get_log_name()));
}
}
}
+12
View File
@@ -53,10 +53,14 @@ from .const import (
KEY_SERIAL1_REQUIRED,
KEY_SERIAL_REQUIRED,
KEY_WAVEFORM_REQUIRED,
PREFERENCES_RTC_EBOOT_WORDS,
PREFERENCES_RTC_NORMAL_WORDS,
THROW_STUBS_HEADER,
check_preferences_fit,
enable_serial,
enable_serial1,
esp8266_ns,
preferences_flash_words,
)
from .gpio import PinInitialState, add_pin_initial_states_array
@@ -416,6 +420,7 @@ async def to_code(config: ConfigType) -> None:
cg.add_define("ESPHOME_VARIANT", "ESP8266")
cg.add_define(ThreadModel.SINGLE)
cg.add_define("USE_ESP8266_CRASH_HANDLER")
cg.add_define("USE_CRASH_HANDLER")
enable_scanf_float = config.get(CONF_ENABLE_SCANF_FLOAT)
if enable_scanf_float is None and lambdas_use_scanf_float(CORE.config):
@@ -467,6 +472,12 @@ async def to_code(config: ConfigType) -> None:
# Other projects like Tasmota & ESPEasy also use this
cg.add_build_flag("-DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH")
cg.add_define("ESP8266_PREFERENCES_RTC_NORMAL_WORDS", PREFERENCES_RTC_NORMAL_WORDS)
cg.add_define("ESP8266_PREFERENCES_RTC_EBOOT_WORDS", PREFERENCES_RTC_EBOOT_WORDS)
cg.add_define(
"ESP8266_PREFERENCES_FLASH_WORDS",
preferences_flash_words(config[CONF_RESTORE_FROM_FLASH]),
)
if config[CONF_RESTORE_FROM_FLASH]:
cg.add_define("USE_ESP8266_PREFERENCES_FLASH")
@@ -528,6 +539,7 @@ async def to_code(config: ConfigType) -> None:
CORE.add_job(add_pin_initial_states_array)
CORE.add_job(finalize_waveform_config)
CORE.add_job(finalize_serial_config)
CORE.add_job(check_preferences_fit)
@coroutine_with_priority(CoroPriority.WORKAROUNDS)
+86 -1
View File
@@ -1,9 +1,13 @@
import logging
import esphome.codegen as cg
# Re-exported from the shared definition; here it indexes the BOARDS
# metadata dicts, whose entries in boards.py spell the literal.
from esphome.const import KEY_FLASH_SIZE # noqa: F401 # pylint: disable=unused-import
from esphome.core import CORE
from esphome.core import CORE, CoroPriority, EsphomeError, coroutine_with_priority
_LOGGER = logging.getLogger(__name__)
KEY_ESP8266 = "esp8266"
KEY_BOARD = "board"
@@ -24,6 +28,87 @@ KEY_LDSCRIPT = "ldscript"
# Crash-decode tools, resolved once per run by __init__
KEY_DECODE_TOOLS = "decode_tools"
# Saved-settings space in 4-byte words; preferences.cpp gets these as defines. A value cannot span the two
# RTC regions, and the flash store is smaller when only explicit flash preferences use it.
PREFERENCES_RTC_NORMAL_WORDS = 78
PREFERENCES_RTC_EBOOT_WORDS = 32
PREFERENCES_RTC_WORDS = PREFERENCES_RTC_NORMAL_WORDS + PREFERENCES_RTC_EBOOT_WORDS
PREFERENCES_FLASH_WORDS = 128
PREFERENCES_FLASH_WORDS_WITHOUT_RESTORE = 64
KEY_PREFERENCES = "preferences"
def preferences_flash_words(restore_from_flash: bool) -> int:
"""Size of the flash store for this configuration."""
return (
PREFERENCES_FLASH_WORDS
if restore_from_flash
else PREFERENCES_FLASH_WORDS_WITHOUT_RESTORE
)
def preference_words(value_bytes: int) -> int:
"""Words a saved value takes, checksum word included (rtc_pref_bytes_to_words plus one)."""
return (value_bytes + 3) // 4 + 1
def reserve_preferences(
count: int, value_bytes: int, in_flash: bool | None = None
) -> None:
"""Record saved values a component creates; check_preferences_fit() adds them all up.
in_flash is the value's own storage, as passed to make_preference(); None is the two-argument
form, which follows restore_from_flash.
"""
if in_flash is None:
in_flash = CORE.config[KEY_ESP8266][CONF_RESTORE_FROM_FLASH]
reservations = CORE.data.setdefault(KEY_ESP8266, {}).setdefault(KEY_PREFERENCES, [])
reservations.extend([(preference_words(value_bytes), in_flash)] * count)
def _rtc_values_can_split(values: list[int]) -> bool:
"""Whether some order fills the two RTC regions without a value spanning them."""
total = sum(values)
if total <= PREFERENCES_RTC_NORMAL_WORDS:
return True
# Bit i of reachable is set when some subset of the values sums to i
reachable = 1
for words in values:
reachable |= reachable << words
lowest = max(0, total - PREFERENCES_RTC_EBOOT_WORDS)
highest = min(total, PREFERENCES_RTC_NORMAL_WORDS)
return any(reachable >> normal & 1 for normal in range(lowest, highest + 1))
@coroutine_with_priority(CoroPriority.FINAL)
async def check_preferences_fit() -> None:
"""Fail the build when the reserved saved values cannot fit the space preferences.cpp has."""
flash_store = preferences_flash_words(
CORE.config[KEY_ESP8266][CONF_RESTORE_FROM_FLASH]
)
reservations = CORE.data[KEY_ESP8266].get(KEY_PREFERENCES, [])
flash_words = sum(w for w, in_flash in reservations if in_flash)
rtc_values = [w for w, in_flash in reservations if not in_flash]
if flash_words > flash_store:
raise EsphomeError(
f"Saved values need {flash_words} words but the ESP8266 flash store has {flash_store}; "
"use fewer or smaller saved values"
)
if not _rtc_values_can_split(rtc_values):
raise EsphomeError(
f"Saved values need {sum(rtc_values)} words but ESP8266 RTC memory has {PREFERENCES_RTC_WORDS} in "
f"regions of {PREFERENCES_RTC_NORMAL_WORDS} and {PREFERENCES_RTC_EBOOT_WORDS} that a value cannot "
"span; use fewer or smaller saved values, or store them in flash"
)
if sum(rtc_values) > PREFERENCES_RTC_NORMAL_WORDS:
_LOGGER.warning(
"Saved values reserve %d of %d words of ESP8266 RTC memory; the fit depends on setup order, and "
"saved settings that do not reserve are not counted",
sum(rtc_values),
PREFERENCES_RTC_WORDS,
)
# esp8266 namespace is already defined by arduino, manually prefix esphome
esp8266_ns = cg.global_ns.namespace("esphome").namespace("esp8266")
+5 -7
View File
@@ -4,6 +4,7 @@
#ifdef USE_ESP8266_CRASH_HANDLER
#include "crash_handler.h"
#include "rtc_layout.h"
#include "esphome/core/log.h"
#include <cinttypes>
@@ -46,11 +47,8 @@ static inline bool IRAM_ATTR is_code_addr(uint32_t val) {
// Recover the actual code address from a windowed-ABI return address on the stack.
static inline uint32_t IRAM_ATTR recover_code_addr(uint32_t val) { return (val & XTENSA_ADDR_MASK) | XTENSA_CODE_BASE; }
// RTC user memory layout for crash backtrace data.
// User-accessible RTC memory: blocks 64-191 (each block = 4 bytes).
// We use blocks 174-191 (last 18 blocks, 72 bytes) to minimize conflicts.
// Crash data lives in the last esphome::esp8266::RTC_CRASH_WORDS of user RTC memory (rtc_layout.h).
// Store 16 raw candidates, filter to real return addresses at log time.
static constexpr uint8_t RTC_CRASH_BASE = 174;
static constexpr size_t MAX_BACKTRACE = 16;
// Magic word packs sentinel, version, and count into one uint32_t:
@@ -78,7 +76,7 @@ struct RtcCrashData {
uint32_t backtrace[MAX_BACKTRACE];
uint32_t epc1; // Fault PC, used to filter duplicates
};
static_assert(sizeof(RtcCrashData) == 72, "RtcCrashData must fit in 18 RTC blocks");
static_assert(sizeof(RtcCrashData) == esphome::esp8266::RTC_CRASH_WORDS * 4, "RtcCrashData must fill its RTC words");
namespace esphome::esp8266 {
@@ -129,7 +127,7 @@ static const LogString *get_reset_reason(uint32_t reason) {
// Returns the number of valid backtrace entries (0 if no data found).
static uint8_t read_rtc_backtrace(uint32_t *backtrace, size_t max_entries) {
RtcCrashData rtc_data;
if (!system_rtc_mem_read(RTC_CRASH_BASE, &rtc_data, sizeof(rtc_data)))
if (!system_rtc_mem_read(esphome::esp8266::RTC_CRASH_BASE, &rtc_data, sizeof(rtc_data)))
return 0;
uint32_t magic = rtc_data.magic;
if ((magic & CRASH_SENTINEL_MASK) != CRASH_SENTINEL || (magic & CRASH_VERSION_MASK) != CRASH_VERSION)
@@ -233,7 +231,7 @@ extern "C" void IRAM_ATTR custom_crash_callback(struct rst_info *rst_info, uint3
data.epc1 = epc1;
data.magic = CRASH_SENTINEL | CRASH_VERSION | count;
system_rtc_mem_write(RTC_CRASH_BASE, &data, sizeof(data));
system_rtc_mem_write(esphome::esp8266::RTC_CRASH_BASE, &data, sizeof(data));
}
#endif // USE_ESP8266_CRASH_HANDLER
+13 -17
View File
@@ -6,6 +6,7 @@ extern "C" {
}
#include "preferences.h"
#include "rtc_layout.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include "esphome/core/preferences_rtc.h"
@@ -17,31 +18,26 @@ namespace esphome::esp8266 {
ESPHOME_LOG_TAG(TAG, "preferences");
static constexpr uint32_t ESP_RTC_USER_MEM_START = 0x60001200;
static constexpr uint32_t ESP_RTC_USER_MEM_SIZE_WORDS = 128;
static constexpr uint32_t ESP_RTC_USER_MEM_SIZE_WORDS = RTC_USER_MEM_WORDS;
static constexpr uint32_t ESP_RTC_USER_MEM_SIZE_BYTES = ESP_RTC_USER_MEM_SIZE_WORDS * 4;
// RTC memory layout:
// RTC memory layout (sizes come from const.py through codegen):
// - Eboot region: RTC words 0-31 (reserved, mapped from preference offset 78-109)
// - Normal region: RTC words 32-109 (mapped from preference offset 0-77)
// - Crash handler: RTC words 110-127 (reserved for crash_handler.cpp backtrace data)
static constexpr uint32_t RTC_EBOOT_REGION_WORDS = 32; // Words 0-31 reserved for eboot
static constexpr uint32_t RTC_NORMAL_REGION_WORDS = 78; // Words 32-109 for normal prefs
static constexpr uint32_t RTC_EBOOT_REGION_WORDS = ESP8266_PREFERENCES_RTC_EBOOT_WORDS;
static constexpr uint32_t RTC_NORMAL_REGION_WORDS = ESP8266_PREFERENCES_RTC_NORMAL_WORDS;
static constexpr uint32_t PREF_TOTAL_WORDS = RTC_EBOOT_REGION_WORDS + RTC_NORMAL_REGION_WORDS; // 110
static_assert(PREF_TOTAL_WORDS <= RTC_CRASH_BASE - RTC_SYSTEM_USER_BASE,
"preference regions overlap the crash handler's RTC words");
// Maximum preference size in words (limited by uint8_t length_words field)
static constexpr uint32_t MAX_PREFERENCE_WORDS = 255;
#define ESP_RTC_USER_MEM ((uint32_t *) ESP_RTC_USER_MEM_START)
// Flash storage size depends on esp8266 -> restore_from_flash YAML option (default: false).
// When enabled (USE_ESP8266_PREFERENCES_FLASH), all preferences default to flash and need
// 128 words (512 bytes). When disabled, only explicit flash prefs use this storage so
// 64 words (256 bytes) suffices since most preferences go to RTC memory instead.
#ifdef USE_ESP8266_PREFERENCES_FLASH
static constexpr uint32_t ESP8266_FLASH_STORAGE_SIZE = 128;
#else
static constexpr uint32_t ESP8266_FLASH_STORAGE_SIZE = 64;
#endif
// Smaller without restore_from_flash, when only explicit flash preferences live here
static constexpr uint32_t ESP8266_FLASH_STORAGE_SIZE = ESP8266_PREFERENCES_FLASH_WORDS;
static uint32_t
s_flash_storage[ESP8266_FLASH_STORAGE_SIZE]; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
@@ -60,7 +56,7 @@ static inline bool esp_rtc_user_mem_write(uint32_t index, uint32_t value) {
if (index >= ESP_RTC_USER_MEM_SIZE_WORDS) {
return false;
}
if (index < 32 && s_prevent_write) {
if (index < RTC_EBOOT_REGION_WORDS && s_prevent_write) {
return false;
}
@@ -123,7 +119,7 @@ static bool load_from_rtc(size_t offset, uint32_t *data, size_t len) {
// Maximum buffer for any single preference - bounded by storage sizes.
// Flash prefs: bounded by ESP8266_FLASH_STORAGE_SIZE (128 or 64 words).
// RTC prefs: bounded by RTC_NORMAL_REGION_WORDS (96) - a single pref can't span both RTC regions.
// RTC prefs: bounded by RTC_NORMAL_REGION_WORDS (78) - a single pref can't span both RTC regions.
static constexpr size_t PREF_MAX_BUFFER_WORDS =
ESP8266_FLASH_STORAGE_SIZE > RTC_NORMAL_REGION_WORDS ? ESP8266_FLASH_STORAGE_SIZE : RTC_NORMAL_REGION_WORDS;
@@ -180,8 +176,8 @@ ESPPreferenceObject ESP8266Preferences::make_preference(size_t length, uint32_t
} else {
uint32_t start = this->current_offset;
bool in_normal = start < RTC_NORMAL_REGION_WORDS;
// Normal: offset 0-95 maps to RTC offset 32-127
// Eboot: offset 96-127 maps to RTC offset 0-31
// Normal: offset 0-77 maps to RTC offset 32-109
// Eboot: offset 78-109 maps to RTC offset 0-31
if (in_normal && start + total_words > RTC_NORMAL_REGION_WORDS) {
// start is in normal but end is not -> switch to Eboot
this->current_offset = start = RTC_NORMAL_REGION_WORDS;
+18
View File
@@ -0,0 +1,18 @@
#pragma once
#ifdef USE_ESP8266
#include <cstdint>
namespace esphome::esp8266 {
// User RTC memory is system blocks 64-191, one 4-byte word each. preferences.cpp owns the first
// words, crash_handler.cpp the last; const.py hands preferences.cpp its region sizes.
static constexpr uint32_t RTC_SYSTEM_USER_BASE = 64;
static constexpr uint32_t RTC_USER_MEM_WORDS = 128;
static constexpr uint32_t RTC_CRASH_WORDS = 18;
static constexpr uint32_t RTC_CRASH_BASE = RTC_SYSTEM_USER_BASE + RTC_USER_MEM_WORDS - RTC_CRASH_WORDS;
} // namespace esphome::esp8266
#endif // USE_ESP8266
+2 -2
View File
@@ -17,11 +17,11 @@ void Event::trigger(const char *event_type) {
}
}
if (found == nullptr) {
ESP_LOGE(TAG, "'%s': invalid event type for trigger(): %s", this->get_name().c_str(), event_type);
ESP_LOGE(TAG, "'%s': invalid event type for trigger(): %s", LOG_STR_ARG(this->get_log_name()), event_type);
return;
}
this->last_event_type_ = found;
ESP_LOGV(TAG, "'%s' >> '%s'", this->get_name().c_str(), this->last_event_type_);
ESP_LOGV(TAG, "'%s' >> '%s'", LOG_STR_ARG(this->get_log_name()), this->last_event_type_);
this->event_callback_.call(StringRef(found));
#if defined(USE_EVENT) && defined(USE_CONTROLLER_REGISTRY)
ControllerRegistry::notify_event(this);
+1 -1
View File
@@ -16,7 +16,7 @@ using EventTypes = ConstVector<const char *, true>;
#define LOG_EVENT(prefix, type, obj) \
if ((obj) != nullptr) { \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), LOG_STR_ARG((obj)->get_log_name())); \
LOG_ENTITY_ICON(TAG, prefix, *(obj)); \
LOG_ENTITY_DEVICE_CLASS(TAG, prefix, *(obj)); \
}
+7 -6
View File
@@ -52,14 +52,15 @@ FanCall &FanCall::set_preset_mode(const char *preset_mode, size_t len) {
this->preset_mode_ = validated_mode; // Store pointer from traits
} else {
// Preset mode not found in traits - log warning and don't set
ESP_LOGW(TAG, "%s: Preset mode '%.*s' not supported", this->parent_.get_name().c_str(), (int) len, preset_mode);
ESP_LOGW(TAG, "%s: Preset mode '%.*s' not supported", LOG_STR_ARG(this->parent_.get_log_name()), (int) len,
preset_mode);
this->preset_mode_ = nullptr;
}
return *this;
}
void FanCall::perform() {
ESP_LOGV(TAG, "'%s' - Setting:", this->parent_.get_name().c_str());
ESP_LOGV(TAG, "'%s' - Setting:", LOG_STR_ARG(this->parent_.get_log_name()));
this->validate_();
if (this->binary_state_.has_value()) {
ESP_LOGV(TAG, " State: %s", ONOFF(*this->binary_state_));
@@ -103,17 +104,17 @@ void FanCall::validate_() {
}
if (this->oscillating_.has_value() && !traits.supports_oscillation()) {
ESP_LOGW(TAG, "%s: Oscillation not supported", this->parent_.get_name().c_str());
ESP_LOGW(TAG, "%s: Oscillation not supported", LOG_STR_ARG(this->parent_.get_log_name()));
this->oscillating_.reset();
}
if (this->speed_.has_value() && !traits.supports_speed()) {
ESP_LOGW(TAG, "%s: Speed control not supported", this->parent_.get_name().c_str());
ESP_LOGW(TAG, "%s: Speed control not supported", LOG_STR_ARG(this->parent_.get_log_name()));
this->speed_.reset();
}
if (this->direction_.has_value() && !traits.supports_direction()) {
ESP_LOGW(TAG, "%s: Direction control not supported", this->parent_.get_name().c_str());
ESP_LOGW(TAG, "%s: Direction control not supported", LOG_STR_ARG(this->parent_.get_log_name()));
this->direction_.reset();
}
}
@@ -221,7 +222,7 @@ void Fan::publish_state() {
ESP_LOGV(TAG,
"'%s' >>\n"
" State: %s",
this->name_.c_str(), ONOFF(this->state));
LOG_STR_ARG(this->get_log_name()), ONOFF(this->state));
if (traits.supports_speed()) {
ESP_LOGV(TAG, " Speed: %d", this->speed);
}
+1 -1
View File
@@ -12,7 +12,7 @@ namespace esphome::fan {
#define LOG_FAN(prefix, type, obj) \
if ((obj) != nullptr) { \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), LOG_STR_ARG((obj)->get_log_name())); \
(obj)->dump_traits_(TAG, prefix); \
}
+10 -10
View File
@@ -98,7 +98,7 @@ void FeedbackCover::set_open_sensor(binary_sensor::BinarySensor *open_feedback)
// setup callbacks to react to sensor changes
open_feedback->add_on_state_callback([this](bool state) {
ESP_LOGD(TAG, "'%s' - Open feedback '%s'.", this->name_.c_str(),
ESP_LOGD(TAG, "'%s' - Open feedback '%s'.", LOG_STR_ARG(this->get_log_name()),
state ? LOG_STR_LITERAL("STARTED") : LOG_STR_LITERAL("ENDED"));
this->recompute_position_();
if (!state && this->infer_endstop_ && this->current_trigger_operation_ == COVER_OPERATION_OPENING) {
@@ -112,7 +112,7 @@ void FeedbackCover::set_close_sensor(binary_sensor::BinarySensor *close_feedback
this->close_feedback_ = close_feedback;
close_feedback->add_on_state_callback([this](bool state) {
ESP_LOGD(TAG, "'%s' - Close feedback '%s'.", this->name_.c_str(),
ESP_LOGD(TAG, "'%s' - Close feedback '%s'.", LOG_STR_ARG(this->get_log_name()),
state ? LOG_STR_LITERAL("STARTED") : LOG_STR_LITERAL("ENDED"));
this->recompute_position_();
if (!state && this->infer_endstop_ && this->current_trigger_operation_ == COVER_OPERATION_CLOSING) {
@@ -151,7 +151,7 @@ void FeedbackCover::endstop_reached_(bool open_endstop) {
// from a position slightly past the endpoint
if (this->current_trigger_operation_ == (open_endstop ? COVER_OPERATION_OPENING : COVER_OPERATION_CLOSING)) {
float dur = (now - this->start_dir_time_) / 1e3f;
ESP_LOGD(TAG, "'%s' - %s endstop reached. Took %.1fs.", this->name_.c_str(),
ESP_LOGD(TAG, "'%s' - %s endstop reached. Took %.1fs.", LOG_STR_ARG(this->get_log_name()),
open_endstop ? LOG_STR_LITERAL("Open") : LOG_STR_LITERAL("Close"), dur);
// if there is no external mechanism, stop the cover
@@ -198,7 +198,7 @@ void FeedbackCover::set_close_obstacle_sensor(binary_sensor::BinarySensor *close
close_obstacle->add_on_state_callback([this](bool state) {
if (state && (this->current_operation == COVER_OPERATION_CLOSING ||
this->current_trigger_operation_ == COVER_OPERATION_CLOSING)) {
ESP_LOGD(TAG, "'%s' - Close obstacle detected.", this->name_.c_str());
ESP_LOGD(TAG, "'%s' - Close obstacle detected.", LOG_STR_ARG(this->get_log_name()));
this->start_direction_(COVER_OPERATION_IDLE);
if (this->obstacle_rollback_) {
@@ -215,7 +215,7 @@ void FeedbackCover::set_open_obstacle_sensor(binary_sensor::BinarySensor *open_o
open_obstacle->add_on_state_callback([this](bool state) {
if (state && (this->current_operation == COVER_OPERATION_OPENING ||
this->current_trigger_operation_ == COVER_OPERATION_OPENING)) {
ESP_LOGD(TAG, "'%s' - Open obstacle detected.", this->name_.c_str());
ESP_LOGD(TAG, "'%s' - Open obstacle detected.", LOG_STR_ARG(this->get_log_name()));
this->start_direction_(COVER_OPERATION_IDLE);
if (this->obstacle_rollback_) {
@@ -251,7 +251,7 @@ void FeedbackCover::loop() {
this->start_overshoot_time_ = now;
} else if (now - this->start_overshoot_time_ > this->overshoot_duration_ ||
now - this->start_dir_time_ > this->max_duration_) {
ESP_LOGD(TAG, "'%s' - Overshoot duration reached. Stopping cover.", this->name_.c_str());
ESP_LOGD(TAG, "'%s' - Overshoot duration reached. Stopping cover.", LOG_STR_ARG(this->get_log_name()));
this->start_direction_(COVER_OPERATION_IDLE);
}
#endif
@@ -259,7 +259,7 @@ void FeedbackCover::loop() {
this->start_direction_(COVER_OPERATION_IDLE);
}
} else if (now - this->start_dir_time_ > this->max_duration_) {
ESP_LOGD(TAG, "'%s' - Max duration reached. Stopping cover.", this->name_.c_str());
ESP_LOGD(TAG, "'%s' - Max duration reached. Stopping cover.", LOG_STR_ARG(this->get_log_name()));
this->start_direction_(COVER_OPERATION_IDLE);
}
}
@@ -390,7 +390,7 @@ void FeedbackCover::start_direction_(CoverOperation dir) {
// check if there is an obstacle to start the new operation -> abort without any change
// the case when an obstacle appears while moving is handled in the callback
if (obstacle != nullptr && obstacle->state) {
ESP_LOGD(TAG, "'%s' - %s obstacle detected. Action not started.", this->name_.c_str(),
ESP_LOGD(TAG, "'%s' - %s obstacle detected. Action not started.", LOG_STR_ARG(this->get_log_name()),
dir == COVER_OPERATION_OPENING ? LOG_STR_LITERAL("Open") : LOG_STR_LITERAL("Close"));
return;
}
@@ -401,13 +401,13 @@ void FeedbackCover::start_direction_(CoverOperation dir) {
if (this->direction_change_waittime_.has_value() && dir != COVER_OPERATION_IDLE &&
this->current_operation != COVER_OPERATION_IDLE && dir != this->current_operation) {
const uint32_t waittime = *this->direction_change_waittime_;
ESP_LOGD(TAG, "'%s' - Reversing direction.", this->name_.c_str());
ESP_LOGD(TAG, "'%s' - Reversing direction.", LOG_STR_ARG(this->get_log_name()));
this->start_direction_(COVER_OPERATION_IDLE);
this->set_timeout(DIRECTION_CHANGE_TIMEOUT_ID, waittime, [this, dir]() { this->start_direction_(dir); });
} else {
this->set_current_operation_(dir, true);
this->prev_command_trigger_ = trig;
ESP_LOGD(TAG, "'%s' - Firing '%s' trigger.", this->name_.c_str(),
ESP_LOGD(TAG, "'%s' - Firing '%s' trigger.", LOG_STR_ARG(this->get_log_name()),
dir == COVER_OPERATION_OPENING ? LOG_STR_LITERAL("OPEN")
: dir == COVER_OPERATION_CLOSING ? LOG_STR_LITERAL("CLOSE")
: LOG_STR_LITERAL("STOP"));
+40
View File
@@ -0,0 +1,40 @@
from esphome import pins
import esphome.codegen as cg
from esphome.components import i2c
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_INTERRUPT_PIN
from esphome.types import ConfigType
CODEOWNERS = ["@remcom"]
DEPENDENCIES = ["i2c"]
DOMAIN = "fusb302b"
CONF_REQUEST_VOLTAGE = "request_voltage"
fusb302b_ns = cg.esphome_ns.namespace("fusb302b")
FUSB302B = fusb302b_ns.class_("FUSB302B", cg.Component, i2c.I2CDevice)
# USB PD fixed supply voltages are 5, 9, 12, 15 and 20 V; the highest one that does not exceed the
# requested voltage is chosen.
validate_voltage = cv.int_range(min=5, max=20)
CONFIG_SCHEMA = cv.All(
cv.Schema(
{
cv.GenerateID(): cv.declare_id(FUSB302B),
cv.Required(CONF_INTERRUPT_PIN): pins.internal_gpio_input_pullup_pin_schema,
cv.Required(CONF_REQUEST_VOLTAGE): validate_voltage,
}
)
.extend(cv.COMPONENT_SCHEMA)
.extend(i2c.i2c_device_schema(0x22)),
cv.only_on_esp32,
)
async def to_code(config: ConfigType) -> None:
interrupt_pin = await cg.gpio_pin_expression(config[CONF_INTERRUPT_PIN])
var = cg.new_Pvariable(config[CONF_ID], interrupt_pin)
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
cg.add(var.set_request_voltage(config[CONF_REQUEST_VOLTAGE]))
+415
View File
@@ -0,0 +1,415 @@
#include "fusb302b.h"
#ifdef USE_ESP32
#include "esphome/core/log.h"
#include "fusb302b_registers.h"
namespace esphome::fusb302b {
ESPHOME_LOG_TAG(TAG, "fusb302b");
// Task notification bits
static constexpr uint32_t NOTIFY_IRQ = 1 << 0;
static constexpr uint32_t NOTIFY_STOP = 1 << 1;
static constexpr uint32_t TASK_STACK_SIZE = 4096;
static constexpr UBaseType_t TASK_PRIORITY = 18;
static constexpr uint32_t STARTUP_DELAY_MS = 2000;
static constexpr uint32_t CC_SETTLE_MS = 5;
static constexpr uint32_t CC_POLL_INTERVAL_MS = 500;
static constexpr uint32_t PD_POLL_INTERVAL_MS = 100;
// Safety net in case an interrupt edge is missed while attached and idle
static constexpr uint32_t IDLE_POLL_INTERVAL_MS = 1000;
// Time to wait for the source to send its capabilities on its own after attach
static constexpr uint32_t SOURCE_CAP_WAIT_MS = 5000;
// Time to wait after each GET_SOURCE_CAP / soft reset before the next recovery step
static constexpr uint32_t SOURCE_CAP_RETRY_MS = 2500;
// Upper bound on back-to-back interrupt handling while INT_N stays asserted
static constexpr uint8_t MAX_IRQ_ROUNDS = 8;
// BC_LVL is read this many extra times to confirm a stable measurement
static constexpr uint8_t CC_STABLE_READS = 5;
static constexpr uint8_t CC_LEVEL_UNSTABLE = 0xFF;
void IRAM_ATTR FUSB302B::gpio_intr(FUSB302B *arg) {
TaskHandle_t handle = arg->task_.get_handle();
if (handle == nullptr)
return;
BaseType_t woken = pdFALSE;
xTaskNotifyFromISR(handle, NOTIFY_IRQ, eSetBits, &woken);
portYIELD_FROM_ISR(woken);
}
void FUSB302B::task_func(void *arg) { static_cast<FUSB302B *>(arg)->run_task_(); }
void FUSB302B::setup() {
uint8_t device_id = 0;
if (!this->read_byte(FUSB_DEVICE_ID, &device_id)) {
this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
return;
}
const uint8_t version = device_id & FUSB_DEVICE_ID_VERSION_MASK;
if (version != FUSB_DEVICE_ID_VERSION_A && version != FUSB_DEVICE_ID_VERSION_B) {
ESP_LOGE(TAG, "Unknown device ID 0x%02X", device_id);
this->mark_failed();
return;
}
ESP_LOGD(TAG, "Device ID 0x%02X (product %u, revision %u)", device_id, (device_id >> 2) & 0x3, device_id & 0x3);
if (!this->init_chip_()) {
this->mark_failed(LOG_STR("Initialization failed"));
return;
}
this->interrupt_pin_->setup();
// The task must exist before the interrupt is attached, since the ISR notifies it
if (!this->task_.create(FUSB302B::task_func, "fusb302b", TASK_STACK_SIZE, this, TASK_PRIORITY, false)) {
this->mark_failed(LOG_STR("Failed to create task"));
return;
}
this->interrupt_pin_->attach_interrupt(&FUSB302B::gpio_intr, this, gpio::INTERRUPT_FALLING_EDGE);
}
void FUSB302B::dump_config() {
ESP_LOGCONFIG(TAG,
"FUSB302B:\n"
" Request Voltage: %u V",
this->get_request_voltage());
LOG_I2C_DEVICE(this);
LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
}
void FUSB302B::on_shutdown() {
this->interrupt_pin_->detach_interrupt();
// The task parks itself instead of being deleted, so it can never be stopped in the middle of an I2C transfer
if (this->task_.is_created())
xTaskNotify(this->task_.get_handle(), NOTIFY_STOP, eSetBits);
}
void FUSB302B::run_task_() {
// Give the source time to settle after power-up before the first CC measurement
vTaskDelay(pdMS_TO_TICKS(STARTUP_DELAY_MS));
while (true) {
uint32_t wait_ms = CC_POLL_INTERVAL_MS;
if (this->attached_) {
const bool busy = this->waiting_for_source_caps_ || this->retry_request_pending_ || this->check_ams_();
wait_ms = busy ? PD_POLL_INTERVAL_MS : IDLE_POLL_INTERVAL_MS;
}
uint32_t bits = 0;
xTaskNotifyWait(0, UINT32_MAX, &bits, pdMS_TO_TICKS(wait_ms));
if (bits & NOTIFY_STOP) {
while (true)
vTaskSuspend(nullptr);
}
// INT_N is held low until the interrupt registers are read, so a new event that arrives while it is
// already low has no falling edge: keep servicing while the pin stays asserted.
if (bits & NOTIFY_IRQ)
this->handle_interrupt_();
for (uint8_t i = 0; i < MAX_IRQ_ROUNDS && !this->interrupt_pin_->digital_read(); i++)
this->handle_interrupt_();
if (!this->attached_) {
this->try_attach_();
continue;
}
this->check_transition_timeout_();
this->check_request_retry_();
this->check_source_caps_();
}
}
bool FUSB302B::init_chip_() {
// Software reset restores all registers to their defaults
if (!this->write_byte(FUSB_RESET, FUSB_RESET_SW_RES))
return false;
if (!this->reset_pd_())
return false;
// Unmask the interrupts used for PD messaging and VBUS changes
if (!this->write_byte(FUSB_MASK1, FUSB_MASK1_DEFAULT) || !this->write_byte(FUSB_MASKA, 0) ||
!this->write_byte(FUSB_MASKB, 0))
return false;
// Let the chip retransmit unacknowledged messages up to 3 times
uint8_t control3 = 0;
if (!this->read_byte(FUSB_CONTROL3, &control3))
return false;
control3 =
(control3 & ~FUSB_CONTROL3_N_RETRIES_MASK) | (3 << FUSB_CONTROL3_N_RETRIES_SHIFT) | FUSB_CONTROL3_AUTO_RETRY;
if (!this->write_byte(FUSB_CONTROL3, control3))
return false;
if (!this->write_byte(FUSB_POWER, FUSB_POWER_ALL))
return false;
return this->reset_pd_();
}
bool FUSB302B::reset_pd_() {
this->reset_protocol_();
// Writing CONTROL0 as a whole byte also clears INT_MASK, which is what enables the INT_N pin
return this->write_byte(FUSB_CONTROL0, FUSB_CONTROL0_TX_FLUSH) &&
this->write_byte(FUSB_CONTROL1, FUSB_CONTROL1_RX_FLUSH) && this->write_byte(FUSB_RESET, FUSB_RESET_PD_RESET);
}
bool FUSB302B::enable_auto_crc_() {
uint8_t switches1 = 0;
return this->read_byte(FUSB_SWITCHES1, &switches1) &&
this->write_byte(FUSB_SWITCHES1, switches1 | FUSB_SWITCHES1_AUTO_CRC);
}
void FUSB302B::handle_interrupt_() {
// STATUS0A, STATUS1A, INTERRUPTA, INTERRUPTB, STATUS0, STATUS1, INTERRUPT; reading clears the interrupts
uint8_t regs[7];
if (this->read_register(FUSB_STATUS0A, regs, sizeof(regs)) != i2c::ERROR_OK) {
this->enter_error_();
return;
}
const uint8_t interrupta = regs[2];
const uint8_t status0 = regs[4];
uint8_t status1 = regs[5];
const uint8_t interrupt = regs[6];
if ((interrupt & FUSB_INTERRUPT_I_VBUSOK) && !(status0 & FUSB_STATUS0_VBUSOK)) {
if (this->attached_)
this->detach_();
return;
}
if (interrupta & FUSB_INTERRUPTA_I_HARDRST) {
if (this->attached_)
this->handle_hard_reset_();
return;
}
if (interrupta & FUSB_INTERRUPTA_I_SOFTRST) {
ESP_LOGV(TAG, "Soft reset received");
}
if ((interrupta & FUSB_INTERRUPTA_I_RETRYFAIL) && this->attached_)
this->handle_transmit_failed_();
if (!this->attached_)
return;
PdMsg msg;
while (!(status1 & FUSB_STATUS1_RX_EMPTY)) {
bool is_sop = false;
if (!this->read_message_(msg, is_sop)) {
ESP_LOGW(TAG, "Reading message failed");
// A partial read leaves the FIFO misaligned
this->write_byte(FUSB_CONTROL1, FUSB_CONTROL1_RX_FLUSH);
return;
}
// Only SOP packets are addressed to a sink; SOP'/SOP'' are for cable plugs
if (is_sop)
this->handle_message_(msg);
if (!this->read_byte(FUSB_STATUS1, &status1))
return;
}
}
bool FUSB302B::read_message_(PdMsg &msg, bool &is_sop) {
uint8_t token = 0;
uint8_t header[2];
if (!this->read_byte(FUSB_FIFOS, &token) || this->read_register(FUSB_FIFOS, header, 2) != i2c::ERROR_OK)
return false;
msg.set_header(encode_uint16(header[1], header[0]));
// Data objects, followed by the CRC32 that the chip already checked
uint8_t buf[PD_MAX_NUM_DATA_OBJECTS * 4 + 4];
if (this->read_register(FUSB_FIFOS, buf, msg.num_of_obj * 4 + 4) != i2c::ERROR_OK)
return false;
for (uint8_t i = 0; i < msg.num_of_obj; i++) {
const uint8_t *obj = &buf[i * 4];
msg.data_objects[i] = encode_uint32(obj[3], obj[2], obj[1], obj[0]);
}
is_sop = (token & FUSB_FIFO_RX_TOKEN_BITS) == FUSB_FIFO_RX_SOP;
return true;
}
bool FUSB302B::send_message(const PdMsg &msg) {
// Register address, then the TX tokens; written in one transfer without the copy write_register() makes
uint8_t buf[1 + 4 + 1 + 2 + PD_MAX_NUM_DATA_OBJECTS * 4 + 4];
size_t len = 0;
const uint16_t header = msg.get_coded_header();
buf[len++] = FUSB_FIFOS;
buf[len++] = FUSB_TX_TOKEN_SYNC1;
buf[len++] = FUSB_TX_TOKEN_SYNC1;
buf[len++] = FUSB_TX_TOKEN_SYNC1;
buf[len++] = FUSB_TX_TOKEN_SYNC2;
buf[len++] = FUSB_TX_TOKEN_PACKSYM | (msg.num_of_obj * 4 + 2);
buf[len++] = header & 0xFF;
buf[len++] = header >> 8;
for (uint8_t i = 0; i < msg.num_of_obj; i++) {
const uint32_t obj = msg.data_objects[i];
buf[len++] = obj & 0xFF;
buf[len++] = (obj >> 8) & 0xFF;
buf[len++] = (obj >> 16) & 0xFF;
buf[len++] = obj >> 24;
}
buf[len++] = FUSB_TX_TOKEN_JAM_CRC;
buf[len++] = FUSB_TX_TOKEN_EOP;
buf[len++] = FUSB_TX_TOKEN_TXOFF;
buf[len++] = FUSB_TX_TOKEN_TXON;
i2c::ErrorCode err = this->write(buf, len);
if (err != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Sending message type %u failed (error %d)", msg.type, static_cast<int>(err));
return false;
}
return true;
}
bool FUSB302B::measure_cc_(uint8_t meas_switch, uint8_t &level) {
if (!this->write_byte(FUSB_SWITCHES0, FUSB_SWITCHES0_PDWN_1 | FUSB_SWITCHES0_PDWN_2 | meas_switch))
return false;
vTaskDelay(pdMS_TO_TICKS(CC_SETTLE_MS));
uint8_t status0 = 0;
if (!this->read_byte(FUSB_STATUS0, &status0))
return false;
level = status0 & FUSB_STATUS0_BC_LVL;
for (uint8_t i = 0; i < CC_STABLE_READS; i++) {
if (!this->read_byte(FUSB_STATUS0, &status0))
return false;
if ((status0 & FUSB_STATUS0_BC_LVL) != level) {
level = CC_LEVEL_UNSTABLE;
return true;
}
}
return true;
}
void FUSB302B::try_attach_() {
if (this->state_ == PdState::PD_STATE_ERROR) {
if (!this->init_chip_())
return;
ESP_LOGI(TAG, "Communication restored");
this->set_state_(PdState::PD_STATE_DISCONNECTED);
}
uint8_t status0 = 0;
if (!this->write_byte(FUSB_POWER, FUSB_POWER_ALL) || !this->read_byte(FUSB_STATUS0, &status0)) {
this->enter_error_();
return;
}
// A source may present its CC pull-up before VBUS is on; detach is only detected when VBUS drops
if (!(status0 & FUSB_STATUS0_VBUSOK)) {
this->cc_ambiguous_logged_ = false;
return;
}
uint8_t cc1 = 0;
uint8_t cc2 = 0;
if (!this->write_byte(FUSB_SWITCHES1, FUSB_SWITCHES1_SPECREV_REV2) ||
!this->write_byte(FUSB_MEASURE, FUSB_MEASURE_MDAC_CC) || !this->measure_cc_(FUSB_SWITCHES0_MEAS_CC1, cc1) ||
!this->measure_cc_(FUSB_SWITCHES0_MEAS_CC2, cc2)) {
this->enter_error_();
return;
}
// Exactly one CC line carries the source pull-up; the other one is open
uint8_t meas_switch;
uint8_t tx_switch;
if (cc1 != CC_LEVEL_UNSTABLE && cc1 > 0 && cc2 == 0) {
meas_switch = FUSB_SWITCHES0_MEAS_CC1;
tx_switch = FUSB_SWITCHES1_TXCC1;
} else if (cc2 != CC_LEVEL_UNSTABLE && cc2 > 0 && cc1 == 0) {
meas_switch = FUSB_SWITCHES0_MEAS_CC2;
tx_switch = FUSB_SWITCHES1_TXCC2;
} else {
if (!this->cc_ambiguous_logged_) {
ESP_LOGD(TAG, "VBUS present but no single CC line with a source pull-up (CC1 %u, CC2 %u)", cc1, cc2);
this->cc_ambiguous_logged_ = true;
}
return;
}
if (!this->write_byte(FUSB_SWITCHES0, FUSB_SWITCHES0_PDWN_1 | FUSB_SWITCHES0_PDWN_2 | meas_switch) ||
!this->write_byte(FUSB_SWITCHES1, tx_switch | FUSB_SWITCHES1_SPECREV_REV2) || !this->enable_auto_crc_() ||
!this->reset_pd_()) {
this->enter_error_();
return;
}
ESP_LOGD(TAG, "Attached on CC%u", meas_switch == FUSB_SWITCHES0_MEAS_CC1 ? 1 : 2);
this->attached_ = true;
this->cc_ambiguous_logged_ = false;
this->start_source_cap_wait_(SourceCapStep::SOURCE_CAP_STEP_WAIT);
this->set_state_and_contract_(PdState::PD_STATE_DEFAULT_CONTRACT, DEFAULT_CONTRACT);
}
void FUSB302B::detach_() {
ESP_LOGD(TAG, "Detached (VBUS lost)");
this->attached_ = false;
this->waiting_for_source_caps_ = false;
this->reset_pd_();
this->clear_contract_();
this->set_state_(PdState::PD_STATE_DISCONNECTED);
}
void FUSB302B::handle_hard_reset_() {
ESP_LOGD(TAG, "Hard reset received");
// The source returns to Type-C default power and sends its capabilities again
// Also flushes the RX FIFO, so the caller has nothing left to read
if (!this->reset_pd_()) {
this->enter_error_();
return;
}
this->set_state_and_contract_(PdState::PD_STATE_DEFAULT_CONTRACT, DEFAULT_CONTRACT);
this->start_source_cap_wait_(SourceCapStep::SOURCE_CAP_STEP_WAIT);
}
void FUSB302B::enter_error_() {
if (this->state_ == PdState::PD_STATE_ERROR)
return;
ESP_LOGW(TAG, "Communication with the chip failed");
this->attached_ = false;
this->waiting_for_source_caps_ = false;
this->clear_contract_();
this->set_state_(PdState::PD_STATE_ERROR);
}
void FUSB302B::send_soft_reset_() {
this->reset_pd_();
this->send_message(this->make_control_msg_(PD_CNTRL_SOFT_RESET));
// The source answers a soft reset with new source capabilities
this->start_source_cap_wait_(SourceCapStep::SOURCE_CAP_STEP_SOFT_RESET_SENT);
}
void FUSB302B::start_source_cap_wait_(SourceCapStep step) {
this->waiting_for_source_caps_ = true;
this->source_cap_step_ = step;
this->source_cap_step_start_ = millis();
}
void FUSB302B::check_source_caps_() {
if (!this->waiting_for_source_caps_ || this->check_ams_())
return;
const uint32_t elapsed = millis() - this->source_cap_step_start_;
switch (this->source_cap_step_) {
case SourceCapStep::SOURCE_CAP_STEP_WAIT:
if (elapsed < SOURCE_CAP_WAIT_MS)
return;
ESP_LOGD(TAG, "No source capabilities received, requesting them");
this->send_message(this->make_control_msg_(PD_CNTRL_GET_SOURCE_CAP));
this->source_cap_step_ = SourceCapStep::SOURCE_CAP_STEP_GET_SENT;
break;
case SourceCapStep::SOURCE_CAP_STEP_GET_SENT:
if (elapsed < SOURCE_CAP_RETRY_MS)
return;
ESP_LOGD(TAG, "No source capabilities received, sending soft reset");
this->send_soft_reset_();
return;
case SourceCapStep::SOURCE_CAP_STEP_SOFT_RESET_SENT:
if (elapsed < SOURCE_CAP_RETRY_MS)
return;
ESP_LOGW(TAG, "PD negotiation failed, staying at 5 V");
this->waiting_for_source_caps_ = false;
this->set_state_(PdState::PD_STATE_PD_TIMEOUT);
return;
}
this->source_cap_step_start_ = millis();
}
} // namespace esphome::fusb302b
#endif // USE_ESP32
+74
View File
@@ -0,0 +1,74 @@
#pragma once
#ifdef USE_ESP32
#include "esphome/components/i2c/i2c.h"
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/static_task.h"
#include "pd.h"
namespace esphome::fusb302b {
/// FUSB302B USB Type-C / Power Delivery sink controller.
///
/// A dedicated task owns all I2C traffic to the chip so that PD replies meet the protocol timing
/// even while the main loop is busy.
class FUSB302B : public PowerDelivery, public Component, public i2c::I2CDevice {
public:
explicit FUSB302B(InternalGPIOPin *interrupt_pin) : interrupt_pin_(interrupt_pin) {}
void setup() override;
void dump_config() override;
void on_shutdown() override;
float get_setup_priority() const override { return setup_priority::HARDWARE; }
protected:
enum class SourceCapStep : uint8_t {
SOURCE_CAP_STEP_WAIT,
SOURCE_CAP_STEP_GET_SENT,
SOURCE_CAP_STEP_SOFT_RESET_SENT,
};
static void gpio_intr(FUSB302B *arg);
static void task_func(void *arg);
bool send_message(const PdMsg &msg) override;
void on_state_changed() override {}
void on_soft_reset_received() override { this->start_source_cap_wait_(SourceCapStep::SOURCE_CAP_STEP_WAIT); }
void on_source_caps_received() override { this->waiting_for_source_caps_ = false; }
void on_transition_timeout() override { this->send_soft_reset_(); }
// PHY task only
[[noreturn]] void run_task_();
bool init_chip_();
bool reset_pd_();
bool enable_auto_crc_();
void handle_interrupt_();
bool read_message_(PdMsg &msg, bool &is_sop);
void try_attach_();
bool measure_cc_(uint8_t meas_switch, uint8_t &level);
void detach_();
void handle_hard_reset_();
void start_source_cap_wait_(SourceCapStep step);
void send_soft_reset_();
void check_source_caps_();
void enter_error_();
InternalGPIOPin *interrupt_pin_;
StaticTask task_;
// PHY task only
uint32_t source_cap_step_start_{0};
SourceCapStep source_cap_step_{SourceCapStep::SOURCE_CAP_STEP_WAIT};
bool attached_{false};
bool waiting_for_source_caps_{false};
bool cc_ambiguous_logged_{false};
};
} // namespace esphome::fusb302b
#endif // USE_ESP32
@@ -0,0 +1,94 @@
#pragma once
#include <cstdint>
namespace esphome::fusb302b {
// Register addresses
static constexpr uint8_t FUSB_DEVICE_ID = 0x01;
static constexpr uint8_t FUSB_SWITCHES0 = 0x02;
static constexpr uint8_t FUSB_SWITCHES1 = 0x03;
static constexpr uint8_t FUSB_MEASURE = 0x04;
static constexpr uint8_t FUSB_CONTROL0 = 0x06;
static constexpr uint8_t FUSB_CONTROL1 = 0x07;
static constexpr uint8_t FUSB_CONTROL3 = 0x09;
static constexpr uint8_t FUSB_MASK1 = 0x0A;
static constexpr uint8_t FUSB_POWER = 0x0B;
static constexpr uint8_t FUSB_RESET = 0x0C;
static constexpr uint8_t FUSB_MASKA = 0x0E;
static constexpr uint8_t FUSB_MASKB = 0x0F;
static constexpr uint8_t FUSB_STATUS0A = 0x3C;
static constexpr uint8_t FUSB_STATUS0 = 0x40;
static constexpr uint8_t FUSB_STATUS1 = 0x41;
static constexpr uint8_t FUSB_FIFOS = 0x43;
// DEVICE_ID: version in bits 7:4, product variant (I2C address 0x22-0x25) in bits 3:2, silicon revision in bits 1:0
static constexpr uint8_t FUSB_DEVICE_ID_VERSION_MASK = 0xF0;
static constexpr uint8_t FUSB_DEVICE_ID_VERSION_A = 0x80;
static constexpr uint8_t FUSB_DEVICE_ID_VERSION_B = 0x90;
// SWITCHES0 bits
static constexpr uint8_t FUSB_SWITCHES0_MEAS_CC2 = 1 << 3;
static constexpr uint8_t FUSB_SWITCHES0_MEAS_CC1 = 1 << 2;
static constexpr uint8_t FUSB_SWITCHES0_PDWN_2 = 1 << 1;
static constexpr uint8_t FUSB_SWITCHES0_PDWN_1 = 1 << 0;
// SWITCHES1 bits
static constexpr uint8_t FUSB_SWITCHES1_SPECREV_REV2 = 0x01 << 5;
static constexpr uint8_t FUSB_SWITCHES1_AUTO_CRC = 1 << 2;
static constexpr uint8_t FUSB_SWITCHES1_TXCC2 = 1 << 1;
static constexpr uint8_t FUSB_SWITCHES1_TXCC1 = 1 << 0;
// MEASURE value used for CC detection
static constexpr uint8_t FUSB_MEASURE_MDAC_CC = 49;
// CONTROL0 bits
static constexpr uint8_t FUSB_CONTROL0_TX_FLUSH = 1 << 6;
// CONTROL1 bits
static constexpr uint8_t FUSB_CONTROL1_RX_FLUSH = 1 << 2;
// CONTROL3 bits
static constexpr uint8_t FUSB_CONTROL3_N_RETRIES_SHIFT = 1;
static constexpr uint8_t FUSB_CONTROL3_N_RETRIES_MASK = 0x3 << FUSB_CONTROL3_N_RETRIES_SHIFT;
static constexpr uint8_t FUSB_CONTROL3_AUTO_RETRY = 1 << 0;
// MASK1: mask the ACTIVITY, CRC_CHK and BC_LVL interrupts
static constexpr uint8_t FUSB_MASK1_DEFAULT = 0x51;
// POWER bits
static constexpr uint8_t FUSB_POWER_ALL = 0x0F;
// RESET bits
static constexpr uint8_t FUSB_RESET_PD_RESET = 1 << 1;
static constexpr uint8_t FUSB_RESET_SW_RES = 1 << 0;
// INTERRUPTA bits
static constexpr uint8_t FUSB_INTERRUPTA_I_RETRYFAIL = 1 << 4;
static constexpr uint8_t FUSB_INTERRUPTA_I_SOFTRST = 1 << 1;
static constexpr uint8_t FUSB_INTERRUPTA_I_HARDRST = 1 << 0;
// INTERRUPT bits
static constexpr uint8_t FUSB_INTERRUPT_I_VBUSOK = 1 << 7;
// STATUS0 bits
static constexpr uint8_t FUSB_STATUS0_VBUSOK = 1 << 7;
static constexpr uint8_t FUSB_STATUS0_BC_LVL = 0x03;
// STATUS1 bits
static constexpr uint8_t FUSB_STATUS1_RX_EMPTY = 1 << 5;
// RX FIFO token
static constexpr uint8_t FUSB_FIFO_RX_TOKEN_BITS = 0xE0;
static constexpr uint8_t FUSB_FIFO_RX_SOP = 0xE0;
// TX FIFO tokens
static constexpr uint8_t FUSB_TX_TOKEN_TXON = 0xA1;
static constexpr uint8_t FUSB_TX_TOKEN_SYNC1 = 0x12;
static constexpr uint8_t FUSB_TX_TOKEN_SYNC2 = 0x13;
static constexpr uint8_t FUSB_TX_TOKEN_PACKSYM = 0x80;
static constexpr uint8_t FUSB_TX_TOKEN_JAM_CRC = 0xFF;
static constexpr uint8_t FUSB_TX_TOKEN_EOP = 0x14;
static constexpr uint8_t FUSB_TX_TOKEN_TXOFF = 0xFE;
} // namespace esphome::fusb302b
+330
View File
@@ -0,0 +1,330 @@
#include "pd.h"
#include <cstring>
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
namespace esphome::fusb302b {
ESPHOME_LOG_TAG(TAG, "fusb302b.pd");
static constexpr uint32_t AMS_TIMEOUT_MS = 2000;
// tSinkRequest: delay before repeating a request that the source answered with Wait
static constexpr uint32_t SINK_REQUEST_MS = 100;
static constexpr uint8_t MAX_WAIT_RETRIES = 10;
// USB PD 6.4.1.3.1 Sink Fixed Supply Power Data Object flags; only the first (5 V) object carries them
static constexpr uint32_t SINK_PDO_HIGHER_CAPABILITY = 1u << 28;
static constexpr uint32_t SINK_PDO_USB_COMMS_CAPABLE = 1u << 26;
// Operational current advertised for every sink object: 3 A (300 x 10 mA)
static constexpr uint16_t SINK_PDO_CURRENT = 300;
// USB PD 6.4.1.2.3 Source Fixed Supply Power Data Object; other PDO types only report their type
static PdContract parse_pdo(uint32_t pdo) {
PdContract info{};
info.type = static_cast<PdPdoType>(pdo >> 30);
if (info.type == PD_PDO_TYPE_FIXED_SUPPLY) {
info.max_v = (pdo >> 10) & 0x3FF;
info.max_i = pdo & 0x3FF;
}
return info;
}
// USB PD 6.4.1.3.1 Sink Fixed Supply Power Data Object; voltage in 50 mV units, current in 10 mA units
static constexpr uint32_t sink_fixed_pdo(uint16_t voltage, uint16_t current) {
return (static_cast<uint32_t>(PD_PDO_TYPE_FIXED_SUPPLY) << 30) | (static_cast<uint32_t>(voltage & 0x3FF) << 10) |
(current & 0x3FF);
}
void PdMsg::set_header(uint16_t header) {
this->type = header & 0x1F;
this->spec_rev = static_cast<PdSpecRevision>((header >> 6) & 0x3);
this->id = (header >> 9) & 0x7;
this->num_of_obj = (header >> 12) & 0x7;
this->extended = (header >> 15) & 0x1;
}
uint16_t PdMsg::get_coded_header() const {
// Data role UFP (bit 5 = 0), power role sink (bit 8 = 0)
return static_cast<uint16_t>(this->type & 0x1F) | (static_cast<uint16_t>(this->spec_rev) << 6) |
(static_cast<uint16_t>(this->id & 0x7) << 9) | (static_cast<uint16_t>(this->num_of_obj & 0x7) << 12) |
(static_cast<uint16_t>(this->extended) << 15);
}
PdMsg PowerDelivery::make_control_msg_(PdControlMsgType type) const {
PdMsg msg;
msg.type = type;
msg.id = this->msg_counter_ % 8;
return msg;
}
PdMsg PowerDelivery::make_data_msg_(PdDataMsgType type, const uint32_t *objects, uint8_t len) const {
PdMsg msg;
msg.type = type;
msg.id = this->msg_counter_ % 8;
msg.num_of_obj = len;
memcpy(msg.data_objects, objects, len * sizeof(uint32_t));
return msg;
}
void PowerDelivery::reset_protocol_() {
this->last_received_msg_id_ = 255;
this->msg_counter_ = 0;
this->active_ams_ = false;
this->ps_rdy_expected_ = false;
this->retry_request_pending_ = false;
this->wait_retries_ = 0;
}
void PowerDelivery::set_state_(PdState state) {
if (this->state_.exchange(state) != state)
this->on_state_changed();
}
void PowerDelivery::set_contract_(const PdContract &contract) {
this->accepted_contract_ = contract;
const uint32_t packed = PackedContract::from(contract).value;
if (this->contract_.exchange(packed) != packed)
this->on_state_changed();
}
void PowerDelivery::clear_contract_() {
this->accepted_contract_ = {};
if (this->contract_.exchange(0) != 0)
this->on_state_changed();
}
void PowerDelivery::set_state_and_contract_(PdState state, const PdContract &contract) {
this->accepted_contract_ = contract;
const uint32_t packed = PackedContract::from(contract).value;
// The contract is stored first, so a reader that sees the new state also sees the new contract
const bool contract_changed = this->contract_.exchange(packed) != packed;
const bool state_changed = this->state_.exchange(state) != state;
if (contract_changed || state_changed)
this->on_state_changed();
}
void PowerDelivery::set_ams_(bool active) {
this->active_ams_ = active;
// A new sequence has no accepted request yet, and an ended one can no longer complete
this->ps_rdy_expected_ = false;
if (active)
this->ams_start_ = millis();
}
bool PowerDelivery::check_ams_() {
if (this->active_ams_ && millis() - this->ams_start_ > AMS_TIMEOUT_MS)
this->set_ams_(false);
return this->active_ams_;
}
void PowerDelivery::check_request_retry_() {
if (!this->retry_request_pending_ || millis() - this->wait_received_ms_ < SINK_REQUEST_MS)
return;
if (this->send_request_()) {
this->retry_request_pending_ = false;
} else if (this->wait_retries_ < MAX_WAIT_RETRIES) {
// Try again after another tSinkRequest; failed attempts count against the same limit as Wait replies
this->wait_retries_++;
this->wait_received_ms_ = millis();
} else {
ESP_LOGW(TAG, "Giving up repeating the request");
this->retry_request_pending_ = false;
this->request_failed_();
}
}
bool PowerDelivery::send_request_() {
this->set_ams_(true);
if (this->send_message(this->make_data_msg_(PD_DATA_REQUEST, &this->last_rdo_, 1)))
return true;
ESP_LOGW(TAG, "Sending the request failed");
this->set_ams_(false);
return false;
}
void PowerDelivery::request_failed_() {
// A contract already in place stays valid. Without one, report that negotiation is over at default power.
if (this->state_ == PdState::PD_STATE_DEFAULT_CONTRACT)
this->set_state_(PdState::PD_STATE_PD_TIMEOUT);
}
void PowerDelivery::check_transition_timeout_() {
// The AMS that sent the request only ends on PS_RDY, so an ended AMS while in transition means a timeout
if (this->state_ != PdState::PD_STATE_TRANSITION || this->check_ams_())
return;
ESP_LOGW(TAG, "Source did not report the new supply ready, falling back to default power");
this->set_state_and_contract_(PdState::PD_STATE_DEFAULT_CONTRACT, DEFAULT_CONTRACT);
this->on_transition_timeout();
}
void PowerDelivery::handle_transmit_failed_() {
// USB PD 6.12.2.2: the counter also moves on after a failed transmission. The source may have received the
// message and only its GoodCRC was lost, so reusing the MessageID would make it drop the next one as a repeat.
ESP_LOGD(TAG, "Message %u not acknowledged", this->msg_counter_ % 8);
this->msg_counter_++;
}
void PowerDelivery::handle_message_(const PdMsg &msg) {
if (msg.num_of_obj == 0 && msg.type == PD_CNTRL_GOODCRC) {
// A GoodCRC carries the MessageID it acknowledges; a duplicate or stray one must not advance the counter
if (msg.id != this->msg_counter_ % 8) {
ESP_LOGD(TAG, "Ignoring GoodCRC for MessageID %u, expected %u", msg.id, this->msg_counter_ % 8);
return;
}
// Our last message was received, so the next one gets a new MessageID
this->msg_counter_++;
return;
}
// Soft_Reset always has MessageID 0, so it must not be dropped as a retransmission of the last message
if (msg.num_of_obj == 0 && msg.type == PD_CNTRL_SOFT_RESET) {
this->handle_soft_reset_(msg);
return;
}
// Retransmissions repeat the MessageID of a message that was already handled
if (msg.id == this->last_received_msg_id_)
return;
this->last_received_msg_id_ = msg.id;
if (msg.num_of_obj == 0) {
this->handle_control_message_(msg);
} else {
this->handle_data_message_(msg);
}
}
void PowerDelivery::handle_data_message_(const PdMsg &msg) {
if (msg.extended || msg.type != PD_DATA_SOURCE_CAP) {
// BIST and Vendor_Defined messages must not be answered with Reject, so unsupported data messages are ignored
ESP_LOGV(TAG, "Ignoring data message type %u%s", msg.type,
msg.extended ? LOG_STR_LITERAL(" (extended)") : LOG_STR_LITERAL(""));
return;
}
this->on_source_caps_received();
this->respond_to_source_caps_(msg);
}
void PowerDelivery::handle_control_message_(const PdMsg &msg) {
switch (msg.type) {
case PD_CNTRL_ACCEPT:
if (!this->check_ams_()) {
ESP_LOGD(TAG, "Ignoring Accept outside a request");
break;
}
this->ps_rdy_expected_ = true;
if (this->requested_contract_ != this->accepted_contract_)
this->set_state_and_contract_(PdState::PD_STATE_TRANSITION, this->requested_contract_);
break;
case PD_CNTRL_PS_RDY:
// check_ams_() first, so a PS_RDY that arrives after the sequence timed out is ignored too
if (!this->check_ams_() || !this->ps_rdy_expected_) {
ESP_LOGD(TAG, "Ignoring PS_RDY without an accepted request");
break;
}
this->set_ams_(false);
this->set_state_(PdState::PD_STATE_EXPLICIT_CONTRACT);
break;
case PD_CNTRL_REJECT:
if (!this->check_ams_()) {
ESP_LOGD(TAG, "Ignoring Reject outside a request");
break;
}
ESP_LOGW(TAG, "Source rejected the request");
this->set_ams_(false);
this->request_failed_();
break;
case PD_CNTRL_WAIT:
if (!this->check_ams_()) {
ESP_LOGD(TAG, "Ignoring Wait outside a request");
break;
}
this->set_ams_(false);
if (this->wait_retries_ < MAX_WAIT_RETRIES) {
ESP_LOGD(TAG, "Source asked to wait, repeating the request");
this->wait_retries_++;
this->retry_request_pending_ = true;
this->wait_received_ms_ = millis();
} else {
ESP_LOGW(TAG, "Source kept answering Wait, giving up");
this->request_failed_();
}
break;
case PD_CNTRL_PING:
case PD_CNTRL_NOT_SUPPORTED:
break;
case PD_CNTRL_GET_SINK_CAP: {
// 5 V first, then the requested voltage. Higher Capability tells the source that 5 V is not enough for
// full operation.
const uint16_t request_v = this->request_voltage_ * 20; // 50 mV units
uint32_t pdos[2];
uint8_t count = 0;
pdos[count++] = sink_fixed_pdo(100, SINK_PDO_CURRENT) | SINK_PDO_USB_COMMS_CAPABLE |
(request_v > 100 ? SINK_PDO_HIGHER_CAPABILITY : 0);
if (request_v > 100)
pdos[count++] = sink_fixed_pdo(request_v, SINK_PDO_CURRENT);
this->send_message(this->make_data_msg_(PD_DATA_SINK_CAP, pdos, count));
break;
}
default:
// Messages are sent as PD 2.0, which has no Not_Supported message
this->send_message(this->make_control_msg_(PD_CNTRL_REJECT));
break;
}
}
void PowerDelivery::handle_soft_reset_(const PdMsg &msg) {
this->reset_protocol_();
this->last_received_msg_id_ = msg.id;
// If this fails the source escalates to a hard reset. The hook still runs, so the PHY starts waiting for source
// capabilities and recovers on its own if neither follows.
if (!this->send_message(this->make_control_msg_(PD_CNTRL_ACCEPT))) {
ESP_LOGW(TAG, "Accepting the soft reset failed");
}
// A soft reset keeps the power level, but the outcome of a transition in progress is unknown
if (this->state_ == PdState::PD_STATE_TRANSITION)
this->set_state_and_contract_(PdState::PD_STATE_DEFAULT_CONTRACT, DEFAULT_CONTRACT);
this->on_soft_reset_received();
}
void PowerDelivery::respond_to_source_caps_(const PdMsg &msg) {
// Pick the highest fixed supply PDO that does not exceed the requested voltage.
// The first PDO is always the 5 V fixed supply.
const uint16_t request_v = this->request_voltage_ * 20; // 50 mV units
PdContract selected{};
uint8_t position = 0; // 1-based object position, 0 = none
for (uint8_t idx = 0; idx < msg.num_of_obj; idx++) {
PdContract info = parse_pdo(msg.data_objects[idx]);
if (info.type != PD_PDO_TYPE_FIXED_SUPPLY)
continue;
if (position == 0 || (info.max_v <= request_v && info.max_v > selected.max_v)) {
selected = info;
position = idx + 1;
}
}
// USB PD 6.4.2 Request Data Object: no USB suspend (bit 24), USB communications capable (bit 25)
uint32_t rdo = (1u << 24) | (1u << 25);
if (position == 0) {
ESP_LOGW(TAG, "No usable PDO in source capabilities, requesting 5 V");
// Object 1 (5 V fixed supply), 300 mA maximum, 100 mA operating current
selected = PdContract{PD_PDO_TYPE_FIXED_SUPPLY, 100, 30};
position = 1;
rdo |= (30u << 0) | (10u << 10);
} else {
// Request the full current the source offers
const uint32_t current = selected.max_i;
rdo |= (current << 0) | (current << 10);
}
rdo |= static_cast<uint32_t>(position) << 28;
ESP_LOGD(TAG, "Requesting PDO %u: %.2f V", position, selected.max_v * 0.05f);
this->requested_contract_ = selected;
this->last_rdo_ = rdo;
this->retry_request_pending_ = false;
this->wait_retries_ = 0;
// A repeat would come too late: the source resets if it gets no Request within tSenderResponse (24-30 ms)
if (!this->send_request_())
this->request_failed_();
}
} // namespace esphome::fusb302b
+179
View File
@@ -0,0 +1,179 @@
#pragma once
#include <algorithm>
#include <atomic>
#include <cstddef>
#include <cstdint>
namespace esphome::fusb302b {
static constexpr uint8_t PD_MAX_NUM_DATA_OBJECTS = 7;
// Standard Power Range fixed supplies, which are the only ones this sink negotiates
static constexpr uint8_t MIN_REQUEST_VOLTAGE = 5;
static constexpr uint8_t MAX_REQUEST_VOLTAGE = 20;
enum PdSpecRevision : uint8_t {
PD_SPEC_REV_1 = 0,
PD_SPEC_REV_2 = 1,
PD_SPEC_REV_3 = 2,
};
enum PdDataMsgType : uint8_t {
PD_DATA_SOURCE_CAP = 0x01,
PD_DATA_REQUEST = 0x02,
PD_DATA_SINK_CAP = 0x04,
};
enum PdControlMsgType : uint8_t {
PD_CNTRL_GOODCRC = 0x01,
PD_CNTRL_ACCEPT = 0x03,
PD_CNTRL_REJECT = 0x04,
PD_CNTRL_PING = 0x05,
PD_CNTRL_PS_RDY = 0x06,
PD_CNTRL_GET_SOURCE_CAP = 0x07,
PD_CNTRL_GET_SINK_CAP = 0x08,
PD_CNTRL_WAIT = 0x0C,
PD_CNTRL_SOFT_RESET = 0x0D,
PD_CNTRL_NOT_SUPPORTED = 0x10,
};
enum PdPdoType : uint8_t {
PD_PDO_TYPE_FIXED_SUPPLY = 0,
PD_PDO_TYPE_BATTERY = 1,
PD_PDO_TYPE_VARIABLE_SUPPLY = 2,
PD_PDO_TYPE_AUGMENTED = 3,
};
enum class PdState : uint8_t {
PD_STATE_DISCONNECTED,
/// Negotiation is over without a PD contract (no capabilities, or the request was not accepted); the source
/// supplies Type-C default power.
PD_STATE_PD_TIMEOUT,
PD_STATE_DEFAULT_CONTRACT,
PD_STATE_TRANSITION,
PD_STATE_EXPLICIT_CONTRACT,
PD_STATE_ERROR,
};
/// Returns true for the states in which a source is attached and supplying power.
inline bool is_connected_state(PdState state) {
return state == PdState::PD_STATE_DEFAULT_CONTRACT || state == PdState::PD_STATE_TRANSITION ||
state == PdState::PD_STATE_EXPLICIT_CONTRACT || state == PdState::PD_STATE_PD_TIMEOUT;
}
struct PdContract {
PdPdoType type{PD_PDO_TYPE_FIXED_SUPPLY};
uint16_t max_v{0}; // 50 mV units
uint16_t max_i{0}; // 10 mA units
bool operator==(const PdContract &other) const {
return this->max_v == other.max_v && this->max_i == other.max_i && this->type == other.type;
}
bool operator!=(const PdContract &other) const { return !(*this == other); }
};
// Type-C default power: 5 V (100 x 50 mV) at 500 mA (50 x 10 mA)
static constexpr PdContract DEFAULT_CONTRACT{PD_PDO_TYPE_FIXED_SUPPLY, 100, 50};
/// A contract packed into one word so it can be shared between tasks as an atomic.
/// 0 means there is no contract (nothing attached).
struct PackedContract {
uint32_t value{0};
static PackedContract from(const PdContract &contract) {
return {(static_cast<uint32_t>(contract.max_v) << 16) | contract.max_i};
}
bool is_set() const { return this->value != 0; }
float voltage() const { return (this->value >> 16) * 0.05f; }
float current() const { return (this->value & 0xFFFF) * 0.01f; }
};
struct PdMsg {
uint8_t type{0};
PdSpecRevision spec_rev{PD_SPEC_REV_2};
uint8_t id{0};
uint8_t num_of_obj{0};
bool extended{false};
uint32_t data_objects[PD_MAX_NUM_DATA_OBJECTS]{};
void set_header(uint16_t header);
uint16_t get_coded_header() const;
};
/// USB-PD sink protocol logic, independent of the PHY chip.
/// Everything except the getters and set_request_voltage() runs in the PHY task, which must also call
/// check_transition_timeout_() and check_request_retry_() regularly; the protocol has no timers of its own.
class PowerDelivery {
public:
void set_request_voltage(uint8_t voltage) {
this->request_voltage_ = std::clamp(voltage, MIN_REQUEST_VOLTAGE, MAX_REQUEST_VOLTAGE);
}
uint8_t get_request_voltage() const { return this->request_voltage_; }
PdState get_state() const { return this->state_; }
bool is_connected() const { return is_connected_state(this->state_); }
protected:
/// Returns false when the message could not be handed to the chip; the PHY logs the failure.
virtual bool send_message(const PdMsg &msg) = 0;
/// Called after state_ or contract_ changed, to let the main loop publish it.
virtual void on_state_changed() = 0;
/// Called after the source soft reset the protocol; it sends new source capabilities next.
virtual void on_soft_reset_received() = 0;
/// Called when source capabilities arrive, before the request for them is sent.
virtual void on_source_caps_received() = 0;
/// Called after an accepted request was not followed by PS_RDY in time and the state fell back to default power;
/// the PHY sends a soft reset so the source offers its capabilities again.
virtual void on_transition_timeout() = 0;
void handle_message_(const PdMsg &msg);
/// Called by the PHY when the chip gave up on a message after its retries without receiving a GoodCRC.
void handle_transmit_failed_();
void reset_protocol_();
void set_state_(PdState state);
void set_contract_(const PdContract &contract);
void clear_contract_();
/// Stores both before calling on_state_changed() once, so the main loop never sees the new state with the old
/// contract.
void set_state_and_contract_(PdState state, const PdContract &contract);
void set_ams_(bool active);
/// Returns true while an atomic message sequence is in progress; ends it after a timeout.
bool check_ams_();
/// Falls back to default power when the source accepted a request but did not report the new supply ready in time.
void check_transition_timeout_();
/// Sends the last request again once tSinkRequest has passed after the source answered Wait.
void check_request_retry_();
PdMsg make_control_msg_(PdControlMsgType type) const;
PdMsg make_data_msg_(PdDataMsgType type, const uint32_t *objects, uint8_t len) const;
// Shared with the main loop
std::atomic<PdState> state_{PdState::PD_STATE_DISCONNECTED};
std::atomic<uint32_t> contract_{0};
std::atomic<uint8_t> request_voltage_{5};
// PHY task only
uint32_t ams_start_{0};
uint32_t wait_received_ms_{0};
bool retry_request_pending_{false};
private:
void handle_data_message_(const PdMsg &msg);
void handle_control_message_(const PdMsg &msg);
void handle_soft_reset_(const PdMsg &msg);
void respond_to_source_caps_(const PdMsg &msg);
bool send_request_();
void request_failed_();
PdContract requested_contract_{};
PdContract accepted_contract_{};
uint32_t last_rdo_{0};
bool active_ams_{false};
// Set by Accept; PS_RDY is only valid after the source accepted the request of the current sequence
bool ps_rdy_expected_{false};
uint8_t last_received_msg_id_{255};
uint8_t msg_counter_{0};
uint8_t wait_retries_{0};
};
} // namespace esphome::fusb302b
@@ -28,7 +28,7 @@ void GPIOSwitch::dump_config() {
for (auto *lock : this->interlock_) {
if (lock == this)
continue;
ESP_LOGCONFIG(TAG, " %s", lock->get_name().c_str());
ESP_LOGCONFIG(TAG, " %s", LOG_STR_ARG(lock->get_log_name()));
}
}
#endif
+3 -3
View File
@@ -58,7 +58,7 @@ void HE60rCover::endstop_reached_(CoverOperation operation) {
this->current_operation = COVER_OPERATION_IDLE;
if (this->last_command_ == operation) {
float dur = (float) (now - this->start_dir_time_) / 1e3f;
ESP_LOGD(TAG, "'%s' - %s endstop reached. Took %.1fs.", this->name_.c_str(),
ESP_LOGD(TAG, "'%s' - %s endstop reached. Took %.1fs.", LOG_STR_ARG(this->get_log_name()),
operation == COVER_OPERATION_OPENING ? LOG_STR_LITERAL("Open") : LOG_STR_LITERAL("Close"), dur);
}
this->publish_state();
@@ -213,7 +213,7 @@ void HE60rCover::start_direction_(CoverOperation dir) {
this->last_command_ = dir;
if (this->current_operation == dir)
return;
ESP_LOGD(TAG, "'%s' - Direction '%s' requested.", this->name_.c_str(),
ESP_LOGD(TAG, "'%s' - Direction '%s' requested.", LOG_STR_ARG(this->get_log_name()),
dir == COVER_OPERATION_OPENING ? LOG_STR_LITERAL("OPEN")
: dir == COVER_OPERATION_CLOSING ? LOG_STR_LITERAL("CLOSE")
: LOG_STR_LITERAL("STOP"));
@@ -229,7 +229,7 @@ void HE60rCover::start_direction_(CoverOperation dir) {
// just stop and reverse
this->toggles_needed_ = 2;
}
ESP_LOGD(TAG, "'%s' - Reversing direction.", this->name_.c_str());
ESP_LOGD(TAG, "'%s' - Reversing direction.", LOG_STR_ARG(this->get_log_name()));
}
this->start_dir_time_ = millis();
}
@@ -5,6 +5,7 @@
#include "esphome/components/api/api_pb2.h"
#include "esphome/components/api/api_server.h"
#include "esphome/core/log.h"
#include "esphome/core/progmem.h"
#include "esphome/core/string_ref.h"
namespace esphome::homeassistant {
@@ -26,27 +27,26 @@ void HomeassistantButton::press_action() {
#ifdef USE_API_WIZARD_LINKED_INPUTS
if (this->entity_id_[0] == '\0') {
ESP_LOGW(TAG, "'%s': No entity ID set yet", this->get_name().c_str());
ESP_LOGW(TAG, "'%s': No entity ID set yet", LOG_STR_ARG(this->get_log_name()));
return;
}
#endif
static constexpr auto SERVICE_BUTTON = StringRef::from_lit("button.press");
static constexpr auto SERVICE_INPUT_BUTTON = StringRef::from_lit("input_button.press");
static constexpr auto ENTITY_ID_KEY = StringRef::from_lit("entity_id");
static constexpr char INPUT_PREFIX[] = "input_";
// In flash on the ESP8266 and encoded from there
static const char SERVICE_BUTTON[] PROGMEM = "button.press";
static const char SERVICE_INPUT_BUTTON[] PROGMEM = "input_button.press";
static const char ENTITY_ID_KEY[] PROGMEM = "entity_id";
api::HomeassistantActionRequest resp;
if (strncmp(this->entity_id_, INPUT_PREFIX, sizeof(INPUT_PREFIX) - 1) == 0) {
resp.service = SERVICE_INPUT_BUTTON;
} else {
resp.service = SERVICE_BUTTON;
}
// An input helper entity id starts with the "input_" its action name starts with
const bool input_helper = ESPHOME_strncmp_P(this->entity_id_, SERVICE_INPUT_BUTTON, sizeof("input_") - 1) == 0;
resp.service =
input_helper ? ProgmemStringRef::from_lit(SERVICE_INPUT_BUTTON) : ProgmemStringRef::from_lit(SERVICE_BUTTON);
resp.data.init(1);
auto &entity_id_kv = resp.data.emplace_back();
entity_id_kv.key = ENTITY_ID_KEY;
entity_id_kv.value = StringRef(this->entity_id_);
entity_id_kv.key = ProgmemStringRef::from_lit(ENTITY_ID_KEY);
entity_id_kv.value = ProgmemStringRef(StringRef(this->entity_id_));
api::global_api_server->send_homeassistant_action(resp);
}
@@ -5,6 +5,7 @@
#include "esphome/components/api/api_pb2.h"
#include "esphome/components/api/api_server.h"
#include "esphome/core/log.h"
#include "esphome/core/progmem.h"
#include "esphome/core/string_ref.h"
namespace esphome::homeassistant {
@@ -31,7 +32,7 @@ void HomeassistantNumber::min_retrieved_(StringRef min) {
ESP_LOGE(TAG, "'%s': Can't convert 'min' value '%s' to number!", this->entity_id_, min.c_str());
return;
}
ESP_LOGD(TAG, "'%s': Min retrieved: %s", get_name().c_str(), min.c_str());
ESP_LOGD(TAG, "'%s': Min retrieved: %s", LOG_STR_ARG(this->get_log_name()), min.c_str());
this->traits.set_min_value(min_value.value());
}
@@ -41,7 +42,7 @@ void HomeassistantNumber::max_retrieved_(StringRef max) {
ESP_LOGE(TAG, "'%s': Can't convert 'max' value '%s' to number!", this->entity_id_, max.c_str());
return;
}
ESP_LOGD(TAG, "'%s': Max retrieved: %s", get_name().c_str(), max.c_str());
ESP_LOGD(TAG, "'%s': Max retrieved: %s", LOG_STR_ARG(this->get_log_name()), max.c_str());
this->traits.set_max_value(max_value.value());
}
@@ -51,7 +52,7 @@ void HomeassistantNumber::step_retrieved_(StringRef step) {
ESP_LOGE(TAG, "'%s': Can't convert 'step' value '%s' to number!", this->entity_id_, step.c_str());
return;
}
ESP_LOGD(TAG, "'%s': Step Retrieved %s", get_name().c_str(), step.c_str());
ESP_LOGD(TAG, "'%s': Step Retrieved %s", LOG_STR_ARG(this->get_log_name()), step.c_str());
this->traits.set_step(step_value.value());
}
@@ -82,38 +83,36 @@ void HomeassistantNumber::control(float value) {
#ifdef USE_API_WIZARD_LINKED_INPUTS
if (this->entity_id_[0] == '\0') {
ESP_LOGW(TAG, "'%s': No entity ID set yet", this->get_name().c_str());
ESP_LOGW(TAG, "'%s': No entity ID set yet", LOG_STR_ARG(this->get_log_name()));
return;
}
#endif
this->publish_state(value);
static constexpr auto NUMBER_SERVICE_NAME = StringRef::from_lit("number.set_value");
static constexpr auto INPUT_NUMBER_SERVICE_NAME = StringRef::from_lit("input_number.set_value");
static constexpr char INPUT_PREFIX[] = "input_";
static constexpr auto ENTITY_ID_KEY = StringRef::from_lit("entity_id");
static constexpr auto VALUE_KEY = StringRef::from_lit("value");
// In flash on the ESP8266 and encoded from there
static const char NUMBER_SERVICE_NAME[] PROGMEM = "number.set_value";
static const char INPUT_NUMBER_SERVICE_NAME[] PROGMEM = "input_number.set_value";
static const char ENTITY_ID_KEY[] PROGMEM = "entity_id";
static const char VALUE_KEY[] PROGMEM = "value";
api::HomeassistantActionRequest resp;
// input_number entities have their own set_value action
if (strncmp(this->entity_id_, INPUT_PREFIX, sizeof(INPUT_PREFIX) - 1) == 0) {
resp.service = INPUT_NUMBER_SERVICE_NAME;
} else {
resp.service = NUMBER_SERVICE_NAME;
}
// An input helper entity id starts with the "input_" its action name starts with
const bool input_helper = ESPHOME_strncmp_P(this->entity_id_, INPUT_NUMBER_SERVICE_NAME, sizeof("input_") - 1) == 0;
resp.service = input_helper ? ProgmemStringRef::from_lit(INPUT_NUMBER_SERVICE_NAME)
: ProgmemStringRef::from_lit(NUMBER_SERVICE_NAME);
resp.data.init(2);
auto &entity_id = resp.data.emplace_back();
entity_id.key = ENTITY_ID_KEY;
entity_id.value = StringRef(this->entity_id_);
entity_id.key = ProgmemStringRef::from_lit(ENTITY_ID_KEY);
entity_id.value = ProgmemStringRef(StringRef(this->entity_id_));
auto &entity_value = resp.data.emplace_back();
entity_value.key = VALUE_KEY;
entity_value.key = ProgmemStringRef::from_lit(VALUE_KEY);
// Stack buffer - no heap allocation; %g produces shortest representation
char value_buf[16];
buf_append_printf(value_buf, sizeof(value_buf), 0, "%g", value);
entity_value.value = StringRef(value_buf);
entity_value.value = ProgmemStringRef(StringRef(value_buf));
api::global_api_server->send_homeassistant_action(resp);
}
@@ -6,6 +6,7 @@
#include "esphome/components/api/api_server.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include "esphome/core/progmem.h"
#include "esphome/core/string_ref.h"
namespace esphome::homeassistant {
@@ -232,32 +233,31 @@ void HomeassistantSelect::control(size_t index) {
#ifdef USE_API_WIZARD_LINKED_INPUTS
if (this->entity_id_[0] == '\0') {
ESP_LOGW(TAG, "'%s': No entity ID set yet", this->get_name().c_str());
ESP_LOGW(TAG, "'%s': No entity ID set yet", LOG_STR_ARG(this->get_log_name()));
return;
}
#endif
static constexpr auto SERVICE_SELECT = StringRef::from_lit("select.select_option");
static constexpr auto SERVICE_INPUT_SELECT = StringRef::from_lit("input_select.select_option");
static constexpr auto ENTITY_ID_KEY = StringRef::from_lit("entity_id");
static constexpr auto OPTION_KEY = StringRef::from_lit("option");
static constexpr char INPUT_PREFIX[] = "input_";
// In flash on the ESP8266 and encoded from there
static const char SERVICE_SELECT[] PROGMEM = "select.select_option";
static const char SERVICE_INPUT_SELECT[] PROGMEM = "input_select.select_option";
static const char ENTITY_ID_KEY[] PROGMEM = "entity_id";
static const char OPTION_KEY[] PROGMEM = "option";
api::HomeassistantActionRequest resp;
if (strncmp(this->entity_id_, INPUT_PREFIX, sizeof(INPUT_PREFIX) - 1) == 0) {
resp.service = SERVICE_INPUT_SELECT;
} else {
resp.service = SERVICE_SELECT;
}
// An input helper entity id starts with the "input_" its action name starts with
const bool input_helper = ESPHOME_strncmp_P(this->entity_id_, SERVICE_INPUT_SELECT, sizeof("input_") - 1) == 0;
resp.service =
input_helper ? ProgmemStringRef::from_lit(SERVICE_INPUT_SELECT) : ProgmemStringRef::from_lit(SERVICE_SELECT);
resp.data.init(2);
auto &entity_id = resp.data.emplace_back();
entity_id.key = ENTITY_ID_KEY;
entity_id.value = StringRef(this->entity_id_);
entity_id.key = ProgmemStringRef::from_lit(ENTITY_ID_KEY);
entity_id.value = ProgmemStringRef(StringRef(this->entity_id_));
auto &option = resp.data.emplace_back();
option.key = OPTION_KEY;
option.value = StringRef(this->option_at(index));
option.key = ProgmemStringRef::from_lit(OPTION_KEY);
option.value = ProgmemStringRef(StringRef(this->option_at(index)));
api::global_api_server->send_homeassistant_action(resp);
}
@@ -1,6 +1,7 @@
#include "homeassistant_switch.h"
#include "esphome/components/api/api_server.h"
#include "esphome/core/log.h"
#include "esphome/core/progmem.h"
#include "esphome/core/string_ref.h"
namespace esphome::homeassistant {
@@ -42,26 +43,23 @@ void HomeassistantSwitch::write_state(bool state) {
#ifdef USE_API_WIZARD_LINKED_INPUTS
if (this->entity_id_[0] == '\0') {
ESP_LOGW(TAG, "'%s': No entity ID set yet", this->get_name().c_str());
ESP_LOGW(TAG, "'%s': No entity ID set yet", LOG_STR_ARG(this->get_log_name()));
return;
}
#endif
static constexpr auto SERVICE_ON = StringRef::from_lit("homeassistant.turn_on");
static constexpr auto SERVICE_OFF = StringRef::from_lit("homeassistant.turn_off");
static constexpr auto ENTITY_ID_KEY = StringRef::from_lit("entity_id");
// In flash on the ESP8266 and encoded from there
static const char SERVICE_ON[] PROGMEM = "homeassistant.turn_on";
static const char SERVICE_OFF[] PROGMEM = "homeassistant.turn_off";
static const char ENTITY_ID_KEY[] PROGMEM = "entity_id";
api::HomeassistantActionRequest resp;
if (state) {
resp.service = SERVICE_ON;
} else {
resp.service = SERVICE_OFF;
}
resp.service = state ? ProgmemStringRef::from_lit(SERVICE_ON) : ProgmemStringRef::from_lit(SERVICE_OFF);
resp.data.init(1);
auto &entity_id_kv = resp.data.emplace_back();
entity_id_kv.key = ENTITY_ID_KEY;
entity_id_kv.value = StringRef(this->entity_id_);
entity_id_kv.key = ProgmemStringRef::from_lit(ENTITY_ID_KEY);
entity_id_kv.value = ProgmemStringRef(StringRef(this->entity_id_));
api::global_api_server->send_homeassistant_action(resp);
}
@@ -6,6 +6,7 @@
#include "esphome/components/api/api_server.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include "esphome/core/progmem.h"
#include "esphome/core/string_ref.h"
namespace esphome::homeassistant {
@@ -81,32 +82,31 @@ void HomeassistantText::control(const std::string &value) {
#ifdef USE_API_WIZARD_LINKED_INPUTS
if (this->entity_id_[0] == '\0') {
ESP_LOGW(TAG, "'%s': No entity ID set yet", this->get_name().c_str());
ESP_LOGW(TAG, "'%s': No entity ID set yet", LOG_STR_ARG(this->get_log_name()));
return;
}
#endif
static constexpr auto SERVICE_TEXT = StringRef::from_lit("text.set_value");
static constexpr auto SERVICE_INPUT_TEXT = StringRef::from_lit("input_text.set_value");
static constexpr auto ENTITY_ID_KEY = StringRef::from_lit("entity_id");
static constexpr auto VALUE_KEY = StringRef::from_lit("value");
static constexpr char INPUT_PREFIX[] = "input_";
// In flash on the ESP8266 and encoded from there
static const char SERVICE_TEXT[] PROGMEM = "text.set_value";
static const char SERVICE_INPUT_TEXT[] PROGMEM = "input_text.set_value";
static const char ENTITY_ID_KEY[] PROGMEM = "entity_id";
static const char VALUE_KEY[] PROGMEM = "value";
api::HomeassistantActionRequest resp;
if (strncmp(this->entity_id_, INPUT_PREFIX, sizeof(INPUT_PREFIX) - 1) == 0) {
resp.service = SERVICE_INPUT_TEXT;
} else {
resp.service = SERVICE_TEXT;
}
// An input helper entity id starts with the "input_" its action name starts with
const bool input_helper = ESPHOME_strncmp_P(this->entity_id_, SERVICE_INPUT_TEXT, sizeof("input_") - 1) == 0;
resp.service =
input_helper ? ProgmemStringRef::from_lit(SERVICE_INPUT_TEXT) : ProgmemStringRef::from_lit(SERVICE_TEXT);
resp.data.init(2);
auto &entity_id_kv = resp.data.emplace_back();
entity_id_kv.key = ENTITY_ID_KEY;
entity_id_kv.value = StringRef(this->entity_id_);
entity_id_kv.key = ProgmemStringRef::from_lit(ENTITY_ID_KEY);
entity_id_kv.value = ProgmemStringRef(StringRef(this->entity_id_));
auto &value_kv = resp.data.emplace_back();
value_kv.key = VALUE_KEY;
value_kv.value = StringRef(value.data(), value.size());
value_kv.key = ProgmemStringRef::from_lit(VALUE_KEY);
value_kv.value = ProgmemStringRef(value.data(), value.size());
api::global_api_server->send_homeassistant_action(resp);
}
+2 -2
View File
@@ -59,8 +59,8 @@ namespace esphome::host {
char **get_argv() { return s_argv; }
const std::string &get_exe_path() {
static const std::string empty;
return s_exe_path != nullptr ? *s_exe_path : empty;
static const std::string EMPTY;
return s_exe_path != nullptr ? *s_exe_path : EMPTY;
}
void arm_reexec(const std::string &path) {
+1 -1
View File
@@ -5,7 +5,7 @@
#include "esphome/core/log.h"
#include "core.h"
#include <time.h>
#include <ctime>
#include <unistd.h>
#include <cerrno>
#include <cstdlib>
+2 -2
View File
@@ -39,8 +39,8 @@ bool Mutex::try_lock() { return static_cast<std::mutex *>(handle_)->try_lock();
void Mutex::unlock() { static_cast<std::mutex *>(handle_)->unlock(); }
void get_mac_address_raw(uint8_t *mac) { // NOLINT(readability-non-const-parameter)
static const uint8_t esphome_host_mac_address[MAC_ADDRESS_SIZE] = USE_ESPHOME_HOST_MAC_ADDRESS;
memcpy(mac, esphome_host_mac_address, sizeof(esphome_host_mac_address));
static constexpr uint8_t HOST_MAC_ADDRESS[MAC_ADDRESS_SIZE] = USE_ESPHOME_HOST_MAC_ADDRESS;
memcpy(mac, HOST_MAC_ADDRESS, sizeof(HOST_MAC_ADDRESS));
}
} // namespace esphome
+4 -4
View File
@@ -2,6 +2,7 @@
#include <filesystem>
#include <fstream>
#include <utility>
#include "preferences.h"
#include "esphome/core/application.h"
#include "esphome/core/log.h"
@@ -45,11 +46,10 @@ void HostPreferences::setup_() {
break;
if (fread(&len, sizeof(len), 1, fp) != 1)
break;
uint8_t data[len];
if (fread(data, sizeof(uint8_t), len, fp) != len)
std::vector<uint8_t> data(len);
if (fread(data.data(), sizeof(uint8_t), len, fp) != len)
break;
std::vector vec(data, data + len);
this->data_[key] = vec;
this->data_[key] = std::move(data);
}
fclose(fp);
}

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