1
0
mirror of https://github.com/esphome/esphome.git synced 2026-02-10 01:32:06 +00:00

Compare commits

...

732 Commits

Author SHA1 Message Date
J. Nick Koston
b77bcf6460 Merge branch 'fix-available-size-t' into integration 2026-02-09 18:57:08 -06:00
Jonathan Swoboda
4d9a75d808 Fix available() return type to size_t across UART components
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 19:42:05 -05:00
J. Nick Koston
59637ceda5 Merge branch 'peername_no_double_ram' into integration 2026-02-09 18:40:28 -06:00
J. Nick Koston
4c006d98af Merge remote-tracking branch 'upstream/dev' into peername_no_double_ram
# Conflicts:
#	esphome/components/api/api_connection.cpp
2026-02-09 18:38:02 -06:00
J. Nick Koston
33c1040668 Merge remote-tracking branch 'upstream/dev' into integration
# Conflicts:
#	esphome/components/dlms_meter/dlms_meter.cpp
#	esphome/components/hlk_fm22x/hlk_fm22x.cpp
#	esphome/components/ld2420/ld2420.cpp
2026-02-09 18:36:55 -06:00
J. Nick Koston
87ac263264 [dsmr] Batch UART reads to reduce per-loop overhead (#13826) 2026-02-10 00:32:52 +00:00
Sean Kelly
097901e9c8 [aqi] Fix AQI calculation for specific pm2.5 or pm10 readings (#13770) 2026-02-09 19:30:37 -05:00
J. Nick Koston
01a90074ba [ld2420] Batch UART reads to reduce loop overhead (#13821)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-02-10 00:25:34 +00:00
J. Nick Koston
57b85a8400 [dlms_meter] Batch UART reads to reduce per-loop overhead (#13828)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-02-10 00:24:20 +00:00
J. Nick Koston
2edfcf278f [hlk_fm22x] Replace per-cycle vector allocation with member buffer (#13859) 2026-02-09 18:21:10 -06:00
J. Nick Koston
bcd4a9fc39 [pylontech] Batch UART reads to reduce loop overhead (#13824) 2026-02-09 18:20:53 -06:00
J. Nick Koston
78df8be31f [logger] Resolve thread name once and pass through logging chain (#13836) 2026-02-09 18:16:27 -06:00
J. Nick Koston
dacc557a16 [uart] Convert parity_to_str to PROGMEM_STRING_TABLE (#13805) 2026-02-09 18:15:48 -06:00
J. Nick Koston
3767c5ec91 [scheduler] Make core timer ID collisions impossible with type-safe internal IDs (#13882)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-02-09 16:48:08 -06:00
J. Nick Koston
13ef9d0fcd Merge remote-tracking branch 'upstream/dev' into integration 2026-02-09 16:46:03 -06:00
George Joseph
7c1327f96a [mipi_dsi] Add WAVESHARE-ESP32-P4-WIFI6-TOUCH-LCD 3.4C and 4C (#13840) 2026-02-10 09:44:47 +11:00
Jonathan Swoboda
475db750e0 [uart] Change available() return type from int to size_t (#13893)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 17:41:16 -05:00
dependabot[bot]
8f74b027b4 Bump setuptools from 80.10.2 to 82.0.0 (#13897) 2026-02-09 16:40:32 -06:00
tomaszduda23
b2b9e0cb0a [nrf52,zigee] print reporting status (#13890)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
2026-02-09 16:00:08 -05:00
tronikos
dbf202bf0d Add get_away and get_on in WaterHeaterCall and deprecate get_state (#13891) 2026-02-09 20:57:36 +00:00
J. Nick Koston
b6fdd29953 [voice_assistant] Replace timer unordered_map with vector to eliminate per-tick heap allocation (#13857) 2026-02-09 14:42:40 -06:00
Clyde Stubbs
00256e3ca0 [mipi_rgb] Allow use on P4 (#13740) 2026-02-10 06:35:41 +11:00
J. Nick Koston
38d3de7c00 Merge remote-tracking branch 'upstream/dev' into integration 2026-02-09 13:19:29 -06:00
J. Nick Koston
e0712cc53b [scheduler] Make core timer ID collisions impossible with type-safe internal IDs (#13882)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-02-09 13:16:22 -06:00
J. Nick Koston
6c6da8a3cd [api] Skip class generation for empty SOURCE_CLIENT protobuf messages (#13880) 2026-02-09 18:45:24 +00:00
J. Nick Koston
e4ea016d1e [ci] Block new std::to_string() usage, suggest snprintf alternatives (#13369) 2026-02-09 12:26:19 -06:00
J. Nick Koston
41a9588d81 [i2c] Replace switch with if-else to avoid CSWTCH table in RAM (#13815) 2026-02-09 12:26:06 -06:00
J. Nick Koston
cd55eb927d [modbus] Batch UART reads to reduce loop overhead (#13822) 2026-02-09 12:21:15 -06:00
J. Nick Koston
4a9ff48f02 [nextion] Batch UART reads to reduce loop overhead (#13823)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-09 12:20:50 -06:00
J. Nick Koston
743fdee8e2 Merge branch 'no_gen_empty_messages' into integration 2026-02-09 12:20:20 -06:00
J. Nick Koston
8b733921b5 rename dict to make bot happy 2026-02-09 12:20:01 -06:00
J. Nick Koston
8fffe7453d [seeed_mr24hpc1/mr60fda2/mr60bha2] Batch UART reads to reduce per-loop overhead (#13825) 2026-02-09 12:18:12 -06:00
J. Nick Koston
a5ee451043 [tuya] Batch UART reads to reduce per-loop overhead (#13827) 2026-02-09 12:17:58 -06:00
J. Nick Koston
e176cf50ab [dfplayer] Batch UART reads to reduce per-loop overhead (#13832) 2026-02-09 12:15:28 -06:00
J. Nick Koston
e7a900fbaa [rf_bridge] Batch UART reads to reduce per-loop overhead (#13831) 2026-02-09 12:15:15 -06:00
J. Nick Koston
623f33c9f9 [rd03d] Batch UART reads to reduce per-loop overhead (#13830) 2026-02-09 12:15:04 -06:00
J. Nick Koston
8b24112be5 [pipsolar] Batch UART reads to reduce per-loop overhead (#13829) 2026-02-09 12:14:48 -06:00
J. Nick Koston
e40fccc526 Merge branch 'no_gen_empty_messages' into integration
# Conflicts:
#	esphome/components/esp32_hosted/update/esp32_hosted_update.cpp
#	esphome/core/component.cpp
2026-02-09 12:12:28 -06:00
J. Nick Koston
04943e71b0 Merge remote-tracking branch 'upstream/dev' into no_gen_empty_messages
# Conflicts:
#	esphome/components/api/api_pb2_service.cpp
#	script/api_protobuf/api_protobuf.py
2026-02-09 12:10:01 -06:00
J. Nick Koston
d33f23dc43 [ld2410] Batch UART reads to reduce loop overhead (#13820) 2026-02-09 12:07:55 -06:00
J. Nick Koston
c43d3889b0 [modbus] Use stack buffer instead of heap vector in send() (#13853) 2026-02-09 12:07:42 -06:00
J. Nick Koston
50fe8e51f9 [ld2412] Batch UART reads to reduce loop overhead (#13819) 2026-02-09 12:07:28 -06:00
J. Nick Koston
c7883cb5ae [ld2450] Batch UART reads to reduce loop overhead (#13818) 2026-02-09 12:06:38 -06:00
J. Nick Koston
3b0df145b7 [cse7766] Batch UART reads to reduce loop overhead (#13817) 2026-02-09 12:05:59 -06:00
J. Nick Koston
2383b6b8b4 [core] Deprecate set_retry, cancel_retry, and RetryResult (#13845) 2026-02-09 12:05:32 -06:00
J. Nick Koston
c658d7b57f [api] Merge auth check into base read_message, eliminate APIServerConnection (#13873) 2026-02-09 12:02:02 -06:00
Jonathan Swoboda
04a6238c7b [esp32] Set UV_CACHE_DIR inside data dir so Clean All clears it (#13888)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 17:49:58 +00:00
J. Nick Koston
919afa1553 [web_server_base] Fix RP2040 compilation when Crypto-no-arduino is present (#13887) 2026-02-09 12:47:59 -05:00
J. Nick Koston
abb88f6b0f Merge remote-tracking branch 'origin/api-flash-string-progmem' into integration 2026-02-09 11:16:41 -06:00
J. Nick Koston
5d5344cf91 Add tests for cg.templatable() auto FlashStringLiteral wrapping
Cover the new automatic ESPHOME_F() wrapping behavior: static strings
with std::string output_type, non-string values, None output_type,
to_exp callable/dict, and lambda passthrough.
2026-02-09 10:56:56 -06:00
J. Nick Koston
b0cf94c409 Auto-wrap static strings in ESPHOME_F() via templatable()
Move FlashStringLiteral wrapping from per-component manual code into
cg.templatable() itself. When output_type is std::string and the value
is a static string (not a lambda), it is automatically wrapped in
ESPHOME_F() for PROGMEM storage on ESP8266. On other platforms
ESPHOME_F() is a no-op returning const char*.

This makes all ~50 existing cg.templatable(..., cg.std_string) call
sites across every component benefit automatically, with no
per-component changes needed.

Simplify api/__init__.py by switching from output_type=None to
cg.std_string and removing the manual isinstance/FlashStringLiteral
checks that are now redundant.
2026-02-09 10:38:41 -06:00
J. Nick Koston
9202d11af0 Merge branch 'dlms_meter_batch_read' into integration 2026-02-09 09:45:54 -06:00
J. Nick Koston
0ed3e033de Merge branch 'pylontech_batch_read' into integration 2026-02-09 09:45:54 -06:00
J. Nick Koston
0948f306ec Merge branch 'tuya_batch_read' into integration 2026-02-09 09:45:53 -06:00
J. Nick Koston
51679d9b6c Merge branch 'modbus_batch_read' into integration 2026-02-09 09:45:53 -06:00
J. Nick Koston
6dd9311f08 Merge branch 'seeed_mr_batch_read' into integration 2026-02-09 09:45:53 -06:00
J. Nick Koston
e2b870bd5c Merge branch 'dfplayer_batch_read' into integration 2026-02-09 09:45:52 -06:00
J. Nick Koston
a2ea81df0e Merge branch 'rd03d_batch_read' into integration 2026-02-09 09:45:52 -06:00
J. Nick Koston
84062c46b0 Merge branch 'nextion_batch_read' into integration 2026-02-09 09:45:52 -06:00
J. Nick Koston
24ab0d9474 Merge branch 'ld2420_batch_read' into integration 2026-02-09 09:45:52 -06:00
J. Nick Koston
30649c280e Merge branch 'ld2410_batch_read' into integration 2026-02-09 09:45:51 -06:00
J. Nick Koston
bb13405775 Merge branch 'ld2412_batch_read' into integration 2026-02-09 09:45:51 -06:00
J. Nick Koston
bfde168035 Merge branch 'ld2450_batch_read' into integration 2026-02-09 09:45:51 -06:00
J. Nick Koston
d842b5411b Merge branch 'cse7766_batch_read' into integration 2026-02-09 09:45:50 -06:00
J. Nick Koston
ca8617cf10 Fix early guard comment 2026-02-09 09:41:45 -06:00
J. Nick Koston
a0bc6a9922 Remove incorrect early guard comment 2026-02-09 09:41:25 -06:00
J. Nick Koston
d5295a894b Remove unnecessary early guard 2026-02-09 09:41:03 -06:00
J. Nick Koston
fb6b96ff58 Remove incorrect early guard comment 2026-02-09 09:40:44 -06:00
J. Nick Koston
e07144ef74 Remove unnecessary early guard 2026-02-09 09:40:26 -06:00
J. Nick Koston
1c4cf1a3e8 Remove unnecessary early guard 2026-02-09 09:40:05 -06:00
J. Nick Koston
a198df34ee Remove unnecessary early guard 2026-02-09 09:39:49 -06:00
J. Nick Koston
15904ab583 Remove unnecessary early guard 2026-02-09 09:39:30 -06:00
J. Nick Koston
9c9e8ac388 Remove unnecessary early guard 2026-02-09 09:39:04 -06:00
J. Nick Koston
04f4636d36 Remove unnecessary early guard 2026-02-09 09:38:47 -06:00
J. Nick Koston
3cbadfe42a Remove unnecessary early guard 2026-02-09 09:38:30 -06:00
J. Nick Koston
277a11f0ea Remove unnecessary early guard 2026-02-09 09:38:12 -06:00
J. Nick Koston
08cca414e7 Remove unnecessary early guard 2026-02-09 09:37:53 -06:00
J. Nick Koston
4db5835b6f Update comment explaining early guard 2026-02-09 09:32:08 -06:00
J. Nick Koston
527003e16b Add comment explaining early guard 2026-02-09 09:31:34 -06:00
J. Nick Koston
e9a0d06880 Add comment explaining early guard 2026-02-09 09:31:19 -06:00
J. Nick Koston
b2879f7f99 Add comment explaining early guard 2026-02-09 09:31:02 -06:00
J. Nick Koston
44e9346e9c Add comment explaining early guard 2026-02-09 09:30:43 -06:00
J. Nick Koston
6670c2b6c4 Add comment explaining early guard 2026-02-09 09:30:24 -06:00
J. Nick Koston
6013b473ca Add comment explaining early guard 2026-02-09 09:30:08 -06:00
J. Nick Koston
cc1f83ac35 Add comment explaining early guard 2026-02-09 09:29:42 -06:00
J. Nick Koston
1f1405364d Add comment explaining early guard 2026-02-09 09:29:25 -06:00
J. Nick Koston
5d3ae8cbec Add comment explaining early guard 2026-02-09 09:29:07 -06:00
J. Nick Koston
59a2f6f538 Add comment explaining early guard 2026-02-09 09:28:51 -06:00
J. Nick Koston
a9c37cae26 Add comment explaining early guard 2026-02-09 09:28:32 -06:00
J. Nick Koston
c8a93f31e9 Add comment explaining early guard 2026-02-09 09:28:15 -06:00
J. Nick Koston
f79448a09a Remove verbose available() comment 2026-02-09 09:27:57 -06:00
J. Nick Koston
5e096826c3 Remove verbose available() comment 2026-02-09 09:27:42 -06:00
J. Nick Koston
457d68256d Keep early guard to avoid stack buffer allocation 2026-02-09 09:27:20 -06:00
J. Nick Koston
a9029fb67a Keep early guard to avoid stack buffer allocation 2026-02-09 09:27:05 -06:00
J. Nick Koston
cd891d4b16 Keep early guard to avoid stack buffer allocation 2026-02-09 09:26:50 -06:00
J. Nick Koston
2784059a64 Keep early guard to avoid stack buffer allocation 2026-02-09 09:26:29 -06:00
J. Nick Koston
4827f53156 Keep early guard to avoid stack buffer allocation 2026-02-09 09:26:13 -06:00
J. Nick Koston
8dff0ee449 Remove redundant early guard 2026-02-09 09:25:23 -06:00
J. Nick Koston
a7f04a6cf9 Remove redundant early guard 2026-02-09 09:25:05 -06:00
J. Nick Koston
53bde863f5 Remove redundant early guard 2026-02-09 09:24:50 -06:00
J. Nick Koston
dfb0c8670d Remove redundant early guard 2026-02-09 09:24:34 -06:00
J. Nick Koston
7490efedd7 Remove redundant early guard 2026-02-09 09:24:15 -06:00
Kevin Ahrendt
c28c97fbaf [mixer] Refactor for stability and to support Sendspin (#12253)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick+github@koston.org>
2026-02-09 10:19:00 -05:00
J. Nick Koston
e24bb092e5 Merge branch 'merge_read_message' into no_gen_empty_messages 2026-02-09 09:09:30 -06:00
J. Nick Koston
9c74efe265 Merge remote-tracking branch 'upstream/dev' into merge_read_message 2026-02-09 08:49:04 -06:00
J. Nick Koston
3cde3daceb [api] Collapse APIServerConnection intermediary layer (#13872) 2026-02-09 08:45:33 -06:00
J. Nick Koston
be4e573cc4 [esp32_hosted] Replace set_retry with set_interval to avoid heap allocation (#13844) 2026-02-09 08:45:18 -06:00
J. Nick Koston
66af998098 [dashboard] Handle malformed Basic Auth headers gracefully (#13866) 2026-02-09 08:45:03 -06:00
J. Nick Koston
938a11595d [speaker] Replace set_retry with set_interval to avoid heap allocation (#13843) 2026-02-09 08:44:50 -06:00
J. Nick Koston
c812ac8b29 [ms8607] Replace set_retry with set_timeout chain to avoid heap allocation (#13842) 2026-02-09 08:44:35 -06:00
J. Nick Koston
248fc06dac [scheduler] Eliminate heap allocation in full_cleanup_removed_items_ (#13837) 2026-02-09 08:44:20 -06:00
J. Nick Koston
8b8acb3b27 [dashboard] Use constant-time comparison for username check (#13865) 2026-02-09 08:31:06 -06:00
J. Nick Koston
1c60efa4b6 [ota] Use secrets module for OTA authentication cnonce (#13863) 2026-02-09 08:30:49 -06:00
J. Nick Koston
4ef238eb7b [analyze-memory] Attribute third-party library symbols via nm scanning (#13878) 2026-02-09 08:26:03 -06:00
J. Nick Koston
c990da265a Add unit tests for FlashStringLiteral
Cover the three lines reported uncovered by codecov in
cpp_generator.py (FlashStringLiteral.__init__ and __str__).
2026-02-09 07:45:03 -06:00
J. Nick Koston
91487e7f14 [api] Store HomeAssistant action strings in PROGMEM on ESP8266
On ESP8266, .rodata is copied to RAM at boot. Every string literal in
HomeAssistantServiceCallAction (service names, data keys, data values)
permanently consumes RAM even though the action may rarely fire.

Add FLASH_STRING type to TemplatableValue that stores PROGMEM pointers
on ESP8266 via the existing __FlashStringHelper* type. At play() time,
strings are copied from flash to temporary std::string storage — safe
because service calls are not in the hot path.

Add FlashStringLiteral codegen helper (cg.FlashStringLiteral) that wraps
strings in ESPHOME_F() — expands to F() on ESP8266 (PROGMEM), plain
string on other platforms (no-op). This helper can be adopted by other
components incrementally.

On non-ESP8266 platforms, FLASH_STRING is never set and all existing
code paths are unchanged.
2026-02-09 07:39:06 -06:00
J. Nick Koston
5e8f9c938e Merge branch 'scheduler_uint32t_core_filters' into integration 2026-02-09 06:45:29 -06:00
J. Nick Koston
22c77866d8 [e131] Remove unnecessary heap allocation from packet receive loop (#13852) 2026-02-09 06:42:26 -06:00
J. Nick Koston
790ac620ab [web_server_idf] Use C++17 nested namespace style (#13856) 2026-02-09 06:42:12 -06:00
J. Nick Koston
4b92465afd [scheduler] Fix stale bitfield comments for name_type_ 2026-02-09 06:39:03 -06:00
J. Nick Koston
65c2b29678 Merge remote-tracking branch 'origin/integration' into integration 2026-02-09 06:33:05 -06:00
J. Nick Koston
a6345eea08 Merge branch 'scheduler_uint32t_core_filters' into integration 2026-02-09 06:32:01 -06:00
J. Nick Koston
78ddacd766 [scheduler] Update NameType comment to mention internal numeric IDs 2026-02-09 06:26:53 -06:00
J. Nick Koston
dd2c5838e1 [scheduler] Use enum class for InternalSchedulerID to avoid materialized constant
The struct + constexpr namespace approach caused the compiler to
materialize POLLING_UPDATE as a 4-byte symbol. An enum class with
uint32_t underlying type gives the same type safety but enum values
are true compile-time constants that never get materialized.
2026-02-09 06:25:55 -06:00
J. Nick Koston
d9f7c26b16 Revert "[scheduler] Inline all trivial Scheduler forwarding methods"
This reverts commit d2728f2ccd.
2026-02-09 06:24:49 -06:00
J. Nick Koston
d2728f2ccd [scheduler] Inline all trivial Scheduler forwarding methods
Move all set_timeout, cancel_timeout, set_interval, cancel_interval
overloads (const char*, uint32_t, std::string) from scheduler.cpp to
inline in scheduler.h. These are all trivial one-liner forwarding calls
to set_timer_common_ or cancel_item_ and benefit from inlining at
call sites rather than being separate symbols.
2026-02-09 06:16:49 -06:00
J. Nick Koston
769d4e4444 [scheduler] Inline InternalSchedulerID overloads to eliminate code size overhead
Move the 4 Scheduler InternalSchedulerID methods from .cpp to inline
in the header. They're trivial one-liners that extract .id and forward
to set_timer_common_/cancel_item_, so the compiler can fold them into
call sites instead of emitting separate function bodies.
2026-02-09 06:07:49 -06:00
J. Nick Koston
3d2b9641a4 [scheduler] Add integration test for internal vs numeric ID isolation
Verifies that NUMERIC_ID_INTERNAL and NUMERIC_ID are completely
independent matching namespaces — same uint32_t value on the same
component does not collide. Tests that cancelling one type does not
affect the other, and that string names also don't cross-match.
2026-02-09 06:05:13 -06:00
J. Nick Koston
ed3acd582e [scheduler] Make core timer ID collisions impossible with type-safe internal IDs
Previously, PollingComponent used the string "update" and DelayAction
used "delay" as scheduler names. If a component subclassing
PollingComponent or using DelayAction also happened to use these same
strings for its own timers, the core timer would be silently cancelled
— a subtle bug that would be extremely hard to track down.

This introduces InternalSchedulerID, a type-safe wrapper that routes
through a new NUMERIC_ID_INTERNAL NameType. Since the scheduler
matches by (component, name_type, id, type), internal IDs can never
collide with component-level NUMERIC_ID or string-based names, even
if the underlying uint32_t values overlap.

Also migrates binary_sensor filters, MultiClickTrigger, and sensor
filters from string-based to uint32_t scheduler IDs. Each filter is
its own Component instance so IDs are scoped per-instance with no
collision risk.
2026-02-09 05:50:41 -06:00
J. Nick Koston
ba2ac54932 Merge branches 'cse7766_batch_read', 'ld2410_batch_read', 'ld2412_batch_read', 'ld2420_batch_read', 'ld2450_batch_read', 'modbus_batch_read', 'nextion_batch_read', 'pylontech_batch_read', 'seeed_mr_batch_read', 'dsmr_batch_read', 'tuya_batch_read', 'dlms_meter_batch_read', 'pipsolar_batch_read', 'rd03d_batch_read', 'rf_bridge_batch_read' and 'dfplayer_batch_read' into integration 2026-02-09 04:35:39 -06:00
J. Nick Koston
a0f736b7aa Future-proof available() check to handle negative return values 2026-02-09 04:35:11 -06:00
J. Nick Koston
21f270677b Future-proof available() check to handle negative return values 2026-02-09 04:34:49 -06:00
J. Nick Koston
d6e692e302 Future-proof available() check to handle negative return values 2026-02-09 04:34:32 -06:00
J. Nick Koston
991ce396a9 Future-proof available() check to handle negative return values 2026-02-09 04:34:14 -06:00
J. Nick Koston
68dfb844bd Future-proof available() check to handle negative return values 2026-02-09 04:32:14 -06:00
J. Nick Koston
9742880bf7 Add comment explaining available() <= 0 check 2026-02-09 04:31:16 -06:00
J. Nick Koston
13f9726534 Add comment explaining available() <= 0 check 2026-02-09 04:30:59 -06:00
J. Nick Koston
dd07e25a8f Future-proof available() check to handle negative return values 2026-02-09 04:30:15 -06:00
J. Nick Koston
a875a2fb9b Future-proof available() check to handle negative return values 2026-02-09 04:29:37 -06:00
J. Nick Koston
4f5be934a0 [api] Skip class generation for empty SOURCE_CLIENT protobuf messages 2026-02-09 04:12:01 -06:00
J. Nick Koston
f3a0d46ee9 Revert "[api] Skip class generation for empty SOURCE_CLIENT protobuf messages"
This reverts commit bd23e3ffa9.
2026-02-09 04:11:39 -06:00
J. Nick Koston
bd23e3ffa9 [api] Skip class generation for empty SOURCE_CLIENT protobuf messages 2026-02-09 04:05:23 -06:00
tronikos
fb93283720 [water_heater] Add state masking to distinguish explicit commands from no-change (#13879) 2026-02-09 03:52:49 -06:00
J. Nick Koston
926e74db9b Merge branch 'collapse_intermediary' into merge_read_message 2026-02-09 03:45:51 -06:00
J. Nick Koston
bed01da345 [api] Guard varint parsing against overlong encodings (#13870) 2026-02-09 03:45:40 -06:00
J. Nick Koston
acdd5e6ac4 Merge remote-tracking branch 'upstream/dev' into collapse_intermediary 2026-02-09 03:44:58 -06:00
J. Nick Koston
422f413680 [lps22] Replace set_retry with set_interval to avoid heap allocation (#13841) 2026-02-09 03:26:44 -06:00
J. Nick Koston
c3c0c40524 [mqtt] Return friendly_name_() by const reference to avoid string copies (#13810) 2026-02-09 03:26:29 -06:00
J. Nick Koston
46f8302d8f [mqtt] Use stack buffer for discovery topic to avoid heap allocation (#13812) 2026-02-09 03:26:15 -06:00
J. Nick Koston
e24528c842 [analyze-memory] Attribute CSWTCH symbols from SDK archives (#13850) 2026-02-09 03:25:59 -06:00
J. Nick Koston
5370687001 [wizard] Use secrets module for fallback AP password generation (#13864) 2026-02-09 03:25:41 -06:00
J. Nick Koston
6ee185c58a [dashboard] Use resolve/relative_to for download path validation (#13867) 2026-02-09 03:25:23 -06:00
J. Nick Koston
eb6a6f8d0d [web_server_idf] Remove unused host() method (#13869) 2026-02-09 03:25:05 -06:00
J. Nick Koston
140ec0639c [api] Elide empty message construction in protobuf dispatch (#13871) 2026-02-09 03:24:45 -06:00
Clyde Stubbs
756f1c6b7e [lvgl] Fix crash with unconfigured top_layer (#13846) 2026-02-08 21:53:43 -05:00
J. Nick Koston
6469863aea Merge remote-tracking branch 'origin/libs_analyze_memory' into integration 2026-02-08 15:04:43 -06:00
J. Nick Koston
8f032d5213 wip 2026-02-08 15:04:33 -06:00
J. Nick Koston
4efaaedb05 Merge remote-tracking branch 'origin/libs_analyze_memory' into integration 2026-02-08 15:01:18 -06:00
J. Nick Koston
12c369cb09 wip 2026-02-08 15:01:01 -06:00
J. Nick Koston
e56d1a6ee9 Merge remote-tracking branch 'origin/libs_analyze_memory' into integration 2026-02-08 14:57:18 -06:00
J. Nick Koston
b728ebd5fb tweaks 2026-02-08 14:56:57 -06:00
J. Nick Koston
5caa54761b tweaks 2026-02-08 14:51:11 -06:00
J. Nick Koston
4efdfda8fb wip 2026-02-08 14:34:37 -06:00
J. Nick Koston
6099dd3065 wip 2026-02-08 14:34:00 -06:00
J. Nick Koston
30558262d8 fix dupes 2026-02-08 14:27:09 -06:00
J. Nick Koston
6dd7f45cc9 tweak 2026-02-08 14:12:35 -06:00
tomaszduda23
28b9487b25 [nrf52,logger] fix printk (#13874) 2026-02-08 17:52:05 +00:00
J. Nick Koston
17664750b7 tweak 2026-02-08 11:20:54 -06:00
J. Nick Koston
95e92716d9 tweak 2026-02-08 11:19:38 -06:00
J. Nick Koston
0f1ece6bdc [analyze-memory] Attribute PlatformIO library symbols via nm archive scanning 2026-02-08 11:14:34 -06:00
J. Nick Koston
43d7b1027b Merge remote-tracking branch 'origin/libretiny_1120' into integration 2026-02-08 10:32:15 -06:00
J. Nick Koston
2eac6b06b7 revert workarounds now that libretiny v1.12.1 is released 2026-02-08 10:28:14 -06:00
J. Nick Koston
491112db69 Generate auth check unconditionally in read_message
If all messages required auth (both no_conn_ids and conn_only_ids
empty), the auth check block would be skipped entirely. Now generates
a simple check_authenticated_() call as fallback when there are no
exceptions to the default auth requirement.
2026-02-08 10:23:22 -06:00
J. Nick Koston
140629a323 Merge branch 'merge_read_message' into integration 2026-02-08 10:19:57 -06:00
J. Nick Koston
2376c020e8 Merge auth check into base read_message, eliminate APIServerConnection
Move the authentication/connection check switch into
APIServerConnectionBase::read_message before the dispatch switch,
eliminating the APIServerConnection class entirely. APIConnection
now inherits directly from APIServerConnectionBase.
2026-02-08 10:11:42 -06:00
J. Nick Koston
d28c3e2641 Collapse APIServerConnection intermediary layer
Remove the 48 on_xxx overrides and 48 pure virtual methods from
the generated APIServerConnection class. APIConnection now overrides
on_xxx() directly from APIServerConnectionBase, eliminating one
virtual call per message dispatch and ~96 vtable entries.
2026-02-08 10:05:10 -06:00
J. Nick Koston
4cf1d7babf Revert "Collapse APIServerConnection intermediary layer"
This reverts commit cac82280b3.
2026-02-08 09:57:03 -06:00
J. Nick Koston
cac82280b3 Collapse APIServerConnection intermediary layer
Remove the 48 on_xxx overrides and 48 pure virtual methods from
the generated APIServerConnection class. APIConnection now overrides
on_xxx() directly from APIServerConnectionBase, eliminating one
virtual call per message dispatch and ~96 vtable entries.
2026-02-08 09:55:31 -06:00
J. Nick Koston
df7e837e3e Merge branch 'empty_messages_reduce' into integration 2026-02-08 09:35:48 -06:00
J. Nick Koston
25b1ce4268 Merge branch 'dev' into empty_messages_reduce 2026-02-08 09:33:42 -06:00
J. Nick Koston
aa38ec379a Address review: fix RECEIVE_CASES type annotation and log format
- Update RECEIVE_CASES type hint from 2-tuple to 3-tuple to match
  the actual stored shape (case, ifdef, message_name)
- Use consistent log format "%s: {}" for empty message overload
  to match the "%s: %s" pattern of non-empty messages
2026-02-08 09:32:40 -06:00
J. Nick Koston
639a704411 Merge branch 'empty_messages_reduce' into integration 2026-02-08 09:21:27 -06:00
J. Nick Koston
c641256396 empty messages 2026-02-08 09:16:53 -06:00
J. Nick Koston
e4dd75f0b5 empty messages 2026-02-08 09:15:15 -06:00
J. Nick Koston
a7d1686cfd empty messages 2026-02-08 09:14:15 -06:00
J. Nick Koston
d71f7df367 empty messages 2026-02-08 09:12:41 -06:00
J. Nick Koston
a56415ca3f empty messages 2026-02-08 09:10:48 -06:00
J. Nick Koston
5cb3af9e0e Merge branch 'hardening/api-varint-guard' into integration 2026-02-08 08:38:46 -06:00
J. Nick Koston
11774df7f6 [api] Guard varint parsing against overlong encodings
Limit varint parsing to 10 bytes maximum (ceil(64/7)) to prevent
undefined behavior from shifting uint64_t by >= 64 bits when
processing malformed protobuf data with excessive continuation bytes.
2026-02-08 08:37:24 -06:00
J. Nick Koston
41fedaedb3 [udp] Eliminate per-loop heap allocation using std::span (#13838)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
2026-02-08 08:26:47 -06:00
J. Nick Koston
380a669377 Merge branch 'cleanup/remove-dead-host-method' into integration 2026-02-08 08:24:03 -06:00
J. Nick Koston
9299d83eeb Remove unused host() method from AsyncWebServerRequest
Dead code with no callers. Also avoids an unchecked .value() on
optional<string> that would crash on HTTP/1.0 requests without
a Host header.
2026-02-08 08:23:27 -06:00
J. Nick Koston
381f4e9f34 Revert "Remove unused host() method from AsyncWebServerRequest"
This reverts commit 99b7996759.
2026-02-08 08:23:04 -06:00
J. Nick Koston
99b7996759 Remove unused host() method from AsyncWebServerRequest
Dead code with no callers. Also avoids an unchecked .value() on
optional<string> that would crash on HTTP/1.0 requests without
a Host header.
2026-02-08 08:22:24 -06:00
J. Nick Koston
31ca8984ed Merge branch 'web_server_idf_heap' into integration 2026-02-08 08:05:13 -06:00
J. Nick Koston
02687615b3 Merge upstream/dev into web_server_idf_heap
Resolve conflict in utils.cpp: use query_len parameter (from dev)
instead of query_url.size() (query_url is const char*, not string).
2026-02-08 08:04:57 -06:00
J. Nick Koston
b283f1ae75 Merge all hardening branches into integration
Resolve conflict in web_server_idf.cpp: keep constant-time compare
logic adapted to stack-allocated digest buffer from integration.
2026-02-08 08:03:23 -06:00
J. Nick Koston
2ceb6ee95b Add comment explaining Windows-specific multiple_dots behavior
On Windows, Path.resolve() treats '....' as parent traversal (403),
while on Unix it is a literal directory name that stays inside the
base directory (404).
2026-02-08 07:55:48 -06:00
J. Nick Koston
4cdd73904f Encode usernames as UTF-8 bytes for hmac.compare_digest
hmac.compare_digest() on str inputs raises TypeError if either
contains non-ASCII characters. Encode both sides as UTF-8 bytes.
Add test with non-ASCII username to prevent regressions.
2026-02-08 07:49:53 -06:00
J. Nick Koston
b8cad678b1 URL-encode whitespace in empty file name test parameter
Replace raw spaces with %20%20 to avoid flakiness from HTTP clients
handling unencoded spaces differently.
2026-02-08 07:48:41 -06:00
J. Nick Koston
5c5bf50e49 Update test docstring to reflect validation instead of sanitization 2026-02-08 07:41:00 -06:00
J. Nick Koston
f2e1c2c650 Derive provided_len from string size and bound loop to digest_len
- Use auth.value().size() instead of strlen() to avoid rescanning
  attacker-controlled header content.
- Iterate over digest_len (expected length) instead of max_len so a
  long Authorization header cannot force extra work. The full-width
  length XOR already rejects any length mismatch.
2026-02-08 07:39:09 -06:00
J. Nick Koston
401d3c2056 Fix idedata test mock to use Path instead of str
The test set mock_image.path to str, but FlashImage.path is a Path.
This masked a pre-existing bug where Path.endswith() doesn't exist.
Fix the mock to match the real type so as_posix() works correctly.
2026-02-08 07:36:02 -06:00
J. Nick Koston
30662bc11b Update module docstring to reflect auth test coverage 2026-02-08 07:34:14 -06:00
J. Nick Koston
b650d2df31 Reject empty file names and fix FlashImage.path endswith call
- Return 400 for empty or whitespace-only file_name to prevent the
  idedata fallback from matching everything via empty-string suffix.
- Use image.path.as_posix().endswith() since FlashImage.path is a Path
  object which does not have a string endswith method.
- Add parametrized test for empty/whitespace file name values.
2026-02-08 07:32:00 -06:00
J. Nick Koston
bb2d7c9742 Use full-width accumulator and iterate max_len in constant-time compare
- Change result accumulator from volatile uint8_t to volatile size_t
  to prevent truncation bypass (e.g. digest_len + 256 XOR).
- Iterate over max(digest_len, provided_len) so trailing bytes in
  either string are also compared.
2026-02-08 07:25:50 -06:00
J. Nick Koston
4795971f1c Use usefixtures for tests that don't reference mock_auth_settings
Replace unused mock_auth_settings parameter with
@pytest.mark.usefixtures decorator to avoid PLW0613 lint warnings.
2026-02-08 07:25:19 -06:00
J. Nick Koston
ea99593575 Build auth_settings on dashboard_settings and monkeypatch env
- Refactor auth_settings fixture to extend dashboard_settings instead
  of duplicating setup.
- Explicitly clear DISABLE_HA_AUTHENTICATION in HA add-on test to
  prevent order-dependent flakiness.
2026-02-08 07:24:44 -06:00
J. Nick Koston
bf7ede1d43 Make mock_token_hex strict on unexpected nbytes
Raise ValueError for unexpected nbytes values so tests fail clearly
if production code starts calling token_hex with an incorrect size.
2026-02-08 07:24:11 -06:00
J. Nick Koston
43448d55f1 Guard against None firmware_bin_path and mock subprocess in tests
- Add None check for storage_json.firmware_bin_path before computing
  base_dir (covers configs from StorageJSON.from_wizard()).
- Mock async_run_system_command in path traversal tests so paths that
  pass validation but don't exist return 404 deterministically.
- Add test for firmware_bin_path=None case.
2026-02-08 07:23:24 -06:00
J. Nick Koston
e362e6fe2f Fix multiple_dots test for Windows path resolution
On Windows, ....//secrets.yaml escapes the base directory (403),
while on Unix it stays inside (404). Use sys.platform to set the
expected status code per platform.
2026-02-08 07:22:08 -06:00
J. Nick Koston
82d9616f1b Add explicit binascii.Error catch and bad-padding test
binascii.Error is already a subclass of ValueError, but listing it
explicitly makes the intent clear. Added test for incorrect base64
padding (e.g. "Basic abc").
2026-02-08 07:18:29 -06:00
J. Nick Koston
999774889d Add comments explaining constant-time comparison logic 2026-02-08 07:17:24 -06:00
J. Nick Koston
c90ca4df87 Use encoder output length and remove early return on length mismatch
Use the out length from esp_crypto_base64_encode instead of strlen.
Fold length mismatch into the accumulator to make comparison fully
constant-time without early return.
2026-02-08 07:16:19 -06:00
J. Nick Koston
a167332518 Fix password_hash type and add HA add-on regression test
Initialize password_hash as b"" (bytes) to match password_hash()
return type, preventing TypeError in hmac.compare_digest when
HA add-on auth is enabled without a password.
2026-02-08 07:14:20 -06:00
J. Nick Koston
1b7efdd051 Match cnonce length to hash algorithm digest size
Use nonce_size // 2 as token_hex argument so MD5 auth produces
a 32-char cnonce and SHA256 auth produces a 64-char cnonce,
matching the original protocol behavior.

Rename mock_random fixture to mock_token_hex and use separate
mock cnonce constants per hash algorithm.
2026-02-08 07:11:56 -06:00
J. Nick Koston
caff93d7b8 Add test coverage for secrets.choice in fallback PSK generation
Verifies that wizard_file uses secrets.choice (not random.choice)
to generate the 12-character fallback hotspot password.
2026-02-08 07:08:25 -06:00
J. Nick Koston
806a86a6ad Add test coverage for is_authenticated base64 handling
Tests malformed base64, invalid UTF-8, missing colon separator,
valid credentials, wrong credentials, and auth-disabled cases.
2026-02-08 07:06:24 -06:00
J. Nick Koston
42126bae72 Add test coverage for check_password
Tests correct credentials, wrong password, wrong username,
both wrong, and auth-disabled cases.
2026-02-08 07:03:29 -06:00
J. Nick Koston
803b9a7a18 Update path traversal tests for resolve/relative_to behavior
Real traversals that escape the base directory now return 403.
Paths like '....' that resolve inside the base directory but
don't exist return 404.
2026-02-08 07:01:37 -06:00
J. Nick Koston
a8fd6c132e Update tests to mock secrets.token_hex instead of random.random
The cnonce generation was changed to use secrets.token_hex(32),
so the test mocks and assertions need to match.
2026-02-08 06:59:32 -06:00
J. Nick Koston
1dcffdc872 [web_server_idf] Use constant-time comparison for Basic Auth
Replace strcmp() with a constant-time XOR accumulation loop
for comparing base64-encoded credentials in HTTP Basic Auth.
2026-02-08 06:51:52 -06:00
J. Nick Koston
a40c87eeed [dashboard] Use resolve/relative_to for download path validation
Replace string-based path sanitization (.replace/.lstrip) with
Path.resolve() and relative_to() validation, matching the
pattern used by other dashboard endpoints (e.g. settings.rel_path).

The previous approach was not exploitable but was inconsistent
with the rest of the codebase.
2026-02-08 06:48:38 -06:00
J. Nick Koston
2829f7b485 [dashboard] Handle malformed Basic Auth headers gracefully
Wrap base64 decode and split in try/except so malformed
Authorization headers return a clean 401 instead of an
unhandled exception producing a 500 response with stack
trace in logs.

Catches ValueError (covers binascii.Error from b64decode)
and UnicodeDecodeError (from .decode()).
2026-02-08 06:47:49 -06:00
J. Nick Koston
79a205eee2 [dashboard] Use constant-time comparison for username check
Use hmac.compare_digest() for the username comparison to match
the existing constant-time password comparison. This prevents
username enumeration via timing analysis.
2026-02-08 06:46:28 -06:00
J. Nick Koston
e039676422 [wizard] Use secrets module for fallback AP password generation
Replace random.choice() with secrets.choice() for generating
the fallback hotspot password. The random module uses Mersenne
Twister which is not cryptographically secure. The secrets
module is the correct choice for credential generation.

The file already imports secrets for other credential generation.
2026-02-08 06:44:45 -06:00
J. Nick Koston
9616596146 [ota] Use secrets module for OTA authentication cnonce
Replace random.random() with secrets.token_hex() for generating
the client nonce in OTA challenge-response authentication.

The random module uses Mersenne Twister which is not
cryptographically secure. The secrets module is the correct
choice for security-sensitive token generation.
2026-02-08 06:40:30 -06:00
J. Nick Koston
ad546edbb7 fix 2026-02-08 03:31:06 -06:00
J. Nick Koston
a07b429510 Merge branch 'hlk_fm22x_eliminate_heap_alloc' into integration 2026-02-08 03:28:50 -06:00
J. Nick Koston
94d6ca22b2 Merge branch 'voice-assistant-timer-vector' into integration 2026-02-08 03:28:43 -06:00
J. Nick Koston
78f98fa08f [hlk_fm22x] Drain exact frame bytes on oversize response
Discard exactly length+1 (payload + checksum) instead of flushing
the entire RX buffer, which could eat bytes from the next frame.
2026-02-08 02:51:27 -06:00
J. Nick Koston
eb3bad823b [hlk_fm22x] Reword comment to avoid lint false positive on 'byte' 2026-02-08 02:40:04 -06:00
J. Nick Koston
5374252470 [hlk_fm22x] Add bounds checks and fix format specifiers
- Flush UART RX buffer when response exceeds max size
- Guard handle_note_ against zero-length data
- Guard handle_reply_ against length < 2
- Validate VERIFY response has full name payload before access
- Guard GET_VERSION against length underflow
- Cast %.*s precision to int, use %zu for size_t
- Improve MAX_RESPONSE_SIZE comment with payload layout
2026-02-08 02:38:40 -06:00
J. Nick Koston
f7630075ff Fix Trigger/Automation type mismatch for timer_tick_trigger_ 2026-02-08 02:32:03 -06:00
J. Nick Koston
8677f3db03 Add timer automation tests for voice_assistant 2026-02-08 02:27:23 -06:00
J. Nick Koston
9add30b900 Pass timer tick vector as const ref through automation to avoid copy 2026-02-08 02:26:22 -06:00
J. Nick Koston
2e50651400 [hlk_fm22x] Replace per-cycle vector allocation with member buffer
Replace std::vector<uint8_t> in recv_command_() with a member
std::array<uint8_t, 36> buffer to eliminate heap allocation on
every polling cycle. Also use pointer+length instead of vector
references in handle_note_/handle_reply_, and use TextSensor's
publish_state(const char*, size_t) overload to avoid temporary
std::string construction for version and face name publishing.
2026-02-08 02:17:22 -06:00
J. Nick Koston
bfee24421a Merge branch 'voice-assistant-timer-vector' into integration 2026-02-08 02:07:34 -06:00
J. Nick Koston
86fee6e4af [voice_assistant] Replace timer unordered_map with vector to eliminate per-tick heap allocation 2026-02-08 02:02:55 -06:00
J. Nick Koston
48ea97139e Merge branch 'web_server_idf_namespace' into integration 2026-02-08 01:47:01 -06:00
J. Nick Koston
67fbd31e46 [web_server_idf] Use C++17 nested namespace style 2026-02-08 01:45:22 -06:00
schrob
7b40e8afcb [epaper_spi] Declare leaf classes final (#13776) 2026-02-07 19:21:37 -06:00
J. Nick Koston
d73384ae46 revert this after https://github.com/libretiny-eu/libretiny/pull/361 2026-02-07 19:07:15 -06:00
J. Nick Koston
456e0f2679 revert this after https://github.com/libretiny-eu/libretiny/pull/361 2026-02-07 18:37:32 -06:00
J. Nick Koston
852efb06a8 Merge remote-tracking branch 'origin/libretiny_1120' into integration 2026-02-07 18:33:46 -06:00
J. Nick Koston
1b1efc5d49 revert this after https://github.com/libretiny-eu/libretiny/pull/361 2026-02-07 18:28:28 -06:00
J. Nick Koston
67bfbee567 revert this after https://github.com/libretiny-eu/libretiny/pull/361 2026-02-07 18:23:59 -06:00
J. Nick Koston
3deea03ff5 revert this after https://github.com/libretiny-eu/libretiny/pull/361 2026-02-07 18:19:08 -06:00
J. Nick Koston
c139aff8d9 Merge remote-tracking branch 'upstream/libretiny_1120' into integration 2026-02-07 18:05:38 -06:00
J. Nick Koston
98f900183d update boards as well 2026-02-07 18:04:41 -06:00
J. Nick Koston
059087ed21 [libretiny] Update LibreTiny to v1.12.0 2026-02-07 18:01:52 -06:00
J. Nick Koston
d3778af3e8 [libretiny] Update LibreTiny to v1.12.0 2026-02-07 18:01:19 -06:00
J. Nick Koston
663151821f Merge branch 'cswitch_sdk' into integration 2026-02-07 18:00:55 -06:00
J. Nick Koston
8c4a732eb7 copilot edge cases 2026-02-07 18:00:30 -06:00
J. Nick Koston
2d0b1db3dd Merge branch 'cswitch_sdk' into integration 2026-02-07 17:53:31 -06:00
J. Nick Koston
4e3ccb4fc5 [analyze-memory] Attribute CSWTCH symbols from SDK archives 2026-02-07 17:52:20 -06:00
J. Nick Koston
66ab62b3fb Merge branch 'deprecate_set_retry' into integration 2026-02-07 17:26:28 -06:00
J. Nick Koston
2a6e20dd32 [core] Deprecate set_retry, cancel_retry, and RetryResult
set_retry does a std::make_shared<RetryArgs>() heap allocation on every
invocation. No core component needs this pattern - all callers have been
migrated to set_timeout or set_interval in prior PRs. The feature wastes
flash and RAM on every firmware for a pattern that set_interval covers
better, and the hidden heap allocation is a footgun for component authors.

Deprecated in 2026.2.0, removal in 2026.8.0.

Depends on:
- #13841 [lps22] Replace set_retry with set_interval
- #13842 [ms8607] Replace set_retry with set_timeout chain
- #13843 [speaker] Replace set_retry with set_interval
- #13844 [esp32_hosted] Replace set_retry with set_interval
2026-02-07 17:25:59 -06:00
J. Nick Koston
7516e418f2 Merge branch 'ms8607_remove_set_retry' into integration 2026-02-07 17:23:25 -06:00
J. Nick Koston
3864f06a15 Merge branch 'esp32_hosted_remove_set_retry' into integration 2026-02-07 17:23:21 -06:00
J. Nick Koston
98dcea6e7d Merge branch 'speaker_media_player_remove_set_retry' into integration 2026-02-07 17:23:15 -06:00
J. Nick Koston
9ee51b06fa Merge branch 'deprecate_set_retry' into integration 2026-02-07 17:23:12 -06:00
J. Nick Koston
4efca40457 Merge branch 'lps22_remove_set_retry' into integration 2026-02-07 17:23:06 -06:00
J. Nick Koston
a43e3e5948 [dashboard] Close WebSocket after process exit to prevent zombie connections (#13834) 2026-02-07 15:19:20 -06:00
J. Nick Koston
f64f71b9ac Merge remote-tracking branch 'upstream/dev' into integration 2026-02-07 15:13:45 -06:00
J. Nick Koston
60298f67b8 [ms8607] Replace set_retry with set_timeout chain to avoid heap allocation
set_retry internally does a std::make_shared<RetryArgs>() heap allocation
on every invocation. Replace with a try_reset_() method that chains
set_timeout calls with manual backoff, preserving the same timing
(immediate, +5ms, +25ms).
2026-02-07 14:53:18 -06:00
J. Nick Koston
4d2354da2e [esp32_hosted] Replace set_retry with set_interval to avoid heap allocation
set_retry internally does a std::make_shared<RetryArgs>() heap allocation
on every invocation. Replace with set_interval + countdown counter which
avoids this entirely. The original code used fixed-interval polling
(no backoff), making set_interval a direct fit.
2026-02-07 14:51:45 -06:00
J. Nick Koston
6a3da67a1e [speaker] Replace set_retry with set_interval to avoid heap allocation
set_retry internally does a std::make_shared<RetryArgs>() heap allocation
on every invocation. Replace with set_interval + countdown counter which
avoids this entirely. All 3 call sites used fixed-interval polling
(no backoff), making set_interval a direct fit.
2026-02-07 14:48:34 -06:00
J. Nick Koston
6ebafa8a9e [core] Deprecate set_retry, cancel_retry, and RetryResult
set_retry does a std::make_shared<RetryArgs>() heap allocation on every
invocation. No core component needs this pattern - all callers have been
migrated to set_timeout or set_interval in prior PRs. The feature wastes
flash and RAM on every firmware for a pattern that set_interval covers
better, and the hidden heap allocation is a footgun for component authors.

Deprecated in 2026.2.0, removal in 2026.8.0.

Depends on:
- #13841 [lps22] Replace set_retry with set_interval
- #13842 [ms8607] Replace set_retry with set_timeout chain
- #13843 [speaker] Replace set_retry with set_interval
- #13844 [esp32_hosted] Replace set_retry with set_interval
2026-02-07 14:44:09 -06:00
J. Nick Koston
3ba7e48615 [lps22] Replace set_retry with set_interval to avoid heap allocation
set_retry internally does a std::make_shared<RetryArgs>() heap allocation
on every invocation. Replace with set_interval + countdown counter which
avoids this entirely.
2026-02-07 14:27:10 -06:00
schrob
9de91539e6 [epaper_spi] Add Waveshare 1.54-G (#13758) 2026-02-08 06:24:57 +11:00
J. Nick Koston
51b0661d9d Merge branch 'scheduler-inplace-cleanup' into integration 2026-02-07 19:56:42 +01:00
J. Nick Koston
3c85ff4744 try to avoid bloat 2026-02-07 19:56:20 +01:00
J. Nick Koston
6a383a62b8 Merge branch 'scheduler-inplace-cleanup' into integration 2026-02-07 19:52:42 +01:00
J. Nick Koston
0fa7050b1c remove temp test 2026-02-07 10:01:57 +01:00
J. Nick Koston
fa1554cac0 [scheduler] Eliminate heap allocation in full_cleanup_removed_items_
Replace the temporary std::vector copy with in-place compaction using a
read/write pointer pattern. This avoids a heap allocation+deallocation
cycle during scheduler cleanup, reducing heap fragmentation on
long-running ESP devices.

The new approach compacts valid items forward in the existing vector,
recycles removed items as they are encountered, then resizes the vector
(no reallocation since size only shrinks). Same O(n) complexity, same
behavior, zero allocations.
2026-02-07 09:54:43 +01:00
J. Nick Koston
14071086bb Merge branch 'logger_thread_name_cleanup' into integration 2026-02-07 09:02:06 +01:00
J. Nick Koston
30f9bfaf83 [logger] Resolve thread name once and pass through logging chain
Eliminate redundant xTaskGetCurrentTaskHandle() and pcTaskGetName()
calls on the hot path by resolving the thread name once in log_vprintf_
and passing it through as const char* to all downstream functions.

- Main task fast path passes nullptr (no task handle lookup needed)
- Non-main thread path resolves name once, passes to both ring buffer
  and emergency console fallback
- Unify log_vprintf_non_main_thread_ to single signature across platforms
- Change send_message_thread_safe() on all platforms from TaskHandle_t
  to const char* thread_name
- Add TaskHandle_t overload for get_thread_name_ as primary on
  ESP32/LibreTiny, with no-arg convenience wrapper
- Use std::span<char> for Host/Zephyr get_thread_name_ buffer parameter
- Document Zephyr single-task path thread safety limitation
2026-02-07 07:47:00 +01:00
J. Nick Koston
daebc2cc39 Merge branch 'dashboard-ws-close-on-exit' into integration 2026-02-07 06:15:23 +01:00
J. Nick Koston
6b089a611c [dashboard] Close WebSocket after process exit to prevent zombie connections
When a subprocess exited, _proc_on_exit sent the exit event but never
closed the server-side WebSocket. This left zombie connections open
until the client eventually disconnected.
2026-02-07 06:14:44 +01:00
J. Nick Koston
836bfc625d restore original byte-at-a-time read in send_cmd_from_array ack loop
The ack polling loop has a tight timing requirement with
delay_microseconds_safe(1450) between iterations. Snapshotting
available() once could leave partial ack response bytes unread
until after the delay, potentially breaking cold boot timing
on some ld2420 units. Keep batch reads only in loop().
2026-02-07 01:01:03 +01:00
J. Nick Koston
2a17592d57 dfplayer: batch UART reads to reduce per-loop overhead 2026-02-07 00:38:54 +01:00
J. Nick Koston
04697ac223 rf_bridge: batch UART reads to reduce per-loop overhead 2026-02-07 00:36:27 +01:00
J. Nick Koston
3f3cf83aab rd03d: batch UART reads to reduce per-loop overhead 2026-02-07 00:32:33 +01:00
J. Nick Koston
39013388dd pipsolar: batch UART reads to reduce per-loop overhead 2026-02-07 00:26:33 +01:00
J. Nick Koston
cfbeea9983 [dlms_meter] Batch UART reads to reduce per-loop overhead
Replace byte-at-a-time read_byte() calls with batched read_array()
in loop(). Each read_byte() internally chains through
read_array(data, 1) -> check_read_timeout_(1) -> available(),
resulting in ~3 UART driver calls per byte. Batching into a 64-byte
stack buffer reduces this to ~3 calls per loop iteration regardless
of how many bytes are available.

Also uses vector insert() for bulk append instead of per-byte
push_back(), and caps reads to remaining buffer capacity upfront
to avoid over-reading from UART.
2026-02-07 00:22:00 +01:00
J. Nick Koston
8f6e1abbce Check read_array return value in drain_rx_buffer_ 2026-02-07 00:18:51 +01:00
J. Nick Koston
c77d70c093 [tuya] Batch UART reads to reduce per-loop overhead
Replace byte-at-a-time read_byte() calls with batched read_array()
in loop(). Each read_byte() internally chains through
read_array(data, 1) -> check_read_timeout_(1) -> available(),
resulting in ~3 UART driver calls per byte. Batching into a 64-byte
stack buffer reduces this to ~3 calls per loop iteration regardless
of how many bytes are available.
2026-02-07 00:17:26 +01:00
J. Nick Koston
25762c62f8 [dsmr] Batch UART reads to reduce per-loop overhead
Replace byte-at-a-time read() calls with batched read_array() in all
four UART read sites: receive_telegram_(), receive_encrypted_telegram_(),
and two drain loops. Each read() internally chains through
read_array(data, 1) -> check_read_timeout_(1) -> available(), resulting
in ~3 UART driver calls per byte. Batching into a 64-byte stack buffer
reduces this to ~3 calls per batch regardless of byte count.

Extract drain_rx_buffer_() helper to deduplicate the two drain sites
in ready_to_request_data_() and stop_requesting_data_().
2026-02-07 00:11:50 +01:00
J. Nick Koston
441ec35d9f [seeed_mr24hpc1/mr60fda2/mr60bha2] Batch UART reads to reduce per-loop overhead
Replace byte-at-a-time read_byte() calls with batched read_array()
in all three Seeed MR sensor components. Each read_byte() internally
chains through read_array(data, 1) -> check_read_timeout_(1) ->
available(), resulting in ~3 UART driver calls per byte. Batching
into a 64-byte stack buffer reduces this to ~3 calls per loop
iteration regardless of how many bytes are available.
2026-02-07 00:07:44 +01:00
J. Nick Koston
33c831dbb8 Update esphome/components/nextion/nextion.cpp
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-07 00:07:05 +01:00
J. Nick Koston
38aeb9be37 [pylontech] Batch UART reads to reduce loop overhead 2026-02-07 00:04:22 +01:00
J. Nick Koston
6b7c52799d [nextion] Batch UART reads to reduce loop overhead 2026-02-07 00:02:10 +01:00
J. Nick Koston
f19bb2cd0a [modbus] Batch UART reads to reduce loop overhead 2026-02-06 23:59:38 +01:00
J. Nick Koston
26c98a1e25 [ld2420] Batch UART reads to reduce loop overhead 2026-02-06 23:54:52 +01:00
J. Nick Koston
8a2c5407d8 Merge branch 'ld2450_batch_read' into integration 2026-02-06 23:40:24 +01:00
J. Nick Koston
52a039585d Merge branch 'ld2410_batch_read' into integration 2026-02-06 23:40:20 +01:00
J. Nick Koston
fd6bd7fb67 Merge branch 'ld2412_batch_read' into integration 2026-02-06 23:40:15 +01:00
J. Nick Koston
b544cf2ffe [ld2410] Batch UART reads to reduce loop overhead 2026-02-06 23:39:31 +01:00
J. Nick Koston
6d1281301f [ld2412] Batch UART reads to reduce loop overhead
Read all available bytes in batches via read_array() instead of
byte-at-a-time read() calls. Each read() internally chains through
read_byte -> read_array(1) -> check_read_timeout_ -> available(),
resulting in 3 UART calls per byte. Batching reduces this
significantly.
2026-02-06 23:36:01 +01:00
J. Nick Koston
901192cca1 [ld2450] Batch UART reads to reduce loop overhead
Read all available bytes in batches via read_array() instead of
byte-at-a-time read() calls. Each read() internally chains through
read_byte -> read_array(1) -> check_read_timeout_ -> available(),
resulting in 3 UART calls per byte. At 256000 baud with ~235 bytes
per loop iteration, this was ~706 UART operations per loop call.
Batching reduces this to ~12.

Measured 33% reduction in loop time (2348ms -> 1577ms per 60s).
2026-02-06 23:33:21 +01:00
J. Nick Koston
3478c68af7 Merge branch 'cse7766_batch_read' into integration 2026-02-06 23:12:14 +01:00
J. Nick Koston
67e7ba4812 handle unlikely 2026-02-06 23:12:00 +01:00
J. Nick Koston
981c132cf4 Merge branch 'cse7766_batch_read' into integration 2026-02-06 23:07:21 +01:00
J. Nick Koston
572376091e loop 2026-02-06 23:07:02 +01:00
J. Nick Koston
803e73fdec Merge branch 'cse7766_batch_read' into integration 2026-02-06 22:59:59 +01:00
J. Nick Koston
e7c9808b87 [cse7766] Batch UART reads to reduce loop overhead 2026-02-06 22:53:31 +01:00
J. Nick Koston
82eb8e3492 Merge branch 'ssd1306-progmem-tables' into integration 2026-02-06 21:39:50 +01:00
J. Nick Koston
21a5c2891e Merge branch 'i2c-arduino-cswtch' into integration 2026-02-06 21:39:46 +01:00
J. Nick Koston
96289775f2 [i2c] Replace switch with if-else to avoid CSWTCH table in RAM
Replace the Wire status-to-ErrorCode switch with if-else to prevent
the compiler from generating a 6-byte lookup table in DRAM on ESP8266.
2026-02-06 21:38:41 +01:00
J. Nick Koston
3e4269d32a Address review: add SSD1306_MODEL_COUNT sentinel and bounds checks
- Add SSD1306_MODEL_COUNT sentinel to enum for compile-time table size validation
- Replace 14 individual static_asserts with table size checks against SSD1306_MODEL_COUNT
- Add bounds checks in get_height_internal()/get_width_internal() to preserve default return 0
2026-02-06 21:37:51 +01:00
J. Nick Koston
bd6d43de52 Merge branch 'ssd1306-progmem-tables' into integration 2026-02-06 21:28:11 +01:00
J. Nick Koston
8da986d41a [ssd1306_base] Move switch tables to PROGMEM with lookup tables
Replace three compiler-generated switch tables (CSWTCH) with PROGMEM
lookup tables, saving 84 bytes of DRAM on ESP8266.

- model_str_(): 56B string pointer table → PROGMEM_STRING_TABLE
- get_height_internal(): 14B byte table → PROGMEM struct array
- get_width_internal(): 14B byte table → PROGMEM struct array

Width and height use a single ModelDimensions struct array for
maintainability. All 14 enum values verified with static_assert.
2026-02-06 21:26:10 +01:00
tronikos
eb7aa3420f Add target_temperature to the template water heater (#13661)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-02-06 21:23:42 +01:00
J. Nick Koston
4fb851954b Merge branch 'debug_progmem' into integration 2026-02-06 21:10:55 +01:00
J. Nick Koston
d1399d9145 [debug] Move ESP8266 switch tables to flash with PROGMEM_STRING_TABLE 2026-02-06 21:10:23 +01:00
J. Nick Koston
ba40fbc53d Merge remote-tracking branch 'origin/mqtt-custom-topic-no-alloc' into integration 2026-02-06 21:01:53 +01:00
J. Nick Koston
c8d4d870e8 [mqtt] Use .c_str() for StringRef payloads to match non-allocating publish overload 2026-02-06 21:00:53 +01:00
J. Nick Koston
fcb458e449 Merge branch 'mqtt-discovery-topic-no-alloc' into integration 2026-02-06 20:57:47 +01:00
J. Nick Koston
48a8f753f5 Merge branch 'mqtt-custom-topic-no-alloc' into integration 2026-02-06 20:57:40 +01:00
J. Nick Koston
6293f3110c Merge branch 'mqtt-friendly-name-const-ref' into integration 2026-02-06 20:57:35 +01:00
J. Nick Koston
ce34b37e02 Merge branch 'mqtt-on-log-no-heap' into integration 2026-02-06 20:57:29 +01:00
J. Nick Koston
b454cac1dc [mqtt] Use stack buffer for discovery topic to avoid heap allocation 2026-02-06 20:48:18 +01:00
J. Nick Koston
a9a943b858 [mqtt] Add zero-allocation topic getters to MQTT_COMPONENT_CUSTOM_TOPIC macro
Add _to() variants that write into a stack buffer and return StringRef,
avoiding heap allocation on every publish. Update publish_state() in
climate (11 allocations eliminated), fan (3), cover (2), and valve (1)
to use the new stack-based getters.

The allocating getters are retained for setup-time paths (subscribe,
send_discovery, dump_config) where the allocation is one-time.
2026-02-06 20:41:51 +01:00
J. Nick Koston
85c1660edf [mqtt] Return friendly_name_() by const reference to avoid string copies 2026-02-06 20:38:33 +01:00
J. Nick Koston
664baac09e [mqtt] Avoid heap allocation in on_log by using const char* publish overload
The on_log callback was constructing a temporary MQTTMessage struct which
copied the log_message_ topic string and created a new std::string from
the message buffer on every log line. This is called frequently at runtime
and contributes to heap fragmentation.

Use the const char* publish overload directly to avoid both allocations.
2026-02-06 20:32:04 +01:00
J. Nick Koston
b247e4a213 Merge remote-tracking branch 'upstream/dev' into integration 2026-02-06 20:12:59 +01:00
J. Nick Koston
86f91eed2f [mqtt] Move switch string tables to PROGMEM_STRING_TABLE (#13802)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-02-06 19:30:05 +01:00
J. Nick Koston
ea7aeb2507 Merge branch 'uart_partity' into integration 2026-02-06 19:25:08 +01:00
J. Nick Koston
61b2416a7c Merge branch 'bmp_progmem' into integration 2026-02-06 19:24:52 +01:00
J. Nick Koston
41cecbfb0f [template] Convert alarm sensor type to PROGMEM_STRING_TABLE and narrow enum to uint8_t (#13804) 2026-02-06 18:22:26 +00:00
J. Nick Koston
e3b547b1b0 [bmp3xx_base/bmp581_base] Convert oversampling and IIR filter strings to PROGMEM_STRING_TABLE 2026-02-06 19:22:11 +01:00
J. Nick Koston
93dd8d0e4f Merge branch 'rtttl_progmem' into integration 2026-02-06 19:19:07 +01:00
J. Nick Koston
12b3e95ed3 [rtttl] Convert state_to_string to PROGMEM_STRING_TABLE 2026-02-06 19:18:27 +01:00
J. Nick Koston
a94c0f745f Merge branch 'sprinkler_progmem' into integration 2026-02-06 19:16:49 +01:00
J. Nick Koston
7fc3b1ddfe [sprinkler] Convert state and request origin strings to PROGMEM_STRING_TABLE 2026-02-06 19:16:13 +01:00
J. Nick Koston
ca33d4111b [uart] Convert parity_to_str to PROGMEM_STRING_TABLE 2026-02-06 19:11:10 +01:00
Jonathan Swoboda
9315da79bc [core] Add missing requests dependency to requirements.txt (#13803)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 13:03:16 -05:00
J. Nick Koston
4325c86b0a Merge branch 'template_alarm_progmem' into integration 2026-02-06 19:01:49 +01:00
J. Nick Koston
e3b9c9362d [template] Convert alarm sensor type to PROGMEM_STRING_TABLE and narrow enum to uint8_t 2026-02-06 18:58:19 +01:00
J. Nick Koston
11882bc6fa Merge branch 'mqtt_progmem_table' into integration 2026-02-06 18:54:52 +01:00
PolarGoose
155447f541 [dsmr] Fix issue with parsing lines like 1-0:0.2.0((ER11)) (#13780) 2026-02-06 12:53:59 -05:00
J. Nick Koston
b908d6ad04 [mqtt] Move switch string tables to PROGMEM_STRING_TABLE 2026-02-06 18:53:33 +01:00
J. Nick Koston
238e40966f [light] Move CSWTCH lookup table to PROGMEM in get_suitable_color_modes_mask_ (#13801) 2026-02-06 17:33:26 +00:00
J. Nick Koston
29e73ef317 Merge remote-tracking branch 'upstream/dev' into integration 2026-02-06 18:25:47 +01:00
J. Nick Koston
f9192b5f75 [wifi] Avoid jump tables in LOG_STR switch statements to save ESP8266 RAM (#13799) 2026-02-06 18:20:46 +01:00
J. Nick Koston
2917057da8 [analyze-memory] Trace CSWTCH switch table symbols to source components (#13798)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-02-06 18:08:30 +01:00
J. Nick Koston
6464fcbc14 Merge branch 'light_cswitch' into integration 2026-02-06 18:03:40 +01:00
J. Nick Koston
305e0172f3 [light] Move CSWTCH lookup table to PROGMEM in get_suitable_color_modes_mask_ 2026-02-06 17:58:30 +01:00
J. Nick Koston
378f608aad Merge branch 'cswitch_analyzer' into integration 2026-02-06 17:41:30 +01:00
J. Nick Koston
9712c1062f Merge remote-tracking branch 'upstream/dev' into integration 2026-02-06 17:41:17 +01:00
J. Nick Koston
c7c9ffe7e1 [light] Convert color_mode_to_human to PROGMEM_STRING_TABLE using to_bit() (#13797) 2026-02-06 17:38:03 +01:00
J. Nick Koston
368ef5687b [update] Move update_state_to_string to update component and convert to PROGMEM_STRING_TABLE (#13796)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-06 17:37:41 +01:00
J. Nick Koston
b7dc975331 [core] Convert entity string lookups to PROGMEM_STRING_TABLE (#13794) 2026-02-06 17:37:19 +01:00
J. Nick Koston
44f308502e [gpio] Convert interrupt_type_to_string to PROGMEM_STRING_TABLE (#13795)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-06 17:37:02 +01:00
J. Nick Koston
ec477801ca [wifi] Defer ESP8266 WiFi listener callbacks from system context to main loop (#13789) 2026-02-06 16:23:19 +00:00
J. Nick Koston
16ef68ea4d bot review 2026-02-06 17:10:14 +01:00
J. Nick Koston
21c4fb3880 bot review 2026-02-06 17:09:46 +01:00
J. Nick Koston
82587fe854 bot review 2026-02-06 17:07:47 +01:00
J. Nick Koston
1d627b47b7 Merge remote-tracking branch 'origin/wifi_no_jump_tables' into integration 2026-02-06 16:52:15 +01:00
J. Nick Koston
f71d6c615a missed one 2026-02-06 16:51:58 +01:00
J. Nick Koston
9272967096 Merge remote-tracking branch 'origin/wifi_no_jump_tables' into integration 2026-02-06 16:50:09 +01:00
J. Nick Koston
9914293777 Merge branch 'cswitch_analyzer' into integration 2026-02-06 16:49:58 +01:00
J. Nick Koston
0e3e060fce [wifi] Avoid jump tables in LOG_STR switch statements to save ESP8266 RAM 2026-02-06 16:49:24 +01:00
J. Nick Koston
c892174378 [analyze-memory] Trace CSWTCH switch table symbols to source components 2026-02-06 16:48:08 +01:00
J. Nick Koston
bbfa4c69af Merge branch 'light_progmem_str' into integration 2026-02-06 16:21:44 +01:00
J. Nick Koston
6d563be2b4 [light] Convert color_mode_to_human to PROGMEM_STRING_TABLE using to_bit() 2026-02-06 16:20:50 +01:00
J. Nick Koston
14ce07b9cb Merge branch 'gpio_progmem_table' into integration 2026-02-06 15:57:40 +01:00
J. Nick Koston
783273cdab Merge branch 'update_progmem_table' into integration 2026-02-06 15:57:34 +01:00
J. Nick Koston
a3e17acc65 Merge branch 'entity_progmem_table' into integration 2026-02-06 15:57:30 +01:00
J. Nick Koston
e130727ed6 Merge remote-tracking branch 'origin/dev' into integration 2026-02-06 15:57:22 +01:00
J. Nick Koston
c3622ef7fb [http_request] Fix chunked transfer encoding on Arduino platforms (#13790) 2026-02-06 15:52:41 +01:00
J. Nick Koston
7018a3b3ba cleanup lock 2026-02-06 15:49:09 +01:00
J. Nick Koston
8cd4d07fe7 [update] Move update_state_to_string to update component and convert to PROGMEM_STRING_TABLE 2026-02-06 15:47:21 +01:00
J. Nick Koston
3f4f438445 [gpio] Convert interrupt_type_to_string to PROGMEM_STRING_TABLE 2026-02-06 15:42:53 +01:00
J. Nick Koston
10821ec078 [core] Convert entity string lookups to PROGMEM_STRING_TABLE 2026-02-06 15:34:07 +01:00
J. Nick Koston
e4ad2082bc [core] Add PROGMEM_STRING_TABLE macro for flash-optimized string lookups (#13659)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-06 15:26:16 +01:00
J. Nick Koston
b1d2eaa54b Merge remote-tracking branch 'upstream/dev' into integration 2026-02-06 12:59:35 +01:00
Andrew Rankin
7afd0eb1aa [esp32_ble] include sdkconfig.h before ESP-Hosted preprocessor guards (#13787)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 11:36:55 +00:00
J. Nick Koston
16b4aba3cf Merge branch 'ard_chunked_http_request' into integration 2026-02-06 12:02:03 +01:00
J. Nick Koston
89330aa157 refaxctor 2026-02-06 11:45:15 +01:00
J. Nick Koston
de84169701 braces 2026-02-06 11:31:24 +01:00
J. Nick Koston
d6466bdbc7 Merge branch 'ard_chunked_http_request' into integration 2026-02-06 11:23:51 +01:00
J. Nick Koston
548b434f49 [http_request] Fix chunked transfer encoding on Arduino platforms 2026-02-06 11:22:52 +01:00
Clyde Stubbs
112a2c5d92 [const] Move some constants to common (#13788) 2026-02-06 20:11:08 +11:00
Jonathan Swoboda
fef5d3f88f [rdm6300] Add ID-20LA compatibility by skipping CR/LF bytes (#13779)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 04:10:22 -05:00
Jonathan Swoboda
8e461db301 [ota] Fix CLI upload option shown when only http_request platform configured (#13784)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 04:09:48 -05:00
dependabot[bot]
6decdfad26 Bump github/codeql-action from 4.32.1 to 4.32.2 (#13781)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-06 10:05:10 +01:00
J. Nick Koston
1bf04d22dc Merge branch 'wifi_callbacks_main_loop_8266' into integration 2026-02-06 09:58:20 +01:00
J. Nick Koston
c4a4a86cff tidy 2026-02-06 09:56:42 +01:00
Jonathan Swoboda
c7729cb019 [esp32] Use underscores in arduino_libs_stub folder name (#13785)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 09:51:13 +01:00
J. Nick Koston
8d8bd21cda cleanup guards 2026-02-06 09:46:58 +01:00
J. Nick Koston
c80c06cabd cleanup guards 2026-02-06 09:45:59 +01:00
J. Nick Koston
7a7cc66141 tweak members 2026-02-06 09:39:22 +01:00
J. Nick Koston
107e470410 de-dupe 2026-02-06 09:37:07 +01:00
J. Nick Koston
3173283166 de-dupe 2026-02-06 09:36:01 +01:00
J. Nick Koston
2a74dd27e1 adjust 2026-02-06 09:33:21 +01:00
J. Nick Koston
2573863d82 adjust 2026-02-06 09:31:06 +01:00
J. Nick Koston
8fa94dbdf3 merge 2026-02-06 09:25:12 +01:00
J. Nick Koston
28364970de Merge remote-tracking branch 'upstream/dev' into wifi_callbacks_main_loop_8266 2026-02-06 09:18:31 +01:00
Marek Beran
ed4f00d4a3 [vbus] Add DeltaSol BS/2 support with sensors and binary sensors (#13762) 2026-02-05 23:11:14 -08:00
J. Nick Koston
1f4c56f6a1 Merge remote-tracking branch 'upstream/dev' into integration 2026-02-05 21:24:00 +01:00
J. Nick Koston
55ef8393af [api] Remove is_single parameter and fix batch buffer preparation (#13773)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-05 15:19:03 +01:00
J. Nick Koston
c3b823552f Merge branch 'get_rid_of_is_single' into integration 2026-02-05 15:00:40 +01:00
Jonathan Swoboda
081f953dc3 [core] Add capacity check to register_component_ (#13778)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 14:00:16 +00:00
J. Nick Koston
f4e410f47f [ci] Block new scanf() usage to prevent ~9.8KB flash bloat (#13657) 2026-02-06 02:56:43 +13:00
J. Nick Koston
da4fc73c06 tweak 2026-02-05 14:44:13 +01:00
J. Nick Koston
3859f48ddd make clang-tidy happy 2026-02-05 14:38:12 +01:00
J. Nick Koston
11bdc66a35 make clang-tidy happy 2026-02-05 14:37:25 +01:00
J. Nick Koston
857c8be1c9 Revert "tidy"
This reverts commit 9d43a9326e.
2026-02-05 14:32:05 +01:00
schrob
bbdb202e2c [epaper_spi] Refactor initialise for future use (#13774) 2026-02-06 02:26:47 +13:00
J. Nick Koston
9d43a9326e tidy 2026-02-05 14:24:58 +01:00
J. Nick Koston
35dc54f242 cleanup 2026-02-05 14:13:54 +01:00
J. Nick Koston
d405af6db7 Merge remote-tracking branch 'origin/get_rid_of_is_single' into get_rid_of_is_single 2026-02-05 14:08:35 +01:00
J. Nick Koston
282b475532 tweak 2026-02-05 14:08:23 +01:00
J. Nick Koston
8300e9ca87 Update esphome/components/api/api_connection.cpp
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-05 14:02:25 +01:00
J. Nick Koston
ef6f93a40c cleanup 2026-02-05 13:54:00 +01:00
J. Nick Koston
f75db5106f reduce 2026-02-05 13:44:08 +01:00
J. Nick Koston
0d09366608 reduce 2026-02-05 13:41:21 +01:00
Jonathan Swoboda
9ea8461440 [esp32] Remove specific claims from framework migration message (#13777)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 11:41:17 +00:00
J. Nick Koston
55116a7462 fixes 2026-02-05 12:09:03 +01:00
J. Nick Koston
161f5eb731 tweak 2026-02-05 12:02:57 +01:00
J. Nick Koston
85995975d8 tweak 2026-02-05 12:02:05 +01:00
Jonathan Swoboda
ed8c0dc99d [esp32] Skip downloading precompiled Arduino libs (#13775)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 05:55:08 -05:00
J. Nick Koston
92fcf2f78c Merge remote-tracking branch 'swoboda1337/arduino-libs-stub' into integration 2026-02-05 11:41:14 +01:00
J. Nick Koston
155d3f8cd8 Merge branch 'get_rid_of_is_single' into integration 2026-02-05 11:37:12 +01:00
J. Nick Koston
b9bb444bf0 Merge remote-tracking branch 'upstream/dev' into integration 2026-02-05 11:37:00 +01:00
J. Nick Koston
4337a4cd0d fix double prep 2026-02-05 11:23:03 +01:00
J. Nick Koston
e5bd6865ca Revert "buffer has to be prepared in advance anyways so set flag there"
This reverts commit caf0fa84af.
2026-02-05 11:19:28 +01:00
J. Nick Koston
87dc930dcf Revert "outline send_message_smart_ to prevent it from being inlined at every call site"
This reverts commit 3b85680ad7.
2026-02-05 11:19:26 +01:00
Jonathan Swoboda
55fb382445 Cleanup 2026-02-05 05:13:40 -05:00
Jonathan Swoboda
aa20b23203 [esp32] DRY Arduino package name constants
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 05:11:06 -05:00
Jonathan Swoboda
940029c844 [esp32] Extract Arduino package names to constants
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 05:08:59 -05:00
J. Nick Koston
3b85680ad7 outline send_message_smart_ to prevent it from being inlined at every call site 2026-02-05 10:59:39 +01:00
Jonathan Swoboda
7babd079ea Merge remote-tracking branch 'upstream/dev' into arduino-libs-stub
# Conflicts:
#	esphome/components/esp32/__init__.py
2026-02-05 04:57:22 -05:00
J. Nick Koston
be44d4801f [esp32] Reduce Arduino build size by 44% and build time by 36% (#13623) 2026-02-05 10:52:43 +01:00
Jonathan Swoboda
7223a1bac8 [esp32] Skip downloading precompiled Arduino libs
Create a stub package for framework-arduinoespressif32-libs to avoid
downloading ~2GB of precompiled libraries that aren't needed when
building from source.

The stub contains minimal package.json and tools.json files that
satisfy PlatformIO's package manager without the actual library
contents. The version is derived from the IDF version.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 04:20:44 -05:00
J. Nick Koston
caf0fa84af buffer has to be prepared in advance anyways so set flag there 2026-02-05 10:15:57 +01:00
J. Nick Koston
1e2cdf3feb [api] Remove redundant is_single parameter from message encoding functions 2026-02-05 09:51:37 +01:00
Jas Strong
7bd8b08e16 [rd03d] Revert incorrect field order swap (#13769)
Co-authored-by: jas <jas@asspa.in>
2026-02-05 03:06:52 -05:00
J. Nick Koston
c27870b15d [web_server] Add some more missing ESPHOME_F macros (#13748) 2026-02-05 06:36:40 +01:00
J. Nick Koston
ed3fb5a1b5 Merge branch 'esp32_ard_compile_time' into integration 2026-02-05 06:29:57 +01:00
J. Nick Koston
08506dcee8 use writer rmtree 2026-02-05 06:28:12 +01:00
J. Nick Koston
25c0073b2d [web_server] Fix ESP8266 watchdog panic by deferring actions to main loop (#13765) 2026-02-05 06:20:04 +01:00
J. Nick Koston
a556824875 [logger] Refactor to reduce code duplication and flash size (#13750) 2026-02-05 06:19:13 +01:00
J. Nick Koston
89fc5ebc97 Fix bare hostname ping fallback in dashboard (#13760) 2026-02-05 06:18:03 +01:00
schrob
67dfa5e2bc [epaper_spi] Validate BUSY pin as input instead of output (#13764) 2026-02-04 23:39:03 +00:00
tomaszduda23
13ddf267bb [nrf52,zigbee] update warnings (#13761) 2026-02-04 15:18:24 -05:00
J. Nick Koston
7366259c64 Merge branch '8266_watchdog_web_server' into integration 2026-02-04 21:16:17 +01:00
J. Nick Koston
1b90ccde27 [web_server] Fix ESP8266 watchdog panic by deferring actions to main loop 2026-02-04 21:14:25 +01:00
Jonathan Swoboda
43d9d6fe64 [esp32] Restore develop branch for dev platform version, bump platformio (#13759)
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-04 15:12:42 -05:00
Copilot
4a579700a0 [cover] Add operation-based triggers and fix repeated trigger firing (#13471) 2026-02-05 06:52:14 +11:00
Jesse Hills
c1b412d5f3 Merge branch 'release' into dev 2026-02-04 17:56:36 +01:00
J. Nick Koston
f3f9911b62 Merge branch 'bare_hostname_ping_fallback' into integration 2026-02-04 17:54:52 +01:00
J. Nick Koston
2c874167a1 Fix bare hostname ping fallback in dashboard 2026-02-04 17:49:09 +01:00
J. Nick Koston
becb6559f1 [components] Remove redundant setup priority overrides that duplicate default (#13745) 2026-02-04 10:48:41 -06:00
J. Nick Koston
92cd779c19 Merge remote-tracking branch 'upstream/dev' into integration 2026-02-04 17:08:56 +01:00
functionpointer
36f2654fa6 [pylontech] Refactor parser to support new firmware version and SysError (#12300) 2026-02-04 17:06:59 +01:00
Jonathan Swoboda
ba18a8b3e3 [adc] Fix ESP32-C2 ADC calibration to use line fitting (#13756)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 11:44:17 +00:00
Jesse Hills
ab8ac72c4f Merge pull request #13757 from esphome/bump-2026.1.4
2026.1.4
2026-02-05 00:01:14 +13:00
Jesse Hills
1b3c9aa98e Bump version to 2026.1.4 2026-02-04 11:01:32 +01:00
Samuel Sieb
bafbd4235a [ultrasonic] adjust timeouts and bring the parameter back (#13738)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2026-02-04 11:01:31 +01:00
J. Nick Koston
900aab45f1 [wifi] Fix wifi.connected condition returning false in connect state listener automations (#13733) 2026-02-04 11:01:29 +01:00
J. Nick Koston
bc7dba2bb6 Merge branch 'copilot/update-cover-component-triggers' into integration 2026-02-04 10:58:55 +01:00
J. Nick Koston
bc41d25657 [cse7766] Fix power reading stuck when load switches off (#13734) 2026-02-04 10:56:42 +01:00
J. Nick Koston
094d64f872 [http_request] Fix requests taking full timeout when response is already complete (#13649) 2026-02-04 10:56:42 +01:00
J. Nick Koston
b085585461 [core] Add missing uint32_t ID overloads for defer() and cancel_defer() (#13720) 2026-02-04 10:56:42 +01:00
rwrozelle
49ef4e00df [mqtt] resolve warnings related to use of ip.str() (#13719) 2026-02-04 10:56:42 +01:00
Jonathan Swoboda
8314ad9ca0 [max7219] Allocate buffer in constructor (#13660)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 10:56:42 +01:00
J0k3r2k1
5544f0d346 [mipi_spi] Fix log_pin() FlashStringHelper compatibility (#13624)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-02-04 10:56:34 +01:00
J. Nick Koston
2cc89c8888 does not match float 2026-02-04 10:47:27 +01:00
J. Nick Koston
4a45fe9849 cleanup 2026-02-04 10:38:54 +01:00
J. Nick Koston
e26996be93 cleanup 2026-02-04 10:38:04 +01:00
J. Nick Koston
ef3fcf6635 cleanup 2026-02-04 10:37:06 +01:00
J. Nick Koston
4704de51be cleanup duplicate code 2026-02-04 10:33:12 +01:00
J. Nick Koston
b633444e11 cleanup duplicate code 2026-02-04 10:32:09 +01:00
J. Nick Koston
dc11bb8709 cleanup duplicate code 2026-02-04 10:29:59 +01:00
Samuel Sieb
5dc8bfe95e [ultrasonic] adjust timeouts and bring the parameter back (#13738)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2026-02-04 04:29:27 -05:00
dependabot[bot]
4d05cd3059 Bump ruff from 0.14.14 to 0.15.0 (#13752)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-02-04 09:24:05 +00:00
J. Nick Koston
c7dfdfc6d7 cleanup duplicate code 2026-02-04 10:22:02 +01:00
J. Nick Koston
2dc2465b13 cleanup duplicate code 2026-02-04 10:20:06 +01:00
J. Nick Koston
f2fdc476b3 cleanup duplicate code 2026-02-04 10:17:22 +01:00
J. Nick Koston
0714c5b7b8 cleanup duplicate code 2026-02-04 10:16:07 +01:00
J. Nick Koston
21815ddfa0 avoid losing type safety 2026-02-04 10:15:05 +01:00
J. Nick Koston
6eefff4e29 Merge branch 'dev' into copilot/update-cover-component-triggers 2026-02-04 10:12:49 +01:00
J. Nick Koston
2541ec1565 [wifi] Fix wifi.connected condition returning false in connect state listener automations (#13733) 2026-02-04 21:42:13 +13:00
J. Nick Koston
e114050222 Merge branch 'logger_8266' into integration 2026-02-04 09:32:31 +01:00
J. Nick Koston
43b6e7962d Merge branch 'dependabot/pip/ruff-0.15.0' into integration 2026-02-04 09:31:37 +01:00
J. Nick Koston
f5f5e2bdae cleanup 2026-02-04 09:28:18 +01:00
pre-commit-ci-lite[bot]
c05f0589fc [pre-commit.ci lite] apply automatic fixes 2026-02-04 08:22:15 +00:00
J. Nick Koston
6b91ba5353 ruff match 2026-02-04 09:19:04 +01:00
J. Nick Koston
5ef8a90aa0 api change not needed now 2026-02-04 08:47:35 +01:00
J. Nick Koston
c9dfaa36b4 simplify design 2026-02-04 08:06:21 +01:00
J. Nick Koston
a74940f1c0 tidy 2026-02-04 07:48:51 +01:00
J. Nick Koston
4da67712a9 tidy 2026-02-04 07:47:21 +01:00
J. Nick Koston
4d9b7c47f8 tidy 2026-02-04 07:42:40 +01:00
J. Nick Koston
3d43b740fd tweak 2026-02-04 07:33:53 +01:00
J. Nick Koston
eeb24b67e1 tweak 2026-02-04 07:29:34 +01:00
J. Nick Koston
4bf9cc6546 tweak 2026-02-04 07:26:34 +01:00
J. Nick Koston
2de3b6aed7 tweak 2026-02-04 07:24:44 +01:00
J. Nick Koston
efd17f78bf tweak 2026-02-04 07:21:14 +01:00
J. Nick Koston
54154e7c68 tweak 2026-02-04 07:18:46 +01:00
J. Nick Koston
935d496c70 tweak 2026-02-04 07:10:28 +01:00
J. Nick Koston
c4994d4f62 tweak 2026-02-04 07:04:09 +01:00
J. Nick Koston
b39b2fbe23 fix 2026-02-04 06:55:06 +01:00
J. Nick Koston
d9261ae66d tweak 2026-02-04 06:40:09 +01:00
J. Nick Koston
7f17b90fb9 tweak 2026-02-04 06:38:33 +01:00
J. Nick Koston
d2e9e8ebd6 tweak 2026-02-04 06:37:07 +01:00
J. Nick Koston
9758b15508 tweak 2026-02-04 06:27:16 +01:00
J. Nick Koston
b2cc98e083 tweak 2026-02-04 06:23:05 +01:00
J. Nick Koston
f27c80cbe4 tweak 2026-02-04 06:20:39 +01:00
J. Nick Koston
4cc2c39a19 tweak 2026-02-04 06:15:22 +01:00
J. Nick Koston
159e2bed69 tweak 2026-02-04 06:15:10 +01:00
J. Nick Koston
54c62428ae tweak 2026-02-04 06:14:04 +01:00
J. Nick Koston
70debb1c98 wip 2026-02-04 06:04:27 +01:00
J. Nick Koston
f0199d5de9 wip 2026-02-04 05:57:38 +01:00
J. Nick Koston
cf26ca6043 wip 2026-02-04 05:56:36 +01:00
J. Nick Koston
a5869c2a9c wip 2026-02-04 05:50:41 +01:00
Jonathan Swoboda
95f39149d7 [rtttl] Fix dotted note parsing order to match RTTTL spec (#13722)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 17:28:59 -05:00
clydebarrow
be1542cc8a Revert change of logging level 2026-02-04 09:04:32 +11:00
clydebarrow
0a5abac71b Merge branch 'dev' of https://github.com/esphome/esphome into copilot/update-cover-component-triggers 2026-02-04 09:03:50 +11:00
dependabot[bot]
1428853e5e Bump ruff from 0.14.14 to 0.15.0
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.14.14 to 0.15.0.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/0.14.14...0.15.0)

---
updated-dependencies:
- dependency-name: ruff
  dependency-version: 0.15.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-03 19:22:46 +00:00
Jonathan Swoboda
e6bae1a97e [adc] Add ESP32-C2 support for curve fitting calibration (#13749)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 11:16:13 -05:00
J. Nick Koston
068fc3476b [logger] Extract shared helpers to reduce code duplication 2026-02-03 17:08:25 +01:00
J. Nick Koston
f11b8615da [cse7766] Fix power reading stuck when load switches off (#13734) 2026-02-04 05:03:02 +13:00
J. Nick Koston
5d4bde98dc [mqtt] Refactor state publishing with dedicated enum-to-string helpers (#13544)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-02-04 04:56:48 +13:00
J. Nick Koston
b8b072cf86 [web_server_idf] Add const char* overloads for getParam/hasParam to avoid temporary string allocations (#13746) 2026-02-04 04:43:27 +13:00
J. Nick Koston
18f7e0e6b3 [pulse_counter][hlw8012] Fix ESP-IDF build by re-enabling legacy driver component (#13747) 2026-02-03 15:42:45 +00:00
J. Nick Koston
a9f3d6a2d9 Merge branch 'web_server_missing_macros' into integration 2026-02-03 16:38:37 +01:00
J. Nick Koston
7755014a7d [web_server] Add some more missing ESPHOME_F macros
There were still a few strings that could be moved to flash on ESP8266
2026-02-03 16:37:46 +01:00
J. Nick Koston
8d0ce49eb4 [api] Eliminate intermediate buffers in protobuf dump helpers (#13742)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-03 16:34:15 +01:00
J. Nick Koston
c93a470e96 Merge branch 'hlw8012_pulse_counter_legacy_driver' into integration 2026-02-03 16:30:13 +01:00
J. Nick Koston
527129f17c [pulse_counter][hlw8012] Fix ESP-IDF build by re-enabling legacy driver component 2026-02-03 16:27:01 +01:00
J. Nick Koston
e23c75d31e Merge branch 'idf_avoids_some_alloc_strings' into integration 2026-02-03 15:31:53 +01:00
J. Nick Koston
f021df399e [web_server_idf] Add const char* overloads for getParam/hasParam to avoid temporary string allocations 2026-02-03 15:30:40 +01:00
J. Nick Koston
ad7ecfa9cb Merge branch 'remove_useless_overrides' into integration 2026-02-03 15:13:06 +01:00
J. Nick Koston
39472ccb41 [components] Remove redundant setup priority overrides that duplicate default 2026-02-03 15:10:03 +01:00
J. Nick Koston
2f77d50e0f Merge remote-tracking branch 'upstream/dev' into integration 2026-02-03 14:38:26 +01:00
Tomer Shalev
21bd0ff6aa [mqtt] Stop sending deprecated color_mode and brightness in light discovery (fixes #13666) (#13667) 2026-02-03 14:37:27 +01:00
J. Nick Koston
d0017ded5b [template] Split TemplateSelect into TemplateSelectWithSetAction to save RAM (#13685)
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
2026-02-03 11:48:31 +00:00
J. Nick Koston
2cc4b551e7 Merge remote-tracking branch 'origin/no_intermed_buffers_api_vv' into integration 2026-02-03 12:30:36 +01:00
J. Nick Koston
77428b79c0 Update esphome/components/api/proto.h
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-03 12:30:05 +01:00
J. Nick Koston
f4d7d06c41 [dlms_meter] Rename test UART package key to match directory name (#13743) 2026-02-03 11:23:05 +00:00
J. Nick Koston
b65f6ab7b4 Merge branch 'no_intermed_buffers_api_vv' into integration 2026-02-03 12:17:23 +01:00
J. Nick Koston
280297cb97 Merge branch 'dlms_meter_package_name' into integration 2026-02-03 12:16:35 +01:00
J. Nick Koston
26419931e2 [dlms_meter] Rename test UART package key to match directory name 2026-02-03 12:14:17 +01:00
J. Nick Koston
c027d9116f [template] Add additional tests for template select (#13741) 2026-02-03 11:13:03 +00:00
J. Nick Koston
79832e60d0 [api] Eliminate intermediate buffers in protobuf dump helpers 2026-02-03 12:07:15 +01:00
J. Nick Koston
de32116ded Merge branch 'template_select_trigger' into integration 2026-02-03 11:53:53 +01:00
J. Nick Koston
181adb081e Merge branch 'template_select_expand_compile_tests' into integration 2026-02-03 11:51:41 +01:00
J. Nick Koston
d9bf8b7343 Add additional tests for template select 2026-02-03 11:47:36 +01:00
J. Nick Koston
2394ac276c avoid duplicating 2026-02-03 11:39:14 +01:00
Clyde Stubbs
b3e09e5c68 [key_collector] Add text sensor and allow multiple callbacks (#13617) 2026-02-03 21:14:09 +11:00
J. Nick Koston
9fad6d0b7e Merge branch 'wifi_connect_defer' into integration 2026-02-03 07:42:19 +01:00
J. Nick Koston
9ee1a51f1a Merge branch 'cse7766_stuck_off' into integration 2026-02-03 07:42:11 +01:00
J. Nick Koston
cf691a43b2 [cse7766] Fix power reading stuck when load switches off
When the load is switched off and current drops below the chip's
measurable threshold (~50mA), the CSE7766 sets have_power=false
indicating no valid power measurement. The code was not publishing
any value in this case, leaving the power sensor stuck at its last
reading (e.g., 200W) for 10-20 seconds.

This regression was introduced in 2024.2.0 when PR #6180 refactored
the code from an accumulator-based design to direct publishing. The
original code handled this case by incrementing power_counts_ when
have_voltage && !have_power, effectively publishing 0W.

Fixes esphome/esphome#13613
2026-02-03 05:48:47 +01:00
J. Nick Koston
d4110bf650 [lock] Store state strings in flash and avoid heap allocation in set_state (#13729) 2026-02-03 05:29:24 +01:00
J. Nick Koston
4b6f6f21d1 [wifi] Fix wifi.connected condition returning false in connect state listener automations 2026-02-03 05:19:52 +01:00
Andrew Gillis
ff6f7d3248 [mipi_dsi] Add WAVESHARE-ESP32-P4-WIFI6-TOUCH-LCD-7B (#13608) 2026-02-03 14:59:51 +11:00
Clyde Stubbs
0a1fa05c8f Merge branch 'dev' into template_select_trigger 2026-02-03 14:57:02 +11:00
clydebarrow
cb9fbf8970 Fix parameter name; set update_interval to never if no lambda to poll 2026-02-03 14:56:21 +11:00
Roger Fachini
a430b3a426 [speaker.media_player]: Add verbose error message for puremagic parsing (#13725)
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-02-03 03:46:46 +00:00
J. Nick Koston
fbeb0e8e54 [opentherm] Fix ESP-IDF build by re-enabling legacy driver component (#13732) 2026-02-03 03:40:44 +00:00
J. Nick Koston
9d63642bdb [media_player] Store command strings in flash and avoid heap allocation in set_command (#13731) 2026-02-03 04:29:43 +01:00
J. Nick Koston
8cb701e412 [water_heater] Store mode strings in flash and avoid heap allocation in set_mode (#13728) 2026-02-03 04:29:31 +01:00
J. Nick Koston
d41c84d624 [wifi] Conditionally compile on_connect/on_disconnect triggers (#13684) 2026-02-03 04:29:18 +01:00
J. Nick Koston
61ccdc2d9e Merge remote-tracking branch 'upstream/dev' into integration 2026-02-03 04:04:35 +01:00
J. Nick Koston
9f1a427ce2 [preferences] Use static storage for singletons and flash buffer (#13727) 2026-02-03 04:03:52 +01:00
J. Nick Koston
e962bdd06f Revert "[light] Store "none" effect string in flash and avoid heap allocation"
This reverts commit ed1a9fd1e2.
2026-02-03 03:59:21 +01:00
J. Nick Koston
32f0fa5a96 Merge branch 'media_player_set_command_flash' into integration 2026-02-03 03:57:14 +01:00
J. Nick Koston
9b69516ac7 [media_player] Store command strings in flash and avoid heap allocation in set_command 2026-02-03 03:56:42 +01:00
J. Nick Koston
8752ffef93 Merge branch 'light_flash_effect' into integration 2026-02-03 03:54:39 +01:00
J. Nick Koston
ed1a9fd1e2 [light] Store "none" effect string in flash and avoid heap allocation 2026-02-03 03:54:06 +01:00
J. Nick Koston
edf9730c24 Merge branch 'lock_set_flash' into integration 2026-02-03 03:49:19 +01:00
J. Nick Koston
97a6d87c2f [lock] Store state strings in flash and avoid heap allocation in set_state 2026-02-03 03:48:37 +01:00
J. Nick Koston
c3220b04a0 Merge branch 'water_heater_set_flash' into integration 2026-02-03 03:46:58 +01:00
J. Nick Koston
08f651fd38 [water_heater] Store mode strings in flash and avoid heap allocation in set_mode 2026-02-03 03:46:27 +01:00
J. Nick Koston
ae71f07abb [http_request] Fix requests taking full timeout when response is already complete (#13649) 2026-02-03 03:19:38 +01:00
clydebarrow
5a2774876a Use templates to customise classes 2026-02-03 13:14:09 +11:00
J. Nick Koston
6de59fa246 Merge branch 'dev' into esp32_ard_compile_time 2026-02-03 03:12:32 +01:00
J. Nick Koston
ccf5c1f7e9 [esp32] Exclude additional unused IDF components (driver, dac, mcpwm, twai, openthread, ulp) (#13664) 2026-02-03 03:12:12 +01:00
J. Nick Koston
f786b4cd95 Merge branch 'prefs_data_static' into integration 2026-02-03 02:57:41 +01:00
J. Nick Koston
72add75eea [preferences] Use static storage for singletons and flash buffer 2026-02-03 02:56:51 +01:00
J. Nick Koston
fc7328197d Merge remote-tracking branch 'upstream/dev' into integration 2026-02-03 02:28:55 +01:00
dependabot[bot]
efecea9450 Bump github/codeql-action from 4.32.0 to 4.32.1 (#13726)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-03 02:27:34 +01:00
J. Nick Koston
26e4cda610 [logger] Use vsnprintf_P directly for ESP8266 flash format strings (#13716) 2026-02-03 02:25:54 +01:00
Jan Kundrát
a6543d32bd [sx126x] fix maximal payload_length (#13723) 2026-02-02 20:15:18 -05:00
J. Nick Koston
aa6650c86d Merge remote-tracking branch 'upstream/dev' into integration 2026-02-03 01:25:44 +01:00
J. Nick Koston
14e379792c Merge branch 'esp8266-logger-vsnprintf-p' into integration 2026-02-03 01:25:26 +01:00
Jonathan Swoboda
da947d060f [wizard] Use API encryption key instead of deprecated password (#13634)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 19:20:24 -05:00
clydebarrow
ede2f205d3 Merge branch 'template_select_trigger' of https://github.com/esphome/esphome into template_select_trigger 2026-02-03 08:33:25 +11:00
Clyde Stubbs
8cf29c40a9 Merge branch 'dev' into template_select_trigger 2026-02-03 08:33:10 +11:00
clydebarrow
0332cbfdd4 Merge branch 'dev' of https://github.com/esphome/esphome into template_select_trigger 2026-02-03 08:32:40 +11:00
J. Nick Koston
1119003eb5 [core] Add missing uint32_t ID overloads for defer() and cancel_defer() (#13720) 2026-02-02 22:22:11 +01:00
J. Nick Koston
c089d9aeac [esp32_hosted] Replace sscanf with strtol for version parsing (#13658) 2026-02-02 22:21:52 +01:00
J. Nick Koston
4f0894e970 [analyze-memory] Add top 30 largest symbols to report (#13673) 2026-02-02 22:05:39 +01:00
J. Nick Koston
848c237159 [time] Use lazy callback for time sync to save 8 bytes (#13652) 2026-02-02 22:05:27 +01:00
J. Nick Koston
6892805094 [api] Align water_heater_command with standard entity command pattern (#13655) 2026-02-02 22:00:46 +01:00
J. Nick Koston
bd3b7aa50a naming 2026-02-02 21:52:34 +01:00
J. Nick Koston
bce4a9c9ab force in 2026-02-02 17:45:25 +01:00
J. Nick Koston
ae0dc8d21f Merge branch 'esp8266-logger-vsnprintf-p' into integration 2026-02-02 17:40:39 +01:00
J. Nick Koston
9ba295d334 preen 2026-02-02 17:40:26 +01:00
J. Nick Koston
aad3ed3411 Merge branch 'esp8266-logger-vsnprintf-p' into integration 2026-02-02 17:34:51 +01:00
J. Nick Koston
dfcf611a67 Merge upstream/dev into esp32_ard_compile_time
Resolved conflict in ethernet/__init__.py:
- Kept on_connect/on_disconnect automation code from upstream
- Removed Arduino WiFi library addition (handled by selective compilation now)
2026-02-02 17:01:42 +01:00
Roger Fachini
aa8ccfc32b [ethernet] Add on_connect and on_disconnect triggers (#13677)
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-02-02 17:00:11 +01:00
rwrozelle
18991686ab [mqtt] resolve warnings related to use of ip.str() (#13719) 2026-02-02 16:48:08 +01:00
J. Nick Koston
1501db38b1 tweak 2026-02-02 13:28:00 +01:00
J. Nick Koston
bc6d88fabe [logger] Use vsnprintf_P directly for ESP8266 flash format strings
Instead of copying the format string from flash to RAM before
formatting, use vsnprintf_P to read the format string directly
from flash memory.

This eliminates:
- The byte-by-byte copy loop from PROGMEM
- The complex dual-purpose buffer management
- Potential buffer overflow if format string is very long

The new format_body_to_buffer_P_() function is a simple variant
that uses vsnprintf_P instead of vsnprintf.
2026-02-02 07:58:49 +01:00
J. Nick Koston
62f34bea83 [template.output] Avoid heap allocation for triggers (#13709) 2026-02-02 07:36:27 +01:00
J. Nick Koston
6114005952 [template.water_heater] Avoid heap allocation for trigger (#13712) 2026-02-02 07:36:08 +01:00
J. Nick Koston
c0e5ae4298 [template.text] Avoid heap allocation for trigger (#13711) 2026-02-02 07:35:21 +01:00
J. Nick Koston
420de987bc [micro_wake_word] Avoid heap allocation for trigger (#13714) 2026-02-02 07:35:03 +01:00
J. Nick Koston
61e33217cd [cc1101] Avoid heap allocation for trigger (#13715) 2026-02-02 07:34:50 +01:00
J. Nick Koston
b5b9a89561 [light] Avoid heap allocation for AutomationLightEffect trigger (#13713) 2026-02-02 07:34:34 +01:00
J. Nick Koston
bc9fc66225 [template.datetime] Avoid heap allocation for triggers (#13710) 2026-02-02 04:30:46 +00:00
J. Nick Koston
6727fe9040 [remote_transmitter] Avoid heap allocation for triggers (#13708) 2026-02-02 04:18:17 +00:00
J. Nick Koston
7e740208aa Merge branch 'remote_transmitter_no_heap_trigger' into integration 2026-02-02 05:16:53 +01:00
J. Nick Koston
e1c8a3e679 Merge remote-tracking branch 'origin/dev' into integration 2026-02-02 05:16:21 +01:00
J. Nick Koston
81317b2108 Merge branch 'micro_wake_word_no_heap_trigger' into integration 2026-02-02 05:16:13 +01:00
J. Nick Koston
b94b98ad90 Merge branch 'cc1101_no_heap_trigger' into integration 2026-02-02 05:16:07 +01:00
J. Nick Koston
56110d4495 [time_based] Avoid heap allocation for cover triggers (#13703) 2026-02-02 05:15:50 +01:00
J. Nick Koston
1362ff6cba [speaker.media_player] Avoid heap allocation for triggers (#13707) 2026-02-02 05:15:33 +01:00
J. Nick Koston
dbd7401721 [feedback] Avoid heap allocation for cover triggers (#13693) 2026-02-02 05:15:13 +01:00
J. Nick Koston
f1f9c14e93 [cc1101] Avoid heap allocation for trigger 2026-02-02 05:14:20 +01:00
J. Nick Koston
f0801ecac0 [template.lock] Avoid heap allocation for triggers (#13704) 2026-02-02 05:14:11 +01:00
J. Nick Koston
89cc20c423 [micro_wake_word] Avoid heap allocation for trigger 2026-02-02 05:12:18 +01:00
J. Nick Koston
1b1d74d679 Merge remote-tracking branch 'origin/template_datetime_no_heap_triggers' into integration 2026-02-02 05:10:29 +01:00
J. Nick Koston
8141d9838b Merge branch 'base_light_effects_no_heap_trigger' into integration 2026-02-02 05:10:15 +01:00
J. Nick Koston
16e645d8d6 Merge branch 'template_water_heater_no_heap_trigger' into integration 2026-02-02 05:10:09 +01:00
J. Nick Koston
379652f631 [thermostat] Remove dead null checks for triggers (#13706) 2026-02-02 04:10:08 +00:00
J. Nick Koston
69d9085944 Merge remote-tracking branch 'origin/template_text_no_heap_trigger' into integration 2026-02-02 05:10:04 +01:00
J. Nick Koston
7cc83400a4 Merge branch 'template_text_no_heap_trigger' into integration 2026-02-02 05:09:57 +01:00
J. Nick Koston
440de782e8 [light] Avoid heap allocation for AutomationLightEffect trigger 2026-02-02 05:09:17 +01:00
J. Nick Koston
333ac18f53 missed another 2026-02-02 05:08:44 +01:00
J. Nick Koston
37389a9709 [template.water_heater] Avoid heap allocation for trigger 2026-02-02 05:06:47 +01:00
J. Nick Koston
b1dff2b2d8 missed a few 2026-02-02 05:06:03 +01:00
J. Nick Koston
6cfce56f98 [template.text] Avoid heap allocation for trigger 2026-02-02 05:03:30 +01:00
J. Nick Koston
3f90fe0623 Merge branch 'template_datetime_no_heap_triggers' into integration 2026-02-02 05:02:08 +01:00
J. Nick Koston
d95d4afdae Merge branch 'template_output' into integration 2026-02-02 05:02:04 +01:00
J. Nick Koston
dbfec4e53a [template.datetime] Avoid heap allocation for triggers 2026-02-02 05:01:13 +01:00
J. Nick Koston
b59c100235 [template.output] Avoid heap allocation for triggers 2026-02-02 04:59:14 +01:00
J. Nick Koston
7a5352453e Merge branch 'speaker_media_player_no_heap_trigger' into integration 2026-02-02 04:57:24 +01:00
J. Nick Koston
45f6321ba1 fix missing include that is unrelated to this 2026-02-02 04:57:12 +01:00
J. Nick Koston
18c152723c [sprinkler] Avoid heap allocation for triggers (#13705) 2026-02-02 04:53:46 +01:00
J. Nick Koston
3fb794206d [remote_transmitter] Avoid heap allocation for triggers 2026-02-02 04:53:02 +01:00
J. Nick Koston
e764483f9a Merge branch 'speaker_media_player_no_heap_trigger' into integration 2026-02-02 04:51:01 +01:00
J. Nick Koston
7fcbb06a9e [speaker.media_player] Avoid heap allocation for triggers 2026-02-02 04:50:22 +01:00
J. Nick Koston
09b76d5e4a [voice_assistant] Avoid heap allocation for triggers (#13689) 2026-02-02 04:50:16 +01:00
J. Nick Koston
8791c24072 [api] Avoid heap allocation for client connected/disconnected triggers (#13688) 2026-02-02 04:50:01 +01:00
J. Nick Koston
652c02b9ab [bang_bang] Avoid heap allocation for climate triggers (#13701) 2026-02-02 04:49:46 +01:00
J. Nick Koston
4ab552d750 [http_request] Avoid heap allocation for triggers (#13690) 2026-02-02 04:47:49 +01:00
J. Nick Koston
e420964b93 [template.switch] Avoid heap allocation for triggers (#13691) 2026-02-02 04:47:34 +01:00
J. Nick Koston
7d717a78dc [template] Avoid heap allocation for number set trigger (#13694) 2026-02-02 04:47:21 +01:00
J. Nick Koston
2f0abd5c3f [template] Avoid heap allocation for cover triggers (#13696) 2026-02-02 04:46:55 +01:00
J. Nick Koston
d49d8095df [template] Avoid heap allocation for valve triggers (#13697) 2026-02-02 04:46:41 +01:00
J. Nick Koston
8a8c1290db [endstop] Avoid heap allocation for cover triggers (#13702) 2026-02-02 04:45:01 +01:00
J. Nick Koston
01ffeba2c2 [api] Avoid heap allocation for homeassistant action triggers (#13695) 2026-02-02 04:44:08 +01:00
J. Nick Koston
78ed898f0b [current_based] Avoid heap allocation for cover triggers (#13700) 2026-02-02 04:43:52 +01:00
J. Nick Koston
75ee9a718a [sx126x] Avoid heap allocation for packet trigger (#13699) 2026-02-02 04:43:30 +01:00
J. Nick Koston
bfeb447178 [sx127x] Avoid heap allocation for packet trigger (#13698) 2026-02-02 04:43:16 +01:00
J. Nick Koston
29f8d70b35 [thermostat] Avoid heap allocation for triggers (#13692) 2026-02-02 04:41:08 +01:00
J. Nick Koston
abbca13abf Merge branch 'template_lock_no_heap_trigger' into integration 2026-02-02 04:37:42 +01:00
J. Nick Koston
54aee071ec [template.lock] Avoid heap allocation for triggers 2026-02-02 04:37:14 +01:00
J. Nick Koston
c8d7b94270 Merge branch 'bang_bang_no_heap_trigger' into integration 2026-02-02 04:30:55 +01:00
J. Nick Koston
9c641dae45 [bang_bang] Avoid heap allocation for climate triggers 2026-02-02 04:30:15 +01:00
J. Nick Koston
dc32535a9b Merge branch 'current_based_trigger_no_heap' into integration 2026-02-02 04:27:57 +01:00
J. Nick Koston
d910386359 [current_based] Avoid heap allocation for cover triggers 2026-02-02 04:27:06 +01:00
J. Nick Koston
8c22fcf8df Merge branch 'sx127x_no_heap_trigger' into integration 2026-02-02 04:25:23 +01:00
J. Nick Koston
719971cbbf Merge branch 'sx126x_no_heap_trigger' into integration 2026-02-02 04:25:14 +01:00
J. Nick Koston
60b1f6a5b3 [sx126x] Avoid heap allocation for packet trigger 2026-02-02 04:24:34 +01:00
J. Nick Koston
dcc80d5d31 [sx127x] Avoid heap allocation for packet trigger 2026-02-02 04:22:46 +01:00
J. Nick Koston
03e5eb1a1e Merge branch 'template_valve_no_heap_trigger' into integration 2026-02-02 04:21:29 +01:00
J. Nick Koston
787dde2a29 [template] Avoid heap allocation for valve triggers 2026-02-02 04:20:56 +01:00
J. Nick Koston
5e80b98947 Merge branch 'template_cover_no_heap_trigger' into integration 2026-02-02 04:19:39 +01:00
J. Nick Koston
2b3c05e4f1 [template] Avoid heap allocation for cover triggers 2026-02-02 04:19:05 +01:00
J. Nick Koston
ae6e4bb7a9 Merge branch 'api_action_triggers_no_heap' into integration 2026-02-02 04:16:04 +01:00
J. Nick Koston
57a16c483d [api] Avoid heap allocation for homeassistant action triggers 2026-02-02 04:15:19 +01:00
J. Nick Koston
fb990698d1 Merge branch 'template_number_no_heap_triggers' into integration 2026-02-02 04:11:14 +01:00
J. Nick Koston
2342551402 [template] Avoid heap allocation for number set trigger 2026-02-02 04:10:36 +01:00
J. Nick Koston
bee957120d Merge branch 'feedback_cover_triggers_no_heap' into integration 2026-02-02 04:05:28 +01:00
J. Nick Koston
57e51f60ef [feedback] Avoid heap allocation for cover triggers 2026-02-02 04:04:58 +01:00
J. Nick Koston
00506984f0 Merge branch 'thermostat_triggers_no_heap' into integration 2026-02-02 04:02:57 +01:00
J. Nick Koston
1462647c4d [thermostat] Avoid heap allocation for triggers 2026-02-02 03:58:23 +01:00
J. Nick Koston
9ee5722618 Merge branch 'template_switch_no_heap_trigger' into integration 2026-02-02 03:46:40 +01:00
J. Nick Koston
4081eb68f8 [template.switch] Avoid heap allocation for triggers 2026-02-02 03:43:03 +01:00
J. Nick Koston
023a702fb9 Merge branch 'voice_assistant_triggers_no_heap' into integration 2026-02-02 03:34:50 +01:00
J. Nick Koston
355da3ad24 Merge branch 'http_request_triggers_no_heap' into integration 2026-02-02 03:34:40 +01:00
J. Nick Koston
79467eee12 [http_request] Avoid heap allocation for triggers 2026-02-02 03:33:58 +01:00
J. Nick Koston
4beed62df1 [voice_assistant] Avoid heap allocation for triggers 2026-02-02 03:31:14 +01:00
J. Nick Koston
6229ad8c9c Merge branch 'wifi_triggers' into integration 2026-02-02 03:12:47 +01:00
J. Nick Koston
b628f0bf29 Merge branch 'template_select_trigger' into integration 2026-02-02 03:12:42 +01:00
J. Nick Koston
1e96573213 Merge branch 'feature/ethernet-automations' into integration 2026-02-02 03:12:36 +01:00
J. Nick Koston
cf33a61e33 avoid extra heap allocation, its unconditionally allocated anyways 2026-02-02 03:08:01 +01:00
J. Nick Koston
cc1cb28856 needless heap 2026-02-02 03:02:43 +01:00
J. Nick Koston
89bd9b610e modify in validation instead to avoid copy 2026-02-02 02:48:00 +01:00
J. Nick Koston
9dbcf1447b integration test 2026-02-02 02:45:43 +01:00
J. Nick Koston
6c853cae57 use pattern from sensor filters 2026-02-02 02:40:45 +01:00
J. Nick Koston
48e6efb6aa use pattern from sensor filters 2026-02-02 02:40:30 +01:00
J. Nick Koston
cfc3b3336f fix 2026-02-02 02:35:51 +01:00
J. Nick Koston
9ca394d1e5 not as bad as I was thinking it would be 2026-02-02 02:31:29 +01:00
J. Nick Koston
634449ff4f merge 2026-02-02 02:19:59 +01:00
J. Nick Koston
89a7f425de Merge branch 'feature/ethernet-automations' into integration 2026-02-01 22:16:02 +01:00
Roger Fachini
7385150178 [ethernet] Add connect/disconnect defines for static analyzers 2026-02-01 12:57:07 -08:00
J. Nick Koston
abce9bb380 Merge branch 'top_30_symbols_analyze_memory' into integration 2026-02-01 21:52:43 +01:00
J. Nick Koston
49c75296cf perform tweak 2026-02-01 21:51:54 +01:00
J. Nick Koston
d94ac0e2f8 Merge branch 'top_30_symbols_analyze_memory' into integration 2026-02-01 21:36:04 +01:00
J. Nick Koston
e6ff6f57e7 comp 2026-02-01 21:35:36 +01:00
Roger Fachini
ad955f02ea [ethernet] Conditionally compile triggers 2026-02-01 12:00:28 -08:00
J. Nick Koston
b2692016f5 Merge branch 'wifi_triggers' into integration 2026-02-01 20:52:06 +01:00
J. Nick Koston
5891a00132 Merge branch 'template_select_trigger' into integration 2026-02-01 20:52:01 +01:00
J. Nick Koston
e62a87afe1 [template] Conditionally compile select set_trigger
Only allocate the set_trigger when set_action is configured.
This saves ~20-24 bytes of heap per template select that doesn't
use set_action.
2026-02-01 20:39:52 +01:00
J. Nick Koston
5b68d9b589 [wifi] Conditionally compile on_connect/on_disconnect triggers 2026-02-01 20:13:34 +01:00
J. Nick Koston
231586e537 Merge branch 'top_30_symbols_analyze_memory' into integration 2026-02-01 13:12:32 +01:00
J. Nick Koston
8c8ae8b9c6 bot review 2026-02-01 13:10:56 +01:00
Roger Fachini
52d7d3c637 Merge branch 'dev' into feature/ethernet-automations 2026-02-01 03:27:41 -08:00
pre-commit-ci-lite[bot]
93ee2ce826 [pre-commit.ci lite] apply automatic fixes 2026-02-01 11:05:05 +00:00
Roger Fachini
083c14f70d [ethernet] Update tests with on_connect/on_disconnect 2026-02-01 02:37:09 -08:00
Roger Fachini
2cfb1ab2e6 [ethernet] Add on_connect and on_disconnect automations 2026-02-01 02:36:44 -08:00
J. Nick Koston
7c27835c03 [wifi] Defer ESP8266 listener callbacks to main loop to fix stack overflow 2026-01-31 02:28:07 -06:00
J. Nick Koston
c08726036e Merge branch 'dev' into peername_no_double_ram 2026-01-30 20:13:13 -06:00
Clyde Stubbs
393f1007e3 Merge branch 'dev' into copilot/update-cover-component-triggers 2026-01-29 06:30:26 +11:00
Clyde Stubbs
4696d7eb6a Merge branch 'dev' into copilot/update-cover-component-triggers 2026-01-28 17:07:10 +11:00
Clyde Stubbs
bc2e8e33ee Merge branch 'dev' into copilot/update-cover-component-triggers 2026-01-28 06:45:50 +11:00
clydebarrow
4c0e0b8d76 Add conditions 2026-01-23 18:10:01 +11:00
clydebarrow
48b80858a7 undo some templating 2026-01-23 17:50:46 +11:00
clydebarrow
081081f69a Fix constants; reduce logging spam 2026-01-23 16:51:52 +11:00
clydebarrow
8abb783b64 Refactored using templates 2026-01-23 16:42:32 +11:00
copilot-swe-agent[bot]
bccfe9eead Move cover-specific constants from const.py to cover component
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
2026-01-23 03:15:06 +00:00
copilot-swe-agent[bot]
35fb44da36 Address code review feedback: add comments and fix trigger initialization
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
2026-01-23 03:06:05 +00:00
copilot-swe-agent[bot]
1dfb8926d3 Test and validate cover triggers implementation
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
2026-01-23 03:03:39 +00:00
copilot-swe-agent[bot]
0d63c755b7 Add new cover triggers: on_opening, on_closing, on_idle, on_opened
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
2026-01-23 02:58:20 +00:00
copilot-swe-agent[bot]
6f2ca4c2a7 Initial plan 2026-01-23 02:54:22 +00:00
594 changed files with 8709 additions and 5388 deletions

View File

@@ -1,21 +1 @@
<<<<<<< HEAD
<<<<<<< HEAD
<<<<<<< HEAD
<<<<<<< HEAD
<<<<<<< HEAD
a172e2f65981e98354cc6b5ecf69bdb055dd13602226042ab2c7acd037a2bf41
=======
d565b0589e35e692b5f2fc0c14723a99595b4828a3a3ef96c442e86a23176c00
>>>>>>> mqtt_enum_flash
=======
a172e2f65981e98354cc6b5ecf69bdb055dd13602226042ab2c7acd037a2bf41
>>>>>>> upstream/dev
=======
08c21fa4c044fd80c8f3296371f30c4f5ff3418f1bc1efe63c5bad938301f122
>>>>>>> rp2040_web_server_only_no_cap
=======
cf3d341206b4184ec8b7fe85141aef4fe4696aa720c3f8a06d4e57930574bdab
>>>>>>> upstream/dev
=======
069fa9526c52f7c580a9ec17c7678d12f142221387e9b561c18f95394d4629a3
>>>>>>> upstream/dev
0f2b9a65dce7c59289d3aeb40936360a62a7be937b585147b45bb1509eaafb36

View File

@@ -58,7 +58,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0
uses: github/codeql-action/init@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v4.32.2
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
@@ -86,6 +86,6 @@ jobs:
exit 1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0
uses: github/codeql-action/analyze@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v4.32.2
with:
category: "/language:${{matrix.language}}"

View File

@@ -11,7 +11,7 @@ ci:
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.14.14
rev: v0.15.0
hooks:
# Run the linter.
- id: ruff

View File

@@ -532,7 +532,7 @@ esphome/components/uart/packet_transport/* @clydebarrow
esphome/components/udp/* @clydebarrow
esphome/components/ufire_ec/* @pvizeli
esphome/components/ufire_ise/* @pvizeli
esphome/components/ultrasonic/* @OttoWinter
esphome/components/ultrasonic/* @ssieb @swoboda1337
esphome/components/update/* @jesserockz
esphome/components/uponor_smatrix/* @kroimon
esphome/components/usb_cdc_acm/* @kbx81

View File

@@ -294,8 +294,13 @@ def has_api() -> bool:
def has_ota() -> bool:
"""Check if OTA is available."""
return CONF_OTA in CORE.config
"""Check if OTA upload is available (requires platform: esphome)."""
if CONF_OTA not in CORE.config:
return False
return any(
ota_item.get(CONF_PLATFORM) == CONF_ESPHOME
for ota_item in CORE.config[CONF_OTA]
)
def has_mqtt_ip_lookup() -> bool:

View File

@@ -12,7 +12,6 @@ from .const import (
CORE_SUBCATEGORY_PATTERNS,
DEMANGLED_PATTERNS,
ESPHOME_COMPONENT_PATTERN,
SECTION_TO_ATTR,
SYMBOL_PATTERNS,
)
from .demangle import batch_demangle
@@ -44,6 +43,7 @@ _READELF_SECTION_PATTERN = re.compile(
# Component category prefixes
_COMPONENT_PREFIX_ESPHOME = "[esphome]"
_COMPONENT_PREFIX_EXTERNAL = "[external]"
_COMPONENT_PREFIX_LIB = "[lib]"
_COMPONENT_CORE = f"{_COMPONENT_PREFIX_ESPHOME}core"
_COMPONENT_API = f"{_COMPONENT_PREFIX_ESPHOME}api"
@@ -57,6 +57,16 @@ SymbolInfoType = tuple[str, int, str]
# RAM sections - symbols in these sections consume RAM
RAM_SECTIONS = frozenset([".data", ".bss"])
# nm symbol types for global/weak defined symbols (used for library symbol mapping)
# Only global (uppercase) and weak symbols are safe to use - local symbols (lowercase)
# can have name collisions across compilation units
_NM_DEFINED_GLOBAL_TYPES = frozenset({"T", "D", "B", "R", "W", "V"})
# Pattern matching compiler-generated local names that can collide across compilation
# units (e.g., packet$19, buf$20, flag$5261). These are unsafe for name-based lookup.
# Does NOT match mangled C++ names with optimization suffixes (e.g., func$isra$0).
_COMPILER_LOCAL_PATTERN = re.compile(r"^[a-zA-Z_]\w*\$\d+$")
@dataclass
class MemorySection:
@@ -91,6 +101,17 @@ class ComponentMemory:
bss_size: int = 0 # Uninitialized data (ram only)
symbol_count: int = 0
def add_section_size(self, section_name: str, size: int) -> None:
"""Add size to the appropriate attribute for a section."""
if section_name == ".text":
self.text_size += size
elif section_name == ".rodata":
self.rodata_size += size
elif section_name == ".data":
self.data_size += size
elif section_name == ".bss":
self.bss_size += size
@property
def flash_total(self) -> int:
"""Total flash usage (text + rodata + data)."""
@@ -167,12 +188,23 @@ class MemoryAnalyzer:
self._elf_symbol_names: set[str] = set()
# SDK symbols not in ELF (static/local symbols from closed-source libs)
self._sdk_symbols: list[SDKSymbol] = []
# CSWTCH symbols: list of (name, size, source_file, component)
self._cswtch_symbols: list[tuple[str, int, str, str]] = []
# Library symbol mapping: symbol_name -> library_name
self._lib_symbol_map: dict[str, str] = {}
# Library dir to name mapping: "lib641" -> "espsoftwareserial",
# "espressif__mdns" -> "mdns"
self._lib_hash_to_name: dict[str, str] = {}
# Heuristic category to library redirect: "mdns_lib" -> "[lib]mdns"
self._heuristic_to_lib: dict[str, str] = {}
def analyze(self) -> dict[str, ComponentMemory]:
"""Analyze the ELF file and return component memory usage."""
self._parse_sections()
self._parse_symbols()
self._scan_libraries()
self._categorize_symbols()
self._analyze_cswtch_symbols()
self._analyze_sdk_libraries()
return dict(self.components)
@@ -255,8 +287,7 @@ class MemoryAnalyzer:
comp_mem.symbol_count += 1
# Update the appropriate size attribute based on section
if attr_name := SECTION_TO_ATTR.get(section_name):
setattr(comp_mem, attr_name, getattr(comp_mem, attr_name) + size)
comp_mem.add_section_size(section_name, size)
# Track uncategorized symbols
if component == "other" and size > 0:
@@ -316,15 +347,19 @@ class MemoryAnalyzer:
# If no component match found, it's core
return _COMPONENT_CORE
# Check library symbol map (more accurate than heuristic patterns)
if lib_name := self._lib_symbol_map.get(symbol_name):
return f"{_COMPONENT_PREFIX_LIB}{lib_name}"
# Check against symbol patterns
for component, patterns in SYMBOL_PATTERNS.items():
if any(pattern in symbol_name for pattern in patterns):
return component
return self._heuristic_to_lib.get(component, component)
# Check against demangled patterns
for component, patterns in DEMANGLED_PATTERNS.items():
if any(pattern in demangled for pattern in patterns):
return component
return self._heuristic_to_lib.get(component, component)
# Special cases that need more complex logic
@@ -372,6 +407,610 @@ class MemoryAnalyzer:
return "Other Core"
def _discover_pio_libraries(
self,
libraries: dict[str, list[Path]],
hash_to_name: dict[str, str],
) -> None:
"""Discover PlatformIO third-party libraries from the build directory.
Scans ``lib<hex>/`` directories under ``.pioenvs/<env>/`` to find
library names and their ``.a`` archive or ``.o`` file paths.
Args:
libraries: Dict to populate with library name -> file path list mappings.
Prefers ``.a`` archives when available, falls back to ``.o`` files
(e.g., pioarduino ESP32 Arduino builds only produce ``.o`` files).
hash_to_name: Dict to populate with dir name -> library name mappings
for CSWTCH attribution (e.g., ``lib641`` -> ``espsoftwareserial``).
"""
build_dir = self.elf_path.parent
for entry in build_dir.iterdir():
if not entry.is_dir() or not entry.name.startswith("lib"):
continue
# Validate that the suffix after "lib" is a hex hash
hex_part = entry.name[3:]
if not hex_part:
continue
try:
int(hex_part, 16)
except ValueError:
continue
# Each lib<hex>/ directory contains a subdirectory named after the library
for lib_subdir in entry.iterdir():
if not lib_subdir.is_dir():
continue
lib_name = lib_subdir.name.lower()
# Prefer .a archive (lib<LibraryName>.a), fall back to .o files
# e.g., lib72a/ESPAsyncTCP/... has lib72a/libESPAsyncTCP.a
archive = entry / f"lib{lib_subdir.name}.a"
if archive.exists():
file_paths = [archive]
elif archives := list(entry.glob("*.a")):
# Case-insensitive fallback
file_paths = [archives[0]]
else:
# No .a archive (e.g., pioarduino CMake builds) - use .o files
file_paths = sorted(lib_subdir.rglob("*.o"))
if file_paths:
libraries[lib_name] = file_paths
hash_to_name[entry.name] = lib_name
_LOGGER.debug(
"Discovered PlatformIO library: %s -> %s",
lib_subdir.name,
file_paths[0],
)
def _discover_idf_managed_components(
self,
libraries: dict[str, list[Path]],
hash_to_name: dict[str, str],
) -> None:
"""Discover ESP-IDF managed component libraries from the build directory.
ESP-IDF managed components (from the IDF component registry) use a
``<vendor>__<name>`` naming convention. Source files live under
``managed_components/<vendor>__<name>/`` and the compiled archives are at
``esp-idf/<vendor>__<name>/lib<vendor>__<name>.a``.
Args:
libraries: Dict to populate with library name -> file path list mappings.
hash_to_name: Dict to populate with dir name -> library name mappings
for CSWTCH attribution (e.g., ``espressif__mdns`` -> ``mdns``).
"""
build_dir = self.elf_path.parent
managed_dir = build_dir / "managed_components"
if not managed_dir.is_dir():
return
espidf_dir = build_dir / "esp-idf"
for entry in managed_dir.iterdir():
if not entry.is_dir() or "__" not in entry.name:
continue
# Extract the short name: espressif__mdns -> mdns
full_name = entry.name # e.g., espressif__mdns
short_name = full_name.split("__", 1)[1].lower()
# Find the .a archive under esp-idf/<vendor>__<name>/
archive = espidf_dir / full_name / f"lib{full_name}.a"
if archive.exists():
libraries[short_name] = [archive]
hash_to_name[full_name] = short_name
_LOGGER.debug(
"Discovered IDF managed component: %s -> %s",
short_name,
archive,
)
def _build_library_symbol_map(
self, libraries: dict[str, list[Path]]
) -> dict[str, str]:
"""Build a symbol-to-library mapping from library archives or object files.
Runs ``nm --defined-only`` on each ``.a`` or ``.o`` file to collect
global and weak defined symbols.
Args:
libraries: Dictionary mapping library name to list of file paths
(``.a`` archives or ``.o`` object files).
Returns:
Dictionary mapping symbol name to library name.
"""
symbol_map: dict[str, str] = {}
if not self.nm_path:
return symbol_map
for lib_name, file_paths in libraries.items():
result = run_tool(
[self.nm_path, "--defined-only", *(str(p) for p in file_paths)],
timeout=10,
)
if result is None or result.returncode != 0:
continue
for line in result.stdout.splitlines():
parts = line.split()
if len(parts) < 3:
continue
sym_type = parts[-2]
sym_name = parts[-1]
# Include global defined symbols (uppercase) and weak symbols (W/V)
if sym_type in _NM_DEFINED_GLOBAL_TYPES:
symbol_map[sym_name] = lib_name
return symbol_map
@staticmethod
def _build_heuristic_to_lib_mapping(
library_names: set[str],
) -> dict[str, str]:
"""Build mapping from heuristic pattern categories to discovered libraries.
Heuristic categories like ``mdns_lib``, ``web_server_lib``, ``async_tcp``
exist as approximations for library attribution. When we discover the
actual library, symbols matching those heuristics should be redirected
to the ``[lib]`` category instead.
The mapping is built by checking if the normalized category name
(stripped of ``_lib`` suffix and underscores) appears as a substring
of any discovered library name.
Examples::
mdns_lib -> mdns -> in "mdns" or "esp8266mdns" -> [lib]mdns
web_server_lib -> webserver -> in "espasyncwebserver" -> [lib]espasyncwebserver
async_tcp -> asynctcp -> in "espasynctcp" -> [lib]espasynctcp
Args:
library_names: Set of discovered library names (lowercase).
Returns:
Dictionary mapping heuristic category to ``[lib]<name>`` string.
"""
mapping: dict[str, str] = {}
all_categories = set(SYMBOL_PATTERNS) | set(DEMANGLED_PATTERNS)
for category in all_categories:
base = category.removesuffix("_lib").replace("_", "")
# Collect all libraries whose name contains the base string
candidates = [lib_name for lib_name in library_names if base in lib_name]
if not candidates:
continue
# Choose a deterministic "best" match:
# 1. Prefer exact name matches over substring matches.
# 2. Among non-exact matches, prefer the shortest library name.
# 3. Break remaining ties lexicographically.
best_lib = min(
candidates,
key=lambda lib_name, _base=base: (
lib_name != _base,
len(lib_name),
lib_name,
),
)
mapping[category] = f"{_COMPONENT_PREFIX_LIB}{best_lib}"
if mapping:
_LOGGER.debug(
"Heuristic-to-library redirects: %s",
", ".join(f"{k} -> {v}" for k, v in sorted(mapping.items())),
)
return mapping
def _parse_map_file(self) -> dict[str, str] | None:
"""Parse linker map file to build authoritative symbol-to-library mapping.
The linker map file contains the definitive source attribution for every
symbol, including local/static ones that ``nm`` cannot safely export.
Map file format (GNU ld)::
.text._mdns_service_task
0x400e9fdc 0x65c .pioenvs/env/esp-idf/espressif__mdns/libespressif__mdns.a(mdns.c.o)
Each section entry has a ``.section.symbol_name`` line followed by an
indented line with address, size, and source path.
Returns:
Symbol-to-library dict, or ``None`` if no usable map file exists.
"""
map_path = self.elf_path.with_suffix(".map")
if not map_path.exists() or map_path.stat().st_size < 10000:
return None
_LOGGER.info("Parsing linker map file: %s", map_path.name)
try:
map_text = map_path.read_text(encoding="utf-8", errors="replace")
except OSError as err:
_LOGGER.warning("Failed to read map file: %s", err)
return None
symbol_map: dict[str, str] = {}
current_symbol: str | None = None
section_prefixes = (".text.", ".rodata.", ".data.", ".bss.", ".literal.")
for line in map_text.splitlines():
# Match section.symbol line: " .text.symbol_name"
# Single space indent, starts with dot
if len(line) > 2 and line[0] == " " and line[1] == ".":
stripped = line.strip()
for prefix in section_prefixes:
if stripped.startswith(prefix):
current_symbol = stripped[len(prefix) :]
break
else:
current_symbol = None
continue
# Match source attribution line: " 0xADDR 0xSIZE source_path"
if current_symbol is None:
continue
fields = line.split()
# Skip compiler-generated local names (e.g., packet$19, buf$20)
# that can collide across compilation units
if (
len(fields) >= 3
and fields[0].startswith("0x")
and fields[1].startswith("0x")
and not _COMPILER_LOCAL_PATTERN.match(current_symbol)
):
source_path = fields[2]
# Check if source path contains a known library directory
for dir_key, lib_name in self._lib_hash_to_name.items():
if dir_key in source_path:
symbol_map[current_symbol] = lib_name
break
current_symbol = None
return symbol_map or None
def _scan_libraries(self) -> None:
"""Discover third-party libraries and build symbol mapping.
Scans both PlatformIO ``lib<hex>/`` directories (Arduino builds) and
ESP-IDF ``managed_components/`` (IDF builds) to find library archives.
Uses the linker map file for authoritative symbol attribution when
available, falling back to ``nm`` scanning with heuristic redirects.
"""
libraries: dict[str, list[Path]] = {}
self._discover_pio_libraries(libraries, self._lib_hash_to_name)
self._discover_idf_managed_components(libraries, self._lib_hash_to_name)
if not libraries:
_LOGGER.debug("No third-party libraries found")
return
_LOGGER.info(
"Scanning %d libraries: %s",
len(libraries),
", ".join(sorted(libraries)),
)
# Heuristic redirect catches local symbols (e.g., mdns_task_buffer$14)
# that can't be safely added to the symbol map due to name collisions
self._heuristic_to_lib = self._build_heuristic_to_lib_mapping(
set(libraries.keys())
)
# Try linker map file first (authoritative, includes local symbols)
map_symbols = self._parse_map_file()
if map_symbols is not None:
self._lib_symbol_map = map_symbols
_LOGGER.info(
"Built library symbol map from linker map: %d symbols",
len(self._lib_symbol_map),
)
return
# Fall back to nm scanning (global symbols only)
self._lib_symbol_map = self._build_library_symbol_map(libraries)
_LOGGER.info(
"Built library symbol map from nm: %d symbols from %d libraries",
len(self._lib_symbol_map),
len(libraries),
)
def _find_object_files_dir(self) -> Path | None:
"""Find the directory containing object files for this build.
Returns:
Path to the directory containing .o files, or None if not found.
"""
# The ELF is typically at .pioenvs/<env>/firmware.elf
# Object files are in .pioenvs/<env>/src/ and .pioenvs/<env>/lib*/
pioenvs_dir = self.elf_path.parent
if pioenvs_dir.exists() and any(pioenvs_dir.glob("src/*.o")):
return pioenvs_dir
return None
@staticmethod
def _parse_nm_cswtch_output(
output: str,
base_dir: Path | None,
cswtch_map: dict[str, list[tuple[str, int]]],
) -> None:
"""Parse nm output for CSWTCH symbols and add to cswtch_map.
Handles both ``.o`` files and ``.a`` archives.
nm output formats::
.o files: /path/file.o:hex_addr hex_size type name
.a files: /path/lib.a:member.o:hex_addr hex_size type name
For ``.o`` files, paths are made relative to *base_dir* when possible.
For ``.a`` archives (detected by ``:`` in the file portion), paths are
formatted as ``archive_stem/member.o`` (e.g. ``liblwip2-536-feat/lwip-esp.o``).
Args:
output: Raw stdout from ``nm --print-file-name -S``.
base_dir: Base directory for computing relative paths of ``.o`` files.
Pass ``None`` when scanning archives outside the build tree.
cswtch_map: Dict to populate, mapping ``"CSWTCH$N:size"`` to source list.
"""
for line in output.splitlines():
if "CSWTCH$" not in line:
continue
# Split on last ":" that precedes a hex address.
# For .o: "filepath.o" : "hex_addr hex_size type name"
# For .a: "filepath.a:member.o" : "hex_addr hex_size type name"
parts_after_colon = line.rsplit(":", 1)
if len(parts_after_colon) != 2:
continue
file_path = parts_after_colon[0]
fields = parts_after_colon[1].split()
# fields: [address, size, type, name]
if len(fields) < 4:
continue
sym_name = fields[3]
if not sym_name.startswith("CSWTCH$"):
continue
try:
size = int(fields[1], 16)
except ValueError:
continue
# Determine readable source path
# Use ".a:" to detect archive format (not bare ":" which matches
# Windows drive letters like "C:\...\file.o").
if ".a:" in file_path:
# Archive format: "archive.a:member.o" → "archive_stem/member.o"
archive_part, member = file_path.rsplit(":", 1)
archive_name = Path(archive_part).stem
rel_path = f"{archive_name}/{member}"
elif base_dir is not None:
try:
rel_path = str(Path(file_path).relative_to(base_dir))
except ValueError:
rel_path = file_path
else:
rel_path = file_path
key = f"{sym_name}:{size}"
cswtch_map[key].append((rel_path, size))
def _run_nm_cswtch_scan(
self,
files: list[Path],
base_dir: Path | None,
cswtch_map: dict[str, list[tuple[str, int]]],
) -> None:
"""Run nm on *files* and add any CSWTCH symbols to *cswtch_map*.
Args:
files: Object (``.o``) or archive (``.a``) files to scan.
base_dir: Base directory for relative path computation (see
:meth:`_parse_nm_cswtch_output`).
cswtch_map: Dict to populate with results.
"""
if not self.nm_path or not files:
return
_LOGGER.debug("Scanning %d files for CSWTCH symbols", len(files))
result = run_tool(
[self.nm_path, "--print-file-name", "-S"] + [str(f) for f in files],
timeout=30,
)
if result is None or result.returncode != 0:
_LOGGER.debug(
"nm failed or timed out scanning %d files for CSWTCH symbols",
len(files),
)
return
self._parse_nm_cswtch_output(result.stdout, base_dir, cswtch_map)
def _scan_cswtch_in_sdk_archives(
self, cswtch_map: dict[str, list[tuple[str, int]]]
) -> None:
"""Scan SDK library archives (.a) for CSWTCH symbols.
Prebuilt SDK libraries (e.g. lwip, bearssl) are not compiled from source,
so their CSWTCH symbols only exist inside ``.a`` archives. Results are
merged into *cswtch_map* for keys not already found in ``.o`` files.
The same source file (e.g. ``lwip-esp.o``) often appears in multiple
library variants (``liblwip2-536.a``, ``liblwip2-1460-feat.a``, etc.),
so results are deduplicated by member name.
"""
sdk_dirs = self._find_sdk_library_dirs()
if not sdk_dirs:
return
sdk_archives = sorted(a for sdk_dir in sdk_dirs for a in sdk_dir.glob("*.a"))
sdk_map: dict[str, list[tuple[str, int]]] = defaultdict(list)
self._run_nm_cswtch_scan(sdk_archives, None, sdk_map)
# Merge SDK results, deduplicating by member name.
for key, sources in sdk_map.items():
if key in cswtch_map:
continue
seen: dict[str, tuple[str, int]] = {}
for path, sz in sources:
member = Path(path).name
if member not in seen:
seen[member] = (path, sz)
cswtch_map[key] = list(seen.values())
def _source_file_to_component(self, source_file: str) -> str:
"""Map a source object file path to its component name.
Args:
source_file: Relative path like 'src/esphome/components/wifi/wifi_component.cpp.o'
Returns:
Component name like '[esphome]wifi' or the source file if unknown.
"""
parts = Path(source_file).parts
# ESPHome component: src/esphome/components/<name>/...
if "components" in parts:
idx = parts.index("components")
if idx + 1 < len(parts):
component_name = parts[idx + 1]
if component_name in get_esphome_components():
return f"{_COMPONENT_PREFIX_ESPHOME}{component_name}"
if component_name in self.external_components:
return f"{_COMPONENT_PREFIX_EXTERNAL}{component_name}"
# ESPHome core: src/esphome/core/... or src/esphome/...
if "core" in parts and "esphome" in parts:
return _COMPONENT_CORE
if "esphome" in parts and "components" not in parts:
return _COMPONENT_CORE
# Framework/library files - check for PlatformIO library hash dirs
# e.g., lib65b/ESPAsyncTCP/... -> [lib]espasynctcp
if parts and parts[0] in self._lib_hash_to_name:
return f"{_COMPONENT_PREFIX_LIB}{self._lib_hash_to_name[parts[0]]}"
# ESP-IDF managed components: managed_components/espressif__mdns/... -> [lib]mdns
if (
len(parts) >= 2
and parts[0] == "managed_components"
and parts[1] in self._lib_hash_to_name
):
return f"{_COMPONENT_PREFIX_LIB}{self._lib_hash_to_name[parts[1]]}"
# Other framework/library files - return the first path component
# e.g., FrameworkArduino/... -> FrameworkArduino
return parts[0] if parts else source_file
def _analyze_cswtch_symbols(self) -> None:
"""Analyze CSWTCH (GCC switch table) symbols by tracing to source objects.
CSWTCH symbols are compiler-generated lookup tables for switch statements.
They are local symbols, so the same name can appear in different object files.
This method scans .o files and SDK archives to attribute them to their
source components.
"""
obj_dir = self._find_object_files_dir()
if obj_dir is None:
_LOGGER.debug("No object files directory found, skipping CSWTCH analysis")
return
# Scan build-dir object files for CSWTCH symbols
cswtch_map: dict[str, list[tuple[str, int]]] = defaultdict(list)
self._run_nm_cswtch_scan(sorted(obj_dir.rglob("*.o")), obj_dir, cswtch_map)
# Also scan SDK library archives (.a) for CSWTCH symbols.
# Prebuilt SDK libraries (e.g. lwip, bearssl) are not compiled from source
# so their symbols only exist inside .a archives, not as loose .o files.
self._scan_cswtch_in_sdk_archives(cswtch_map)
if not cswtch_map:
_LOGGER.debug("No CSWTCH symbols found in object files or SDK archives")
return
# Collect CSWTCH symbols from the ELF (already parsed in sections)
# Include section_name for re-attribution of component totals
elf_cswtch = [
(symbol_name, size, section_name)
for section_name, section in self.sections.items()
for symbol_name, size, _ in section.symbols
if symbol_name.startswith("CSWTCH$")
]
_LOGGER.debug(
"Found %d CSWTCH symbols in ELF, %d unique in object files",
len(elf_cswtch),
len(cswtch_map),
)
# Match ELF CSWTCH symbols to source files and re-attribute component totals.
# _categorize_symbols() already ran and put these into "other" since CSWTCH$
# names don't match any component pattern. We move the bytes to the correct
# component based on the object file mapping.
other_mem = self.components.get("other")
for sym_name, size, section_name in elf_cswtch:
key = f"{sym_name}:{size}"
sources = cswtch_map.get(key, [])
if len(sources) == 1:
source_file = sources[0][0]
component = self._source_file_to_component(source_file)
elif len(sources) > 1:
# Ambiguous - multiple object files have same CSWTCH name+size
source_file = "ambiguous"
component = "ambiguous"
_LOGGER.debug(
"Ambiguous CSWTCH %s (%d B) found in %d files: %s",
sym_name,
size,
len(sources),
", ".join(src for src, _ in sources),
)
else:
source_file = "unknown"
component = "unknown"
self._cswtch_symbols.append((sym_name, size, source_file, component))
# Re-attribute from "other" to the correct component
if (
component not in ("other", "unknown", "ambiguous")
and other_mem is not None
):
other_mem.add_section_size(section_name, -size)
if component not in self.components:
self.components[component] = ComponentMemory(component)
self.components[component].add_section_size(section_name, size)
# Sort by size descending
self._cswtch_symbols.sort(key=lambda x: x[1], reverse=True)
total_size = sum(size for _, size, _, _ in self._cswtch_symbols)
_LOGGER.debug(
"CSWTCH analysis: %d symbols, %d bytes total",
len(self._cswtch_symbols),
total_size,
)
def get_unattributed_ram(self) -> tuple[int, int, int]:
"""Get unattributed RAM sizes (SDK/framework overhead).

View File

@@ -4,7 +4,9 @@ from __future__ import annotations
from collections import defaultdict
from collections.abc import Callable
import heapq
import json
from operator import itemgetter
import sys
from typing import TYPE_CHECKING
@@ -13,6 +15,7 @@ from . import (
_COMPONENT_CORE,
_COMPONENT_PREFIX_ESPHOME,
_COMPONENT_PREFIX_EXTERNAL,
_COMPONENT_PREFIX_LIB,
RAM_SECTIONS,
MemoryAnalyzer,
)
@@ -32,6 +35,8 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
RAM_SYMBOL_SIZE_THRESHOLD: int = 24
# Number of top symbols to show in the largest symbols report
TOP_SYMBOLS_LIMIT: int = 30
# Width for symbol name display in top symbols report
COL_TOP_SYMBOL_NAME: int = 55
# Column width constants
COL_COMPONENT: int = 29
@@ -152,32 +157,81 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
def _add_top_symbols(self, lines: list[str]) -> None:
"""Add a section showing the top largest symbols in the binary."""
# Collect all symbols from all components
all_symbols: list[
tuple[str, str, int, str, str]
] = [] # (symbol, demangled, size, section, component)
for component, symbols in self._component_symbols.items():
for symbol, demangled, size, section in symbols:
all_symbols.append((symbol, demangled, size, section, component))
# Collect all symbols from all components: (symbol, demangled, size, section, component)
all_symbols = [
(symbol, demangled, size, section, component)
for component, symbols in self._component_symbols.items()
for symbol, demangled, size, section in symbols
]
# Sort by size descending
all_symbols.sort(key=lambda x: x[2], reverse=True)
# Get top N symbols by size using heapq for efficiency
top_symbols = heapq.nlargest(
self.TOP_SYMBOLS_LIMIT, all_symbols, key=itemgetter(2)
)
lines.append("")
lines.append(f"Top {self.TOP_SYMBOLS_LIMIT} Largest Symbols:")
for i, (symbol, demangled, size, section, component) in enumerate(
all_symbols[: self.TOP_SYMBOLS_LIMIT]
):
# Calculate truncation limit from column width (leaving room for "...")
truncate_limit = self.COL_TOP_SYMBOL_NAME - 3
for i, (_, demangled, size, section, component) in enumerate(top_symbols):
# Format section label
section_label = f"[{section[1:]}]" if section else ""
# Truncate demangled name if too long
demangled_display = (
f"{demangled[:50]}..." if len(demangled) > 50 else demangled
f"{demangled[:truncate_limit]}..."
if len(demangled) > self.COL_TOP_SYMBOL_NAME
else demangled
)
lines.append(
f"{i + 1:>2}. {size:>7,} B {section_label:<8} {demangled_display:<55} {component}"
f"{i + 1:>2}. {size:>7,} B {section_label:<8} {demangled_display:<{self.COL_TOP_SYMBOL_NAME}} {component}"
)
def _add_cswtch_analysis(self, lines: list[str]) -> None:
"""Add CSWTCH (GCC switch table lookup) analysis section."""
self._add_section_header(lines, "CSWTCH Analysis (GCC Switch Table Lookups)")
total_size = sum(size for _, size, _, _ in self._cswtch_symbols)
lines.append(
f"Total: {len(self._cswtch_symbols)} switch table(s), {total_size:,} B"
)
lines.append("")
# Group by component
by_component: dict[str, list[tuple[str, int, str]]] = defaultdict(list)
for sym_name, size, source_file, component in self._cswtch_symbols:
by_component[component].append((sym_name, size, source_file))
# Sort components by total size descending
sorted_components = sorted(
by_component.items(),
key=lambda x: sum(s[1] for s in x[1]),
reverse=True,
)
for component, symbols in sorted_components:
comp_total = sum(s[1] for s in symbols)
lines.append(f"{component} ({comp_total:,} B, {len(symbols)} tables):")
# Group by source file within component
by_file: dict[str, list[tuple[str, int]]] = defaultdict(list)
for sym_name, size, source_file in symbols:
by_file[source_file].append((sym_name, size))
for source_file, file_symbols in sorted(
by_file.items(),
key=lambda x: sum(s[1] for s in x[1]),
reverse=True,
):
file_total = sum(s[1] for s in file_symbols)
lines.append(
f" {source_file} ({file_total:,} B, {len(file_symbols)} tables)"
)
for sym_name, size in sorted(
file_symbols, key=lambda x: x[1], reverse=True
):
lines.append(f" {size:>6,} B {sym_name}")
lines.append("")
def generate_report(self, detailed: bool = False) -> str:
"""Generate a formatted memory report."""
components = sorted(
@@ -355,6 +409,11 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
for name, mem in components
if name.startswith(_COMPONENT_PREFIX_EXTERNAL)
]
library_components = [
(name, mem)
for name, mem in components
if name.startswith(_COMPONENT_PREFIX_LIB)
]
top_esphome_components = sorted(
esphome_components, key=lambda x: x[1].flash_total, reverse=True
@@ -365,6 +424,11 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
external_components, key=lambda x: x[1].flash_total, reverse=True
)
# Include all library components
top_library_components = sorted(
library_components, key=lambda x: x[1].flash_total, reverse=True
)
# Check if API component exists and ensure it's included
api_component = None
for name, mem in components:
@@ -383,10 +447,11 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
if name in system_components_to_include
]
# Combine all components to analyze: top ESPHome + all external + API if not already included + system components
# Combine all components to analyze: top ESPHome + all external + libraries + API if not already included + system components
components_to_analyze = (
list(top_esphome_components)
+ list(top_external_components)
+ list(top_library_components)
+ system_components
)
if api_component and api_component not in components_to_analyze:
@@ -465,6 +530,10 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
lines.append(f" ... and {len(large_ram_syms) - 10} more")
lines.append("")
# CSWTCH (GCC switch table) analysis
if self._cswtch_symbols:
self._add_cswtch_analysis(lines)
lines.append(
"Note: This analysis covers symbols in the ELF file. Some runtime allocations may not be included."
)

View File

@@ -66,15 +66,6 @@ SECTION_MAPPING = {
),
}
# Section to ComponentMemory attribute mapping
# Maps section names to the attribute name in ComponentMemory dataclass
SECTION_TO_ATTR = {
".text": "text_size",
".rodata": "rodata_size",
".data": "data_size",
".bss": "bss_size",
}
# Component identification rules
# Symbol patterns: patterns found in raw symbol names
SYMBOL_PATTERNS = {
@@ -513,7 +504,9 @@ SYMBOL_PATTERNS = {
"__FUNCTION__$",
"DAYS_IN_MONTH",
"_DAYS_BEFORE_MONTH",
"CSWTCH$",
# Note: CSWTCH$ symbols are GCC switch table lookup tables.
# They are attributed to their source object files via _analyze_cswtch_symbols()
# rather than being lumped into libc.
"dst$",
"sulp",
"_strtol_l", # String to long with locale

View File

@@ -11,6 +11,7 @@
from esphome.cpp_generator import ( # noqa: F401
ArrayInitializer,
Expression,
FlashStringLiteral,
LineComment,
LogStringLiteral,
MockObj,
@@ -87,6 +88,7 @@ from esphome.cpp_types import ( # noqa: F401
size_t,
std_ns,
std_shared_ptr,
std_span,
std_string,
std_string_ref,
std_vector,

View File

@@ -45,8 +45,6 @@ void AbsoluteHumidityComponent::dump_config() {
this->temperature_sensor_->get_name().c_str(), this->humidity_sensor_->get_name().c_str());
}
float AbsoluteHumidityComponent::get_setup_priority() const { return setup_priority::DATA; }
void AbsoluteHumidityComponent::loop() {
if (!this->next_update_) {
return;

View File

@@ -24,7 +24,6 @@ class AbsoluteHumidityComponent : public sensor::Sensor, public Component {
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void loop() override;
protected:

View File

@@ -68,11 +68,6 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
/// This method is called during the ESPHome setup process to log the configuration.
void dump_config() override;
/// Return the setup priority for this component.
/// Components with higher priority are initialized earlier during setup.
/// @return A float representing the setup priority.
float get_setup_priority() const override;
#ifdef USE_ZEPHYR
/// Set the ADC channel to be used by the ADC sensor.
/// @param channel Pointer to an adc_dt_spec structure representing the ADC channel.

View File

@@ -79,7 +79,5 @@ void ADCSensor::set_sample_count(uint8_t sample_count) {
void ADCSensor::set_sampling_mode(SamplingMode sampling_mode) { this->sampling_mode_ = sampling_mode; }
float ADCSensor::get_setup_priority() const { return setup_priority::DATA; }
} // namespace adc
} // namespace esphome

View File

@@ -42,11 +42,11 @@ void ADCSensor::setup() {
adc_oneshot_unit_init_cfg_t init_config = {}; // Zero initialize
init_config.unit_id = this->adc_unit_;
init_config.ulp_mode = ADC_ULP_MODE_DISABLE;
#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \
USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2
#if USE_ESP32_VARIANT_ESP32C2 || USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || \
USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2
init_config.clk_src = ADC_DIGI_CLK_SRC_DEFAULT;
#endif // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 ||
// USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2
#endif // USE_ESP32_VARIANT_ESP32C2 || USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 ||
// USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2
esp_err_t err = adc_oneshot_new_unit(&init_config, &ADCSensor::shared_adc_handles[this->adc_unit_]);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error initializing %s: %d", LOG_STR_ARG(adc_unit_to_str(this->adc_unit_)), err);
@@ -76,7 +76,7 @@ void ADCSensor::setup() {
#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \
USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S3
// RISC-V variants and S3 use curve fitting calibration
// RISC-V variants (except C2) and S3 use curve fitting calibration
adc_cali_curve_fitting_config_t cali_config = {}; // Zero initialize first
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
cali_config.chan = this->channel_;
@@ -94,14 +94,14 @@ void ADCSensor::setup() {
ESP_LOGW(TAG, "Curve fitting calibration failed with error %d, will use uncalibrated readings", err);
this->setup_flags_.calibration_complete = false;
}
#else // Other ESP32 variants use line fitting calibration
#else // ESP32, ESP32-S2, and ESP32-C2 use line fitting calibration
adc_cali_line_fitting_config_t cali_config = {
.unit_id = this->adc_unit_,
.atten = this->attenuation_,
.bitwidth = ADC_BITWIDTH_DEFAULT,
#if !defined(USE_ESP32_VARIANT_ESP32S2)
#if !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32C2)
.default_vref = 1100, // Default reference voltage in mV
#endif // !defined(USE_ESP32_VARIANT_ESP32S2)
#endif // !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32C2)
};
err = adc_cali_create_scheme_line_fitting(&cali_config, &handle);
if (err == ESP_OK) {
@@ -112,7 +112,7 @@ void ADCSensor::setup() {
ESP_LOGW(TAG, "Line fitting calibration failed with error %d, will use uncalibrated readings", err);
this->setup_flags_.calibration_complete = false;
}
#endif // USE_ESP32_VARIANT_ESP32C3 || ESP32C5 || ESP32C6 || ESP32C61 || ESP32H2 || ESP32P4 || ESP32S3
#endif // ESP32C3 || ESP32C5 || ESP32C6 || ESP32C61 || ESP32H2 || ESP32P4 || ESP32S3
}
this->setup_flags_.init_complete = true;
@@ -189,7 +189,7 @@ float ADCSensor::sample_fixed_attenuation_() {
adc_cali_delete_scheme_curve_fitting(this->calibration_handle_);
#else // Other ESP32 variants use line fitting calibration
adc_cali_delete_scheme_line_fitting(this->calibration_handle_);
#endif // USE_ESP32_VARIANT_ESP32C3 || ESP32C5 || ESP32C6 || ESP32C61 || ESP32H2 || ESP32P4 || ESP32S3
#endif // ESP32C3 || ESP32C5 || ESP32C6 || ESP32C61 || ESP32H2 || ESP32P4 || ESP32S3
this->calibration_handle_ = nullptr;
}
}
@@ -247,7 +247,7 @@ float ADCSensor::sample_autorange_() {
.unit_id = this->adc_unit_,
.atten = atten,
.bitwidth = ADC_BITWIDTH_DEFAULT,
#if !defined(USE_ESP32_VARIANT_ESP32S2)
#if !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32C2)
.default_vref = 1100,
#endif
};

View File

@@ -9,8 +9,6 @@ static const char *const TAG = "adc128s102.sensor";
ADC128S102Sensor::ADC128S102Sensor(uint8_t channel) : channel_(channel) {}
float ADC128S102Sensor::get_setup_priority() const { return setup_priority::DATA; }
void ADC128S102Sensor::dump_config() {
LOG_SENSOR("", "ADC128S102 Sensor", this);
ESP_LOGCONFIG(TAG, " Pin: %u", this->channel_);

View File

@@ -19,7 +19,6 @@ class ADC128S102Sensor : public PollingComponent,
void update() override;
void dump_config() override;
float get_setup_priority() const override;
float sample() override;
protected:

View File

@@ -150,8 +150,6 @@ void AHT10Component::update() {
this->restart_read_();
}
float AHT10Component::get_setup_priority() const { return setup_priority::DATA; }
void AHT10Component::dump_config() {
ESP_LOGCONFIG(TAG, "AHT10:");
LOG_I2C_DEVICE(this);

View File

@@ -16,7 +16,6 @@ class AHT10Component : public PollingComponent, public i2c::I2CDevice {
void setup() override;
void update() override;
void dump_config() override;
float get_setup_priority() const override;
void set_variant(AHT10Variant variant) { this->variant_ = variant; }
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }

View File

@@ -1,32 +1,15 @@
#include "alarm_control_panel_state.h"
#include "esphome/core/progmem.h"
namespace esphome::alarm_control_panel {
// Alarm control panel state strings indexed by AlarmControlPanelState enum (0-9)
PROGMEM_STRING_TABLE(AlarmControlPanelStateStrings, "DISARMED", "ARMED_HOME", "ARMED_AWAY", "ARMED_NIGHT",
"ARMED_VACATION", "ARMED_CUSTOM_BYPASS", "PENDING", "ARMING", "DISARMING", "TRIGGERED", "UNKNOWN");
const LogString *alarm_control_panel_state_to_string(AlarmControlPanelState state) {
switch (state) {
case ACP_STATE_DISARMED:
return LOG_STR("DISARMED");
case ACP_STATE_ARMED_HOME:
return LOG_STR("ARMED_HOME");
case ACP_STATE_ARMED_AWAY:
return LOG_STR("ARMED_AWAY");
case ACP_STATE_ARMED_NIGHT:
return LOG_STR("ARMED_NIGHT");
case ACP_STATE_ARMED_VACATION:
return LOG_STR("ARMED_VACATION");
case ACP_STATE_ARMED_CUSTOM_BYPASS:
return LOG_STR("ARMED_CUSTOM_BYPASS");
case ACP_STATE_PENDING:
return LOG_STR("PENDING");
case ACP_STATE_ARMING:
return LOG_STR("ARMING");
case ACP_STATE_DISARMING:
return LOG_STR("DISARMING");
case ACP_STATE_TRIGGERED:
return LOG_STR("TRIGGERED");
default:
return LOG_STR("UNKNOWN");
}
return AlarmControlPanelStateStrings::get_log_str(static_cast<uint8_t>(state),
AlarmControlPanelStateStrings::LAST_INDEX);
}
} // namespace esphome::alarm_control_panel

View File

@@ -176,7 +176,5 @@ void AM2315C::dump_config() {
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
}
float AM2315C::get_setup_priority() const { return setup_priority::DATA; }
} // namespace am2315c
} // namespace esphome

View File

@@ -33,7 +33,6 @@ class AM2315C : public PollingComponent, public i2c::I2CDevice {
void dump_config() override;
void update() override;
void setup() override;
float get_setup_priority() const override;
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { this->temperature_sensor_ = temperature_sensor; }
void set_humidity_sensor(sensor::Sensor *humidity_sensor) { this->humidity_sensor_ = humidity_sensor; }

View File

@@ -51,7 +51,6 @@ void AM2320Component::dump_config() {
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
}
float AM2320Component::get_setup_priority() const { return setup_priority::DATA; }
bool AM2320Component::read_bytes_(uint8_t a_register, uint8_t *data, uint8_t len, uint32_t conversion) {
if (!this->write_bytes(a_register, data, 2)) {

View File

@@ -11,7 +11,6 @@ class AM2320Component : public PollingComponent, public i2c::I2CDevice {
public:
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void update() override;
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }

View File

@@ -384,7 +384,6 @@ void APDS9960::process_dataset_(int up, int down, int left, int right) {
}
}
}
float APDS9960::get_setup_priority() const { return setup_priority::DATA; }
bool APDS9960::is_proximity_enabled_() const {
return
#ifdef USE_SENSOR

View File

@@ -32,7 +32,6 @@ class APDS9960 : public PollingComponent, public i2c::I2CDevice {
public:
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void update() override;
void loop() override;

View File

@@ -524,24 +524,24 @@ async def homeassistant_service_to_code(
cg.add_define("USE_API_HOMEASSISTANT_SERVICES")
serv = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, serv, False)
templ = await cg.templatable(config[CONF_ACTION], args, None)
templ = await cg.templatable(config[CONF_ACTION], args, cg.std_string)
cg.add(var.set_service(templ))
# Initialize FixedVectors with exact sizes from config
cg.add(var.init_data(len(config[CONF_DATA])))
for key, value in config[CONF_DATA].items():
templ = await cg.templatable(value, args, None)
cg.add(var.add_data(key, templ))
templ = await cg.templatable(value, args, cg.std_string)
cg.add(var.add_data(cg.FlashStringLiteral(key), templ))
cg.add(var.init_data_template(len(config[CONF_DATA_TEMPLATE])))
for key, value in config[CONF_DATA_TEMPLATE].items():
templ = await cg.templatable(value, args, None)
cg.add(var.add_data_template(key, templ))
templ = await cg.templatable(value, args, cg.std_string)
cg.add(var.add_data_template(cg.FlashStringLiteral(key), templ))
cg.add(var.init_variables(len(config[CONF_VARIABLES])))
for key, value in config[CONF_VARIABLES].items():
templ = await cg.templatable(value, args, None)
cg.add(var.add_variable(key, templ))
templ = await cg.templatable(value, args, cg.std_string)
cg.add(var.add_variable(cg.FlashStringLiteral(key), templ))
if on_error := config.get(CONF_ON_ERROR):
cg.add_define("USE_API_HOMEASSISTANT_ACTION_RESPONSES")
@@ -609,24 +609,24 @@ async def homeassistant_event_to_code(config, action_id, template_arg, args):
cg.add_define("USE_API_HOMEASSISTANT_SERVICES")
serv = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, serv, True)
templ = await cg.templatable(config[CONF_EVENT], args, None)
templ = await cg.templatable(config[CONF_EVENT], args, cg.std_string)
cg.add(var.set_service(templ))
# Initialize FixedVectors with exact sizes from config
cg.add(var.init_data(len(config[CONF_DATA])))
for key, value in config[CONF_DATA].items():
templ = await cg.templatable(value, args, None)
cg.add(var.add_data(key, templ))
templ = await cg.templatable(value, args, cg.std_string)
cg.add(var.add_data(cg.FlashStringLiteral(key), templ))
cg.add(var.init_data_template(len(config[CONF_DATA_TEMPLATE])))
for key, value in config[CONF_DATA_TEMPLATE].items():
templ = await cg.templatable(value, args, None)
cg.add(var.add_data_template(key, templ))
templ = await cg.templatable(value, args, cg.std_string)
cg.add(var.add_data_template(cg.FlashStringLiteral(key), templ))
cg.add(var.init_variables(len(config[CONF_VARIABLES])))
for key, value in config[CONF_VARIABLES].items():
templ = await cg.templatable(value, args, None)
cg.add(var.add_variable(key, templ))
templ = await cg.templatable(value, args, cg.std_string)
cg.add(var.add_variable(cg.FlashStringLiteral(key), templ))
return var
@@ -649,11 +649,11 @@ async def homeassistant_tag_scanned_to_code(config, action_id, template_arg, arg
cg.add_define("USE_API_HOMEASSISTANT_SERVICES")
serv = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, serv, True)
cg.add(var.set_service("esphome.tag_scanned"))
cg.add(var.set_service(cg.FlashStringLiteral("esphome.tag_scanned")))
# Initialize FixedVector with exact size (1 data field)
cg.add(var.init_data(1))
templ = await cg.templatable(config[CONF_TAG], args, cg.std_string)
cg.add(var.add_data("tag_id", templ))
cg.add(var.add_data(cg.FlashStringLiteral("tag_id"), templ))
return var

File diff suppressed because it is too large Load Diff

View File

@@ -28,7 +28,7 @@ static constexpr size_t MAX_INITIAL_PER_BATCH = 34; // For clients >= AP
static_assert(MAX_MESSAGES_PER_BATCH >= MAX_INITIAL_PER_BATCH,
"MAX_MESSAGES_PER_BATCH must be >= MAX_INITIAL_PER_BATCH");
class APIConnection final : public APIServerConnection {
class APIConnection final : public APIServerConnectionBase {
public:
friend class APIServer;
friend class ListEntitiesIterator;
@@ -47,72 +47,72 @@ class APIConnection final : public APIServerConnection {
#endif
#ifdef USE_COVER
bool send_cover_state(cover::Cover *cover);
void cover_command(const CoverCommandRequest &msg) override;
void on_cover_command_request(const CoverCommandRequest &msg) override;
#endif
#ifdef USE_FAN
bool send_fan_state(fan::Fan *fan);
void fan_command(const FanCommandRequest &msg) override;
void on_fan_command_request(const FanCommandRequest &msg) override;
#endif
#ifdef USE_LIGHT
bool send_light_state(light::LightState *light);
void light_command(const LightCommandRequest &msg) override;
void on_light_command_request(const LightCommandRequest &msg) override;
#endif
#ifdef USE_SENSOR
bool send_sensor_state(sensor::Sensor *sensor);
#endif
#ifdef USE_SWITCH
bool send_switch_state(switch_::Switch *a_switch);
void switch_command(const SwitchCommandRequest &msg) override;
void on_switch_command_request(const SwitchCommandRequest &msg) override;
#endif
#ifdef USE_TEXT_SENSOR
bool send_text_sensor_state(text_sensor::TextSensor *text_sensor);
#endif
#ifdef USE_CAMERA
void set_camera_state(std::shared_ptr<camera::CameraImage> image);
void camera_image(const CameraImageRequest &msg) override;
void on_camera_image_request(const CameraImageRequest &msg) override;
#endif
#ifdef USE_CLIMATE
bool send_climate_state(climate::Climate *climate);
void climate_command(const ClimateCommandRequest &msg) override;
void on_climate_command_request(const ClimateCommandRequest &msg) override;
#endif
#ifdef USE_NUMBER
bool send_number_state(number::Number *number);
void number_command(const NumberCommandRequest &msg) override;
void on_number_command_request(const NumberCommandRequest &msg) override;
#endif
#ifdef USE_DATETIME_DATE
bool send_date_state(datetime::DateEntity *date);
void date_command(const DateCommandRequest &msg) override;
void on_date_command_request(const DateCommandRequest &msg) override;
#endif
#ifdef USE_DATETIME_TIME
bool send_time_state(datetime::TimeEntity *time);
void time_command(const TimeCommandRequest &msg) override;
void on_time_command_request(const TimeCommandRequest &msg) override;
#endif
#ifdef USE_DATETIME_DATETIME
bool send_datetime_state(datetime::DateTimeEntity *datetime);
void datetime_command(const DateTimeCommandRequest &msg) override;
void on_date_time_command_request(const DateTimeCommandRequest &msg) override;
#endif
#ifdef USE_TEXT
bool send_text_state(text::Text *text);
void text_command(const TextCommandRequest &msg) override;
void on_text_command_request(const TextCommandRequest &msg) override;
#endif
#ifdef USE_SELECT
bool send_select_state(select::Select *select);
void select_command(const SelectCommandRequest &msg) override;
void on_select_command_request(const SelectCommandRequest &msg) override;
#endif
#ifdef USE_BUTTON
void button_command(const ButtonCommandRequest &msg) override;
void on_button_command_request(const ButtonCommandRequest &msg) override;
#endif
#ifdef USE_LOCK
bool send_lock_state(lock::Lock *a_lock);
void lock_command(const LockCommandRequest &msg) override;
void on_lock_command_request(const LockCommandRequest &msg) override;
#endif
#ifdef USE_VALVE
bool send_valve_state(valve::Valve *valve);
void valve_command(const ValveCommandRequest &msg) override;
void on_valve_command_request(const ValveCommandRequest &msg) override;
#endif
#ifdef USE_MEDIA_PLAYER
bool send_media_player_state(media_player::MediaPlayer *media_player);
void media_player_command(const MediaPlayerCommandRequest &msg) override;
void on_media_player_command_request(const MediaPlayerCommandRequest &msg) override;
#endif
bool try_send_log_message(int level, const char *tag, const char *line, size_t message_len);
#ifdef USE_API_HOMEASSISTANT_SERVICES
@@ -126,18 +126,18 @@ class APIConnection final : public APIServerConnection {
#endif // USE_API_HOMEASSISTANT_ACTION_RESPONSES
#endif // USE_API_HOMEASSISTANT_SERVICES
#ifdef USE_BLUETOOTH_PROXY
void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
void unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) override;
void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
void on_unsubscribe_bluetooth_le_advertisements_request() override;
void bluetooth_device_request(const BluetoothDeviceRequest &msg) override;
void bluetooth_gatt_read(const BluetoothGATTReadRequest &msg) override;
void bluetooth_gatt_write(const BluetoothGATTWriteRequest &msg) override;
void bluetooth_gatt_read_descriptor(const BluetoothGATTReadDescriptorRequest &msg) override;
void bluetooth_gatt_write_descriptor(const BluetoothGATTWriteDescriptorRequest &msg) override;
void bluetooth_gatt_get_services(const BluetoothGATTGetServicesRequest &msg) override;
void bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) override;
bool send_subscribe_bluetooth_connections_free_response(const SubscribeBluetoothConnectionsFreeRequest &msg) override;
void bluetooth_scanner_set_mode(const BluetoothScannerSetModeRequest &msg) override;
void on_bluetooth_device_request(const BluetoothDeviceRequest &msg) override;
void on_bluetooth_gatt_read_request(const BluetoothGATTReadRequest &msg) override;
void on_bluetooth_gatt_write_request(const BluetoothGATTWriteRequest &msg) override;
void on_bluetooth_gatt_read_descriptor_request(const BluetoothGATTReadDescriptorRequest &msg) override;
void on_bluetooth_gatt_write_descriptor_request(const BluetoothGATTWriteDescriptorRequest &msg) override;
void on_bluetooth_gatt_get_services_request(const BluetoothGATTGetServicesRequest &msg) override;
void on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &msg) override;
void on_subscribe_bluetooth_connections_free_request() override;
void on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &msg) override;
#endif
#ifdef USE_HOMEASSISTANT_TIME
@@ -148,33 +148,33 @@ class APIConnection final : public APIServerConnection {
#endif
#ifdef USE_VOICE_ASSISTANT
void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) override;
void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) override;
void on_voice_assistant_response(const VoiceAssistantResponse &msg) override;
void on_voice_assistant_event_response(const VoiceAssistantEventResponse &msg) override;
void on_voice_assistant_audio(const VoiceAssistantAudio &msg) override;
void on_voice_assistant_timer_event_response(const VoiceAssistantTimerEventResponse &msg) override;
void on_voice_assistant_announce_request(const VoiceAssistantAnnounceRequest &msg) override;
bool send_voice_assistant_get_configuration_response(const VoiceAssistantConfigurationRequest &msg) override;
void voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) override;
void on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &msg) override;
void on_voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) override;
#endif
#ifdef USE_ZWAVE_PROXY
void zwave_proxy_frame(const ZWaveProxyFrame &msg) override;
void zwave_proxy_request(const ZWaveProxyRequest &msg) override;
void on_z_wave_proxy_frame(const ZWaveProxyFrame &msg) override;
void on_z_wave_proxy_request(const ZWaveProxyRequest &msg) override;
#endif
#ifdef USE_ALARM_CONTROL_PANEL
bool send_alarm_control_panel_state(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel);
void alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) override;
void on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &msg) override;
#endif
#ifdef USE_WATER_HEATER
bool send_water_heater_state(water_heater::WaterHeater *water_heater);
void water_heater_command(const WaterHeaterCommandRequest &msg) override;
void on_water_heater_command_request(const WaterHeaterCommandRequest &msg) override;
#endif
#ifdef USE_IR_RF
void infrared_rf_transmit_raw_timings(const InfraredRFTransmitRawTimingsRequest &msg) override;
void on_infrared_rf_transmit_raw_timings_request(const InfraredRFTransmitRawTimingsRequest &msg) override;
void send_infrared_rf_receive_event(const InfraredRFReceiveEvent &msg);
#endif
@@ -184,11 +184,11 @@ class APIConnection final : public APIServerConnection {
#ifdef USE_UPDATE
bool send_update_state(update::UpdateEntity *update);
void update_command(const UpdateCommandRequest &msg) override;
void on_update_command_request(const UpdateCommandRequest &msg) override;
#endif
void on_disconnect_response(const DisconnectResponse &value) override;
void on_ping_response(const PingResponse &value) override {
void on_disconnect_response() override;
void on_ping_response() override {
// we initiated ping
this->flags_.sent_ping = false;
}
@@ -198,12 +198,12 @@ class APIConnection final : public APIServerConnection {
#ifdef USE_HOMEASSISTANT_TIME
void on_get_time_response(const GetTimeResponse &value) override;
#endif
bool send_hello_response(const HelloRequest &msg) override;
bool send_disconnect_response(const DisconnectRequest &msg) override;
bool send_ping_response(const PingRequest &msg) override;
bool send_device_info_response(const DeviceInfoRequest &msg) override;
void list_entities(const ListEntitiesRequest &msg) override { this->begin_iterator_(ActiveIterator::LIST_ENTITIES); }
void subscribe_states(const SubscribeStatesRequest &msg) override {
void on_hello_request(const HelloRequest &msg) override;
void on_disconnect_request() override;
void on_ping_request() override;
void on_device_info_request() override;
void on_list_entities_request() override { this->begin_iterator_(ActiveIterator::LIST_ENTITIES); }
void on_subscribe_states_request() override {
this->flags_.state_subscription = true;
// Start initial state iterator only if no iterator is active
// If list_entities is running, we'll start initial_state when it completes
@@ -211,21 +211,19 @@ class APIConnection final : public APIServerConnection {
this->begin_iterator_(ActiveIterator::INITIAL_STATE);
}
}
void subscribe_logs(const SubscribeLogsRequest &msg) override {
void on_subscribe_logs_request(const SubscribeLogsRequest &msg) override {
this->flags_.log_subscription = msg.level;
if (msg.dump_config)
App.schedule_dump_config();
}
#ifdef USE_API_HOMEASSISTANT_SERVICES
void subscribe_homeassistant_services(const SubscribeHomeassistantServicesRequest &msg) override {
this->flags_.service_call_subscription = true;
}
void on_subscribe_homeassistant_services_request() override { this->flags_.service_call_subscription = true; }
#endif
#ifdef USE_API_HOMEASSISTANT_STATES
void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) override;
void on_subscribe_home_assistant_states_request() override;
#endif
#ifdef USE_API_USER_DEFINED_ACTIONS
void execute_service(const ExecuteServiceRequest &msg) override;
void on_execute_service_request(const ExecuteServiceRequest &msg) override;
#ifdef USE_API_USER_DEFINED_ACTION_RESPONSES
void send_execute_service_response(uint32_t call_id, bool success, StringRef error_message);
#ifdef USE_API_USER_DEFINED_ACTION_RESPONSES_JSON
@@ -235,7 +233,7 @@ class APIConnection final : public APIServerConnection {
#endif // USE_API_USER_DEFINED_ACTION_RESPONSES
#endif
#ifdef USE_API_NOISE
bool send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyRequest &msg) override;
void on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &msg) override;
#endif
bool is_authenticated() override {
@@ -255,17 +253,7 @@ class APIConnection final : public APIServerConnection {
void on_fatal_error() override;
void on_no_setup_connection() override;
ProtoWriteBuffer create_buffer(uint32_t reserve_size) override {
// FIXME: ensure no recursive writes can happen
// Get header padding size - used for both reserve and insert
uint8_t header_padding = this->helper_->frame_header_padding();
// Get shared buffer from parent server
std::vector<uint8_t> &shared_buf = this->parent_->get_shared_buffer_ref();
this->prepare_first_message_buffer(shared_buf, header_padding,
reserve_size + header_padding + this->helper_->frame_footer_size());
return {&shared_buf};
}
bool send_message_impl(const ProtoMessage &msg, uint8_t message_type) override;
void prepare_first_message_buffer(std::vector<uint8_t> &shared_buf, size_t header_padding, size_t total_size) {
shared_buf.clear();
@@ -277,6 +265,13 @@ class APIConnection final : public APIServerConnection {
shared_buf.resize(header_padding);
}
// Convenience overload - computes frame overhead internally
void prepare_first_message_buffer(std::vector<uint8_t> &shared_buf, size_t payload_size) {
const uint8_t header_padding = this->helper_->frame_header_padding();
const uint8_t footer_size = this->helper_->frame_footer_size();
this->prepare_first_message_buffer(shared_buf, header_padding, payload_size + header_padding + footer_size);
}
bool try_to_clear_buffer(bool log_out_of_space);
bool send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) override;
@@ -290,6 +285,21 @@ class APIConnection final : public APIServerConnection {
// Helper function to handle authentication completion
void complete_authentication_();
// Pattern B helpers: send response and return success/failure
bool send_hello_response_(const HelloRequest &msg);
bool send_disconnect_response_();
bool send_ping_response_();
bool send_device_info_response_();
#ifdef USE_API_NOISE
bool send_noise_encryption_set_key_response_(const NoiseEncryptionSetKeyRequest &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_subscribe_bluetooth_connections_free_response_();
#endif
#ifdef USE_VOICE_ASSISTANT
bool send_voice_assistant_get_configuration_response_(const VoiceAssistantConfigurationRequest &msg);
#endif
#ifdef USE_CAMERA
void try_send_camera_image_();
#endif
@@ -300,21 +310,21 @@ class APIConnection final : public APIServerConnection {
// Non-template helper to encode any ProtoMessage
static uint16_t encode_message_to_buffer(ProtoMessage &msg, uint8_t message_type, APIConnection *conn,
uint32_t remaining_size, bool is_single);
uint32_t remaining_size);
// Helper to fill entity state base and encode message
static uint16_t fill_and_encode_entity_state(EntityBase *entity, StateResponseProtoMessage &msg, uint8_t message_type,
APIConnection *conn, uint32_t remaining_size, bool is_single) {
APIConnection *conn, uint32_t remaining_size) {
msg.key = entity->get_object_id_hash();
#ifdef USE_DEVICES
msg.device_id = entity->get_device_id();
#endif
return encode_message_to_buffer(msg, message_type, conn, remaining_size, is_single);
return encode_message_to_buffer(msg, message_type, conn, remaining_size);
}
// Helper to fill entity info base and encode message
static uint16_t fill_and_encode_entity_info(EntityBase *entity, InfoResponseProtoMessage &msg, uint8_t message_type,
APIConnection *conn, uint32_t remaining_size, bool is_single) {
APIConnection *conn, uint32_t remaining_size) {
// Set common fields that are shared by all entity types
msg.key = entity->get_object_id_hash();
@@ -341,7 +351,7 @@ class APIConnection final : public APIServerConnection {
#ifdef USE_DEVICES
msg.device_id = entity->get_device_id();
#endif
return encode_message_to_buffer(msg, message_type, conn, remaining_size, is_single);
return encode_message_to_buffer(msg, message_type, conn, remaining_size);
}
#ifdef USE_VOICE_ASSISTANT
@@ -372,141 +382,108 @@ class APIConnection final : public APIServerConnection {
}
#ifdef USE_BINARY_SENSOR
static uint16_t try_send_binary_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_binary_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_binary_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
static uint16_t try_send_binary_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
#endif
#ifdef USE_COVER
static uint16_t try_send_cover_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_cover_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
static uint16_t try_send_cover_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
static uint16_t try_send_cover_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
#endif
#ifdef USE_FAN
static uint16_t try_send_fan_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
static uint16_t try_send_fan_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
static uint16_t try_send_fan_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
static uint16_t try_send_fan_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
#endif
#ifdef USE_LIGHT
static uint16_t try_send_light_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_light_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
static uint16_t try_send_light_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
static uint16_t try_send_light_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
#endif
#ifdef USE_SENSOR
static uint16_t try_send_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
static uint16_t try_send_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
#endif
#ifdef USE_SWITCH
static uint16_t try_send_switch_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_switch_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_switch_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
static uint16_t try_send_switch_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
#endif
#ifdef USE_TEXT_SENSOR
static uint16_t try_send_text_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_text_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_text_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
static uint16_t try_send_text_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
#endif
#ifdef USE_CLIMATE
static uint16_t try_send_climate_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_climate_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_climate_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
static uint16_t try_send_climate_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
#endif
#ifdef USE_NUMBER
static uint16_t try_send_number_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_number_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_number_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
static uint16_t try_send_number_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
#endif
#ifdef USE_DATETIME_DATE
static uint16_t try_send_date_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
static uint16_t try_send_date_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
static uint16_t try_send_date_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
static uint16_t try_send_date_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
#endif
#ifdef USE_DATETIME_TIME
static uint16_t try_send_time_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
static uint16_t try_send_time_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
static uint16_t try_send_time_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
static uint16_t try_send_time_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
#endif
#ifdef USE_DATETIME_DATETIME
static uint16_t try_send_datetime_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_datetime_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_datetime_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
static uint16_t try_send_datetime_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
#endif
#ifdef USE_TEXT
static uint16_t try_send_text_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
static uint16_t try_send_text_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
static uint16_t try_send_text_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
static uint16_t try_send_text_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
#endif
#ifdef USE_SELECT
static uint16_t try_send_select_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_select_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_select_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
static uint16_t try_send_select_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
#endif
#ifdef USE_BUTTON
static uint16_t try_send_button_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_button_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
#endif
#ifdef USE_LOCK
static uint16_t try_send_lock_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
static uint16_t try_send_lock_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
static uint16_t try_send_lock_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
static uint16_t try_send_lock_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
#endif
#ifdef USE_VALVE
static uint16_t try_send_valve_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_valve_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
static uint16_t try_send_valve_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
static uint16_t try_send_valve_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
#endif
#ifdef USE_MEDIA_PLAYER
static uint16_t try_send_media_player_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_media_player_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_media_player_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
static uint16_t try_send_media_player_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
#endif
#ifdef USE_ALARM_CONTROL_PANEL
static uint16_t try_send_alarm_control_panel_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_alarm_control_panel_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_alarm_control_panel_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
static uint16_t try_send_alarm_control_panel_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
#endif
#ifdef USE_WATER_HEATER
static uint16_t try_send_water_heater_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_water_heater_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_water_heater_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
static uint16_t try_send_water_heater_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
#endif
#ifdef USE_INFRARED
static uint16_t try_send_infrared_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_infrared_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
#endif
#ifdef USE_EVENT
static uint16_t try_send_event_response(event::Event *event, StringRef event_type, APIConnection *conn,
uint32_t remaining_size, bool is_single);
static uint16_t try_send_event_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
uint32_t remaining_size);
static uint16_t try_send_event_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
#endif
#ifdef USE_UPDATE
static uint16_t try_send_update_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_update_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_update_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
static uint16_t try_send_update_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
#endif
#ifdef USE_CAMERA
static uint16_t try_send_camera_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_camera_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
#endif
// Method for ListEntitiesDone batching
static uint16_t try_send_list_info_done(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_list_info_done(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
// Method for DisconnectRequest batching
static uint16_t try_send_disconnect_request(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_disconnect_request(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
// Batch message method for ping requests
static uint16_t try_send_ping_request(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_ping_request(EntityBase *entity, APIConnection *conn, uint32_t remaining_size);
// === Optimal member ordering for 32-bit systems ===
@@ -541,7 +518,7 @@ class APIConnection final : public APIServerConnection {
#endif
// Function pointer type for message encoding
using MessageCreatorPtr = uint16_t (*)(EntityBase *, APIConnection *, uint32_t remaining_size, bool is_single);
using MessageCreatorPtr = uint16_t (*)(EntityBase *, APIConnection *, uint32_t remaining_size);
// Generic batching mechanism for both state updates and entity info
struct DeferredBatch {
@@ -654,7 +631,7 @@ class APIConnection final : public APIServerConnection {
// Dispatch message encoding based on message_type - replaces function pointer storage
// Switch assigns pointer, single call site for smaller code size
uint16_t dispatch_message_(const DeferredBatch::BatchItem &item, uint32_t remaining_size, bool is_single);
uint16_t dispatch_message_(const DeferredBatch::BatchItem &item, uint32_t remaining_size, bool batch_first);
#ifdef HAS_PROTO_MESSAGE_DUMP
void log_batch_item_(const DeferredBatch::BatchItem &item) {
@@ -686,19 +663,7 @@ class APIConnection final : public APIServerConnection {
// Tries immediate send if should_send_immediately_() returns true and buffer has space
// Falls back to batching if immediate send fails or isn't applicable
bool send_message_smart_(EntityBase *entity, uint8_t message_type, uint8_t estimated_size,
uint8_t aux_data_index = DeferredBatch::AUX_DATA_UNUSED) {
if (this->should_send_immediately_(message_type) && this->helper_->can_write_without_blocking()) {
DeferredBatch::BatchItem item{entity, message_type, estimated_size, aux_data_index};
if (this->dispatch_message_(item, MAX_BATCH_PACKET_SIZE, true) &&
this->send_buffer(ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, message_type)) {
#ifdef HAS_PROTO_MESSAGE_DUMP
this->log_batch_item_(item);
#endif
return true;
}
}
return this->schedule_message_(entity, message_type, estimated_size, aux_data_index);
}
uint8_t aux_data_index = DeferredBatch::AUX_DATA_UNUSED);
// Helper function to schedule a deferred message with known message type
bool schedule_message_(EntityBase *entity, uint8_t message_type, uint8_t estimated_size,

View File

@@ -440,19 +440,6 @@ class PingResponse final : public ProtoMessage {
protected:
};
class DeviceInfoRequest final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 9;
static constexpr uint8_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "device_info_request"; }
#endif
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
#endif
protected:
};
#ifdef USE_AREAS
class AreaInfo final : public ProtoMessage {
public:
@@ -546,19 +533,6 @@ class DeviceInfoResponse final : public ProtoMessage {
protected:
};
class ListEntitiesRequest final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 11;
static constexpr uint8_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "list_entities_request"; }
#endif
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
#endif
protected:
};
class ListEntitiesDoneResponse final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 19;
@@ -572,19 +546,6 @@ class ListEntitiesDoneResponse final : public ProtoMessage {
protected:
};
class SubscribeStatesRequest final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 20;
static constexpr uint8_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "subscribe_states_request"; }
#endif
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
#endif
protected:
};
#ifdef USE_BINARY_SENSOR
class ListEntitiesBinarySensorResponse final : public InfoResponseProtoMessage {
public:
@@ -1037,19 +998,6 @@ class NoiseEncryptionSetKeyResponse final : public ProtoMessage {
};
#endif
#ifdef USE_API_HOMEASSISTANT_SERVICES
class SubscribeHomeassistantServicesRequest final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 34;
static constexpr uint8_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "subscribe_homeassistant_services_request"; }
#endif
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
#endif
protected:
};
class HomeassistantServiceMap final : public ProtoMessage {
public:
StringRef key{};
@@ -1117,19 +1065,6 @@ class HomeassistantActionResponse final : public ProtoDecodableMessage {
};
#endif
#ifdef USE_API_HOMEASSISTANT_STATES
class SubscribeHomeAssistantStatesRequest final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 38;
static constexpr uint8_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "subscribe_home_assistant_states_request"; }
#endif
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
#endif
protected:
};
class SubscribeHomeAssistantStateResponse final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 39;
@@ -2160,19 +2095,6 @@ class BluetoothGATTNotifyDataResponse final : public ProtoMessage {
protected:
};
class SubscribeBluetoothConnectionsFreeRequest final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 80;
static constexpr uint8_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "subscribe_bluetooth_connections_free_request"; }
#endif
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
#endif
protected:
};
class BluetoothConnectionsFreeResponse final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 81;
@@ -2279,19 +2201,6 @@ class BluetoothDeviceUnpairingResponse final : public ProtoMessage {
protected:
};
class UnsubscribeBluetoothLEAdvertisementsRequest final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 87;
static constexpr uint8_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "unsubscribe_bluetooth_le_advertisements_request"; }
#endif
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *dump_to(DumpBuffer &out) const override;
#endif
protected:
};
class BluetoothDeviceClearCacheResponse final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 88;

View File

@@ -23,15 +23,8 @@ static inline void append_field_prefix(DumpBuffer &out, const char *field_name,
out.append(indent, ' ').append(field_name).append(": ");
}
static inline void append_with_newline(DumpBuffer &out, const char *str) {
out.append(str);
out.append("\n");
}
static inline void append_uint(DumpBuffer &out, uint32_t value) {
char buf[16];
snprintf(buf, sizeof(buf), "%" PRIu32, value);
out.append(buf);
out.set_pos(buf_append_printf(out.data(), DumpBuffer::CAPACITY, out.pos(), "%" PRIu32, value));
}
// RAII helper for message dump formatting
@@ -49,31 +42,23 @@ class MessageDumpHelper {
// Helper functions to reduce code duplication in dump methods
static void dump_field(DumpBuffer &out, const char *field_name, int32_t value, int indent = 2) {
char buffer[64];
append_field_prefix(out, field_name, indent);
snprintf(buffer, 64, "%" PRId32, value);
append_with_newline(out, buffer);
out.set_pos(buf_append_printf(out.data(), DumpBuffer::CAPACITY, out.pos(), "%" PRId32 "\n", value));
}
static void dump_field(DumpBuffer &out, const char *field_name, uint32_t value, int indent = 2) {
char buffer[64];
append_field_prefix(out, field_name, indent);
snprintf(buffer, 64, "%" PRIu32, value);
append_with_newline(out, buffer);
out.set_pos(buf_append_printf(out.data(), DumpBuffer::CAPACITY, out.pos(), "%" PRIu32 "\n", value));
}
static void dump_field(DumpBuffer &out, const char *field_name, float value, int indent = 2) {
char buffer[64];
append_field_prefix(out, field_name, indent);
snprintf(buffer, 64, "%g", value);
append_with_newline(out, buffer);
out.set_pos(buf_append_printf(out.data(), DumpBuffer::CAPACITY, out.pos(), "%g\n", value));
}
static void dump_field(DumpBuffer &out, const char *field_name, uint64_t value, int indent = 2) {
char buffer[64];
append_field_prefix(out, field_name, indent);
snprintf(buffer, 64, "%" PRIu64, value);
append_with_newline(out, buffer);
out.set_pos(buf_append_printf(out.data(), DumpBuffer::CAPACITY, out.pos(), "%" PRIu64 "\n", value));
}
static void dump_field(DumpBuffer &out, const char *field_name, bool value, int indent = 2) {
@@ -112,7 +97,7 @@ static void dump_bytes_field(DumpBuffer &out, const char *field_name, const uint
char hex_buf[format_hex_pretty_size(160)];
append_field_prefix(out, field_name, indent);
format_hex_pretty_to(hex_buf, data, len);
append_with_newline(out, hex_buf);
out.append(hex_buf).append("\n");
}
template<> const char *proto_enum_to_string<enums::EntityCategory>(enums::EntityCategory value) {
@@ -779,10 +764,6 @@ const char *PingResponse::dump_to(DumpBuffer &out) const {
out.append("PingResponse {}");
return out.c_str();
}
const char *DeviceInfoRequest::dump_to(DumpBuffer &out) const {
out.append("DeviceInfoRequest {}");
return out.c_str();
}
#ifdef USE_AREAS
const char *AreaInfo::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, "AreaInfo");
@@ -863,18 +844,10 @@ const char *DeviceInfoResponse::dump_to(DumpBuffer &out) const {
#endif
return out.c_str();
}
const char *ListEntitiesRequest::dump_to(DumpBuffer &out) const {
out.append("ListEntitiesRequest {}");
return out.c_str();
}
const char *ListEntitiesDoneResponse::dump_to(DumpBuffer &out) const {
out.append("ListEntitiesDoneResponse {}");
return out.c_str();
}
const char *SubscribeStatesRequest::dump_to(DumpBuffer &out) const {
out.append("SubscribeStatesRequest {}");
return out.c_str();
}
#ifdef USE_BINARY_SENSOR
const char *ListEntitiesBinarySensorResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, "ListEntitiesBinarySensorResponse");
@@ -1206,10 +1179,6 @@ const char *NoiseEncryptionSetKeyResponse::dump_to(DumpBuffer &out) const {
}
#endif
#ifdef USE_API_HOMEASSISTANT_SERVICES
const char *SubscribeHomeassistantServicesRequest::dump_to(DumpBuffer &out) const {
out.append("SubscribeHomeassistantServicesRequest {}");
return out.c_str();
}
const char *HomeassistantServiceMap::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, "HomeassistantServiceMap");
dump_field(out, "key", this->key);
@@ -1260,10 +1229,6 @@ const char *HomeassistantActionResponse::dump_to(DumpBuffer &out) const {
}
#endif
#ifdef USE_API_HOMEASSISTANT_STATES
const char *SubscribeHomeAssistantStatesRequest::dump_to(DumpBuffer &out) const {
out.append("SubscribeHomeAssistantStatesRequest {}");
return out.c_str();
}
const char *SubscribeHomeAssistantStateResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, "SubscribeHomeAssistantStateResponse");
dump_field(out, "entity_id", this->entity_id);
@@ -1939,10 +1904,6 @@ const char *BluetoothGATTNotifyDataResponse::dump_to(DumpBuffer &out) const {
dump_bytes_field(out, "data", this->data_ptr_, this->data_len_);
return out.c_str();
}
const char *SubscribeBluetoothConnectionsFreeRequest::dump_to(DumpBuffer &out) const {
out.append("SubscribeBluetoothConnectionsFreeRequest {}");
return out.c_str();
}
const char *BluetoothConnectionsFreeResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, "BluetoothConnectionsFreeResponse");
dump_field(out, "free", this->free);
@@ -1985,10 +1946,6 @@ const char *BluetoothDeviceUnpairingResponse::dump_to(DumpBuffer &out) const {
dump_field(out, "error", this->error);
return out.c_str();
}
const char *UnsubscribeBluetoothLEAdvertisementsRequest::dump_to(DumpBuffer &out) const {
out.append("UnsubscribeBluetoothLEAdvertisementsRequest {}");
return out.c_str();
}
const char *BluetoothDeviceClearCacheResponse::dump_to(DumpBuffer &out) const {
MessageDumpHelper helper(out, "BluetoothDeviceClearCacheResponse");
dump_field(out, "address", this->address);

View File

@@ -15,9 +15,29 @@ void APIServerConnectionBase::log_receive_message_(const LogString *name, const
DumpBuffer dump_buf;
ESP_LOGVV(TAG, "%s: %s", LOG_STR_ARG(name), msg.dump_to(dump_buf));
}
void APIServerConnectionBase::log_receive_message_(const LogString *name) {
ESP_LOGVV(TAG, "%s: {}", LOG_STR_ARG(name));
}
#endif
void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) {
// Check authentication/connection requirements
switch (msg_type) {
case HelloRequest::MESSAGE_TYPE: // No setup required
case DisconnectRequest::MESSAGE_TYPE: // No setup required
case PingRequest::MESSAGE_TYPE: // No setup required
break;
case 9 /* DeviceInfoRequest is empty */: // Connection setup only
if (!this->check_connection_setup_()) {
return;
}
break;
default:
if (!this->check_authenticated_()) {
return;
}
break;
}
switch (msg_type) {
case HelloRequest::MESSAGE_TYPE: {
HelloRequest msg;
@@ -29,66 +49,52 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
break;
}
case DisconnectRequest::MESSAGE_TYPE: {
DisconnectRequest msg;
// Empty message: no decode needed
#ifdef HAS_PROTO_MESSAGE_DUMP
this->log_receive_message_(LOG_STR("on_disconnect_request"), msg);
this->log_receive_message_(LOG_STR("on_disconnect_request"));
#endif
this->on_disconnect_request(msg);
this->on_disconnect_request();
break;
}
case DisconnectResponse::MESSAGE_TYPE: {
DisconnectResponse msg;
// Empty message: no decode needed
#ifdef HAS_PROTO_MESSAGE_DUMP
this->log_receive_message_(LOG_STR("on_disconnect_response"), msg);
this->log_receive_message_(LOG_STR("on_disconnect_response"));
#endif
this->on_disconnect_response(msg);
this->on_disconnect_response();
break;
}
case PingRequest::MESSAGE_TYPE: {
PingRequest msg;
// Empty message: no decode needed
#ifdef HAS_PROTO_MESSAGE_DUMP
this->log_receive_message_(LOG_STR("on_ping_request"), msg);
this->log_receive_message_(LOG_STR("on_ping_request"));
#endif
this->on_ping_request(msg);
this->on_ping_request();
break;
}
case PingResponse::MESSAGE_TYPE: {
PingResponse msg;
// Empty message: no decode needed
#ifdef HAS_PROTO_MESSAGE_DUMP
this->log_receive_message_(LOG_STR("on_ping_response"), msg);
this->log_receive_message_(LOG_STR("on_ping_response"));
#endif
this->on_ping_response(msg);
this->on_ping_response();
break;
}
case DeviceInfoRequest::MESSAGE_TYPE: {
DeviceInfoRequest msg;
// Empty message: no decode needed
case 9 /* DeviceInfoRequest is empty */: {
#ifdef HAS_PROTO_MESSAGE_DUMP
this->log_receive_message_(LOG_STR("on_device_info_request"), msg);
this->log_receive_message_(LOG_STR("on_device_info_request"));
#endif
this->on_device_info_request(msg);
this->on_device_info_request();
break;
}
case ListEntitiesRequest::MESSAGE_TYPE: {
ListEntitiesRequest msg;
// Empty message: no decode needed
case 11 /* ListEntitiesRequest is empty */: {
#ifdef HAS_PROTO_MESSAGE_DUMP
this->log_receive_message_(LOG_STR("on_list_entities_request"), msg);
this->log_receive_message_(LOG_STR("on_list_entities_request"));
#endif
this->on_list_entities_request(msg);
this->on_list_entities_request();
break;
}
case SubscribeStatesRequest::MESSAGE_TYPE: {
SubscribeStatesRequest msg;
// Empty message: no decode needed
case 20 /* SubscribeStatesRequest is empty */: {
#ifdef HAS_PROTO_MESSAGE_DUMP
this->log_receive_message_(LOG_STR("on_subscribe_states_request"), msg);
this->log_receive_message_(LOG_STR("on_subscribe_states_request"));
#endif
this->on_subscribe_states_request(msg);
this->on_subscribe_states_request();
break;
}
case SubscribeLogsRequest::MESSAGE_TYPE: {
@@ -145,13 +151,11 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
}
#endif
#ifdef USE_API_HOMEASSISTANT_SERVICES
case SubscribeHomeassistantServicesRequest::MESSAGE_TYPE: {
SubscribeHomeassistantServicesRequest msg;
// Empty message: no decode needed
case 34 /* SubscribeHomeassistantServicesRequest is empty */: {
#ifdef HAS_PROTO_MESSAGE_DUMP
this->log_receive_message_(LOG_STR("on_subscribe_homeassistant_services_request"), msg);
this->log_receive_message_(LOG_STR("on_subscribe_homeassistant_services_request"));
#endif
this->on_subscribe_homeassistant_services_request(msg);
this->on_subscribe_homeassistant_services_request();
break;
}
#endif
@@ -165,13 +169,11 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
break;
}
#ifdef USE_API_HOMEASSISTANT_STATES
case SubscribeHomeAssistantStatesRequest::MESSAGE_TYPE: {
SubscribeHomeAssistantStatesRequest msg;
// Empty message: no decode needed
case 38 /* SubscribeHomeAssistantStatesRequest is empty */: {
#ifdef HAS_PROTO_MESSAGE_DUMP
this->log_receive_message_(LOG_STR("on_subscribe_home_assistant_states_request"), msg);
this->log_receive_message_(LOG_STR("on_subscribe_home_assistant_states_request"));
#endif
this->on_subscribe_home_assistant_states_request(msg);
this->on_subscribe_home_assistant_states_request();
break;
}
#endif
@@ -374,24 +376,20 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
}
#endif
#ifdef USE_BLUETOOTH_PROXY
case SubscribeBluetoothConnectionsFreeRequest::MESSAGE_TYPE: {
SubscribeBluetoothConnectionsFreeRequest msg;
// Empty message: no decode needed
case 80 /* SubscribeBluetoothConnectionsFreeRequest is empty */: {
#ifdef HAS_PROTO_MESSAGE_DUMP
this->log_receive_message_(LOG_STR("on_subscribe_bluetooth_connections_free_request"), msg);
this->log_receive_message_(LOG_STR("on_subscribe_bluetooth_connections_free_request"));
#endif
this->on_subscribe_bluetooth_connections_free_request(msg);
this->on_subscribe_bluetooth_connections_free_request();
break;
}
#endif
#ifdef USE_BLUETOOTH_PROXY
case UnsubscribeBluetoothLEAdvertisementsRequest::MESSAGE_TYPE: {
UnsubscribeBluetoothLEAdvertisementsRequest msg;
// Empty message: no decode needed
case 87 /* UnsubscribeBluetoothLEAdvertisementsRequest is empty */: {
#ifdef HAS_PROTO_MESSAGE_DUMP
this->log_receive_message_(LOG_STR("on_unsubscribe_bluetooth_le_advertisements_request"), msg);
this->log_receive_message_(LOG_STR("on_unsubscribe_bluetooth_le_advertisements_request"));
#endif
this->on_unsubscribe_bluetooth_le_advertisements_request(msg);
this->on_unsubscribe_bluetooth_le_advertisements_request();
break;
}
#endif
@@ -642,231 +640,4 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
}
}
void APIServerConnection::on_hello_request(const HelloRequest &msg) {
if (!this->send_hello_response(msg)) {
this->on_fatal_error();
}
}
void APIServerConnection::on_disconnect_request(const DisconnectRequest &msg) {
if (!this->send_disconnect_response(msg)) {
this->on_fatal_error();
}
}
void APIServerConnection::on_ping_request(const PingRequest &msg) {
if (!this->send_ping_response(msg)) {
this->on_fatal_error();
}
}
void APIServerConnection::on_device_info_request(const DeviceInfoRequest &msg) {
if (!this->send_device_info_response(msg)) {
this->on_fatal_error();
}
}
void APIServerConnection::on_list_entities_request(const ListEntitiesRequest &msg) { this->list_entities(msg); }
void APIServerConnection::on_subscribe_states_request(const SubscribeStatesRequest &msg) {
this->subscribe_states(msg);
}
void APIServerConnection::on_subscribe_logs_request(const SubscribeLogsRequest &msg) { this->subscribe_logs(msg); }
#ifdef USE_API_HOMEASSISTANT_SERVICES
void APIServerConnection::on_subscribe_homeassistant_services_request(
const SubscribeHomeassistantServicesRequest &msg) {
this->subscribe_homeassistant_services(msg);
}
#endif
#ifdef USE_API_HOMEASSISTANT_STATES
void APIServerConnection::on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) {
this->subscribe_home_assistant_states(msg);
}
#endif
#ifdef USE_API_USER_DEFINED_ACTIONS
void APIServerConnection::on_execute_service_request(const ExecuteServiceRequest &msg) { this->execute_service(msg); }
#endif
#ifdef USE_API_NOISE
void APIServerConnection::on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &msg) {
if (!this->send_noise_encryption_set_key_response(msg)) {
this->on_fatal_error();
}
}
#endif
#ifdef USE_BUTTON
void APIServerConnection::on_button_command_request(const ButtonCommandRequest &msg) { this->button_command(msg); }
#endif
#ifdef USE_CAMERA
void APIServerConnection::on_camera_image_request(const CameraImageRequest &msg) { this->camera_image(msg); }
#endif
#ifdef USE_CLIMATE
void APIServerConnection::on_climate_command_request(const ClimateCommandRequest &msg) { this->climate_command(msg); }
#endif
#ifdef USE_COVER
void APIServerConnection::on_cover_command_request(const CoverCommandRequest &msg) { this->cover_command(msg); }
#endif
#ifdef USE_DATETIME_DATE
void APIServerConnection::on_date_command_request(const DateCommandRequest &msg) { this->date_command(msg); }
#endif
#ifdef USE_DATETIME_DATETIME
void APIServerConnection::on_date_time_command_request(const DateTimeCommandRequest &msg) {
this->datetime_command(msg);
}
#endif
#ifdef USE_FAN
void APIServerConnection::on_fan_command_request(const FanCommandRequest &msg) { this->fan_command(msg); }
#endif
#ifdef USE_LIGHT
void APIServerConnection::on_light_command_request(const LightCommandRequest &msg) { this->light_command(msg); }
#endif
#ifdef USE_LOCK
void APIServerConnection::on_lock_command_request(const LockCommandRequest &msg) { this->lock_command(msg); }
#endif
#ifdef USE_MEDIA_PLAYER
void APIServerConnection::on_media_player_command_request(const MediaPlayerCommandRequest &msg) {
this->media_player_command(msg);
}
#endif
#ifdef USE_NUMBER
void APIServerConnection::on_number_command_request(const NumberCommandRequest &msg) { this->number_command(msg); }
#endif
#ifdef USE_SELECT
void APIServerConnection::on_select_command_request(const SelectCommandRequest &msg) { this->select_command(msg); }
#endif
#ifdef USE_SIREN
void APIServerConnection::on_siren_command_request(const SirenCommandRequest &msg) { this->siren_command(msg); }
#endif
#ifdef USE_SWITCH
void APIServerConnection::on_switch_command_request(const SwitchCommandRequest &msg) { this->switch_command(msg); }
#endif
#ifdef USE_TEXT
void APIServerConnection::on_text_command_request(const TextCommandRequest &msg) { this->text_command(msg); }
#endif
#ifdef USE_DATETIME_TIME
void APIServerConnection::on_time_command_request(const TimeCommandRequest &msg) { this->time_command(msg); }
#endif
#ifdef USE_UPDATE
void APIServerConnection::on_update_command_request(const UpdateCommandRequest &msg) { this->update_command(msg); }
#endif
#ifdef USE_VALVE
void APIServerConnection::on_valve_command_request(const ValveCommandRequest &msg) { this->valve_command(msg); }
#endif
#ifdef USE_WATER_HEATER
void APIServerConnection::on_water_heater_command_request(const WaterHeaterCommandRequest &msg) {
this->water_heater_command(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
const SubscribeBluetoothLEAdvertisementsRequest &msg) {
this->subscribe_bluetooth_le_advertisements(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_device_request(const BluetoothDeviceRequest &msg) {
this->bluetooth_device_request(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_gatt_get_services_request(const BluetoothGATTGetServicesRequest &msg) {
this->bluetooth_gatt_get_services(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_gatt_read_request(const BluetoothGATTReadRequest &msg) {
this->bluetooth_gatt_read(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_gatt_write_request(const BluetoothGATTWriteRequest &msg) {
this->bluetooth_gatt_write(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_gatt_read_descriptor_request(const BluetoothGATTReadDescriptorRequest &msg) {
this->bluetooth_gatt_read_descriptor(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_gatt_write_descriptor_request(const BluetoothGATTWriteDescriptorRequest &msg) {
this->bluetooth_gatt_write_descriptor(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &msg) {
this->bluetooth_gatt_notify(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_subscribe_bluetooth_connections_free_request(
const SubscribeBluetoothConnectionsFreeRequest &msg) {
if (!this->send_subscribe_bluetooth_connections_free_response(msg)) {
this->on_fatal_error();
}
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_unsubscribe_bluetooth_le_advertisements_request(
const UnsubscribeBluetoothLEAdvertisementsRequest &msg) {
this->unsubscribe_bluetooth_le_advertisements(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &msg) {
this->bluetooth_scanner_set_mode(msg);
}
#endif
#ifdef USE_VOICE_ASSISTANT
void APIServerConnection::on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) {
this->subscribe_voice_assistant(msg);
}
#endif
#ifdef USE_VOICE_ASSISTANT
void APIServerConnection::on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &msg) {
if (!this->send_voice_assistant_get_configuration_response(msg)) {
this->on_fatal_error();
}
}
#endif
#ifdef USE_VOICE_ASSISTANT
void APIServerConnection::on_voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) {
this->voice_assistant_set_configuration(msg);
}
#endif
#ifdef USE_ALARM_CONTROL_PANEL
void APIServerConnection::on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &msg) {
this->alarm_control_panel_command(msg);
}
#endif
#ifdef USE_ZWAVE_PROXY
void APIServerConnection::on_z_wave_proxy_frame(const ZWaveProxyFrame &msg) { this->zwave_proxy_frame(msg); }
#endif
#ifdef USE_ZWAVE_PROXY
void APIServerConnection::on_z_wave_proxy_request(const ZWaveProxyRequest &msg) { this->zwave_proxy_request(msg); }
#endif
#ifdef USE_IR_RF
void APIServerConnection::on_infrared_rf_transmit_raw_timings_request(const InfraredRFTransmitRawTimingsRequest &msg) {
this->infrared_rf_transmit_raw_timings(msg);
}
#endif
void APIServerConnection::read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) {
// Check authentication/connection requirements for messages
switch (msg_type) {
case HelloRequest::MESSAGE_TYPE: // No setup required
case DisconnectRequest::MESSAGE_TYPE: // No setup required
case PingRequest::MESSAGE_TYPE: // No setup required
break; // Skip all checks for these messages
case DeviceInfoRequest::MESSAGE_TYPE: // Connection setup only
if (!this->check_connection_setup_()) {
return; // Connection not setup
}
break;
default:
// All other messages require authentication (which includes connection check)
if (!this->check_authenticated_()) {
return; // Authentication failed
}
break;
}
// Call base implementation to process the message
APIServerConnectionBase::read_message(msg_size, msg_type, msg_data);
}
} // namespace esphome::api

View File

@@ -14,6 +14,7 @@ class APIServerConnectionBase : public ProtoService {
protected:
void log_send_message_(const char *name, const char *dump);
void log_receive_message_(const LogString *name, const ProtoMessage &msg);
void log_receive_message_(const LogString *name);
public:
#endif
@@ -23,20 +24,20 @@ class APIServerConnectionBase : public ProtoService {
DumpBuffer dump_buf;
this->log_send_message_(msg.message_name(), msg.dump_to(dump_buf));
#endif
return this->send_message_(msg, message_type);
return this->send_message_impl(msg, message_type);
}
virtual void on_hello_request(const HelloRequest &value){};
virtual void on_disconnect_request(const DisconnectRequest &value){};
virtual void on_disconnect_response(const DisconnectResponse &value){};
virtual void on_ping_request(const PingRequest &value){};
virtual void on_ping_response(const PingResponse &value){};
virtual void on_device_info_request(const DeviceInfoRequest &value){};
virtual void on_disconnect_request(){};
virtual void on_disconnect_response(){};
virtual void on_ping_request(){};
virtual void on_ping_response(){};
virtual void on_device_info_request(){};
virtual void on_list_entities_request(const ListEntitiesRequest &value){};
virtual void on_list_entities_request(){};
virtual void on_subscribe_states_request(const SubscribeStatesRequest &value){};
virtual void on_subscribe_states_request(){};
#ifdef USE_COVER
virtual void on_cover_command_request(const CoverCommandRequest &value){};
@@ -61,14 +62,14 @@ class APIServerConnectionBase : public ProtoService {
#endif
#ifdef USE_API_HOMEASSISTANT_SERVICES
virtual void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &value){};
virtual void on_subscribe_homeassistant_services_request(){};
#endif
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES
virtual void on_homeassistant_action_response(const HomeassistantActionResponse &value){};
#endif
#ifdef USE_API_HOMEASSISTANT_STATES
virtual void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &value){};
virtual void on_subscribe_home_assistant_states_request(){};
#endif
#ifdef USE_API_HOMEASSISTANT_STATES
@@ -147,12 +148,11 @@ class APIServerConnectionBase : public ProtoService {
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void on_subscribe_bluetooth_connections_free_request(const SubscribeBluetoothConnectionsFreeRequest &value){};
virtual void on_subscribe_bluetooth_connections_free_request(){};
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void on_unsubscribe_bluetooth_le_advertisements_request(
const UnsubscribeBluetoothLEAdvertisementsRequest &value){};
virtual void on_unsubscribe_bluetooth_le_advertisements_request(){};
#endif
#ifdef USE_BLUETOOTH_PROXY
@@ -228,272 +228,4 @@ class APIServerConnectionBase : public ProtoService {
void read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) override;
};
class APIServerConnection : public APIServerConnectionBase {
public:
virtual bool send_hello_response(const HelloRequest &msg) = 0;
virtual bool send_disconnect_response(const DisconnectRequest &msg) = 0;
virtual bool send_ping_response(const PingRequest &msg) = 0;
virtual bool send_device_info_response(const DeviceInfoRequest &msg) = 0;
virtual void list_entities(const ListEntitiesRequest &msg) = 0;
virtual void subscribe_states(const SubscribeStatesRequest &msg) = 0;
virtual void subscribe_logs(const SubscribeLogsRequest &msg) = 0;
#ifdef USE_API_HOMEASSISTANT_SERVICES
virtual void subscribe_homeassistant_services(const SubscribeHomeassistantServicesRequest &msg) = 0;
#endif
#ifdef USE_API_HOMEASSISTANT_STATES
virtual void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) = 0;
#endif
#ifdef USE_API_USER_DEFINED_ACTIONS
virtual void execute_service(const ExecuteServiceRequest &msg) = 0;
#endif
#ifdef USE_API_NOISE
virtual bool send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyRequest &msg) = 0;
#endif
#ifdef USE_BUTTON
virtual void button_command(const ButtonCommandRequest &msg) = 0;
#endif
#ifdef USE_CAMERA
virtual void camera_image(const CameraImageRequest &msg) = 0;
#endif
#ifdef USE_CLIMATE
virtual void climate_command(const ClimateCommandRequest &msg) = 0;
#endif
#ifdef USE_COVER
virtual void cover_command(const CoverCommandRequest &msg) = 0;
#endif
#ifdef USE_DATETIME_DATE
virtual void date_command(const DateCommandRequest &msg) = 0;
#endif
#ifdef USE_DATETIME_DATETIME
virtual void datetime_command(const DateTimeCommandRequest &msg) = 0;
#endif
#ifdef USE_FAN
virtual void fan_command(const FanCommandRequest &msg) = 0;
#endif
#ifdef USE_LIGHT
virtual void light_command(const LightCommandRequest &msg) = 0;
#endif
#ifdef USE_LOCK
virtual void lock_command(const LockCommandRequest &msg) = 0;
#endif
#ifdef USE_MEDIA_PLAYER
virtual void media_player_command(const MediaPlayerCommandRequest &msg) = 0;
#endif
#ifdef USE_NUMBER
virtual void number_command(const NumberCommandRequest &msg) = 0;
#endif
#ifdef USE_SELECT
virtual void select_command(const SelectCommandRequest &msg) = 0;
#endif
#ifdef USE_SIREN
virtual void siren_command(const SirenCommandRequest &msg) = 0;
#endif
#ifdef USE_SWITCH
virtual void switch_command(const SwitchCommandRequest &msg) = 0;
#endif
#ifdef USE_TEXT
virtual void text_command(const TextCommandRequest &msg) = 0;
#endif
#ifdef USE_DATETIME_TIME
virtual void time_command(const TimeCommandRequest &msg) = 0;
#endif
#ifdef USE_UPDATE
virtual void update_command(const UpdateCommandRequest &msg) = 0;
#endif
#ifdef USE_VALVE
virtual void valve_command(const ValveCommandRequest &msg) = 0;
#endif
#ifdef USE_WATER_HEATER
virtual void water_heater_command(const WaterHeaterCommandRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void bluetooth_device_request(const BluetoothDeviceRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void bluetooth_gatt_get_services(const BluetoothGATTGetServicesRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void bluetooth_gatt_read(const BluetoothGATTReadRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void bluetooth_gatt_write(const BluetoothGATTWriteRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void bluetooth_gatt_read_descriptor(const BluetoothGATTReadDescriptorRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void bluetooth_gatt_write_descriptor(const BluetoothGATTWriteDescriptorRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual bool send_subscribe_bluetooth_connections_free_response(
const SubscribeBluetoothConnectionsFreeRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void bluetooth_scanner_set_mode(const BluetoothScannerSetModeRequest &msg) = 0;
#endif
#ifdef USE_VOICE_ASSISTANT
virtual void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) = 0;
#endif
#ifdef USE_VOICE_ASSISTANT
virtual bool send_voice_assistant_get_configuration_response(const VoiceAssistantConfigurationRequest &msg) = 0;
#endif
#ifdef USE_VOICE_ASSISTANT
virtual void voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) = 0;
#endif
#ifdef USE_ALARM_CONTROL_PANEL
virtual void alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) = 0;
#endif
#ifdef USE_ZWAVE_PROXY
virtual void zwave_proxy_frame(const ZWaveProxyFrame &msg) = 0;
#endif
#ifdef USE_ZWAVE_PROXY
virtual void zwave_proxy_request(const ZWaveProxyRequest &msg) = 0;
#endif
#ifdef USE_IR_RF
virtual void infrared_rf_transmit_raw_timings(const InfraredRFTransmitRawTimingsRequest &msg) = 0;
#endif
protected:
void on_hello_request(const HelloRequest &msg) override;
void on_disconnect_request(const DisconnectRequest &msg) override;
void on_ping_request(const PingRequest &msg) override;
void on_device_info_request(const DeviceInfoRequest &msg) override;
void on_list_entities_request(const ListEntitiesRequest &msg) override;
void on_subscribe_states_request(const SubscribeStatesRequest &msg) override;
void on_subscribe_logs_request(const SubscribeLogsRequest &msg) override;
#ifdef USE_API_HOMEASSISTANT_SERVICES
void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &msg) override;
#endif
#ifdef USE_API_HOMEASSISTANT_STATES
void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) override;
#endif
#ifdef USE_API_USER_DEFINED_ACTIONS
void on_execute_service_request(const ExecuteServiceRequest &msg) override;
#endif
#ifdef USE_API_NOISE
void on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &msg) override;
#endif
#ifdef USE_BUTTON
void on_button_command_request(const ButtonCommandRequest &msg) override;
#endif
#ifdef USE_CAMERA
void on_camera_image_request(const CameraImageRequest &msg) override;
#endif
#ifdef USE_CLIMATE
void on_climate_command_request(const ClimateCommandRequest &msg) override;
#endif
#ifdef USE_COVER
void on_cover_command_request(const CoverCommandRequest &msg) override;
#endif
#ifdef USE_DATETIME_DATE
void on_date_command_request(const DateCommandRequest &msg) override;
#endif
#ifdef USE_DATETIME_DATETIME
void on_date_time_command_request(const DateTimeCommandRequest &msg) override;
#endif
#ifdef USE_FAN
void on_fan_command_request(const FanCommandRequest &msg) override;
#endif
#ifdef USE_LIGHT
void on_light_command_request(const LightCommandRequest &msg) override;
#endif
#ifdef USE_LOCK
void on_lock_command_request(const LockCommandRequest &msg) override;
#endif
#ifdef USE_MEDIA_PLAYER
void on_media_player_command_request(const MediaPlayerCommandRequest &msg) override;
#endif
#ifdef USE_NUMBER
void on_number_command_request(const NumberCommandRequest &msg) override;
#endif
#ifdef USE_SELECT
void on_select_command_request(const SelectCommandRequest &msg) override;
#endif
#ifdef USE_SIREN
void on_siren_command_request(const SirenCommandRequest &msg) override;
#endif
#ifdef USE_SWITCH
void on_switch_command_request(const SwitchCommandRequest &msg) override;
#endif
#ifdef USE_TEXT
void on_text_command_request(const TextCommandRequest &msg) override;
#endif
#ifdef USE_DATETIME_TIME
void on_time_command_request(const TimeCommandRequest &msg) override;
#endif
#ifdef USE_UPDATE
void on_update_command_request(const UpdateCommandRequest &msg) override;
#endif
#ifdef USE_VALVE
void on_valve_command_request(const ValveCommandRequest &msg) override;
#endif
#ifdef USE_WATER_HEATER
void on_water_heater_command_request(const WaterHeaterCommandRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_bluetooth_device_request(const BluetoothDeviceRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_bluetooth_gatt_get_services_request(const BluetoothGATTGetServicesRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_bluetooth_gatt_read_request(const BluetoothGATTReadRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_bluetooth_gatt_write_request(const BluetoothGATTWriteRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_bluetooth_gatt_read_descriptor_request(const BluetoothGATTReadDescriptorRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_bluetooth_gatt_write_descriptor_request(const BluetoothGATTWriteDescriptorRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_subscribe_bluetooth_connections_free_request(const SubscribeBluetoothConnectionsFreeRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_unsubscribe_bluetooth_le_advertisements_request(
const UnsubscribeBluetoothLEAdvertisementsRequest &msg) override;
#endif
#ifdef USE_BLUETOOTH_PROXY
void on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &msg) override;
#endif
#ifdef USE_VOICE_ASSISTANT
void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) override;
#endif
#ifdef USE_VOICE_ASSISTANT
void on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &msg) override;
#endif
#ifdef USE_VOICE_ASSISTANT
void on_voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) override;
#endif
#ifdef USE_ALARM_CONTROL_PANEL
void on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &msg) override;
#endif
#ifdef USE_ZWAVE_PROXY
void on_z_wave_proxy_frame(const ZWaveProxyFrame &msg) override;
#endif
#ifdef USE_ZWAVE_PROXY
void on_z_wave_proxy_request(const ZWaveProxyRequest &msg) override;
#endif
#ifdef USE_IR_RF
void on_infrared_rf_transmit_raw_timings_request(const InfraredRFTransmitRawTimingsRequest &msg) override;
#endif
void read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) override;
};
} // namespace esphome::api

View File

@@ -215,7 +215,7 @@ void APIServer::loop() {
#ifdef USE_API_CLIENT_DISCONNECTED_TRIGGER
// Fire trigger after client is removed so api.connected reflects the true state
this->client_disconnected_trigger_->trigger(client_name, client_peername);
this->client_disconnected_trigger_.trigger(client_name, client_peername);
#endif
// Don't increment client_index since we need to process the swapped element
}

View File

@@ -227,12 +227,10 @@ class APIServer : public Component,
#endif
#ifdef USE_API_CLIENT_CONNECTED_TRIGGER
Trigger<std::string, std::string> *get_client_connected_trigger() const { return this->client_connected_trigger_; }
Trigger<std::string, std::string> *get_client_connected_trigger() { return &this->client_connected_trigger_; }
#endif
#ifdef USE_API_CLIENT_DISCONNECTED_TRIGGER
Trigger<std::string, std::string> *get_client_disconnected_trigger() const {
return this->client_disconnected_trigger_;
}
Trigger<std::string, std::string> *get_client_disconnected_trigger() { return &this->client_disconnected_trigger_; }
#endif
protected:
@@ -253,10 +251,10 @@ class APIServer : public Component,
// Pointers and pointer-like types first (4 bytes each)
std::unique_ptr<socket::Socket> socket_ = nullptr;
#ifdef USE_API_CLIENT_CONNECTED_TRIGGER
Trigger<std::string, std::string> *client_connected_trigger_ = new Trigger<std::string, std::string>();
Trigger<std::string, std::string> client_connected_trigger_;
#endif
#ifdef USE_API_CLIENT_DISCONNECTED_TRIGGER
Trigger<std::string, std::string> *client_disconnected_trigger_ = new Trigger<std::string, std::string>();
Trigger<std::string, std::string> client_disconnected_trigger_;
#endif
// 4-byte aligned types

View File

@@ -128,6 +128,20 @@ template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts
this->add_kv_(this->variables_, key, std::forward<V>(value));
}
#ifdef USE_ESP8266
// On ESP8266, ESPHOME_F() returns __FlashStringHelper* (PROGMEM pointer).
// Store as const char* — populate_service_map copies from PROGMEM at play() time.
template<typename V> void add_data(const __FlashStringHelper *key, V &&value) {
this->add_kv_(this->data_, reinterpret_cast<const char *>(key), std::forward<V>(value));
}
template<typename V> void add_data_template(const __FlashStringHelper *key, V &&value) {
this->add_kv_(this->data_template_, reinterpret_cast<const char *>(key), std::forward<V>(value));
}
template<typename V> void add_variable(const __FlashStringHelper *key, V &&value) {
this->add_kv_(this->variables_, reinterpret_cast<const char *>(key), std::forward<V>(value));
}
#endif
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES
template<typename T> void set_response_template(T response_template) {
this->response_template_ = response_template;
@@ -138,12 +152,10 @@ template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts
void set_wants_response() { this->flags_.wants_response = true; }
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
Trigger<JsonObjectConst, Ts...> *get_success_trigger_with_response() const {
return this->success_trigger_with_response_;
}
Trigger<JsonObjectConst, Ts...> *get_success_trigger_with_response() { return &this->success_trigger_with_response_; }
#endif
Trigger<Ts...> *get_success_trigger() const { return this->success_trigger_; }
Trigger<std::string, Ts...> *get_error_trigger() const { return this->error_trigger_; }
Trigger<Ts...> *get_success_trigger() { return &this->success_trigger_; }
Trigger<std::string, Ts...> *get_error_trigger() { return &this->error_trigger_; }
#endif // USE_API_HOMEASSISTANT_ACTION_RESPONSES
void play(const Ts &...x) override {
@@ -189,14 +201,14 @@ template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts
if (response.is_success()) {
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
if (this->flags_.wants_response) {
this->success_trigger_with_response_->trigger(response.get_json(), args...);
this->success_trigger_with_response_.trigger(response.get_json(), args...);
} else
#endif
{
this->success_trigger_->trigger(args...);
this->success_trigger_.trigger(args...);
}
} else {
this->error_trigger_->trigger(response.get_error_message(), args...);
this->error_trigger_.trigger(response.get_error_message(), args...);
}
},
captured_args);
@@ -221,7 +233,31 @@ template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts
Ts... x) {
dest.init(source.size());
// Count non-static strings to allocate exact storage needed
#ifdef USE_ESP8266
// On ESP8266, keys may be in PROGMEM (from ESPHOME_F in codegen) and
// FLASH_STRING values need copying via _P functions.
// Allocate storage for all keys + all values (2 entries per source item).
// strlen_P/memcpy_P handle both RAM and PROGMEM pointers safely.
value_storage.init(source.size() * 2);
for (auto &it : source) {
auto &kv = dest.emplace_back();
// Key: copy from possible PROGMEM
{
size_t key_len = strlen_P(it.key);
value_storage.push_back(std::string(key_len, '\0'));
memcpy_P(value_storage.back().data(), it.key, key_len);
kv.key = StringRef(value_storage.back());
}
// Value: value() handles FLASH_STRING via _P functions internally
value_storage.push_back(it.value.value(x...));
kv.value = StringRef(value_storage.back());
}
#else
// On non-ESP8266, strings are directly readable from flash-mapped memory.
// Count non-static strings to allocate exact storage needed.
size_t lambda_count = 0;
for (const auto &it : source) {
if (!it.value.is_static_string()) {
@@ -235,14 +271,15 @@ template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts
kv.key = StringRef(it.key);
if (it.value.is_static_string()) {
// Static string from YAML - zero allocation
// Static string — pointer directly readable, zero allocation
kv.value = StringRef(it.value.get_static_string());
} else {
// Lambda evaluation - store result, reference it
// Lambda evaluate and store result
value_storage.push_back(it.value.value(x...));
kv.value = StringRef(value_storage.back());
}
}
#endif
}
APIServer *parent_;
@@ -253,10 +290,10 @@ template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
TemplatableStringValue<Ts...> response_template_{""};
Trigger<JsonObjectConst, Ts...> *success_trigger_with_response_ = new Trigger<JsonObjectConst, Ts...>();
Trigger<JsonObjectConst, Ts...> success_trigger_with_response_;
#endif // USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON
Trigger<Ts...> *success_trigger_ = new Trigger<Ts...>();
Trigger<std::string, Ts...> *error_trigger_ = new Trigger<std::string, Ts...>();
Trigger<Ts...> success_trigger_;
Trigger<std::string, Ts...> error_trigger_;
#endif // USE_API_HOMEASSISTANT_ACTION_RESPONSES
struct Flags {

View File

@@ -112,8 +112,12 @@ class ProtoVarInt {
uint64_t result = buffer[0] & 0x7F;
uint8_t bitpos = 7;
// A 64-bit varint is at most 10 bytes (ceil(64/7)). Reject overlong encodings
// to avoid undefined behavior from shifting uint64_t by >= 64 bits.
uint32_t max_len = std::min(len, uint32_t(10));
// Start from the second byte since we've already processed the first
for (uint32_t i = 1; i < len; i++) {
for (uint32_t i = 1; i < max_len; i++) {
uint8_t val = buffer[i];
result |= uint64_t(val & 0x7F) << uint64_t(bitpos);
bitpos += 7;
@@ -402,6 +406,20 @@ class DumpBuffer {
const char *c_str() const { return buf_; }
size_t size() const { return pos_; }
/// Get writable buffer pointer for use with buf_append_printf
char *data() { return buf_; }
/// Get current position for use with buf_append_printf
size_t pos() const { return pos_; }
/// Update position after buf_append_printf call
void set_pos(size_t pos) {
if (pos >= CAPACITY) {
pos_ = CAPACITY - 1;
} else {
pos_ = pos;
}
buf_[pos_] = '\0';
}
private:
void append_impl_(const char *str, size_t len) {
size_t space = CAPACITY - 1 - pos_;
@@ -943,32 +961,16 @@ class ProtoService {
virtual bool is_connection_setup() = 0;
virtual void on_fatal_error() = 0;
virtual void on_no_setup_connection() = 0;
/**
* Create a buffer with a reserved size.
* @param reserve_size The number of bytes to pre-allocate in the buffer. This is a hint
* to optimize memory usage and avoid reallocations during encoding.
* Implementations should aim to allocate at least this size.
* @return A ProtoWriteBuffer object with the reserved size.
*/
virtual ProtoWriteBuffer create_buffer(uint32_t reserve_size) = 0;
virtual bool send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) = 0;
virtual void read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) = 0;
// Optimized method that pre-allocates buffer based on message size
bool send_message_(const ProtoMessage &msg, uint8_t message_type) {
ProtoSize size;
msg.calculate_size(size);
uint32_t msg_size = size.get_size();
// Create a pre-sized buffer
auto buffer = this->create_buffer(msg_size);
// Encode message into the buffer
msg.encode(buffer);
// Send the buffer
return this->send_buffer(buffer, message_type);
}
/**
* Send a protobuf message by calculating its size, allocating a buffer, encoding, and sending.
* This is the implementation method - callers should use send_message() which adds logging.
* @param msg The protobuf message to send.
* @param message_type The message type identifier.
* @return True if the message was sent successfully, false otherwise.
*/
virtual bool send_message_impl(const ProtoMessage &msg, uint8_t message_type) = 0;
// Authentication helper methods
inline bool check_connection_setup_() {

View File

@@ -1,5 +1,6 @@
#pragma once
#include <algorithm>
#include <cmath>
#include <limits>
#include "abstract_aqi_calculator.h"
@@ -14,7 +15,11 @@ class AQICalculator : public AbstractAQICalculator {
float pm2_5_index = calculate_index(pm2_5_value, PM2_5_GRID);
float pm10_0_index = calculate_index(pm10_0_value, PM10_0_GRID);
return static_cast<uint16_t>(std::round((pm2_5_index < pm10_0_index) ? pm10_0_index : pm2_5_index));
float aqi = std::max(pm2_5_index, pm10_0_index);
if (aqi < 0.0f) {
aqi = 0.0f;
}
return static_cast<uint16_t>(std::lround(aqi));
}
protected:
@@ -22,13 +27,27 @@ class AQICalculator : public AbstractAQICalculator {
static constexpr int INDEX_GRID[NUM_LEVELS][2] = {{0, 50}, {51, 100}, {101, 150}, {151, 200}, {201, 300}, {301, 500}};
static constexpr float PM2_5_GRID[NUM_LEVELS][2] = {{0.0f, 9.0f}, {9.1f, 35.4f},
{35.5f, 55.4f}, {55.5f, 125.4f},
{125.5f, 225.4f}, {225.5f, std::numeric_limits<float>::max()}};
static constexpr float PM2_5_GRID[NUM_LEVELS][2] = {
// clang-format off
{0.0f, 9.1f},
{9.1f, 35.5f},
{35.5f, 55.5f},
{55.5f, 125.5f},
{125.5f, 225.5f},
{225.5f, std::numeric_limits<float>::max()}
// clang-format on
};
static constexpr float PM10_0_GRID[NUM_LEVELS][2] = {{0.0f, 54.0f}, {55.0f, 154.0f},
{155.0f, 254.0f}, {255.0f, 354.0f},
{355.0f, 424.0f}, {425.0f, std::numeric_limits<float>::max()}};
static constexpr float PM10_0_GRID[NUM_LEVELS][2] = {
// clang-format off
{0.0f, 55.0f},
{55.0f, 155.0f},
{155.0f, 255.0f},
{255.0f, 355.0f},
{355.0f, 425.0f},
{425.0f, std::numeric_limits<float>::max()}
// clang-format on
};
static float calculate_index(float value, const float array[NUM_LEVELS][2]) {
int grid_index = get_grid_index(value, array);
@@ -45,7 +64,10 @@ class AQICalculator : public AbstractAQICalculator {
static int get_grid_index(float value, const float array[NUM_LEVELS][2]) {
for (int i = 0; i < NUM_LEVELS; i++) {
if (value >= array[i][0] && value <= array[i][1]) {
const bool in_range =
(value >= array[i][0]) && ((i == NUM_LEVELS - 1) ? (value <= array[i][1]) // last bucket inclusive
: (value < array[i][1])); // others exclusive on hi
if (in_range) {
return i;
}
}

View File

@@ -10,7 +10,6 @@ class AQISensor : public sensor::Sensor, public Component {
public:
void setup() override;
void dump_config() override;
float get_setup_priority() const override { return setup_priority::DATA; }
void set_pm_2_5_sensor(sensor::Sensor *sensor) { this->pm_2_5_sensor_ = sensor; }
void set_pm_10_0_sensor(sensor::Sensor *sensor) { this->pm_10_0_sensor_ = sensor; }

View File

@@ -1,5 +1,6 @@
#pragma once
#include <algorithm>
#include <cmath>
#include <limits>
#include "abstract_aqi_calculator.h"
@@ -12,7 +13,11 @@ class CAQICalculator : public AbstractAQICalculator {
float pm2_5_index = calculate_index(pm2_5_value, PM2_5_GRID);
float pm10_0_index = calculate_index(pm10_0_value, PM10_0_GRID);
return static_cast<uint16_t>(std::round((pm2_5_index < pm10_0_index) ? pm10_0_index : pm2_5_index));
float aqi = std::max(pm2_5_index, pm10_0_index);
if (aqi < 0.0f) {
aqi = 0.0f;
}
return static_cast<uint16_t>(std::lround(aqi));
}
protected:
@@ -21,10 +26,24 @@ class CAQICalculator : public AbstractAQICalculator {
static constexpr int INDEX_GRID[NUM_LEVELS][2] = {{0, 25}, {26, 50}, {51, 75}, {76, 100}, {101, 400}};
static constexpr float PM2_5_GRID[NUM_LEVELS][2] = {
{0.0f, 15.0f}, {15.1f, 30.0f}, {30.1f, 55.0f}, {55.1f, 110.0f}, {110.1f, std::numeric_limits<float>::max()}};
// clang-format off
{0.0f, 15.1f},
{15.1f, 30.1f},
{30.1f, 55.1f},
{55.1f, 110.1f},
{110.1f, std::numeric_limits<float>::max()}
// clang-format on
};
static constexpr float PM10_0_GRID[NUM_LEVELS][2] = {
{0.0f, 25.0f}, {25.1f, 50.0f}, {50.1f, 90.0f}, {90.1f, 180.0f}, {180.1f, std::numeric_limits<float>::max()}};
// clang-format off
{0.0f, 25.1f},
{25.1f, 50.1f},
{50.1f, 90.1f},
{90.1f, 180.1f},
{180.1f, std::numeric_limits<float>::max()}
// clang-format on
};
static float calculate_index(float value, const float array[NUM_LEVELS][2]) {
int grid_index = get_grid_index(value, array);
@@ -42,7 +61,10 @@ class CAQICalculator : public AbstractAQICalculator {
static int get_grid_index(float value, const float array[NUM_LEVELS][2]) {
for (int i = 0; i < NUM_LEVELS; i++) {
if (value >= array[i][0] && value <= array[i][1]) {
const bool in_range =
(value >= array[i][0]) && ((i == NUM_LEVELS - 1) ? (value <= array[i][1]) // last bucket inclusive
: (value < array[i][1])); // others exclusive on hi
if (in_range) {
return i;
}
}

View File

@@ -41,8 +41,6 @@ void AS3935Component::dump_config() {
#endif
}
float AS3935Component::get_setup_priority() const { return setup_priority::DATA; }
void AS3935Component::loop() {
if (!this->irq_pin_->digital_read())
return;

View File

@@ -74,7 +74,6 @@ class AS3935Component : public Component {
public:
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void loop() override;
void set_irq_pin(GPIOPin *irq_pin) { irq_pin_ = irq_pin; }

View File

@@ -22,8 +22,6 @@ static const uint8_t REGISTER_STATUS = 0x0B; // 8 bytes / R
static const uint8_t REGISTER_AGC = 0x1A; // 8 bytes / R
static const uint8_t REGISTER_MAGNITUDE = 0x1B; // 16 bytes / R
float AS5600Sensor::get_setup_priority() const { return setup_priority::DATA; }
void AS5600Sensor::dump_config() {
LOG_SENSOR("", "AS5600 Sensor", this);
ESP_LOGCONFIG(TAG, " Out of Range Mode: %u", this->out_of_range_mode_);

View File

@@ -14,7 +14,6 @@ class AS5600Sensor : public PollingComponent, public Parented<AS5600Component>,
public:
void update() override;
void dump_config() override;
float get_setup_priority() const override;
void set_angle_sensor(sensor::Sensor *angle_sensor) { this->angle_sensor_ = angle_sensor; }
void set_raw_angle_sensor(sensor::Sensor *raw_angle_sensor) { this->raw_angle_sensor_ = raw_angle_sensor; }

View File

@@ -58,8 +58,6 @@ void AS7341Component::dump_config() {
LOG_SENSOR(" ", "NIR", this->nir_);
}
float AS7341Component::get_setup_priority() const { return setup_priority::DATA; }
void AS7341Component::update() {
this->read_channels(this->channel_readings_);

View File

@@ -78,7 +78,6 @@ class AS7341Component : public PollingComponent, public i2c::I2CDevice {
public:
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void update() override;
void set_f1_sensor(sensor::Sensor *f1_sensor) { this->f1_ = f1_sensor; }

View File

@@ -146,7 +146,6 @@ void ATM90E26Component::dump_config() {
LOG_SENSOR(" ", "Active Reverse Energy A", this->reverse_active_energy_sensor_);
LOG_SENSOR(" ", "Frequency", this->freq_sensor_);
}
float ATM90E26Component::get_setup_priority() const { return setup_priority::DATA; }
uint16_t ATM90E26Component::read16_(uint8_t a_register) {
uint8_t data[2];

View File

@@ -13,7 +13,6 @@ class ATM90E26Component : public PollingComponent,
public:
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void update() override;
void set_voltage_sensor(sensor::Sensor *obj) { this->voltage_sensor_ = obj; }

View File

@@ -6,8 +6,7 @@ namespace bang_bang {
static const char *const TAG = "bang_bang.climate";
BangBangClimate::BangBangClimate()
: idle_trigger_(new Trigger<>()), cool_trigger_(new Trigger<>()), heat_trigger_(new Trigger<>()) {}
BangBangClimate::BangBangClimate() = default;
void BangBangClimate::setup() {
this->sensor_->add_on_state_callback([this](float state) {
@@ -160,13 +159,13 @@ void BangBangClimate::switch_to_action_(climate::ClimateAction action) {
switch (action) {
case climate::CLIMATE_ACTION_OFF:
case climate::CLIMATE_ACTION_IDLE:
trig = this->idle_trigger_;
trig = &this->idle_trigger_;
break;
case climate::CLIMATE_ACTION_COOLING:
trig = this->cool_trigger_;
trig = &this->cool_trigger_;
break;
case climate::CLIMATE_ACTION_HEATING:
trig = this->heat_trigger_;
trig = &this->heat_trigger_;
break;
default:
trig = nullptr;
@@ -204,9 +203,9 @@ void BangBangClimate::set_away_config(const BangBangClimateTargetTempConfig &awa
void BangBangClimate::set_sensor(sensor::Sensor *sensor) { this->sensor_ = sensor; }
void BangBangClimate::set_humidity_sensor(sensor::Sensor *humidity_sensor) { this->humidity_sensor_ = humidity_sensor; }
Trigger<> *BangBangClimate::get_idle_trigger() const { return this->idle_trigger_; }
Trigger<> *BangBangClimate::get_cool_trigger() const { return this->cool_trigger_; }
Trigger<> *BangBangClimate::get_heat_trigger() const { return this->heat_trigger_; }
Trigger<> *BangBangClimate::get_idle_trigger() { return &this->idle_trigger_; }
Trigger<> *BangBangClimate::get_cool_trigger() { return &this->cool_trigger_; }
Trigger<> *BangBangClimate::get_heat_trigger() { return &this->heat_trigger_; }
void BangBangClimate::set_supports_cool(bool supports_cool) { this->supports_cool_ = supports_cool; }
void BangBangClimate::set_supports_heat(bool supports_heat) { this->supports_heat_ = supports_heat; }

View File

@@ -30,9 +30,9 @@ class BangBangClimate : public climate::Climate, public Component {
void set_normal_config(const BangBangClimateTargetTempConfig &normal_config);
void set_away_config(const BangBangClimateTargetTempConfig &away_config);
Trigger<> *get_idle_trigger() const;
Trigger<> *get_cool_trigger() const;
Trigger<> *get_heat_trigger() const;
Trigger<> *get_idle_trigger();
Trigger<> *get_cool_trigger();
Trigger<> *get_heat_trigger();
protected:
/// Override control to change settings of the climate device.
@@ -57,17 +57,13 @@ class BangBangClimate : public climate::Climate, public Component {
*
* In idle mode, the controller is assumed to have both heating and cooling disabled.
*/
Trigger<> *idle_trigger_{nullptr};
Trigger<> idle_trigger_;
/** The trigger to call when the controller should switch to cooling mode.
*/
Trigger<> *cool_trigger_{nullptr};
Trigger<> cool_trigger_;
/** The trigger to call when the controller should switch to heating mode.
*
* A null value for this attribute means that the controller has no heating action
* For example window blinds, where only cooling (blinds closed) and not-cooling
* (blinds open) is possible.
*/
Trigger<> *heat_trigger_{nullptr};
Trigger<> heat_trigger_;
/** A reference to the trigger that was previously active.
*
* This is so that the previous trigger can be stopped before enabling a new one.

View File

@@ -265,6 +265,4 @@ void BH1750Sensor::fail_and_reset_() {
this->state_ = IDLE;
}
float BH1750Sensor::get_setup_priority() const { return setup_priority::DATA; }
} // namespace esphome::bh1750

View File

@@ -21,7 +21,6 @@ class BH1750Sensor : public sensor::Sensor, public PollingComponent, public i2c:
void dump_config() override;
void update() override;
void loop() override;
float get_setup_priority() const override;
protected:
// State machine states

View File

@@ -5,6 +5,14 @@ namespace esphome::binary_sensor {
static const char *const TAG = "binary_sensor.automation";
// MultiClickTrigger timeout IDs.
// MultiClickTrigger is its own Component instance, so the scheduler scopes
// IDs by component pointer — no risk of collisions between instances.
constexpr uint32_t MULTICLICK_TRIGGER_ID = 0;
constexpr uint32_t MULTICLICK_COOLDOWN_ID = 1;
constexpr uint32_t MULTICLICK_IS_VALID_ID = 2;
constexpr uint32_t MULTICLICK_IS_NOT_VALID_ID = 3;
void MultiClickTrigger::on_state_(bool state) {
// Handle duplicate events
if (state == this->last_state_) {
@@ -27,7 +35,7 @@ void MultiClickTrigger::on_state_(bool state) {
evt.min_length, evt.max_length);
this->at_index_ = 1;
if (this->timing_.size() == 1 && evt.max_length == 4294967294UL) {
this->set_timeout("trigger", evt.min_length, [this]() { this->trigger_(); });
this->set_timeout(MULTICLICK_TRIGGER_ID, evt.min_length, [this]() { this->trigger_(); });
} else {
this->schedule_is_valid_(evt.min_length);
this->schedule_is_not_valid_(evt.max_length);
@@ -57,13 +65,13 @@ void MultiClickTrigger::on_state_(bool state) {
this->schedule_is_not_valid_(evt.max_length);
} else if (*this->at_index_ + 1 != this->timing_.size()) {
ESP_LOGV(TAG, "B i=%zu min=%" PRIu32, *this->at_index_, evt.min_length); // NOLINT
this->cancel_timeout("is_not_valid");
this->cancel_timeout(MULTICLICK_IS_NOT_VALID_ID);
this->schedule_is_valid_(evt.min_length);
} else {
ESP_LOGV(TAG, "C i=%zu min=%" PRIu32, *this->at_index_, evt.min_length); // NOLINT
this->is_valid_ = false;
this->cancel_timeout("is_not_valid");
this->set_timeout("trigger", evt.min_length, [this]() { this->trigger_(); });
this->cancel_timeout(MULTICLICK_IS_NOT_VALID_ID);
this->set_timeout(MULTICLICK_TRIGGER_ID, evt.min_length, [this]() { this->trigger_(); });
}
*this->at_index_ = *this->at_index_ + 1;
@@ -71,14 +79,14 @@ void MultiClickTrigger::on_state_(bool state) {
void MultiClickTrigger::schedule_cooldown_() {
ESP_LOGV(TAG, "Multi Click: Invalid length of press, starting cooldown of %" PRIu32 " ms", this->invalid_cooldown_);
this->is_in_cooldown_ = true;
this->set_timeout("cooldown", this->invalid_cooldown_, [this]() {
this->set_timeout(MULTICLICK_COOLDOWN_ID, this->invalid_cooldown_, [this]() {
ESP_LOGV(TAG, "Multi Click: Cooldown ended, matching is now enabled again.");
this->is_in_cooldown_ = false;
});
this->at_index_.reset();
this->cancel_timeout("trigger");
this->cancel_timeout("is_valid");
this->cancel_timeout("is_not_valid");
this->cancel_timeout(MULTICLICK_TRIGGER_ID);
this->cancel_timeout(MULTICLICK_IS_VALID_ID);
this->cancel_timeout(MULTICLICK_IS_NOT_VALID_ID);
}
void MultiClickTrigger::schedule_is_valid_(uint32_t min_length) {
if (min_length == 0) {
@@ -86,13 +94,13 @@ void MultiClickTrigger::schedule_is_valid_(uint32_t min_length) {
return;
}
this->is_valid_ = false;
this->set_timeout("is_valid", min_length, [this]() {
this->set_timeout(MULTICLICK_IS_VALID_ID, min_length, [this]() {
ESP_LOGV(TAG, "Multi Click: You can now %s the button.", this->parent_->state ? "RELEASE" : "PRESS");
this->is_valid_ = true;
});
}
void MultiClickTrigger::schedule_is_not_valid_(uint32_t max_length) {
this->set_timeout("is_not_valid", max_length, [this]() {
this->set_timeout(MULTICLICK_IS_NOT_VALID_ID, max_length, [this]() {
ESP_LOGV(TAG, "Multi Click: You waited too long to %s.", this->parent_->state ? "RELEASE" : "PRESS");
this->is_valid_ = false;
this->schedule_cooldown_();
@@ -106,9 +114,9 @@ void MultiClickTrigger::cancel() {
void MultiClickTrigger::trigger_() {
ESP_LOGV(TAG, "Multi Click: Hooray, multi click is valid. Triggering!");
this->at_index_.reset();
this->cancel_timeout("trigger");
this->cancel_timeout("is_valid");
this->cancel_timeout("is_not_valid");
this->cancel_timeout(MULTICLICK_TRIGGER_ID);
this->cancel_timeout(MULTICLICK_IS_VALID_ID);
this->cancel_timeout(MULTICLICK_IS_NOT_VALID_ID);
this->trigger();
}

View File

@@ -6,6 +6,14 @@ namespace esphome::binary_sensor {
static const char *const TAG = "sensor.filter";
// Timeout IDs for filter classes.
// Each filter is its own Component instance, so the scheduler scopes
// IDs by component pointer — no risk of collisions between instances.
constexpr uint32_t FILTER_TIMEOUT_ID = 0;
// AutorepeatFilter needs two distinct IDs (both timeouts on the same component)
constexpr uint32_t AUTOREPEAT_TIMING_ID = 0;
constexpr uint32_t AUTOREPEAT_ON_OFF_ID = 1;
void Filter::output(bool value) {
if (this->next_ == nullptr) {
this->parent_->send_state_internal(value);
@@ -23,16 +31,16 @@ void Filter::input(bool value) {
}
void TimeoutFilter::input(bool value) {
this->set_timeout("timeout", this->timeout_delay_.value(), [this]() { this->parent_->invalidate_state(); });
this->set_timeout(FILTER_TIMEOUT_ID, this->timeout_delay_.value(), [this]() { this->parent_->invalidate_state(); });
// we do not de-dup here otherwise changes from invalid to valid state will not be output
this->output(value);
}
optional<bool> DelayedOnOffFilter::new_value(bool value) {
if (value) {
this->set_timeout("ON_OFF", this->on_delay_.value(), [this]() { this->output(true); });
this->set_timeout(FILTER_TIMEOUT_ID, this->on_delay_.value(), [this]() { this->output(true); });
} else {
this->set_timeout("ON_OFF", this->off_delay_.value(), [this]() { this->output(false); });
this->set_timeout(FILTER_TIMEOUT_ID, this->off_delay_.value(), [this]() { this->output(false); });
}
return {};
}
@@ -41,10 +49,10 @@ float DelayedOnOffFilter::get_setup_priority() const { return setup_priority::HA
optional<bool> DelayedOnFilter::new_value(bool value) {
if (value) {
this->set_timeout("ON", this->delay_.value(), [this]() { this->output(true); });
this->set_timeout(FILTER_TIMEOUT_ID, this->delay_.value(), [this]() { this->output(true); });
return {};
} else {
this->cancel_timeout("ON");
this->cancel_timeout(FILTER_TIMEOUT_ID);
return false;
}
}
@@ -53,10 +61,10 @@ float DelayedOnFilter::get_setup_priority() const { return setup_priority::HARDW
optional<bool> DelayedOffFilter::new_value(bool value) {
if (!value) {
this->set_timeout("OFF", this->delay_.value(), [this]() { this->output(false); });
this->set_timeout(FILTER_TIMEOUT_ID, this->delay_.value(), [this]() { this->output(false); });
return {};
} else {
this->cancel_timeout("OFF");
this->cancel_timeout(FILTER_TIMEOUT_ID);
return true;
}
}
@@ -76,8 +84,8 @@ optional<bool> AutorepeatFilter::new_value(bool value) {
this->next_timing_();
return true;
} else {
this->cancel_timeout("TIMING");
this->cancel_timeout("ON_OFF");
this->cancel_timeout(AUTOREPEAT_TIMING_ID);
this->cancel_timeout(AUTOREPEAT_ON_OFF_ID);
this->active_timing_ = 0;
return false;
}
@@ -88,8 +96,10 @@ void AutorepeatFilter::next_timing_() {
// 1st time: starts waiting the first delay
// 2nd time: starts waiting the second delay and starts toggling with the first time_off / _on
// last time: no delay to start but have to bump the index to reflect the last
if (this->active_timing_ < this->timings_.size())
this->set_timeout("TIMING", this->timings_[this->active_timing_].delay, [this]() { this->next_timing_(); });
if (this->active_timing_ < this->timings_.size()) {
this->set_timeout(AUTOREPEAT_TIMING_ID, this->timings_[this->active_timing_].delay,
[this]() { this->next_timing_(); });
}
if (this->active_timing_ <= this->timings_.size()) {
this->active_timing_++;
@@ -104,7 +114,8 @@ void AutorepeatFilter::next_timing_() {
void AutorepeatFilter::next_value_(bool val) {
const AutorepeatFilterTiming &timing = this->timings_[this->active_timing_ - 2];
this->output(val); // This is at least the second one so not initial
this->set_timeout("ON_OFF", val ? timing.time_on : timing.time_off, [this, val]() { this->next_value_(!val); });
this->set_timeout(AUTOREPEAT_ON_OFF_ID, val ? timing.time_on : timing.time_off,
[this, val]() { this->next_value_(!val); });
}
float AutorepeatFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
@@ -115,7 +126,7 @@ optional<bool> LambdaFilter::new_value(bool value) { return this->f_(value); }
optional<bool> SettleFilter::new_value(bool value) {
if (!this->steady_) {
this->set_timeout("SETTLE", this->delay_.value(), [this, value]() {
this->set_timeout(FILTER_TIMEOUT_ID, this->delay_.value(), [this, value]() {
this->steady_ = true;
this->output(value);
});
@@ -123,7 +134,7 @@ optional<bool> SettleFilter::new_value(bool value) {
} else {
this->steady_ = false;
this->output(value);
this->set_timeout("SETTLE", this->delay_.value(), [this]() { this->steady_ = true; });
this->set_timeout(FILTER_TIMEOUT_ID, this->delay_.value(), [this]() { this->steady_ = true; });
return value;
}
}

View File

@@ -159,6 +159,10 @@ BK72XX_BOARD_PINS = {
"A0": 23,
},
"cbu": {
"SPI0_CS": 15,
"SPI0_MISO": 17,
"SPI0_MOSI": 16,
"SPI0_SCK": 14,
"WIRE1_SCL": 20,
"WIRE1_SDA": 21,
"WIRE2_SCL": 0,
@@ -227,6 +231,10 @@ BK72XX_BOARD_PINS = {
"A0": 23,
},
"generic-bk7231t-qfn32-tuya": {
"SPI0_CS": 15,
"SPI0_MISO": 17,
"SPI0_MOSI": 16,
"SPI0_SCK": 14,
"WIRE1_SCL": 20,
"WIRE1_SDA": 21,
"WIRE2_SCL": 0,
@@ -295,6 +303,10 @@ BK72XX_BOARD_PINS = {
"A0": 23,
},
"generic-bk7231n-qfn32-tuya": {
"SPI0_CS": 15,
"SPI0_MISO": 17,
"SPI0_MOSI": 16,
"SPI0_SCK": 14,
"WIRE1_SCL": 20,
"WIRE1_SDA": 21,
"WIRE2_SCL": 0,
@@ -485,8 +497,7 @@ BK72XX_BOARD_PINS = {
},
"cb3s": {
"WIRE1_SCL": 20,
"WIRE1_SDA_0": 21,
"WIRE1_SDA_1": 21,
"WIRE1_SDA": 21,
"SERIAL1_RX": 10,
"SERIAL1_TX": 11,
"SERIAL2_TX": 0,
@@ -647,6 +658,10 @@ BK72XX_BOARD_PINS = {
"A0": 23,
},
"generic-bk7252": {
"SPI0_CS": 15,
"SPI0_MISO": 17,
"SPI0_MOSI": 16,
"SPI0_SCK": 14,
"WIRE1_SCL": 20,
"WIRE1_SDA": 21,
"WIRE2_SCL": 0,
@@ -1096,6 +1111,10 @@ BK72XX_BOARD_PINS = {
"A0": 23,
},
"cb3se": {
"SPI0_CS": 15,
"SPI0_MISO": 17,
"SPI0_MOSI": 16,
"SPI0_SCK": 14,
"WIRE2_SCL": 0,
"WIRE2_SDA": 1,
"SERIAL1_RX": 10,

View File

@@ -46,16 +46,16 @@ static const uint32_t PKT_TIMEOUT_MS = 200;
void BL0942::loop() {
DataPacket buffer;
int avail = this->available();
size_t avail = this->available();
if (!avail) {
return;
}
if (static_cast<size_t>(avail) < sizeof(buffer)) {
if (avail < sizeof(buffer)) {
if (!this->rx_start_) {
this->rx_start_ = millis();
} else if (millis() > this->rx_start_ + PKT_TIMEOUT_MS) {
ESP_LOGW(TAG, "Junk on wire. Throwing away partial message (%d bytes)", avail);
ESP_LOGW(TAG, "Junk on wire. Throwing away partial message (%zu bytes)", avail);
this->read_array((uint8_t *) &buffer, avail);
this->rx_start_ = 0;
}

View File

@@ -199,7 +199,6 @@ void BME280Component::dump_config() {
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
ESP_LOGCONFIG(TAG, " Oversampling: %s", oversampling_to_str(this->humidity_oversampling_));
}
float BME280Component::get_setup_priority() const { return setup_priority::DATA; }
inline uint8_t oversampling_to_time(BME280Oversampling over_sampling) { return (1 << uint8_t(over_sampling)) >> 1; }

View File

@@ -76,7 +76,6 @@ class BME280Component : public PollingComponent {
// (In most use cases you won't need these)
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void update() override;
protected:

View File

@@ -233,8 +233,6 @@ void BME680Component::dump_config() {
}
}
float BME680Component::get_setup_priority() const { return setup_priority::DATA; }
void BME680Component::update() {
uint8_t meas_control = 0; // No need to fetch, we're setting all fields
meas_control |= (this->temperature_oversampling_ & 0b111) << 5;

View File

@@ -99,7 +99,6 @@ class BME680Component : public PollingComponent, public i2c::I2CDevice {
// (In most use cases you won't need these)
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void update() override;
protected:

View File

@@ -181,8 +181,6 @@ void BME680BSECComponent::dump_config() {
LOG_SENSOR(" ", "Breath VOC Equivalent", this->breath_voc_equivalent_sensor_);
}
float BME680BSECComponent::get_setup_priority() const { return setup_priority::DATA; }
void BME680BSECComponent::loop() {
this->run_();

View File

@@ -64,7 +64,6 @@ class BME680BSECComponent : public Component, public i2c::I2CDevice {
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void loop() override;
protected:

View File

@@ -106,8 +106,6 @@ void BME68xBSEC2Component::dump_config() {
#endif
}
float BME68xBSEC2Component::get_setup_priority() const { return setup_priority::DATA; }
void BME68xBSEC2Component::loop() {
this->run_();

View File

@@ -48,7 +48,6 @@ class BME68xBSEC2Component : public Component {
public:
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void loop() override;
void set_algorithm_output(AlgorithmOutput algorithm_output) { this->algorithm_output_ = algorithm_output; }

View File

@@ -263,7 +263,6 @@ void BMI160Component::update() {
this->status_clear_warning();
}
float BMI160Component::get_setup_priority() const { return setup_priority::DATA; }
} // namespace bmi160
} // namespace esphome

View File

@@ -14,8 +14,6 @@ class BMI160Component : public PollingComponent, public i2c::I2CDevice {
void update() override;
float get_setup_priority() const override;
void set_accel_x_sensor(sensor::Sensor *accel_x_sensor) { accel_x_sensor_ = accel_x_sensor; }
void set_accel_y_sensor(sensor::Sensor *accel_y_sensor) { accel_y_sensor_ = accel_y_sensor; }
void set_accel_z_sensor(sensor::Sensor *accel_z_sensor) { accel_z_sensor_ = accel_z_sensor; }

View File

@@ -131,7 +131,6 @@ bool BMP085Component::set_mode_(uint8_t mode) {
ESP_LOGV(TAG, "Setting mode to 0x%02X", mode);
return this->write_byte(BMP085_REGISTER_CONTROL, mode);
}
float BMP085Component::get_setup_priority() const { return setup_priority::DATA; }
} // namespace bmp085
} // namespace esphome

View File

@@ -18,8 +18,6 @@ class BMP085Component : public PollingComponent, public i2c::I2CDevice {
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
protected:
struct CalibrationData {
int16_t ac1, ac2, ac3;

View File

@@ -148,7 +148,6 @@ void BMP280Component::dump_config() {
LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
ESP_LOGCONFIG(TAG, " Oversampling: %s", oversampling_to_str(this->pressure_oversampling_));
}
float BMP280Component::get_setup_priority() const { return setup_priority::DATA; }
inline uint8_t oversampling_to_time(BMP280Oversampling over_sampling) { return (1 << uint8_t(over_sampling)) >> 1; }

View File

@@ -64,7 +64,6 @@ class BMP280Component : public PollingComponent {
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void update() override;
protected:

View File

@@ -6,8 +6,9 @@
*/
#include "bmp3xx_base.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
#include "esphome/core/progmem.h"
#include <cinttypes>
namespace esphome {
@@ -26,46 +27,18 @@ static const LogString *chip_type_to_str(uint8_t chip_type) {
}
}
// Oversampling strings indexed by Oversampling enum (0-5): NONE, X2, X4, X8, X16, X32
PROGMEM_STRING_TABLE(OversamplingStrings, "None", "2x", "4x", "8x", "16x", "32x", "");
static const LogString *oversampling_to_str(Oversampling oversampling) {
switch (oversampling) {
case Oversampling::OVERSAMPLING_NONE:
return LOG_STR("None");
case Oversampling::OVERSAMPLING_X2:
return LOG_STR("2x");
case Oversampling::OVERSAMPLING_X4:
return LOG_STR("4x");
case Oversampling::OVERSAMPLING_X8:
return LOG_STR("8x");
case Oversampling::OVERSAMPLING_X16:
return LOG_STR("16x");
case Oversampling::OVERSAMPLING_X32:
return LOG_STR("32x");
default:
return LOG_STR("");
}
return OversamplingStrings::get_log_str(static_cast<uint8_t>(oversampling), OversamplingStrings::LAST_INDEX);
}
// IIR filter strings indexed by IIRFilter enum (0-7): OFF, 2, 4, 8, 16, 32, 64, 128
PROGMEM_STRING_TABLE(IIRFilterStrings, "OFF", "2x", "4x", "8x", "16x", "32x", "64x", "128x", "");
static const LogString *iir_filter_to_str(IIRFilter filter) {
switch (filter) {
case IIRFilter::IIR_FILTER_OFF:
return LOG_STR("OFF");
case IIRFilter::IIR_FILTER_2:
return LOG_STR("2x");
case IIRFilter::IIR_FILTER_4:
return LOG_STR("4x");
case IIRFilter::IIR_FILTER_8:
return LOG_STR("8x");
case IIRFilter::IIR_FILTER_16:
return LOG_STR("16x");
case IIRFilter::IIR_FILTER_32:
return LOG_STR("32x");
case IIRFilter::IIR_FILTER_64:
return LOG_STR("64x");
case IIRFilter::IIR_FILTER_128:
return LOG_STR("128x");
default:
return LOG_STR("");
}
return IIRFilterStrings::get_log_str(static_cast<uint8_t>(filter), IIRFilterStrings::LAST_INDEX);
}
void BMP3XXComponent::setup() {
@@ -179,7 +152,6 @@ void BMP3XXComponent::dump_config() {
ESP_LOGCONFIG(TAG, " Oversampling: %s", LOG_STR_ARG(oversampling_to_str(this->pressure_oversampling_)));
}
}
float BMP3XXComponent::get_setup_priority() const { return setup_priority::DATA; }
inline uint8_t oversampling_to_time(Oversampling over_sampling) { return (1 << uint8_t(over_sampling)); }

View File

@@ -73,7 +73,6 @@ class BMP3XXComponent : public PollingComponent {
public:
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void update() override;
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }

View File

@@ -11,57 +11,26 @@
*/
#include "bmp581_base.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
#include "esphome/core/progmem.h"
namespace esphome::bmp581_base {
static const char *const TAG = "bmp581";
// Oversampling strings indexed by Oversampling enum (0-7): NONE, X2, X4, X8, X16, X32, X64, X128
PROGMEM_STRING_TABLE(OversamplingStrings, "None", "2x", "4x", "8x", "16x", "32x", "64x", "128x", "");
static const LogString *oversampling_to_str(Oversampling oversampling) {
switch (oversampling) {
case Oversampling::OVERSAMPLING_NONE:
return LOG_STR("None");
case Oversampling::OVERSAMPLING_X2:
return LOG_STR("2x");
case Oversampling::OVERSAMPLING_X4:
return LOG_STR("4x");
case Oversampling::OVERSAMPLING_X8:
return LOG_STR("8x");
case Oversampling::OVERSAMPLING_X16:
return LOG_STR("16x");
case Oversampling::OVERSAMPLING_X32:
return LOG_STR("32x");
case Oversampling::OVERSAMPLING_X64:
return LOG_STR("64x");
case Oversampling::OVERSAMPLING_X128:
return LOG_STR("128x");
default:
return LOG_STR("");
}
return OversamplingStrings::get_log_str(static_cast<uint8_t>(oversampling), OversamplingStrings::LAST_INDEX);
}
// IIR filter strings indexed by IIRFilter enum (0-7): OFF, 2, 4, 8, 16, 32, 64, 128
PROGMEM_STRING_TABLE(IIRFilterStrings, "OFF", "2x", "4x", "8x", "16x", "32x", "64x", "128x", "");
static const LogString *iir_filter_to_str(IIRFilter filter) {
switch (filter) {
case IIRFilter::IIR_FILTER_OFF:
return LOG_STR("OFF");
case IIRFilter::IIR_FILTER_2:
return LOG_STR("2x");
case IIRFilter::IIR_FILTER_4:
return LOG_STR("4x");
case IIRFilter::IIR_FILTER_8:
return LOG_STR("8x");
case IIRFilter::IIR_FILTER_16:
return LOG_STR("16x");
case IIRFilter::IIR_FILTER_32:
return LOG_STR("32x");
case IIRFilter::IIR_FILTER_64:
return LOG_STR("64x");
case IIRFilter::IIR_FILTER_128:
return LOG_STR("128x");
default:
return LOG_STR("");
}
return IIRFilterStrings::get_log_str(static_cast<uint8_t>(filter), IIRFilterStrings::LAST_INDEX);
}
void BMP581Component::dump_config() {

View File

@@ -156,7 +156,7 @@ void CC1101Component::call_listeners_(const std::vector<uint8_t> &packet, float
for (auto &listener : this->listeners_) {
listener->on_packet(packet, freq_offset, rssi, lqi);
}
this->packet_trigger_->trigger(packet, freq_offset, rssi, lqi);
this->packet_trigger_.trigger(packet, freq_offset, rssi, lqi);
}
void CC1101Component::loop() {

View File

@@ -79,7 +79,7 @@ class CC1101Component : public Component,
// Packet mode operations
CC1101Error transmit_packet(const std::vector<uint8_t> &packet);
void register_listener(CC1101Listener *listener) { this->listeners_.push_back(listener); }
Trigger<std::vector<uint8_t>, float, float, uint8_t> *get_packet_trigger() const { return this->packet_trigger_; }
Trigger<std::vector<uint8_t>, float, float, uint8_t> *get_packet_trigger() { return &this->packet_trigger_; }
protected:
uint16_t chip_id_{0};
@@ -96,8 +96,7 @@ class CC1101Component : public Component,
// Packet handling
void call_listeners_(const std::vector<uint8_t> &packet, float freq_offset, float rssi, uint8_t lqi);
Trigger<std::vector<uint8_t>, float, float, uint8_t> *packet_trigger_{
new Trigger<std::vector<uint8_t>, float, float, uint8_t>()};
Trigger<std::vector<uint8_t>, float, float, uint8_t> packet_trigger_;
std::vector<uint8_t> packet_;
std::vector<CC1101Listener *> listeners_;

View File

@@ -7,8 +7,6 @@ namespace cd74hc4067 {
static const char *const TAG = "cd74hc4067";
float CD74HC4067Component::get_setup_priority() const { return setup_priority::DATA; }
void CD74HC4067Component::setup() {
this->pin_s0_->setup();
this->pin_s1_->setup();

View File

@@ -13,7 +13,6 @@ class CD74HC4067Component : public Component {
/// Set up the internal sensor array.
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
/// setting pin active by setting the right combination of the four multiplexer input pins
void activate_pin(uint8_t pin);

View File

@@ -1,109 +1,44 @@
#include "climate_mode.h"
#include "esphome/core/progmem.h"
namespace esphome::climate {
// Climate mode strings indexed by ClimateMode enum (0-6): OFF, HEAT_COOL, COOL, HEAT, FAN_ONLY, DRY, AUTO
PROGMEM_STRING_TABLE(ClimateModeStrings, "OFF", "HEAT_COOL", "COOL", "HEAT", "FAN_ONLY", "DRY", "AUTO", "UNKNOWN");
const LogString *climate_mode_to_string(ClimateMode mode) {
switch (mode) {
case CLIMATE_MODE_OFF:
return LOG_STR("OFF");
case CLIMATE_MODE_HEAT_COOL:
return LOG_STR("HEAT_COOL");
case CLIMATE_MODE_AUTO:
return LOG_STR("AUTO");
case CLIMATE_MODE_COOL:
return LOG_STR("COOL");
case CLIMATE_MODE_HEAT:
return LOG_STR("HEAT");
case CLIMATE_MODE_FAN_ONLY:
return LOG_STR("FAN_ONLY");
case CLIMATE_MODE_DRY:
return LOG_STR("DRY");
default:
return LOG_STR("UNKNOWN");
}
return ClimateModeStrings::get_log_str(static_cast<uint8_t>(mode), ClimateModeStrings::LAST_INDEX);
}
// Climate action strings indexed by ClimateAction enum (0,2-6): OFF, (gap), COOLING, HEATING, IDLE, DRYING, FAN
PROGMEM_STRING_TABLE(ClimateActionStrings, "OFF", "UNKNOWN", "COOLING", "HEATING", "IDLE", "DRYING", "FAN", "UNKNOWN");
const LogString *climate_action_to_string(ClimateAction action) {
switch (action) {
case CLIMATE_ACTION_OFF:
return LOG_STR("OFF");
case CLIMATE_ACTION_COOLING:
return LOG_STR("COOLING");
case CLIMATE_ACTION_HEATING:
return LOG_STR("HEATING");
case CLIMATE_ACTION_IDLE:
return LOG_STR("IDLE");
case CLIMATE_ACTION_DRYING:
return LOG_STR("DRYING");
case CLIMATE_ACTION_FAN:
return LOG_STR("FAN");
default:
return LOG_STR("UNKNOWN");
}
return ClimateActionStrings::get_log_str(static_cast<uint8_t>(action), ClimateActionStrings::LAST_INDEX);
}
// Climate fan mode strings indexed by ClimateFanMode enum (0-9): ON, OFF, AUTO, LOW, MEDIUM, HIGH, MIDDLE, FOCUS,
// DIFFUSE, QUIET
PROGMEM_STRING_TABLE(ClimateFanModeStrings, "ON", "OFF", "AUTO", "LOW", "MEDIUM", "HIGH", "MIDDLE", "FOCUS", "DIFFUSE",
"QUIET", "UNKNOWN");
const LogString *climate_fan_mode_to_string(ClimateFanMode fan_mode) {
switch (fan_mode) {
case climate::CLIMATE_FAN_ON:
return LOG_STR("ON");
case climate::CLIMATE_FAN_OFF:
return LOG_STR("OFF");
case climate::CLIMATE_FAN_AUTO:
return LOG_STR("AUTO");
case climate::CLIMATE_FAN_LOW:
return LOG_STR("LOW");
case climate::CLIMATE_FAN_MEDIUM:
return LOG_STR("MEDIUM");
case climate::CLIMATE_FAN_HIGH:
return LOG_STR("HIGH");
case climate::CLIMATE_FAN_MIDDLE:
return LOG_STR("MIDDLE");
case climate::CLIMATE_FAN_FOCUS:
return LOG_STR("FOCUS");
case climate::CLIMATE_FAN_DIFFUSE:
return LOG_STR("DIFFUSE");
case climate::CLIMATE_FAN_QUIET:
return LOG_STR("QUIET");
default:
return LOG_STR("UNKNOWN");
}
return ClimateFanModeStrings::get_log_str(static_cast<uint8_t>(fan_mode), ClimateFanModeStrings::LAST_INDEX);
}
// Climate swing mode strings indexed by ClimateSwingMode enum (0-3): OFF, BOTH, VERTICAL, HORIZONTAL
PROGMEM_STRING_TABLE(ClimateSwingModeStrings, "OFF", "BOTH", "VERTICAL", "HORIZONTAL", "UNKNOWN");
const LogString *climate_swing_mode_to_string(ClimateSwingMode swing_mode) {
switch (swing_mode) {
case climate::CLIMATE_SWING_OFF:
return LOG_STR("OFF");
case climate::CLIMATE_SWING_BOTH:
return LOG_STR("BOTH");
case climate::CLIMATE_SWING_VERTICAL:
return LOG_STR("VERTICAL");
case climate::CLIMATE_SWING_HORIZONTAL:
return LOG_STR("HORIZONTAL");
default:
return LOG_STR("UNKNOWN");
}
return ClimateSwingModeStrings::get_log_str(static_cast<uint8_t>(swing_mode), ClimateSwingModeStrings::LAST_INDEX);
}
// Climate preset strings indexed by ClimatePreset enum (0-7): NONE, HOME, AWAY, BOOST, COMFORT, ECO, SLEEP, ACTIVITY
PROGMEM_STRING_TABLE(ClimatePresetStrings, "NONE", "HOME", "AWAY", "BOOST", "COMFORT", "ECO", "SLEEP", "ACTIVITY",
"UNKNOWN");
const LogString *climate_preset_to_string(ClimatePreset preset) {
switch (preset) {
case climate::CLIMATE_PRESET_NONE:
return LOG_STR("NONE");
case climate::CLIMATE_PRESET_HOME:
return LOG_STR("HOME");
case climate::CLIMATE_PRESET_ECO:
return LOG_STR("ECO");
case climate::CLIMATE_PRESET_AWAY:
return LOG_STR("AWAY");
case climate::CLIMATE_PRESET_BOOST:
return LOG_STR("BOOST");
case climate::CLIMATE_PRESET_COMFORT:
return LOG_STR("COMFORT");
case climate::CLIMATE_PRESET_SLEEP:
return LOG_STR("SLEEP");
case climate::CLIMATE_PRESET_ACTIVITY:
return LOG_STR("ACTIVITY");
default:
return LOG_STR("UNKNOWN");
}
return ClimatePresetStrings::get_log_str(static_cast<uint8_t>(preset), ClimatePresetStrings::LAST_INDEX);
}
} // namespace esphome::climate

View File

@@ -10,8 +10,6 @@ namespace cm1106 {
class CM1106Component : public PollingComponent, public uart::UARTDevice {
public:
float get_setup_priority() const override { return esphome::setup_priority::DATA; }
void setup() override;
void update() override;
void dump_config() override;

View File

@@ -10,8 +10,6 @@ namespace combination {
class CombinationComponent : public Component, public sensor::Sensor {
public:
float get_setup_priority() const override { return esphome::setup_priority::DATA; }
/// @brief Logs all source sensor's names
virtual void log_source_sensors() = 0;

View File

@@ -17,3 +17,9 @@ CONF_ON_STATE_CHANGE = "on_state_change"
CONF_REQUEST_HEADERS = "request_headers"
CONF_ROWS = "rows"
CONF_USE_PSRAM = "use_psram"
ICON_CURRENT_DC = "mdi:current-dc"
ICON_SOLAR_PANEL = "mdi:solar-panel"
ICON_SOLAR_POWER = "mdi:solar-power"
UNIT_AMPERE_HOUR = "Ah"

View File

@@ -1,3 +1,5 @@
import logging
from esphome import automation
from esphome.automation import Condition, maybe_simple_id
import esphome.codegen as cg
@@ -9,6 +11,7 @@ from esphome.const import (
CONF_ICON,
CONF_ID,
CONF_MQTT_ID,
CONF_ON_IDLE,
CONF_ON_OPEN,
CONF_POSITION,
CONF_POSITION_COMMAND_TOPIC,
@@ -32,9 +35,10 @@ from esphome.const import (
DEVICE_CLASS_SHUTTER,
DEVICE_CLASS_WINDOW,
)
from esphome.core import CORE, CoroPriority, coroutine_with_priority
from esphome.core import CORE, ID, CoroPriority, coroutine_with_priority
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
from esphome.cpp_generator import MockObjClass
from esphome.cpp_generator import MockObj, MockObjClass
from esphome.types import ConfigType, TemplateArgsType
IS_PLATFORM_COMPONENT = True
@@ -53,6 +57,8 @@ DEVICE_CLASSES = [
DEVICE_CLASS_WINDOW,
]
_LOGGER = logging.getLogger(__name__)
cover_ns = cg.esphome_ns.namespace("cover")
Cover = cover_ns.class_("Cover", cg.EntityBase)
@@ -83,14 +89,29 @@ ControlAction = cover_ns.class_("ControlAction", automation.Action)
CoverPublishAction = cover_ns.class_("CoverPublishAction", automation.Action)
CoverIsOpenCondition = cover_ns.class_("CoverIsOpenCondition", Condition)
CoverIsClosedCondition = cover_ns.class_("CoverIsClosedCondition", Condition)
# Triggers
CoverOpenTrigger = cover_ns.class_("CoverOpenTrigger", automation.Trigger.template())
CoverOpenedTrigger = cover_ns.class_(
"CoverOpenedTrigger", automation.Trigger.template()
)
CoverClosedTrigger = cover_ns.class_(
"CoverClosedTrigger", automation.Trigger.template()
)
CoverTrigger = cover_ns.class_("CoverTrigger", automation.Trigger.template())
# Cover-specific constants
CONF_ON_CLOSED = "on_closed"
CONF_ON_OPENED = "on_opened"
CONF_ON_OPENING = "on_opening"
CONF_ON_CLOSING = "on_closing"
TRIGGERS = {
CONF_ON_OPEN: CoverOpenedTrigger, # Deprecated, use on_opened
CONF_ON_OPENED: CoverOpenedTrigger,
CONF_ON_CLOSED: CoverClosedTrigger,
CONF_ON_CLOSING: CoverTrigger.template(CoverOperation.COVER_OPERATION_CLOSING),
CONF_ON_OPENING: CoverTrigger.template(CoverOperation.COVER_OPERATION_OPENING),
CONF_ON_IDLE: CoverTrigger.template(CoverOperation.COVER_OPERATION_IDLE),
}
_COVER_SCHEMA = (
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
@@ -111,16 +132,14 @@ _COVER_SCHEMA = (
cv.Optional(CONF_TILT_STATE_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.subscribe_topic
),
cv.Optional(CONF_ON_OPEN): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CoverOpenTrigger),
}
),
cv.Optional(CONF_ON_CLOSED): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CoverClosedTrigger),
}
),
**{
cv.Optional(conf): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(trigger_class),
}
)
for conf, trigger_class in TRIGGERS.items()
},
}
)
)
@@ -157,12 +176,14 @@ async def setup_cover_core_(var, config):
if (device_class := config.get(CONF_DEVICE_CLASS)) is not None:
cg.add(var.set_device_class(device_class))
for conf in config.get(CONF_ON_OPEN, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_CLOSED, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
if CONF_ON_OPEN in config:
_LOGGER.warning(
"'on_open' is deprecated, use 'on_opened'. Will be removed in 2026.8.0"
)
for trigger_conf in TRIGGERS:
for conf in config.get(trigger_conf, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
if (mqtt_id := config.get(CONF_MQTT_ID)) is not None:
mqtt_ = cg.new_Pvariable(mqtt_id, var)
@@ -258,6 +279,26 @@ async def cover_control_to_code(config, action_id, template_arg, args):
return var
COVER_CONDITION_SCHEMA = cv.maybe_simple_value(
{cv.Required(CONF_ID): cv.use_id(Cover)}, key=CONF_ID
)
async def cover_condition_to_code(
config: ConfigType, condition_id: ID, template_arg: MockObj, args: TemplateArgsType
) -> MockObj:
paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(condition_id, template_arg, paren)
automation.register_condition(
"cover.is_open", CoverIsOpenCondition, COVER_CONDITION_SCHEMA
)(cover_condition_to_code)
automation.register_condition(
"cover.is_closed", CoverIsClosedCondition, COVER_CONDITION_SCHEMA
)(cover_condition_to_code)
@coroutine_with_priority(CoroPriority.CORE)
async def to_code(config):
cg.add_global(cover_ns.using)

View File

@@ -90,44 +90,53 @@ template<typename... Ts> class CoverPublishAction : public Action<Ts...> {
Cover *cover_;
};
template<typename... Ts> class CoverIsOpenCondition : public Condition<Ts...> {
template<bool OPEN, typename... Ts> class CoverPositionCondition : public Condition<Ts...> {
public:
CoverIsOpenCondition(Cover *cover) : cover_(cover) {}
bool check(const Ts &...x) override { return this->cover_->is_fully_open(); }
CoverPositionCondition(Cover *cover) : cover_(cover) {}
bool check(const Ts &...x) override { return this->cover_->position == (OPEN ? COVER_OPEN : COVER_CLOSED); }
protected:
Cover *cover_;
};
template<typename... Ts> class CoverIsClosedCondition : public Condition<Ts...> {
template<typename... Ts> using CoverIsOpenCondition = CoverPositionCondition<true, Ts...>;
template<typename... Ts> using CoverIsClosedCondition = CoverPositionCondition<false, Ts...>;
template<bool OPEN> class CoverPositionTrigger : public Trigger<> {
public:
CoverIsClosedCondition(Cover *cover) : cover_(cover) {}
bool check(const Ts &...x) override { return this->cover_->is_fully_closed(); }
CoverPositionTrigger(Cover *a_cover) {
a_cover->add_on_state_callback([this, a_cover]() {
if (a_cover->position != this->last_position_) {
this->last_position_ = a_cover->position;
if (a_cover->position == (OPEN ? COVER_OPEN : COVER_CLOSED))
this->trigger();
}
});
}
protected:
Cover *cover_;
float last_position_{NAN};
};
class CoverOpenTrigger : public Trigger<> {
using CoverOpenedTrigger = CoverPositionTrigger<true>;
using CoverClosedTrigger = CoverPositionTrigger<false>;
template<CoverOperation OP> class CoverTrigger : public Trigger<> {
public:
CoverOpenTrigger(Cover *a_cover) {
CoverTrigger(Cover *a_cover) {
a_cover->add_on_state_callback([this, a_cover]() {
if (a_cover->is_fully_open()) {
this->trigger();
auto current_op = a_cover->current_operation;
if (current_op == OP) {
if (!this->last_operation_.has_value() || this->last_operation_.value() != OP) {
this->trigger();
}
}
this->last_operation_ = current_op;
});
}
};
class CoverClosedTrigger : public Trigger<> {
public:
CoverClosedTrigger(Cover *a_cover) {
a_cover->add_on_state_callback([this, a_cover]() {
if (a_cover->is_fully_closed()) {
this->trigger();
}
});
}
protected:
optional<CoverOperation> last_operation_{};
};
} // namespace esphome::cover

View File

@@ -10,9 +10,6 @@ namespace esphome::cover {
static const char *const TAG = "cover";
const float COVER_OPEN = 1.0f;
const float COVER_CLOSED = 0.0f;
const LogString *cover_command_to_str(float pos) {
if (pos == COVER_OPEN) {
return LOG_STR("OPEN");

View File

@@ -10,8 +10,8 @@
namespace esphome::cover {
const extern float COVER_OPEN;
const extern float COVER_CLOSED;
static constexpr float COVER_OPEN = 1.0f;
static constexpr float COVER_CLOSED = 0.0f;
#define LOG_COVER(prefix, type, obj) \
if ((obj) != nullptr) { \

View File

@@ -62,8 +62,6 @@ void CSE7761Component::dump_config() {
this->check_uart_settings(38400, 1, uart::UART_CONFIG_PARITY_EVEN, 8);
}
float CSE7761Component::get_setup_priority() const { return setup_priority::DATA; }
void CSE7761Component::update() {
if (this->data_.ready) {
this->get_data_();

View File

@@ -28,7 +28,6 @@ class CSE7761Component : public PollingComponent, public uart::UARTDevice {
void set_current_2_sensor(sensor::Sensor *current_sensor_2) { current_sensor_2_ = current_sensor_2; }
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void update() override;
protected:

View File

@@ -7,7 +7,6 @@ namespace esphome {
namespace cse7766 {
static const char *const TAG = "cse7766";
static constexpr size_t CSE7766_RAW_DATA_SIZE = 24;
void CSE7766Component::loop() {
const uint32_t now = App.get_loop_component_start_time();
@@ -16,28 +15,41 @@ void CSE7766Component::loop() {
this->raw_data_index_ = 0;
}
if (this->available() == 0) {
// Early return prevents updating last_transmission_ when no data is available.
size_t avail = this->available();
if (avail == 0) {
return;
}
this->last_transmission_ = now;
while (this->available() != 0) {
this->read_byte(&this->raw_data_[this->raw_data_index_]);
if (!this->check_byte_()) {
this->raw_data_index_ = 0;
this->status_set_warning();
continue;
}
if (this->raw_data_index_ == 23) {
this->parse_data_();
this->status_clear_warning();
// Read all available bytes in batches to reduce UART call overhead.
// At 4800 baud (~480 bytes/sec) with ~122 Hz loop rate, typically ~4 bytes per call.
uint8_t buf[CSE7766_RAW_DATA_SIZE];
while (avail > 0) {
size_t to_read = std::min(avail, sizeof(buf));
if (!this->read_array(buf, to_read)) {
break;
}
avail -= to_read;
this->raw_data_index_ = (this->raw_data_index_ + 1) % 24;
for (size_t i = 0; i < to_read; i++) {
this->raw_data_[this->raw_data_index_] = buf[i];
if (!this->check_byte_()) {
this->raw_data_index_ = 0;
this->status_set_warning();
continue;
}
if (this->raw_data_index_ == CSE7766_RAW_DATA_SIZE - 1) {
this->parse_data_();
this->status_clear_warning();
}
this->raw_data_index_ = (this->raw_data_index_ + 1) % CSE7766_RAW_DATA_SIZE;
}
}
}
float CSE7766Component::get_setup_priority() const { return setup_priority::DATA; }
bool CSE7766Component::check_byte_() {
uint8_t index = this->raw_data_index_;
@@ -54,14 +66,15 @@ bool CSE7766Component::check_byte_() {
return true;
}
if (index == 23) {
if (index == CSE7766_RAW_DATA_SIZE - 1) {
uint8_t checksum = 0;
for (uint8_t i = 2; i < 23; i++) {
for (uint8_t i = 2; i < CSE7766_RAW_DATA_SIZE - 1; i++) {
checksum += this->raw_data_[i];
}
if (checksum != this->raw_data_[23]) {
ESP_LOGW(TAG, "Invalid checksum from CSE7766: 0x%02X != 0x%02X", checksum, this->raw_data_[23]);
if (checksum != this->raw_data_[CSE7766_RAW_DATA_SIZE - 1]) {
ESP_LOGW(TAG, "Invalid checksum from CSE7766: 0x%02X != 0x%02X", checksum,
this->raw_data_[CSE7766_RAW_DATA_SIZE - 1]);
return false;
}
return true;
@@ -152,6 +165,10 @@ void CSE7766Component::parse_data_() {
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);
}
float current = 0.0f;

View File

@@ -8,6 +8,8 @@
namespace esphome {
namespace cse7766 {
static constexpr size_t CSE7766_RAW_DATA_SIZE = 24;
class CSE7766Component : public Component, public uart::UARTDevice {
public:
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
@@ -23,7 +25,6 @@ class CSE7766Component : public Component, public uart::UARTDevice {
void set_power_factor_sensor(sensor::Sensor *power_factor_sensor) { power_factor_sensor_ = power_factor_sensor; }
void loop() override;
float get_setup_priority() const override;
void dump_config() override;
protected:
@@ -34,7 +35,7 @@ class CSE7766Component : public Component, public uart::UARTDevice {
this->raw_data_[start_index + 2]);
}
uint8_t raw_data_[24];
uint8_t raw_data_[CSE7766_RAW_DATA_SIZE];
uint8_t raw_data_index_{0};
uint32_t last_transmission_{0};
sensor::Sensor *voltage_sensor_{nullptr};

View File

@@ -66,7 +66,7 @@ void CurrentBasedCover::loop() {
if (this->current_operation == COVER_OPERATION_OPENING) {
if (this->malfunction_detection_ && this->is_closing_()) { // Malfunction
this->direction_idle_();
this->malfunction_trigger_->trigger();
this->malfunction_trigger_.trigger();
ESP_LOGI(TAG, "'%s' - Malfunction detected during opening. Current flow detected in close circuit",
this->name_.c_str());
} else if (this->is_opening_blocked_()) { // Blocked
@@ -87,7 +87,7 @@ void CurrentBasedCover::loop() {
} else if (this->current_operation == COVER_OPERATION_CLOSING) {
if (this->malfunction_detection_ && this->is_opening_()) { // Malfunction
this->direction_idle_();
this->malfunction_trigger_->trigger();
this->malfunction_trigger_.trigger();
ESP_LOGI(TAG, "'%s' - Malfunction detected during closing. Current flow detected in open circuit",
this->name_.c_str());
} else if (this->is_closing_blocked_()) { // Blocked
@@ -159,7 +159,6 @@ void CurrentBasedCover::dump_config() {
this->start_sensing_delay_ / 1e3f, YESNO(this->malfunction_detection_));
}
float CurrentBasedCover::get_setup_priority() const { return setup_priority::DATA; }
void CurrentBasedCover::stop_prev_trigger_() {
if (this->prev_command_trigger_ != nullptr) {
this->prev_command_trigger_->stop_action();
@@ -221,15 +220,15 @@ void CurrentBasedCover::start_direction_(CoverOperation dir) {
Trigger<> *trig;
switch (dir) {
case COVER_OPERATION_IDLE:
trig = this->stop_trigger_;
trig = &this->stop_trigger_;
break;
case COVER_OPERATION_OPENING:
this->last_operation_ = dir;
trig = this->open_trigger_;
trig = &this->open_trigger_;
break;
case COVER_OPERATION_CLOSING:
this->last_operation_ = dir;
trig = this->close_trigger_;
trig = &this->close_trigger_;
break;
default:
return;

View File

@@ -14,11 +14,10 @@ class CurrentBasedCover : public cover::Cover, public Component {
void setup() override;
void loop() override;
void dump_config() override;
float get_setup_priority() const override;
Trigger<> *get_stop_trigger() const { return this->stop_trigger_; }
Trigger<> *get_stop_trigger() { return &this->stop_trigger_; }
Trigger<> *get_open_trigger() const { return this->open_trigger_; }
Trigger<> *get_open_trigger() { return &this->open_trigger_; }
void set_open_sensor(sensor::Sensor *open_sensor) { this->open_sensor_ = open_sensor; }
void set_open_moving_current_threshold(float open_moving_current_threshold) {
this->open_moving_current_threshold_ = open_moving_current_threshold;
@@ -28,7 +27,7 @@ class CurrentBasedCover : public cover::Cover, public Component {
}
void set_open_duration(uint32_t open_duration) { this->open_duration_ = open_duration; }
Trigger<> *get_close_trigger() const { return this->close_trigger_; }
Trigger<> *get_close_trigger() { return &this->close_trigger_; }
void set_close_sensor(sensor::Sensor *close_sensor) { this->close_sensor_ = close_sensor; }
void set_close_moving_current_threshold(float close_moving_current_threshold) {
this->close_moving_current_threshold_ = close_moving_current_threshold;
@@ -44,7 +43,7 @@ class CurrentBasedCover : public cover::Cover, public Component {
void set_malfunction_detection(bool malfunction_detection) { this->malfunction_detection_ = malfunction_detection; }
void set_start_sensing_delay(uint32_t start_sensing_delay) { this->start_sensing_delay_ = start_sensing_delay; }
Trigger<> *get_malfunction_trigger() const { return this->malfunction_trigger_; }
Trigger<> *get_malfunction_trigger() { return &this->malfunction_trigger_; }
cover::CoverTraits get_traits() override;
@@ -64,23 +63,23 @@ class CurrentBasedCover : public cover::Cover, public Component {
void recompute_position_();
Trigger<> *stop_trigger_{new Trigger<>()};
Trigger<> stop_trigger_;
sensor::Sensor *open_sensor_{nullptr};
Trigger<> *open_trigger_{new Trigger<>()};
Trigger<> open_trigger_;
float open_moving_current_threshold_;
float open_obstacle_current_threshold_{FLT_MAX};
uint32_t open_duration_;
sensor::Sensor *close_sensor_{nullptr};
Trigger<> *close_trigger_{new Trigger<>()};
Trigger<> close_trigger_;
float close_moving_current_threshold_;
float close_obstacle_current_threshold_{FLT_MAX};
uint32_t close_duration_;
uint32_t max_duration_{UINT32_MAX};
bool malfunction_detection_{true};
Trigger<> *malfunction_trigger_{new Trigger<>()};
Trigger<> malfunction_trigger_;
uint32_t start_sensing_delay_;
float obstacle_rollback_;

View File

@@ -104,8 +104,6 @@ void DalyBmsComponent::loop() {
}
}
float DalyBmsComponent::get_setup_priority() const { return setup_priority::DATA; }
void DalyBmsComponent::request_data_(uint8_t data_id) {
uint8_t request_message[DALY_FRAME_SIZE];

View File

@@ -72,7 +72,6 @@ class DalyBmsComponent : public PollingComponent, public uart::UARTDevice {
void update() override;
void loop() override;
float get_setup_priority() const override;
void set_address(uint8_t address) { this->addr_ = address; }
protected:

View File

@@ -1,5 +1,6 @@
import esphome.codegen as cg
from esphome.components import sensor
from esphome.components.const import ICON_CURRENT_DC, UNIT_AMPERE_HOUR
import esphome.config_validation as cv
from esphome.const import (
CONF_BATTERY_LEVEL,
@@ -55,14 +56,11 @@ CONF_CELL_15_VOLTAGE = "cell_15_voltage"
CONF_CELL_16_VOLTAGE = "cell_16_voltage"
CONF_CELL_17_VOLTAGE = "cell_17_voltage"
CONF_CELL_18_VOLTAGE = "cell_18_voltage"
ICON_CURRENT_DC = "mdi:current-dc"
ICON_BATTERY_OUTLINE = "mdi:battery-outline"
ICON_THERMOMETER_CHEVRON_UP = "mdi:thermometer-chevron-up"
ICON_THERMOMETER_CHEVRON_DOWN = "mdi:thermometer-chevron-down"
ICON_CAR_BATTERY = "mdi:car-battery"
UNIT_AMPERE_HOUR = "Ah"
TYPES = [
CONF_VOLTAGE,
CONF_CURRENT,

View File

@@ -1,6 +1,7 @@
#include "debug_component.h"
#ifdef USE_ESP8266
#include "esphome/core/log.h"
#include "esphome/core/progmem.h"
#include <Esp.h>
extern "C" {
@@ -19,27 +20,31 @@ namespace debug {
static const char *const TAG = "debug";
// PROGMEM string table for reset reasons, indexed by reason code (0-6), with "Unknown" as fallback
// clang-format off
PROGMEM_STRING_TABLE(ResetReasonStrings,
"Power On", // 0 = REASON_DEFAULT_RST
"Hardware Watchdog", // 1 = REASON_WDT_RST
"Exception", // 2 = REASON_EXCEPTION_RST
"Software Watchdog", // 3 = REASON_SOFT_WDT_RST
"Software/System restart", // 4 = REASON_SOFT_RESTART
"Deep-Sleep Wake", // 5 = REASON_DEEP_SLEEP_AWAKE
"External System", // 6 = REASON_EXT_SYS_RST
"Unknown" // 7 = fallback
);
// clang-format on
static_assert(REASON_DEFAULT_RST == 0, "Reset reason enum values must match table indices");
static_assert(REASON_EXT_SYS_RST == 6, "Reset reason enum values must match table indices");
// PROGMEM string table for flash chip modes, indexed by mode code (0-3), with "UNKNOWN" as fallback
PROGMEM_STRING_TABLE(FlashModeStrings, "QIO", "QOUT", "DIO", "DOUT", "UNKNOWN");
static_assert(FM_QIO == 0, "Flash mode enum values must match table indices");
static_assert(FM_DOUT == 3, "Flash mode enum values must match table indices");
// Get reset reason string from reason code (no heap allocation)
// Returns LogString* pointing to flash (PROGMEM) on ESP8266
static const LogString *get_reset_reason_str(uint32_t reason) {
switch (reason) {
case REASON_DEFAULT_RST:
return LOG_STR("Power On");
case REASON_WDT_RST:
return LOG_STR("Hardware Watchdog");
case REASON_EXCEPTION_RST:
return LOG_STR("Exception");
case REASON_SOFT_WDT_RST:
return LOG_STR("Software Watchdog");
case REASON_SOFT_RESTART:
return LOG_STR("Software/System restart");
case REASON_DEEP_SLEEP_AWAKE:
return LOG_STR("Deep-Sleep Wake");
case REASON_EXT_SYS_RST:
return LOG_STR("External System");
default:
return LOG_STR("Unknown");
}
return ResetReasonStrings::get_log_str(reason, ResetReasonStrings::LAST_INDEX);
}
// Size for core version hex buffer
@@ -92,23 +97,8 @@ size_t DebugComponent::get_device_info_(std::span<char, DEVICE_INFO_BUFFER_SIZE>
constexpr size_t size = DEVICE_INFO_BUFFER_SIZE;
char *buf = buffer.data();
const LogString *flash_mode;
switch (ESP.getFlashChipMode()) { // NOLINT(readability-static-accessed-through-instance)
case FM_QIO:
flash_mode = LOG_STR("QIO");
break;
case FM_QOUT:
flash_mode = LOG_STR("QOUT");
break;
case FM_DIO:
flash_mode = LOG_STR("DIO");
break;
case FM_DOUT:
flash_mode = LOG_STR("DOUT");
break;
default:
flash_mode = LOG_STR("UNKNOWN");
}
// NOLINTNEXTLINE(readability-static-accessed-through-instance)
const LogString *flash_mode = FlashModeStrings::get_log_str(ESP.getFlashChipMode(), FlashModeStrings::LAST_INDEX);
uint32_t flash_size = ESP.getFlashChipSize() / 1024; // NOLINT(readability-static-accessed-through-instance)
uint32_t flash_speed = ESP.getFlashChipSpeed() / 1000000; // NOLINT(readability-static-accessed-through-instance)
ESP_LOGD(TAG, "Flash Chip: Size=%" PRIu32 "kB Speed=%" PRIu32 "MHz Mode=%s", flash_size, flash_speed,

View File

@@ -1,4 +1,5 @@
#include "dfplayer.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
@@ -131,140 +132,149 @@ void DFPlayer::send_cmd_(uint8_t cmd, uint16_t argument) {
}
void DFPlayer::loop() {
// Read message
while (this->available()) {
uint8_t byte;
this->read_byte(&byte);
if (this->read_pos_ == DFPLAYER_READ_BUFFER_LENGTH)
this->read_pos_ = 0;
switch (this->read_pos_) {
case 0: // Start mark
if (byte != 0x7E)
continue;
break;
case 1: // Version
if (byte != 0xFF) {
ESP_LOGW(TAG, "Expected Version 0xFF, got %#02x", byte);
this->read_pos_ = 0;
continue;
}
break;
case 2: // Buffer length
if (byte != 0x06) {
ESP_LOGW(TAG, "Expected Buffer length 0x06, got %#02x", byte);
this->read_pos_ = 0;
continue;
}
break;
case 9: // End byte
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
char byte_sequence[100];
byte_sequence[0] = '\0';
for (size_t i = 0; i < this->read_pos_ + 1; ++i) {
snprintf(byte_sequence + strlen(byte_sequence), sizeof(byte_sequence) - strlen(byte_sequence), "%02X ",
this->read_buffer_[i]);
}
ESP_LOGVV(TAG, "Received byte sequence: %s", byte_sequence);
#endif
if (byte != 0xEF) {
ESP_LOGW(TAG, "Expected end byte 0xEF, got %#02x", byte);
this->read_pos_ = 0;
continue;
}
// Parse valid received command
uint8_t cmd = this->read_buffer_[3];
uint16_t argument = (this->read_buffer_[5] << 8) | this->read_buffer_[6];
ESP_LOGV(TAG, "Received message cmd: %#02x arg %#04x", cmd, argument);
switch (cmd) {
case 0x3A:
if (argument == 1) {
ESP_LOGI(TAG, "USB loaded");
} else if (argument == 2) {
ESP_LOGI(TAG, "TF Card loaded");
}
break;
case 0x3B:
if (argument == 1) {
ESP_LOGI(TAG, "USB unloaded");
} else if (argument == 2) {
ESP_LOGI(TAG, "TF Card unloaded");
}
break;
case 0x3F:
if (argument == 1) {
ESP_LOGI(TAG, "USB available");
} else if (argument == 2) {
ESP_LOGI(TAG, "TF Card available");
} else if (argument == 3) {
ESP_LOGI(TAG, "USB, TF Card available");
}
break;
case 0x40:
ESP_LOGV(TAG, "Nack");
this->ack_set_is_playing_ = false;
this->ack_reset_is_playing_ = false;
switch (argument) {
case 0x01:
ESP_LOGE(TAG, "Module is busy or uninitialized");
break;
case 0x02:
ESP_LOGE(TAG, "Module is in sleep mode");
break;
case 0x03:
ESP_LOGE(TAG, "Serial receive error");
break;
case 0x04:
ESP_LOGE(TAG, "Checksum incorrect");
break;
case 0x05:
ESP_LOGE(TAG, "Specified track is out of current track scope");
this->is_playing_ = false;
break;
case 0x06:
ESP_LOGE(TAG, "Specified track is not found");
this->is_playing_ = false;
break;
case 0x07:
ESP_LOGE(TAG, "Insertion error (an inserting operation only can be done when a track is being played)");
break;
case 0x08:
ESP_LOGE(TAG, "SD card reading failed (SD card pulled out or damaged)");
break;
case 0x09:
ESP_LOGE(TAG, "Entered into sleep mode");
this->is_playing_ = false;
break;
}
break;
case 0x41:
ESP_LOGV(TAG, "Ack ok");
this->is_playing_ |= this->ack_set_is_playing_;
this->is_playing_ &= !this->ack_reset_is_playing_;
this->ack_set_is_playing_ = false;
this->ack_reset_is_playing_ = false;
break;
case 0x3C:
ESP_LOGV(TAG, "Playback finished (USB drive)");
this->is_playing_ = false;
this->on_finished_playback_callback_.call();
case 0x3D:
ESP_LOGV(TAG, "Playback finished (SD card)");
this->is_playing_ = false;
this->on_finished_playback_callback_.call();
break;
default:
ESP_LOGE(TAG, "Received unknown cmd %#02x arg %#04x", cmd, argument);
}
this->sent_cmd_ = 0;
this->read_pos_ = 0;
continue;
// Read all available bytes in batches to reduce UART call overhead.
size_t avail = this->available();
uint8_t buf[64];
while (avail > 0) {
size_t to_read = std::min(avail, sizeof(buf));
if (!this->read_array(buf, to_read)) {
break;
}
avail -= to_read;
for (size_t bi = 0; bi < to_read; bi++) {
uint8_t byte = buf[bi];
if (this->read_pos_ == DFPLAYER_READ_BUFFER_LENGTH)
this->read_pos_ = 0;
switch (this->read_pos_) {
case 0: // Start mark
if (byte != 0x7E)
continue;
break;
case 1: // Version
if (byte != 0xFF) {
ESP_LOGW(TAG, "Expected Version 0xFF, got %#02x", byte);
this->read_pos_ = 0;
continue;
}
break;
case 2: // Buffer length
if (byte != 0x06) {
ESP_LOGW(TAG, "Expected Buffer length 0x06, got %#02x", byte);
this->read_pos_ = 0;
continue;
}
break;
case 9: // End byte
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
char byte_sequence[100];
byte_sequence[0] = '\0';
for (size_t i = 0; i < this->read_pos_ + 1; ++i) {
snprintf(byte_sequence + strlen(byte_sequence), sizeof(byte_sequence) - strlen(byte_sequence), "%02X ",
this->read_buffer_[i]);
}
ESP_LOGVV(TAG, "Received byte sequence: %s", byte_sequence);
#endif
if (byte != 0xEF) {
ESP_LOGW(TAG, "Expected end byte 0xEF, got %#02x", byte);
this->read_pos_ = 0;
continue;
}
// Parse valid received command
uint8_t cmd = this->read_buffer_[3];
uint16_t argument = (this->read_buffer_[5] << 8) | this->read_buffer_[6];
ESP_LOGV(TAG, "Received message cmd: %#02x arg %#04x", cmd, argument);
switch (cmd) {
case 0x3A:
if (argument == 1) {
ESP_LOGI(TAG, "USB loaded");
} else if (argument == 2) {
ESP_LOGI(TAG, "TF Card loaded");
}
break;
case 0x3B:
if (argument == 1) {
ESP_LOGI(TAG, "USB unloaded");
} else if (argument == 2) {
ESP_LOGI(TAG, "TF Card unloaded");
}
break;
case 0x3F:
if (argument == 1) {
ESP_LOGI(TAG, "USB available");
} else if (argument == 2) {
ESP_LOGI(TAG, "TF Card available");
} else if (argument == 3) {
ESP_LOGI(TAG, "USB, TF Card available");
}
break;
case 0x40:
ESP_LOGV(TAG, "Nack");
this->ack_set_is_playing_ = false;
this->ack_reset_is_playing_ = false;
switch (argument) {
case 0x01:
ESP_LOGE(TAG, "Module is busy or uninitialized");
break;
case 0x02:
ESP_LOGE(TAG, "Module is in sleep mode");
break;
case 0x03:
ESP_LOGE(TAG, "Serial receive error");
break;
case 0x04:
ESP_LOGE(TAG, "Checksum incorrect");
break;
case 0x05:
ESP_LOGE(TAG, "Specified track is out of current track scope");
this->is_playing_ = false;
break;
case 0x06:
ESP_LOGE(TAG, "Specified track is not found");
this->is_playing_ = false;
break;
case 0x07:
ESP_LOGE(TAG,
"Insertion error (an inserting operation only can be done when a track is being played)");
break;
case 0x08:
ESP_LOGE(TAG, "SD card reading failed (SD card pulled out or damaged)");
break;
case 0x09:
ESP_LOGE(TAG, "Entered into sleep mode");
this->is_playing_ = false;
break;
}
break;
case 0x41:
ESP_LOGV(TAG, "Ack ok");
this->is_playing_ |= this->ack_set_is_playing_;
this->is_playing_ &= !this->ack_reset_is_playing_;
this->ack_set_is_playing_ = false;
this->ack_reset_is_playing_ = false;
break;
case 0x3C:
ESP_LOGV(TAG, "Playback finished (USB drive)");
this->is_playing_ = false;
this->on_finished_playback_callback_.call();
case 0x3D:
ESP_LOGV(TAG, "Playback finished (SD card)");
this->is_playing_ = false;
this->on_finished_playback_callback_.call();
break;
default:
ESP_LOGE(TAG, "Received unknown cmd %#02x arg %#04x", cmd, argument);
}
this->sent_cmd_ = 0;
this->read_pos_ = 0;
continue;
}
this->read_buffer_[this->read_pos_] = byte;
this->read_pos_++;
}
this->read_buffer_[this->read_pos_] = byte;
this->read_pos_++;
}
}
void DFPlayer::dump_config() {

View File

@@ -63,8 +63,6 @@ void DHT::update() {
}
}
float DHT::get_setup_priority() const { return setup_priority::DATA; }
void DHT::set_dht_model(DHTModel model) {
this->model_ = model;
this->is_auto_detect_ = model == DHT_MODEL_AUTO_DETECT;

View File

@@ -51,8 +51,6 @@ class DHT : public PollingComponent {
void dump_config() override;
/// Update sensor values and push them to the frontend.
void update() override;
/// HARDWARE_LATE setup priority.
float get_setup_priority() const override;
protected:
bool read_sensor_(float *temperature, float *humidity, bool report_errors);

View File

@@ -49,7 +49,7 @@ void DHT12Component::dump_config() {
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
}
float DHT12Component::get_setup_priority() const { return setup_priority::DATA; }
bool DHT12Component::read_data_(uint8_t *data) {
if (!this->read_bytes(0, data, 5)) {
ESP_LOGW(TAG, "Updating DHT12 failed!");

View File

@@ -11,7 +11,6 @@ class DHT12Component : public PollingComponent, public i2c::I2CDevice {
public:
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void update() override;
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }

View File

@@ -28,15 +28,28 @@ void DlmsMeterComponent::dump_config() {
void DlmsMeterComponent::loop() {
// Read while data is available, netznoe uses two frames so allow 2x max frame length
while (this->available()) {
if (this->receive_buffer_.size() >= MBUS_MAX_FRAME_LENGTH * 2) {
size_t avail = this->available();
if (avail > 0) {
size_t remaining = MBUS_MAX_FRAME_LENGTH * 2 - this->receive_buffer_.size();
if (remaining == 0) {
ESP_LOGW(TAG, "Receive buffer full, dropping remaining bytes");
break;
} else {
// Read all available bytes in batches to reduce UART call overhead.
// Cap reads to remaining buffer capacity.
if (avail > remaining) {
avail = remaining;
}
uint8_t buf[64];
while (avail > 0) {
size_t to_read = std::min(avail, sizeof(buf));
if (!this->read_array(buf, to_read)) {
break;
}
avail -= to_read;
this->receive_buffer_.insert(this->receive_buffer_.end(), buf, buf + to_read);
this->last_read_ = millis();
}
}
uint8_t c;
this->read_byte(&c);
this->receive_buffer_.push_back(c);
this->last_read_ = millis();
}
if (!this->receive_buffer_.empty() && millis() - this->last_read_ > this->read_timeout_) {

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