Compare commits

..

1651 Commits

Author SHA1 Message Date
J. Nick Koston
7744b2db79 Merge branch 'proto_field_ifdefs' into integration 2025-07-15 20:02:50 -10:00
J. Nick Koston
8fcbcebf84 Merge branch 'entity_dupe_code' into integration 2025-07-15 20:02:45 -10:00
J. Nick Koston
561ed32b2a fix a few more that are missing 2025-07-15 19:58:28 -10:00
J. Nick Koston
5adfb71fe1 fix a few more that are missing 2025-07-15 19:58:13 -10:00
J. Nick Koston
dfdec8ec0a fix a few more that are missing 2025-07-15 19:58:08 -10:00
J. Nick Koston
f6c12229e5 fix a few more that are missing 2025-07-15 19:57:43 -10:00
J. Nick Koston
c80481baab fix a few more that are missing 2025-07-15 19:57:34 -10:00
J. Nick Koston
86ceccbb1c fix a few more that are missing 2025-07-15 19:57:15 -10:00
J. Nick Koston
628caf63fc fix a few more that are missing 2025-07-15 19:57:10 -10:00
J. Nick Koston
1ce5a994d8 fix a few more that are missing 2025-07-15 19:56:40 -10:00
J. Nick Koston
2abccce297 Merge remote-tracking branch 'upstream/dev' into proto_field_ifdefs 2025-07-15 19:51:56 -10:00
J. Nick Koston
0448a66960 Merge remote-tracking branch 'upstream/dev' into entity_dupe_code 2025-07-15 19:49:33 -10:00
J. Nick Koston
f3c0c0c00c Remove legacy unique_id field from entities (#9022)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-07-16 04:56:49 +00:00
J. Nick Koston
231bcb1f7d Fix CI failures from merge collisions (#9535) 2025-07-16 15:24:20 +12:00
Thomas Rupprecht
9cac1c824e [ssd1306_base] fix typo brighrness (#9491) 2025-07-15 22:22:33 -05:00
J. Nick Koston
b497f11af0 Merge branch 'proto_field_ifdefs' into integration 2025-07-15 17:17:19 -10:00
J. Nick Koston
07f16dc065 Merge branch 'drop_unique_id' into integration 2025-07-15 17:16:13 -10:00
pre-commit-ci-lite[bot]
d2deba6b69 [pre-commit.ci lite] apply automatic fixes 2025-07-16 01:56:19 +00:00
J. Nick Koston
545fa1f1bc Merge remote-tracking branch 'upstream/dev' into proto_field_ifdefs 2025-07-15 15:52:18 -10:00
J. Nick Koston
c691f01c7f Reduce flash usage by replacing ProtoSize template with specialized methods (#9487) 2025-07-16 01:50:32 +00:00
J. Nick Koston
b648944973 Optimize API connection batch priority message handling to reduce flash usage (#9510) 2025-07-16 13:46:12 +12:00
J. Nick Koston
40935f7ae4 Skip API log message calls for unsubscribed log levels (#9514) 2025-07-16 13:43:55 +12:00
J. Nick Koston
e152690867 Optimize API component LOGCONFIG usage for flash memory savings (#9526) 2025-07-16 13:42:55 +12:00
J. Nick Koston
b1c86fe30e Optimize scheduler timing by reducing millis() calls (#9524) 2025-07-16 13:41:55 +12:00
Jonathan Swoboda
b695f13f86 [i2c] Use new driver with IDF 5.4.2+ (#8483) 2025-07-15 20:40:28 -05:00
pre-commit-ci-lite[bot]
a477249266 [pre-commit.ci lite] apply automatic fixes 2025-07-16 01:35:26 +00:00
J. Nick Koston
b974ccabac Merge branch 'dev' from upstream 2025-07-15 15:25:51 -10:00
J. Nick Koston
ab54a880c1 Optimize MedianFilter memory allocation by adding vector reserve (#9531) 2025-07-16 01:25:41 +00:00
J. Nick Koston
f745135bdc Drop Python 3.10 support, require Python 3.11+ (#9522)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2025-07-15 15:20:58 -10:00
J. Nick Koston
8f38be0914 Merge remote-tracking branch 'upstream/dev' into proto_field_ifdefs 2025-07-15 15:19:26 -10:00
J. Nick Koston
30c4b91697 Remove parsed advertisement support from bluetooth_proxy to save memory (#9489) 2025-07-16 13:19:03 +12:00
J. Nick Koston
bfaf2547e3 Reduce API component flash usage by consolidating error logging (#9468) 2025-07-16 13:15:23 +12:00
J. Nick Koston
b5be45273f Improve API protobuf decode method readability and reduce code size (#9455) 2025-07-16 13:15:11 +12:00
J. Nick Koston
5c2dea79ef Make API ConnectRequest optional for passwordless connections (#9445) 2025-07-16 13:14:43 +12:00
J. Nick Koston
e012fd5b32 Add runtime_stats component for performance debugging and analysis (#9386)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-07-16 13:13:51 +12:00
J. Nick Koston
856cb182fc Remove dead code: 64-bit protobuf types never used in 7 years (#9471) 2025-07-15 15:12:12 -10:00
J. Nick Koston
2b12307b49 Merge remote-tracking branch 'upstream/drop_unique_id' into drop_unique_id 2025-07-15 15:10:29 -10:00
J. Nick Koston
e340d61a85 Merge upstream/dev into drop_unique_id 2025-07-15 15:09:56 -10:00
Jesse Hills
0110a376b7 Merge branch 'dev' into drop_unique_id 2025-07-16 13:09:52 +12:00
Clyde Stubbs
6486147da1 [mipi_spi] Template code, partial buffer support (#9314)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-07-16 11:05:27 +10:00
Edward Firmo
5480675dd8 [adc] Use new library with ESP-IDF v5 (#9021)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-07-16 13:03:19 +12:00
tomaszduda23
6ab3de65a6 remove duplication from component_iterator (#7210)
Co-authored-by: Samuel Tardieu <sam@rfc1149.net>
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-07-16 13:02:14 +12:00
tomaszduda23
5d9cba3dce [nrf52, core] nrf52 core based on zephyr (#7049)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: Samuel Sieb <samuel-github@sieb.net>
Co-authored-by: Tomasz Duda <tomaszduda23@gmai.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-07-16 13:00:21 +12:00
Jesse Hills
eb81b8a1c8 Merge branch 'beta' into dev 2025-07-16 11:58:43 +12:00
Jesse Hills
de0656a188 Merge pull request #9532 from esphome/bump-2025.7.0b5
2025.7.0b5
2025-07-16 11:58:12 +12:00
Jesse Hills
90a16ffa89 Bump version to 2025.7.0b5 2025-07-16 10:45:20 +12:00
Samuel Sieb
4182076f64 [as3935_spi] remove unnecessary includes (#9528) 2025-07-16 10:45:19 +12:00
J. Nick Koston
8c8c08d40c Fix timing overflow when components disable themselves during loop (#9529)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-16 10:45:19 +12:00
Samuel Sieb
82120bc5d7 [as3935_spi] remove unnecessary includes (#9528) 2025-07-16 10:03:02 +12:00
J. Nick Koston
9769f8a4cc Fix timing overflow when components disable themselves during loop (#9529)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-15 21:51:01 +00:00
J. Nick Koston
0f6dad9c62 Merge branch 'scheduler_opt' into integration 2025-07-15 11:15:19 -10:00
J. Nick Koston
58541aa739 simplify 2025-07-15 11:11:15 -10:00
J. Nick Koston
4c91dead3d Merge branch 'loop_time_update_disable' into integration 2025-07-15 11:07:42 -10:00
J. Nick Koston
78a0fecc08 Fix timing overflow when components disable themselves during loop 2025-07-15 11:03:56 -10:00
J. Nick Koston
42e1b1a2c1 Merge branch 'api_logconfig' into integration 2025-07-15 09:38:35 -10:00
J. Nick Koston
0d360938c2 Optimize API component LOGCONFIG usage for flash memory savings 2025-07-15 09:37:15 -10:00
Jesse Hills
0968338064 Merge branch 'beta' into dev 2025-07-16 07:35:15 +12:00
Jesse Hills
18e2f41424 Merge pull request #9518 from esphome/bump-2025.7.0b4
2025.7.0b4
2025-07-16 07:34:42 +12:00
Christian Glombek
bd0fe34b14 [ms8607] Fix humidity calc (#9499) 2025-07-16 07:33:49 +12:00
Christian Glombek
6e90feeccf [ms8607] Fix humidity calc (#9499) 2025-07-16 07:33:15 +12:00
J. Nick Koston
36ca3546f5 Merge branch 'scheduler_opt' into integration 2025-07-15 09:24:51 -10:00
J. Nick Koston
5536bdf0c9 Optimize scheduler timing by reducing millis() calls 2025-07-15 09:21:32 -10:00
Jesse Hills
37982290f7 Bump version to 2025.7.0b4 2025-07-15 23:35:55 +12:00
Jesse Hills
02b7db7311 [component] Fix `is_ready` flag when loop disabled (#9501)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-07-15 23:35:55 +12:00
Clyde Stubbs
9bc3ff5f53 [core] Don't issue -Wno-volatile for host platform (#9511) 2025-07-15 23:35:55 +12:00
J. Nick Koston
786cb7ded5 Add missing clang-tidy NOLINT comments for ArduinoJson v7 in IDF webserver (#9508) 2025-07-15 23:35:55 +12:00
Keith Burzinski
7f01c25782 [servo] Fix `lerp` (#9507) 2025-07-15 23:35:55 +12:00
Keith Burzinski
321f2f87b0 [opentherm.output] Fix `lerp` (#9506) 2025-07-15 23:35:55 +12:00
Clyde Stubbs
11a051401f [captive_portal] Add test case for libretiny (#9457)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-07-15 23:35:55 +12:00
J. Nick Koston
6148dd7e41 Fix LibreTiny compilation error by updating ESPAsyncWebServer and dependencies (#9492)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-07-15 23:35:55 +12:00
skyegecko
42b6939e90 [fan] Do not save state for fan if configured as NO_RESTORE (#9472) 2025-07-15 23:35:55 +12:00
Kevin Ahrendt
35b3f75f7c [json] Bump ArduinoJson library to 7.4.2 (#8857)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-07-15 23:35:55 +12:00
Clyde Stubbs
78e8001aa8 [online_image] Support byte_order (#9502) 2025-07-15 23:35:55 +12:00
J. Nick Koston
84fc6ff71a Suppress spurious volatile and Python syntax warnings during builds (#9488) 2025-07-15 23:35:55 +12:00
Jesse Hills
a896190de5 [repo] Fix issue template config.yml (#9516) 2025-07-15 22:13:18 +12:00
Jesse Hills
e599ab1a03 Enable issue tracking (#9515) 2025-07-15 21:55:55 +12:00
J. Nick Koston
5aaa99d0e4 Merge branch 'logger_check_outside_func' into integration 2025-07-14 21:23:49 -10:00
J. Nick Koston
60e9ad2240 Skip API log message calls for unsubscribed log levels 2025-07-14 21:22:45 -10:00
Jesse Hills
d3342d6a1a [component] Fix `is_ready` flag when loop disabled (#9501)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-07-15 07:20:18 +00:00
J. Nick Koston
74b0a29a52 Merge branch 'api_connection_batch_prio' into integration 2025-07-14 20:59:52 -10:00
Clyde Stubbs
3f492e3b82 [core] Don't issue -Wno-volatile for host platform (#9511) 2025-07-14 20:59:20 -10:00
J. Nick Koston
909356698c Optimize API connection batch priority message handling to reduce flash usage 2025-07-14 20:31:34 -10:00
J. Nick Koston
b959baf3d6 Add missing clang-tidy NOLINT comments for ArduinoJson v7 in IDF webserver (#9508) 2025-07-15 06:26:54 +00:00
J. Nick Koston
63b8a219e6 Include entire platformio.ini in clang-tidy hash calculation (#9509) 2025-07-15 01:26:39 -05:00
Keith Burzinski
84349b6d05 [servo] Fix `lerp` (#9507) 2025-07-15 03:45:38 +00:00
Keith Burzinski
0f15250f12 [opentherm.output] Fix `lerp` (#9506) 2025-07-15 03:43:00 +00:00
Clyde Stubbs
c2f7dcfa6d [captive_portal] Add test case for libretiny (#9457)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-07-14 17:36:07 -10:00
J. Nick Koston
f34fe95f1c wip 2025-07-14 17:28:30 -10:00
J. Nick Koston
388fde1ee8 Merge entity_dupe_code: Refactor API connection entity encoding to reduce code duplication 2025-07-14 17:25:02 -10:00
J. Nick Koston
b13842f44e Refactor API connection entity encoding to reduce code duplication 2025-07-14 17:17:49 -10:00
J. Nick Koston
1229766436 Merge branch 'drop_unique_id' into integration 2025-07-14 17:03:10 -10:00
J. Nick Koston
77a5430f00 Merge remote-tracking branch 'upstream/dev' into integration 2025-07-14 16:56:37 -10:00
J. Nick Koston
778b586d78 Fix LibreTiny compilation error by updating ESPAsyncWebServer and dependencies (#9492)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-07-15 02:49:12 +00:00
J. Nick Koston
d3d1ba553d Fix blocked CI cancellation caused by always() in clang-tidy workflow (#9503) 2025-07-15 14:17:56 +12:00
skyegecko
a572d4eb47 [fan] Do not save state for fan if configured as NO_RESTORE (#9472) 2025-07-15 14:15:47 +12:00
Kevin Ahrendt
9ae45ba8aa [json] Bump ArduinoJson library to 7.4.2 (#8857)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-07-15 14:11:10 +12:00
Clyde Stubbs
8f58ca3a2a [online_image] Support byte_order (#9502) 2025-07-15 02:09:18 +00:00
J. Nick Koston
8ac6075321 Merge remote-tracking branch 'origin/dev' into integration 2025-07-14 15:52:35 -10:00
J. Nick Koston
6e1e8ed321 Merge branch 'remove_parsed_adv_support_bluetooth_proxy' into integration 2025-07-14 15:52:20 -10:00
J. Nick Koston
e3da197adf Remove yamllint job from CI since its now handled by pre-commit job (#9500) 2025-07-15 12:52:03 +12:00
J. Nick Koston
0826ade69d Merge remote-tracking branch 'origin/dev' into integration 2025-07-14 14:33:50 -10:00
J. Nick Koston
cf9130f906 Merge remote-tracking branch 'kahrendt/json-bump-library' into integration 2025-07-14 14:33:42 -10:00
J. Nick Koston
b2a8b0a22f Add pre-commit hooks to fix common formatting issues causing CI failures (#9494) 2025-07-15 12:25:18 +12:00
Kevin Ahrendt
d268c14f7e Apply suggestions from code review
Fixes unsigned integer wrong type

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-07-14 20:20:46 -04:00
J. Nick Koston
619e2d69c0 Remove redundant pyupgrade CI job (follow-up to #9484) (#9493) 2025-07-15 12:20:05 +12:00
J. Nick Koston
3cca7a6161 fix incorrect comment 2025-07-14 12:31:41 -10:00
J. Nick Koston
52d6801618 address bot comments 2025-07-14 12:29:40 -10:00
J. Nick Koston
a714e8da0b last ones 2025-07-14 11:52:11 -10:00
J. Nick Koston
1778776b73 use NOLINTBEGIN/NOLINTEND for the multi occ cases 2025-07-14 11:30:43 -10:00
J. Nick Koston
d6e05061f8 a few more 2025-07-14 11:19:49 -10:00
J. Nick Koston
13ceda899b add some more , rearrange 2025-07-14 11:00:25 -10:00
J. Nick Koston
40d436746c webserver needs as well 2025-07-14 10:28:10 -10:00
J. Nick Koston
5c59b4fcad Merge remote-tracking branch 'kahrendt/json-bump-library' into integration 2025-07-14 10:14:19 -10:00
J. Nick Koston
4de36ffeb4 Merge branch 'dev' into json-bump-library 2025-07-14 10:13:33 -10:00
J. Nick Koston
f78e71c86a Fix WebServer routes constant naming convention (#9497) 2025-07-14 10:13:24 -10:00
J. Nick Koston
4b3393ce64 location fixes 2025-07-14 10:12:46 -10:00
J. Nick Koston
4c8bb878bf Merge branch 'json-bump-library' of https://github.com/kahrendt/esphome into json-bump-library 2025-07-14 09:57:16 -10:00
Kevin Ahrendt
8a3cb32531 a few formatting things 2025-07-14 15:57:01 -04:00
Kevin Ahrendt
d3ab7f320e a few missing nolint messages 2025-07-14 15:52:40 -04:00
J. Nick Koston
85351bb952 a few more 2025-07-14 09:50:29 -10:00
Kevin Ahrendt
b47f9158b2 fix a few wrong types 2025-07-14 15:47:22 -04:00
J. Nick Koston
ee5242ec8d no real good option but to disable them all manually 2025-07-14 09:33:33 -10:00
J. Nick Koston
238909c0de no real good option but to disable them all manually 2025-07-14 09:31:22 -10:00
J. Nick Koston
fe9316c95d Merge branch 'json-bump-library' of https://github.com/kahrendt/esphome into json-bump-library 2025-07-14 09:30:45 -10:00
J. Nick Koston
a7e74bb7de no real good option but to disable them all manually 2025-07-14 09:30:43 -10:00
Kevin Ahrendt
b9cb690986 use better types 2025-07-14 15:28:54 -04:00
J. Nick Koston
808066f564 no real good option but to disable them all manually 2025-07-14 09:24:38 -10:00
J. Nick Koston
f8c45573f3 Refactor WebServer request handling for improved maintainability (#9470) 2025-07-15 07:24:20 +12:00
J. Nick Koston
2057af8396 Revert "try another way"
This reverts commit 96d39403f4.
2025-07-14 09:21:42 -10:00
J. Nick Koston
b95449615f Revert "more aggressive fix"
This reverts commit 4f10a0ccf7.
2025-07-14 09:21:36 -10:00
Kevin Ahrendt
5e8f1d82c3 specify data types 2025-07-14 14:58:16 -04:00
Kevin Ahrendt
6afda9d4dc don't set string define 2025-07-14 14:58:16 -04:00
J. Nick Koston
4f10a0ccf7 more aggressive fix 2025-07-14 08:53:32 -10:00
J. Nick Koston
96d39403f4 try another way 2025-07-14 08:45:33 -10:00
J. Nick Koston
aeb56cc3d0 Revert "do not analyze platformio files"
This reverts commit f76cba0af6.
2025-07-14 08:45:15 -10:00
J. Nick Koston
33389f9c7f Revert "do not analyze platformio files"
This reverts commit 55a7926670.
2025-07-14 08:45:06 -10:00
Jesse Hills
e231d334a3 Merge branch 'beta' into dev 2025-07-15 06:39:20 +12:00
J. Nick Koston
55a7926670 do not analyze platformio files 2025-07-14 08:13:45 -10:00
J. Nick Koston
f76cba0af6 do not analyze platformio files 2025-07-14 08:10:23 -10:00
J. Nick Koston
1dc75f6ffa Merge branch 'bump_ESPAsyncWebServer_AsyncTCP' into integration 2025-07-14 07:18:15 -10:00
J. Nick Koston
acb0fdc288 one more dep 2025-07-14 07:16:41 -10:00
J. Nick Koston
de235b638a Fix LibreTiny compilation error by updating ESPAsyncWebServer to 3.7.10 2025-07-14 07:12:07 -10:00
Kevin Ahrendt
ab454e9928 explicitly define support for std::string 2025-07-14 11:54:02 -04:00
Kevin Ahrendt
0a8af3ec85 Revert "testing a different approach"
This reverts commit 51eecac2de.
2025-07-14 11:52:26 -04:00
Kevin Ahrendt
51eecac2de testing a different approach 2025-07-14 11:43:31 -04:00
Kevin Ahrendt
815744b0f6 fix merge issues and clean up old comments 2025-07-14 10:47:38 -04:00
Kevin Ahrendt
a1281febe9 bump to 7.4.2 2025-07-14 10:35:10 -04:00
Kevin Ahrendt
9ef982fa4d clang fix 2025-07-14 10:35:10 -04:00
Kevin Ahrendt
44f97e2de4 move allocator to be a protected variable 2025-07-14 10:35:10 -04:00
Kevin Ahrendt
8ad4d3b6f5 fix type of ota object 2025-07-14 10:33:32 -04:00
Kevin Ahrendt
d97f473e4a include proper header for allocator and mark the functions as override 2025-07-14 10:33:32 -04:00
Kevin Ahrendt
ef072eb655 update createNestedObject 2025-07-14 10:33:32 -04:00
Kevin Ahrendt
8040c7cd92 update createdNestedArray calls 2025-07-14 10:33:32 -04:00
Kevin Ahrendt
8648acab5d remove old capacity() call 2025-07-14 10:33:32 -04:00
Kevin Ahrendt
1155e9b88a use new syntax instead of containsKey 2025-07-14 10:33:32 -04:00
Kevin Ahrendt
c069a66625 bump ArduinoJSON library to 7.4.1 2025-07-14 10:33:32 -04:00
J. Nick Koston
2a10f58bdd Remove parsed advertisement support from bluetooth_proxy to save memory 2025-07-13 22:09:53 -10:00
J. Nick Koston
e713b0bd8c Remove parsed advertisement support from bluetooth_proxy to save memory 2025-07-13 22:01:44 -10:00
J. Nick Koston
e7d819a656 Suppress spurious volatile and Python syntax warnings during builds (#9488) 2025-07-14 17:47:52 +10:00
J. Nick Koston
fd13ed78ab Merge branch 'proto_size_remove_cond' into integration 2025-07-13 18:07:57 -10:00
J. Nick Koston
221f380ca3 single func 2025-07-13 18:03:21 -10:00
Jesse Hills
16292a9f13 Merge pull request #9483 from esphome/bump-2025.7.0b3
2025.7.0b3
2025-07-14 15:41:59 +12:00
J. Nick Koston
85fa884382 Merge remote-tracking branch 'upstream/dev' into integration 2025-07-13 17:37:13 -10:00
J. Nick Koston
873f4125c5 Fix pre-commit CI issues by switching to lite mode (#9484) 2025-07-13 17:30:34 -10:00
Jesse Hills
90f0ebb22b Dont autofix PR 2025-07-14 14:12:26 +12:00
Jesse Hills
4153380f99 Remove hook that doesnt exist on beta 2025-07-14 13:48:41 +12:00
J. Nick Koston
740c0ef9d7 Fix pre-commit CI failures by skipping local hooks that require virtual environment (#9476) 2025-07-14 13:46:14 +12:00
Jesse Hills
b4521e1d8c Bump version to 2025.7.0b3 2025-07-14 13:08:24 +12:00
J. Nick Koston
10ca7ed85b Fix dormant bug in RAMAllocator::reallocate() manual_size calculation (#9482) 2025-07-14 13:08:23 +12:00
J. Nick Koston
e43efdaaec Follow logging best practices by removing redundant component prefix (#9481) 2025-07-14 13:08:23 +12:00
Clyde Stubbs
9207bf97f3 [esp_ldo] Component schema; default priority (#9479) 2025-07-14 13:08:23 +12:00
Javier Peletier
c13317f807 [substitutions] Fix #7189 (#9469) 2025-07-14 13:08:23 +12:00
J. Nick Koston
77d1d0414d Automatically disable interrupts for ESP8266 GPIO16 binary sensors (#9467) 2025-07-14 13:08:23 +12:00
Peter Zich
8f42bc6aac [lvgl] Post-process size arguments in meter config (#9466)
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
2025-07-14 13:08:23 +12:00
Peter Zich
9beb4e2cd4 (Maybe?) fix I2S speaker internal DAC mode (#9435) 2025-07-14 13:08:23 +12:00
Keith Burzinski
097aac2183 [ld2420] Memory optimization, code clean-up (#9426) 2025-07-14 13:08:23 +12:00
J. Nick Koston
d31b8ad2e2 Fix dormant bug in RAMAllocator::reallocate() manual_size calculation (#9482) 2025-07-14 00:58:07 +00:00
J. Nick Koston
6cd443e9dc Merge remote-tracking branch 'upstream/dev' into integration 2025-07-13 14:51:19 -10:00
J. Nick Koston
f5c8595a46 Follow logging best practices by removing redundant component prefix (#9481) 2025-07-14 00:41:49 +00:00
J. Nick Koston
02d1894a9f Refactor format_hex_pretty functions to eliminate code duplication (#9480) 2025-07-14 00:32:16 +00:00
J. Nick Koston
c8c1573fbb Merge branch 'component_logging' into integration 2025-07-13 14:12:35 -10:00
J. Nick Koston
e7fb069bb3 Merge branch 'ram_realloc_bug' into integration 2025-07-13 14:12:31 -10:00
J. Nick Koston
fe7e5feba7 Fix dormant bug in RAMAllocator::reallocate() manual_size calculation 2025-07-13 14:11:23 -10:00
J. Nick Koston
1abdc23a23 Follow logging best practices by removing redundant component prefix 2025-07-13 13:55:35 -10:00
Clyde Stubbs
fc337aef69 [esp_ldo] Component schema; default priority (#9479) 2025-07-13 23:47:52 +00:00
J. Nick Koston
ec3660e8ae Merge remote-tracking branch 'upstream/dev' into integration 2025-07-13 13:05:16 -10:00
J. Nick Koston
b21c76a6c6 Fix clang-tidy skipping when Python linters are skipped (#9463) 2025-07-14 11:04:14 +12:00
J. Nick Koston
5416cee2c9 Fix pre-commit CI failures by skipping local hooks that require virtual environment (#9476) 2025-07-14 10:44:21 +12:00
J. Nick Koston
f6251cf6e4 Merge branch 'proto_field_ifdefs' into integration 2025-07-13 12:43:54 -10:00
J. Nick Koston
77258b5e62 Merge remote-tracking branch 'upstream/proto_field_ifdefs' into proto_field_ifdefs 2025-07-13 12:12:11 -10:00
J. Nick Koston
dc7996922b missing ifdefs 2025-07-13 12:11:59 -10:00
Javier Peletier
9e002cd7a3 [substitutions] Fix #7189 (#9469) 2025-07-14 09:58:52 +12:00
J. Nick Koston
31a6ae00b5 Merge branch 'dev' into proto_field_ifdefs 2025-07-13 11:57:55 -10:00
dependabot[bot]
9451781915 Bump aioesphomeapi from 34.2.1 to 35.0.1 (#9474)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-07-13 21:53:04 +00:00
J. Nick Koston
d2569c0f1e Reduce binary size with field-level conditional compilation for protobuf messages 2025-07-13 11:16:36 -10:00
J. Nick Koston
d8f786cfdf Merge branch 'wire_type_1' into integration 2025-07-13 10:20:21 -10:00
J. Nick Koston
75ef572a24 Remove dead code: 64-bit protobuf types never used in 7 years 2025-07-13 10:18:55 -10:00
J. Nick Koston
f0f30224d3 Merge branch 'webserver_cleanups' into integration 2025-07-13 09:10:24 -10:00
J. Nick Koston
ec6e61e688 Refactor WebServer request handling for improved maintainability 2025-07-13 09:08:51 -10:00
J. Nick Koston
3dd457c471 Merge branch 'api_errors' into integration 2025-07-13 08:08:52 -10:00
J. Nick Koston
3b8a34c8d0 Reduce API component flash usage by consolidating error logging 2025-07-13 07:57:27 -10:00
J. Nick Koston
84956b6dc5 Automatically disable interrupts for ESP8266 GPIO16 binary sensors (#9467) 2025-07-13 20:09:55 +12:00
Peter Zich
6f19808eff [lvgl] Post-process size arguments in meter config (#9466)
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
2025-07-13 15:43:32 +10:00
J. Nick Koston
fe76955f3e Merge remote-tracking branch 'upstream/dev' into integration 2025-07-12 16:50:25 -10:00
Peter Zich
cd8e1548bf (Maybe?) fix I2S speaker internal DAC mode (#9435) 2025-07-13 13:22:07 +12:00
Jesse Hills
48d55a70c0 Merge branch 'beta' into dev 2025-07-13 13:18:50 +12:00
Jesse Hills
18787b0be0 Merge pull request #9462 from esphome/bump-2025.7.0b2
2025.7.0b2
2025-07-13 13:18:17 +12:00
J. Nick Koston
db8767fb05 Merge remote-tracking branch 'upstream/dev' into drop_unique_id 2025-07-12 14:25:47 -10:00
J. Nick Koston
7dad9c2ab0 Merge branch 'reformat_api_jump_tables' into integration 2025-07-12 13:52:14 -10:00
J. Nick Koston
4c0c0954a4 Merge remote-tracking branch 'upstream/dev' into reformat_api_jump_tables 2025-07-12 13:51:50 -10:00
Jesse Hills
39e01c42e1 Bump version to 2025.7.0b2 2025-07-13 11:05:14 +12:00
Jonathan Swoboda
c760f89e46 [libretiny] Set lib_compat_mode to soft for libretiny (#9439) 2025-07-13 11:05:13 +12:00
Clyde Stubbs
01b4e214b9 [usb_uart] Be flexible about descriptor layout for CDC-ACM devices (#9425) 2025-07-13 11:05:13 +12:00
J. Nick Koston
bc7cfeb9cd Only generate protobuf encode/decode methods for the message direction they're used (#9461) 2025-07-13 11:05:13 +12:00
dependabot[bot]
36dd203e74 Bump aioesphomeapi from 34.2.0 to 34.2.1 (#9460)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-13 11:05:13 +12:00
J. Nick Koston
8605994cc6 Apply existing protobuf buffer optimization to nested message encoding (~2.3x speed up) (#9458) 2025-07-13 11:05:13 +12:00
Jonathan Swoboda
80fbe28088 [sx127x, sx126x] Fix preamble_size default and validation (#9454) 2025-07-13 11:05:13 +12:00
Clyde Stubbs
1d9f17a57c [packet_transport] Don't run update if ping_pong not enabled. (#9434) 2025-07-13 11:05:13 +12:00
J. Nick Koston
42947bcf56 Conditionally compile API user services to save 4.3KB flash (follow-up to #9262) (#9451) 2025-07-13 11:05:13 +12:00
J. Nick Koston
3c864b2bca Reduce API flash usage by eliminating unnecessary template instantiations (#9452) 2025-07-13 11:05:13 +12:00
Keith Burzinski
35d88fc0d6 [ld2410] Remove redundant `delay()` calls, minor optimizations (#9453) 2025-07-13 11:05:13 +12:00
J. Nick Koston
7a6894e087 Optimize API proto size calculations by removing redundant force parameter (#9449) 2025-07-13 11:05:13 +12:00
J. Nick Koston
1b222ceca3 Optimize API flash usage by storing message size at compile time (#9447) 2025-07-13 11:05:13 +12:00
Samuel Sieb
bab3deee1b [wizard] use lowercase to match (#9448)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2025-07-13 11:05:13 +12:00
J. Nick Koston
ccd30110b1 Fix scheduler crash when cancelling items with NULL names (#9444) 2025-07-13 11:05:13 +12:00
J. Nick Koston
904c7b8a3a Sync api.proto from aioesphomeapi (#9393) 2025-07-13 11:05:13 +12:00
DT-art1
fa262673e4 Replace remaining instances of USE_ESP32_CAMERA with USE_CAMERA (#9401) 2025-07-13 11:05:13 +12:00
Adam Liddell
0ef5f1fd65 Handle ESP32 chunked MQTT messages missing topic on non-first chunks, causing panic (#5786)
Co-authored-by: Samuel Sieb <samuel-github@sieb.net>
2025-07-13 11:05:13 +12:00
J. Nick Koston
23dd2d648e Exclude internal entities from name uniqueness validation (#9410) 2025-07-13 11:05:13 +12:00
@RubenKelevra
5ba493acc3 debug: bufferoverflow mitigation in DebugComponent::on_shutdown() (#9422) 2025-07-13 11:05:12 +12:00
Jonathan Swoboda
a5055094d0 [esp32] Set lib_compat_mode to strict (#9408) 2025-07-13 11:05:12 +12:00
Jonathan Swoboda
92d03dd196 [esp32_touch] Fix touch v1 (#9414) 2025-07-13 11:05:12 +12:00
J. Nick Koston
bd75f0dfea Fix another race in the string lifetime scheduler test (#9399) 2025-07-13 11:05:12 +12:00
Jonathan Swoboda
f4ac951b15 [libretiny] Set lib_compat_mode to soft for libretiny (#9439) 2025-07-13 11:00:38 +12:00
Clyde Stubbs
e020110579 [usb_uart] Be flexible about descriptor layout for CDC-ACM devices (#9425) 2025-07-13 10:59:49 +12:00
J. Nick Koston
1fda40f0ce Only generate protobuf encode/decode methods for the message direction they're used (#9461) 2025-07-13 10:58:57 +12:00
J. Nick Koston
f5a486a7da Merge branch 'source_server' into integration 2025-07-12 10:50:31 -10:00
J. Nick Koston
425d57ba7d other direction 2025-07-12 10:23:21 -10:00
J. Nick Koston
1965a41725 Skip generating decode methods for SOURCE_SERVER protobuf messages 2025-07-12 10:18:53 -10:00
J. Nick Koston
27f352b7dc Merge remote-tracking branch 'upstream/dev' into integration 2025-07-12 10:08:17 -10:00
dependabot[bot]
a5e42e1bd0 Bump aioesphomeapi from 34.2.0 to 34.2.1 (#9460)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-12 10:07:58 -10:00
J. Nick Koston
8863188dd8 Apply existing protobuf buffer optimization to nested message encoding (~2.3x speed up) (#9458) 2025-07-13 06:55:32 +12:00
J. Nick Koston
e01fb0b677 merge 2025-07-12 07:24:12 -10:00
J. Nick Koston
671f0d62c7 Merge remote-tracking branch 'origin/runtime_stats' into integration 2025-07-12 07:21:51 -10:00
J. Nick Koston
f1b888b309 Merge branch 'dev' into runtime_stats 2025-07-12 07:21:44 -10:00
J. Nick Koston
9cbcd0497a Merge branch 'encode_perf' into integration 2025-07-12 07:14:06 -10:00
J. Nick Koston
0139de37ba fixup 2025-07-12 06:51:40 -10:00
J. Nick Koston
4e7fe88da3 Apply existing protobuf buffer optimization to nested message encoding 2025-07-12 06:45:37 -10:00
Jonathan Swoboda
7747a5aa62 [sx127x, sx126x] Fix preamble_size default and validation (#9454) 2025-07-12 17:03:03 +10:00
Clyde Stubbs
32419645ca [packet_transport] Don't run update if ping_pong not enabled. (#9434) 2025-07-12 17:00:52 +10:00
J. Nick Koston
634aa55364 Disable WiFi when using Ethernet to save memory (#9456) 2025-07-12 05:19:53 +00:00
J. Nick Koston
6af4961695 Merge remote-tracking branch 'upstream/dev' into integration 2025-07-11 19:08:23 -10:00
J. Nick Koston
dd5ba5a90c Conditionally compile API user services to save 4.3KB flash (follow-up to #9262) (#9451) 2025-07-11 19:08:03 -10:00
J. Nick Koston
ae78f7798f Merge branch 'disable_wifi_ethernet_pr' into integration 2025-07-11 19:00:15 -10:00
J. Nick Koston
53295fde7e Disable WiFi when using Ethernet to save memory 2025-07-11 18:59:31 -10:00
J. Nick Koston
df461c08a9 Merge branch 'reformat_api_jump_tables' into integration 2025-07-11 18:34:07 -10:00
J. Nick Koston
bbf9dcc15a Merge remote-tracking branch 'origin/integration' into integration 2025-07-11 18:33:57 -10:00
J. Nick Koston
a3e7105cb0 Merge remote-tracking branch 'upstream/dev' into integration 2025-07-11 18:33:40 -10:00
J. Nick Koston
10e5400d1f Improve API protobuf decode method readability and reduce code size 2025-07-11 18:30:08 -10:00
dependabot[bot]
0138ef36cf Bump ruff from 0.12.2 to 0.12.3 (#9446)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-07-12 04:01:54 +00:00
J. Nick Koston
ca5ee0ce07 Reduce API flash usage by eliminating unnecessary template instantiations (#9452) 2025-07-12 03:56:08 +00:00
Keith Burzinski
79b5fcf31a [ld2420] Memory optimization, code clean-up (#9426) 2025-07-11 22:33:36 -05:00
J. Nick Koston
97eb949670 Merge remote-tracking branch 'origin/integration' into integration 2025-07-11 17:32:30 -10:00
J. Nick Koston
8f484a89f3 Merge branch 'enum_uint32' into integration 2025-07-11 17:32:15 -10:00
J. Nick Koston
1f35c35e2a oops, removed wrong one 2025-07-11 17:32:07 -10:00
J. Nick Koston
427560f814 address bot review comments 2025-07-11 17:24:55 -10:00
J. Nick Koston
4a3000bcc5 Merge branch 'guard_custom_services' into integration 2025-07-11 17:06:28 -10:00
J. Nick Koston
25cac3e04e make clang-tidy happy 2025-07-11 17:06:08 -10:00
Keith Burzinski
2243e44750 [ld2410] Remove redundant `delay()` calls, minor optimizations (#9453) 2025-07-11 22:05:06 -05:00
J. Nick Koston
fc386a2648 Merge branch 'integration' of https://github.com/esphome/esphome into integration 2025-07-11 16:52:42 -10:00
J. Nick Koston
3fd9a3507c Merge branch 'reduce_api_size' into integration 2025-07-11 16:52:27 -10:00
J. Nick Koston
d92005113a Merge branch 'guard_custom_services' into integration 2025-07-11 16:52:15 -10:00
J. Nick Koston
fb2d764c89 tidy 2025-07-11 16:48:20 -10:00
J. Nick Koston
0251bb48ec Merge branch 'guard_custom_services' into integration 2025-07-11 16:34:10 -10:00
J. Nick Koston
a035db1d11 Merge remote-tracking branch 'origin/enum_uint32' into integration 2025-07-11 16:34:05 -10:00
J. Nick Koston
88049f9801 Merge branch 'dev' into enum_uint32 2025-07-11 16:33:45 -10:00
J. Nick Koston
42a9125ea7 preen 2025-07-11 16:25:49 -10:00
J. Nick Koston
63972ff272 preen 2025-07-11 16:24:16 -10:00
J. Nick Koston
dc53473e7e less templates 2025-07-11 16:16:57 -10:00
J. Nick Koston
2c290e3bee less templates 2025-07-11 16:10:39 -10:00
J. Nick Koston
c37494ea55 less templates 2025-07-11 16:09:33 -10:00
J. Nick Koston
01f949e097 Optimize API proto size calculations by removing redundant force parameter (#9449) 2025-07-11 21:08:52 -05:00
J. Nick Koston
b3e8963a33 less templates 2025-07-11 16:06:50 -10:00
J. Nick Koston
e2c77c0c4f less templates 2025-07-11 16:05:55 -10:00
J. Nick Koston
139ce4c655 lint 2025-07-11 15:54:20 -10:00
J. Nick Koston
010dc35efc etst 2025-07-11 15:49:59 -10:00
J. Nick Koston
413969300b etst 2025-07-11 15:48:36 -10:00
J. Nick Koston
b67a88027d guard 2025-07-11 15:35:26 -10:00
J. Nick Koston
2384b54ee3 Guard custom services 2025-07-11 15:30:37 -10:00
J. Nick Koston
7e3a203f0b Merge branch 'remove_false' into integration 2025-07-11 14:42:18 -10:00
J. Nick Koston
054e39316c Merge remote-tracking branch 'upstream/dev' into integration 2025-07-11 14:42:09 -10:00
J. Nick Koston
7ce879521d Merge branch 'dev' into remove_false 2025-07-11 14:39:09 -10:00
J. Nick Koston
143bf694c7 Optimize API flash usage by storing message size at compile time (#9447) 2025-07-11 19:38:23 -05:00
J. Nick Koston
6f5f378857 address lint comment 2025-07-11 14:36:54 -10:00
J. Nick Koston
773950332b address lint comment 2025-07-11 14:36:38 -10:00
J. Nick Koston
35cd8616a2 Merge branch 'remove_false' into integration 2025-07-11 14:17:44 -10:00
J. Nick Koston
95786ce269 review feedback from bot 2025-07-11 14:16:17 -10:00
J. Nick Koston
c082ee616e address feedback 2025-07-11 14:12:03 -10:00
J. Nick Koston
869f96f832 Optimize API proto size calculations by removing redundant force parameter 2025-07-11 14:06:27 -10:00
Samuel Sieb
983db6215f [wizard] use lowercase to match (#9448)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2025-07-11 18:35:52 -05:00
J. Nick Koston
504ca09451 revert 2025-07-11 11:59:39 -10:00
J. Nick Koston
90c4b71d3f revert 2025-07-11 11:59:14 -10:00
J. Nick Koston
7e1db7a75c Merge remote-tracking branch 'origin/integration' into integration 2025-07-11 11:54:39 -10:00
J. Nick Koston
2f298992cf Merge branch 'auto_auth' into integration 2025-07-11 11:54:23 -10:00
J. Nick Koston
c8c5c26896 Merge remote-tracking branch 'origin/integration' into integration 2025-07-11 11:54:03 -10:00
J. Nick Koston
d8545ef946 Merge branch 'reduce_api_size' into integration 2025-07-11 11:53:49 -10:00
J. Nick Koston
db68f9571b revert 2025-07-11 11:47:09 -10:00
J. Nick Koston
0350471fa9 revert 2025-07-11 11:46:03 -10:00
J. Nick Koston
3ed533d709 tweak 2025-07-11 11:37:17 -10:00
J. Nick Koston
e472a345c9 tweak 2025-07-11 11:05:28 -10:00
J. Nick Koston
42be5d892a cleanup 2025-07-11 10:58:05 -10:00
J. Nick Koston
9a0d5019e1 tweak 2025-07-11 10:27:59 -10:00
J. Nick Koston
fc8c1ac9dd make sure we did not break password auth 2025-07-11 10:19:33 -10:00
J. Nick Koston
536134e2b5 preen 2025-07-11 10:17:41 -10:00
J. Nick Koston
005d4354d5 test this 2025-07-11 10:14:59 -10:00
J. Nick Koston
7107b5cfef preen 2025-07-11 10:11:26 -10:00
J. Nick Koston
bb153d42dc review 2025-07-11 10:02:51 -10:00
J. Nick Koston
a3806e4de2 Optimize API performance and flash usage by eliminating runtime message size lookup 2025-07-11 10:01:11 -10:00
J. Nick Koston
4dbe19a56e Auto auth if no password is required
Next step in password deprecation
2025-07-11 09:22:40 -10:00
J. Nick Koston
0b74122d6f Auto auth if no password is required
Next step in password deprecation
2025-07-11 09:21:18 -10:00
J. Nick Koston
e148c22f25 Auto auth if no password is required
Next step in password deprecation
2025-07-11 09:20:42 -10:00
J. Nick Koston
bef20b60d0 Fix scheduler crash when cancelling items with NULL names (#9444) 2025-07-12 07:11:45 +12:00
J. Nick Koston
475fe60f27 Sync api.proto from aioesphomeapi (#9393) 2025-07-11 08:33:18 -10:00
J. Nick Koston
8953e53a04 CI: Centralize test determination logic to reduce unnecessary job runners (#9432) 2025-07-11 21:54:57 +12:00
DT-art1
143702beef Replace remaining instances of USE_ESP32_CAMERA with USE_CAMERA (#9401) 2025-07-10 20:35:24 +12:00
Adam Liddell
05238b447f Handle ESP32 chunked MQTT messages missing topic on non-first chunks, causing panic (#5786)
Co-authored-by: Samuel Sieb <samuel-github@sieb.net>
2025-07-10 20:34:43 +12:00
J. Nick Koston
0d94246858 Exclude internal entities from name uniqueness validation (#9410) 2025-07-10 20:34:01 +12:00
Samuel Sieb
2be4951ad9 [esp32] remove debug log (#9424) 2025-07-10 08:24:39 +00:00
Clyde Stubbs
16bb81814c [config] Add bitrate validator (#9423) 2025-07-10 04:14:42 +00:00
@RubenKelevra
7d92499e4c debug: bufferoverflow mitigation in DebugComponent::on_shutdown() (#9422) 2025-07-09 17:01:21 -10:00
J. Nick Koston
1678eb0591 Merge remote-tracking branch 'upstream/dev' into integration 2025-07-09 15:20:35 -10:00
J. Nick Koston
dfa4328604 tidy up 2025-07-08 13:03:01 -06:00
J. Nick Koston
9dab840c58 tidy up 2025-07-08 11:42:17 -06:00
J. Nick Koston
29fff967f5 tweak 2025-07-08 11:38:14 -06:00
J. Nick Koston
2a35c95718 fixes 2025-07-08 11:31:25 -06:00
J. Nick Koston
4a70aa26e8 Merge branch 'runtime_stats' into integration 2025-07-08 11:25:09 -06:00
J. Nick Koston
748604d374 preen 2025-07-08 11:12:46 -06:00
J. Nick Koston
d32db20aa0 preen 2025-07-08 11:10:32 -06:00
J. Nick Koston
ae346bb94e remove dead code 2025-07-08 10:11:47 -06:00
J. Nick Koston
cb67010574 remove dead code 2025-07-08 10:11:24 -06:00
J. Nick Koston
defa452aa1 preen 2025-07-08 09:58:02 -06:00
J. Nick Koston
97a476b475 stats 2025-07-08 09:52:44 -06:00
J. Nick Koston
07a4f6f53c fixes 2025-07-08 09:40:12 -06:00
J. Nick Koston
7d2726ab21 fixes 2025-07-08 09:37:07 -06:00
J. Nick Koston
3862e3b4e7 fixes 2025-07-08 09:35:31 -06:00
J. Nick Koston
be84f12100 fixes 2025-07-08 09:34:56 -06:00
J. Nick Koston
0097a55eaa fixes 2025-07-08 09:34:16 -06:00
J. Nick Koston
d1609de25a cleanup 2025-07-08 09:28:09 -06:00
J. Nick Koston
02395c92a1 cleanup 2025-07-08 09:26:13 -06:00
J. Nick Koston
f2ac6b0af6 cleanup 2025-07-08 09:25:00 -06:00
J. Nick Koston
a3c8f667a7 cleanup 2025-07-08 09:16:42 -06:00
J. Nick Koston
d06bab01ac runtime_stats 2025-07-08 09:09:07 -06:00
J. Nick Koston
591786a787 Merge remote-tracking branch 'upstream/dev' into integration 2025-07-08 09:08:03 -06:00
J. Nick Koston
5de7b874b0 sync 2025-07-07 17:23:01 -05:00
J. Nick Koston
a27f6c72b9 Merge remote-tracking branch 'upstream/dev' into integration 2025-07-07 17:22:35 -05:00
J. Nick Koston
a80d3012bd Merge remote-tracking branch 'origin/integration' into integration 2025-07-07 16:34:03 -05:00
J. Nick Koston
1c0a646309 Merge upstream/dev into integration 2025-07-07 16:33:30 -05:00
J. Nick Koston
eabb781e5f Merge remote-tracking branch 'upstream/dev' into integration 2025-07-07 15:46:19 -05:00
J. Nick Koston
03c2cda17c Merge branch 'logger_strlen' into integration 2025-07-07 15:38:53 -05:00
J. Nick Koston
26b72ccb10 Merge remote-tracking branch 'origin/logger_strlen' into logger_strlen 2025-07-07 15:18:37 -05:00
J. Nick Koston
ab993c6d5a add diagram 2025-07-07 15:18:27 -05:00
J. Nick Koston
999090fa18 Merge remote-tracking branch 'upstream/dev' into integration 2025-07-07 15:11:18 -05:00
J. Nick Koston
2eed309224 Merge branch 'dev' into logger_strlen 2025-07-07 15:09:16 -05:00
J. Nick Koston
12980847a8 Merge branch 'logger_strlen' into integration 2025-07-07 15:08:21 -05:00
J. Nick Koston
01a6b38b89 null term is already there 2025-07-07 15:08:11 -05:00
J. Nick Koston
73b786c22e fix calculation 2025-07-07 15:01:15 -05:00
J. Nick Koston
c1a6e82322 fix calculation 2025-07-07 14:58:45 -05:00
J. Nick Koston
ec848bc7b4 Merge branch 'bluetooth_proxy_guard_var' into integration 2025-07-07 14:39:39 -05:00
J. Nick Koston
e5df43b934 cleanup 2025-07-07 14:38:49 -05:00
J. Nick Koston
d13f87e891 Merge upstream/dev while preserving guard variable optimization 2025-07-07 14:37:28 -05:00
J. Nick Koston
132d56fe1a lint 2025-07-07 14:24:55 -05:00
J. Nick Koston
171e19381f Update esphome/components/text_sensor/text_sensor.cpp 2025-07-07 14:19:17 -05:00
J. Nick Koston
9cc7b060c9 Update esphome/components/sensor/sensor.h 2025-07-07 14:18:57 -05:00
J. Nick Koston
d178e2da6f reduce more 2025-07-07 14:05:14 -05:00
J. Nick Koston
1e8f961362 reduce more 2025-07-07 14:03:15 -05:00
J. Nick Koston
166f77610f reduce more 2025-07-07 14:02:15 -05:00
J. Nick Koston
97dc244d1e reduce more 2025-07-07 13:55:22 -05:00
J. Nick Koston
085ddebf7d Merge remote-tracking branch 'upstream/dev' into drop_unique_id 2025-07-07 13:51:53 -05:00
J. Nick Koston
bccc3d79d8 Merge branch 'api_voice' into integration 2025-07-07 13:39:47 -05:00
J. Nick Koston
4df3bfe85d review 2025-07-07 13:39:37 -05:00
J. Nick Koston
99345574e4 Merge branch 'api_voice' into integration 2025-07-07 13:09:45 -05:00
J. Nick Koston
80c66b0742 preen 2025-07-07 13:09:30 -05:00
J. Nick Koston
17d820570b Merge branch 'api_dispatch_dry' into integration 2025-07-07 12:24:09 -05:00
J. Nick Koston
c979d5c9b1 bad linter suggestion again 2025-07-07 12:23:57 -05:00
J. Nick Koston
13ac6df1dd Merge branch 'api_voice' into integration 2025-07-07 12:06:12 -05:00
J. Nick Koston
98d091fbc3 Refactor voice assistant API methods to reduce code duplication 2025-07-07 12:05:31 -05:00
J. Nick Koston
22e67f9754 Merge branch 'prepare_commands_for_device_id' into integration 2025-07-07 11:48:43 -05:00
J. Nick Koston
515a97de76 clang-format 2025-07-07 11:48:35 -05:00
J. Nick Koston
68b5337ed3 Merge branch 'api_dispatch_dry' into integration 2025-07-07 11:44:49 -05:00
J. Nick Koston
8ee86c717b update is a special case as well 2025-07-07 11:44:31 -05:00
J. Nick Koston
10530cdef3 Merge branch 'api_dispatch_dry' into integration 2025-07-07 11:40:41 -05:00
J. Nick Koston
5de0f9efc9 Refactor API entity update dispatch to reduce code duplication 2025-07-07 11:37:06 -05:00
J. Nick Koston
6d1d7f137f Merge branch 'prepare_commands_for_device_id' into integration 2025-07-07 11:15:32 -05:00
J. Nick Koston
38e16efa11 Refactor entity lookup methods with macros in preparation for device_id support 2025-07-07 11:10:23 -05:00
J. Nick Koston
5e2f0f7f5e Merge branch 'socket_read_dupe_code' into integration 2025-07-07 10:33:51 -05:00
J. Nick Koston
83c7afc46f Refactor duplicate socket read error handling in API frame helper 2025-07-07 10:33:04 -05:00
J. Nick Koston
10753f0f99 Merge branch 'more_str_len_logger' into integration 2025-07-07 10:10:17 -05:00
J. Nick Koston
34a852d433 Optimize logger performance by eliminating redundant strlen calls 2025-07-07 10:09:51 -05:00
J. Nick Koston
3922fbdef7 Merge branch 'logger_strlen' into integration 2025-07-07 09:04:17 -05:00
J. Nick Koston
e5415abf20 tidy 2025-07-07 09:03:52 -05:00
J. Nick Koston
67e1a92cce Merge branch 'logger_strlen' into integration 2025-07-07 08:53:11 -05:00
J. Nick Koston
4c64511a15 apply suggestions from review 2025-07-07 08:52:52 -05:00
J. Nick Koston
75f3e0900e apply suggestions from review 2025-07-07 08:52:28 -05:00
J. Nick Koston
abd33c21bf Update esphome/components/syslog/esphome_syslog.cpp
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-07 08:50:40 -05:00
J. Nick Koston
d592ba2c5e Update esphome/components/web_server/web_server.cpp
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-07 08:50:33 -05:00
J. Nick Koston
321eba5184 Merge branch 'logger_strlen' into integration 2025-07-07 08:48:45 -05:00
J. Nick Koston
82b9ec53fd fix merge error 2025-07-07 08:48:35 -05:00
J. Nick Koston
b9262f967b Merge branch 'logger_strlen' into integration 2025-07-07 08:47:27 -05:00
J. Nick Koston
949fb9a890 Optimize logger callback API by including message length parameter 2025-07-07 08:46:21 -05:00
J. Nick Koston
99952a701f Merge remote-tracking branch 'upstream/jesserockz-2025-282' into integration 2025-07-07 07:34:48 -05:00
J. Nick Koston
88878adb6c Merge branch 'filter_compile_warning_vv' into integration 2025-07-07 07:33:53 -05:00
J. Nick Koston
17e3b49ebb Merge branch 'api_sprint' into integration 2025-07-07 07:33:49 -05:00
J. Nick Koston
a217747f5d Replace deprecated sprintf with snprintf in API protobuf code generation 2025-07-07 07:32:22 -05:00
J. Nick Koston
790c9cbb84 Fix format specifier warnings in QuantileFilter logging 2025-07-07 07:27:31 -05:00
J. Nick Koston
da5fb6e24f Merge branch '20250707-ld2450-clean-up' into integration 2025-07-07 07:18:12 -05:00
Jesse Hills
a77439b4b7 Ignore new helper files for namespace inclusion 2025-07-07 23:24:30 +12:00
Jesse Hills
1a049bdcbb More missing includes 2025-07-07 21:06:50 +12:00
Keith Burzinski
79686239d3 Rename button, sort vars 2025-07-07 03:33:21 -05:00
Keith Burzinski
c934e84e21 [ld2450] Clean-up for consistency, reduce CPU usage when idle 2025-07-07 03:23:04 -05:00
Jesse Hills
5e2f8cb018 Missing includes 2025-07-07 17:33:05 +12:00
J. Nick Koston
6bd0af6d85 Merge branch 'heap_scheduler_stress_component' into integration 2025-07-06 23:58:09 -05:00
J. Nick Koston
0f28a49822 tidy 2025-07-06 23:57:46 -05:00
Jesse Hills
66d96646b1 [core] Move platform helper implementations into their own file 2025-07-07 16:40:45 +12:00
J. Nick Koston
be4cf6505f Merge branch 'heap_scheduler_stress_component' into integration 2025-07-06 22:56:46 -05:00
J. Nick Koston
e8ea7825a9 Merge branch 'dev' into heap_scheduler_stress_component 2025-07-06 22:56:18 -05:00
J. Nick Koston
8c13eab731 no flakey 2025-07-06 22:54:46 -05:00
J. Nick Koston
bf4cbb0aee Merge branch 'heap_scheduler_stress_component' into integration 2025-07-06 22:14:38 -05:00
J. Nick Koston
aaec4b7bd3 validation consistent 2025-07-06 22:13:35 -05:00
J. Nick Koston
7bddcd4f64 Merge branch 'heap_scheduler_stress_component' into integration 2025-07-06 22:07:21 -05:00
J. Nick Koston
af205a5267 one more test 2025-07-06 22:01:19 -05:00
J. Nick Koston
c2599d7719 safer 2025-07-06 21:43:03 -05:00
J. Nick Koston
4ea6f23d9e Merge branch 'heap_scheduler_stress_component' into integration 2025-07-06 21:32:06 -05:00
J. Nick Koston
f23fd52a26 clarify what we know 2025-07-06 21:31:39 -05:00
J. Nick Koston
cfd43c81fb clarify what we know 2025-07-06 21:30:39 -05:00
J. Nick Koston
3dcba675b4 Merge branch 'heap_scheduler_stress_component' into integration 2025-07-06 21:23:51 -05:00
J. Nick Koston
bb51031ec6 preen 2025-07-06 21:23:30 -05:00
J. Nick Koston
ecb99cbcce fix flakey test 2025-07-06 21:19:38 -05:00
J. Nick Koston
0a514821c6 preen 2025-07-06 21:04:23 -05:00
J. Nick Koston
074fbb522c preen 2025-07-06 21:01:52 -05:00
J. Nick Koston
71d6ba242e preen 2025-07-06 21:01:25 -05:00
J. Nick Koston
37ffd64b48 Merge remote-tracking branch 'upstream/heap_scheduler_stress_component' into heap_scheduler_stress_component 2025-07-06 20:59:59 -05:00
J. Nick Koston
ec65652567 add missed remake 2025-07-06 20:59:43 -05:00
J. Nick Koston
731613421d fix flakey 2025-07-06 20:59:08 -05:00
J. Nick Koston
58dfad4ed0 Merge branch 'heap_scheduler_stress_component' into integration 2025-07-06 20:38:10 -05:00
J. Nick Koston
7eb029f4b9 cleanup 2025-07-06 20:38:00 -05:00
J. Nick Koston
8da8d938f0 Merge branch 'heap_scheduler_stress_component' into integration 2025-07-06 20:36:50 -05:00
J. Nick Koston
64ac0d2bde cover 2025-07-06 20:36:32 -05:00
J. Nick Koston
7d3cdd15ad cleanup 2025-07-06 20:31:28 -05:00
J. Nick Koston
53baf02087 cleanup 2025-07-06 20:30:40 -05:00
J. Nick Koston
a0d2392344 cleanup 2025-07-06 20:26:43 -05:00
J. Nick Koston
fb3c092eaa cleanup 2025-07-06 20:25:27 -05:00
J. Nick Koston
c169cf1e77 Merge remote-tracking branch 'origin/heap_scheduler_stress_component' into heap_scheduler_stress_component 2025-07-06 20:13:48 -05:00
J. Nick Koston
fa4d8e083a Merge remote-tracking branch 'origin/dev' into heap_scheduler_stress_component 2025-07-06 20:13:34 -05:00
J. Nick Koston
2cfeccfd71 cleanup locking 2025-07-06 20:13:21 -05:00
J. Nick Koston
f36ca93752 Merge branch 'dev' into heap_scheduler_stress_component 2025-07-06 20:05:23 -05:00
J. Nick Koston
dc8714c277 fix race 2025-07-06 19:59:11 -05:00
J. Nick Koston
90fcb5fbcd fix another race 2025-07-06 19:54:07 -05:00
J. Nick Koston
932d0a5d8b fix another race 2025-07-06 19:50:54 -05:00
J. Nick Koston
4cafa18fa4 fix another race 2025-07-06 19:46:23 -05:00
J. Nick Koston
b12d7db5a7 prevent future refactoring errors 2025-07-06 19:27:33 -05:00
J. Nick Koston
e84345594d Merge branch 'heap_scheduler_stress_component' into integration 2025-07-06 19:01:11 -05:00
J. Nick Koston
add7bec7f2 tweak 2025-07-06 18:54:00 -05:00
J. Nick Koston
db84d8e8dc tweak 2025-07-06 18:49:41 -05:00
J. Nick Koston
ad51e647af tweak 2025-07-06 18:48:50 -05:00
J. Nick Koston
c45901746b tweak 2025-07-06 18:46:48 -05:00
J. Nick Koston
033c469250 tweak 2025-07-06 18:44:19 -05:00
J. Nick Koston
0900fd3cea tweak 2025-07-06 18:42:47 -05:00
J. Nick Koston
ba8f3d3f63 tweak 2025-07-06 18:36:05 -05:00
J. Nick Koston
2759f3828e tweak 2025-07-06 18:34:56 -05:00
J. Nick Koston
f395767766 tweak 2025-07-06 18:27:49 -05:00
J. Nick Koston
2dc222aea6 tweak 2025-07-06 18:26:29 -05:00
J. Nick Koston
82d68c87e2 adjust 2025-07-06 18:24:00 -05:00
J. Nick Koston
f213657753 adjust 2025-07-06 18:18:47 -05:00
J. Nick Koston
e077e6cec7 adjust 2025-07-06 18:17:16 -05:00
J. Nick Koston
339a3270f6 adjust 2025-07-06 18:16:25 -05:00
J. Nick Koston
462b44ee23 adjust 2025-07-06 18:15:11 -05:00
J. Nick Koston
52d3dba89c adjust 2025-07-06 18:11:04 -05:00
J. Nick Koston
939d01dd99 preen 2025-07-06 18:08:50 -05:00
J. Nick Koston
4900f7c7ca preen 2025-07-06 18:07:34 -05:00
J. Nick Koston
48957aee8b preen 2025-07-06 18:03:53 -05:00
J. Nick Koston
e355ce04f7 preen 2025-07-06 18:01:21 -05:00
J. Nick Koston
758e5b89bb preen 2025-07-06 17:53:56 -05:00
J. Nick Koston
3ffdd1d451 preen 2025-07-06 17:42:57 -05:00
J. Nick Koston
4c1b8c8b96 preen 2025-07-06 17:33:50 -05:00
J. Nick Koston
3ca956cd6a fix merge error 2025-07-06 17:27:32 -05:00
J. Nick Koston
2e24a11a1d Merge remote-tracking branch 'upstream/dev' into heap_scheduler_stress_component 2025-07-06 17:21:36 -05:00
J. Nick Koston
10a03ad538 tidy 2025-07-06 15:37:09 -05:00
J. Nick Koston
69839ec4dc Merge branch 'filter_files' into integration 2025-07-06 14:22:31 -05:00
J. Nick Koston
28a66d4bf0 preen 2025-07-06 14:18:17 -05:00
J. Nick Koston
782d894801 preen 2025-07-06 14:18:05 -05:00
J. Nick Koston
06dd731c78 preen 2025-07-06 14:10:20 -05:00
J. Nick Koston
6af74302dc missed one 2025-07-06 14:06:03 -05:00
J. Nick Koston
03380a6ecd some tests 2025-07-06 13:51:51 -05:00
J. Nick Koston
8d8db11dd9 some tests 2025-07-06 13:51:17 -05:00
J. Nick Koston
28886a896b some tests 2025-07-06 13:48:11 -05:00
J. Nick Koston
05253991c2 fixes 2025-07-06 13:42:44 -05:00
J. Nick Koston
96f0fda477 fixes 2025-07-06 13:42:18 -05:00
J. Nick Koston
023fa4d220 fixes 2025-07-06 13:37:41 -05:00
J. Nick Koston
a1f63c0dfc fixes 2025-07-06 13:24:50 -05:00
J. Nick Koston
ef98f42e7e tweaks 2025-07-06 13:18:24 -05:00
J. Nick Koston
737e1284af tweaks 2025-07-06 13:18:10 -05:00
J. Nick Koston
8677918157 tweaks 2025-07-06 13:16:49 -05:00
J. Nick Koston
629c891dfc Filter unused files 2025-07-06 12:12:16 -05:00
J. Nick Koston
8e8ef83780 cleanup 2025-07-06 11:05:18 -05:00
J. Nick Koston
2a15f35e9d cleanup 2025-07-06 11:04:04 -05:00
J. Nick Koston
9bfa942cf2 merge 2025-07-06 10:58:15 -05:00
J. Nick Koston
b00adbddce fix race 2025-07-06 10:40:44 -05:00
J. Nick Koston
a71030c4de fix race 2025-07-06 10:40:19 -05:00
J. Nick Koston
6bb32c2e61 tweaks 2025-07-06 10:12:29 -05:00
J. Nick Koston
7bc2c685e0 tweaks 2025-07-06 10:12:14 -05:00
J. Nick Koston
9205338cc8 Merge branch 'heap_scheduler_stress_component' into integration 2025-07-06 10:08:35 -05:00
J. Nick Koston
04336f7ba3 Merge branch 'dev' into heap_scheduler_stress_component 2025-07-06 10:08:03 -05:00
J. Nick Koston
6f64312d08 remove debugging 2025-07-06 10:06:45 -05:00
J. Nick Koston
79dfb86830 remove debugging 2025-07-06 10:04:17 -05:00
J. Nick Koston
453dc29540 preen 2025-07-06 10:03:28 -05:00
J. Nick Koston
f4260d370c preen 2025-07-06 10:03:24 -05:00
J. Nick Koston
655f9489a8 preen 2025-07-06 10:02:58 -05:00
J. Nick Koston
4b3cc52afe preen 2025-07-06 10:02:47 -05:00
J. Nick Koston
fd3f15637a lint 2025-07-06 10:01:07 -05:00
J. Nick Koston
1311e1b8b0 lint 2025-07-06 10:00:55 -05:00
J. Nick Koston
64e84872da lint 2025-07-06 10:00:35 -05:00
J. Nick Koston
bc7379030e lint 2025-07-06 10:00:25 -05:00
J. Nick Koston
ecfb6dc8ed lint 2025-07-06 10:00:17 -05:00
J. Nick Koston
75d67af932 Add heap scheduler tests 2025-07-06 09:55:14 -05:00
J. Nick Koston
845dad6ee7 Merge branch 'idf_webserver_guard' into integration 2025-07-05 23:48:48 -05:00
J. Nick Koston
e2e86da64b make bot happy 2025-07-05 23:48:37 -05:00
J. Nick Koston
90ec63589f Merge branch 'core_ram' into integration 2025-07-05 23:40:51 -05:00
J. Nick Koston
ea308eaaa2 add comments to explain why its safe and the bot is wrong 2025-07-05 23:39:25 -05:00
J. Nick Koston
87f1fac2bf nolint 2025-07-05 23:28:39 -05:00
J. Nick Koston
c23651527f Merge branch 'bluetooth_proxy_guard_var' into integration 2025-07-05 23:02:44 -05:00
J. Nick Koston
2cc263a707 lint 2025-07-05 23:01:49 -05:00
J. Nick Koston
fb336718de Merge branch 'core_ram' into integration 2025-07-05 23:00:30 -05:00
J. Nick Koston
e2e35bf965 simplify 2025-07-05 22:58:27 -05:00
J. Nick Koston
bdd25c7268 Merge branch 'idf_webserver_guard' into integration 2025-07-05 22:26:54 -05:00
J. Nick Koston
82c788d6ce Eliminate web_server_idf guard variable to save 8 bytes RAM 2025-07-05 22:24:26 -05:00
J. Nick Koston
5167184cc7 merge 2025-07-05 22:18:20 -05:00
J. Nick Koston
a5d1b11204 Merge branch 'bluetooth_proxy_guard_var' into integration 2025-07-05 22:17:32 -05:00
J. Nick Koston
dc8f2fd37e Eliminate bluetooth_proxy guard variable to save 8 bytes RAM 2025-07-05 22:15:45 -05:00
J. Nick Koston
7c85886ce8 Merge branch 'api_guard_var' into integration 2025-07-05 21:59:47 -05:00
J. Nick Koston
12f172436d Eliminate API component guard variable to save 8 bytes RAM 2025-07-05 21:59:08 -05:00
J. Nick Koston
e69ac0478e Merge branch 'core_ram' into integration 2025-07-05 21:38:49 -05:00
J. Nick Koston
a45743c2b7 Reduce core RAM usage by 40 bytes with static initialization optimizations 2025-07-05 21:35:32 -05:00
J. Nick Koston
ebe1531927 Merge branch 'color_memory' into integration 2025-07-05 21:24:23 -05:00
J. Nick Koston
a88a059c6a Reduce RAM usage by optimizing Color constant storage 2025-07-05 21:21:43 -05:00
J. Nick Koston
d314cbb0d5 Merge branch 'icon_opt_pay_as_you_go' into integration 2025-07-05 17:44:05 -05:00
J. Nick Koston
4d75758eb2 tests 2025-07-05 17:39:02 -05:00
J. Nick Koston
0eecc29039 Merge branch 'icon_opt_pay_as_you_go' into integration 2025-07-05 17:38:13 -05:00
J. Nick Koston
294fb67410 Optimize entity icon memory usage with USE_ENTITY_ICON flag 2025-07-05 17:36:51 -05:00
J. Nick Koston
2f1f098b47 revert 2025-07-05 16:55:15 -05:00
J. Nick Koston
77be414261 Merge branch 'deep_sleep_loop' into integration 2025-07-05 16:12:06 -05:00
J. Nick Koston
c34fc3c4c7 simplify 2025-07-05 16:07:43 -05:00
J. Nick Koston
8aac2f525e simplify 2025-07-05 16:01:59 -05:00
J. Nick Koston
f85dcdca4e unreachable 2025-07-05 15:57:21 -05:00
J. Nick Koston
e7a1ef7aa1 Merge branch 'deep_sleep_loop' into integration 2025-07-05 15:54:13 -05:00
J. Nick Koston
7c2d2ef5a3 deep_sleep: Replace polling loop with event-driven state machine 2025-07-05 15:53:12 -05:00
J. Nick Koston
1449001747 Merge branch 'ld2450_cpu_drain_spam' into integration 2025-07-05 15:07:58 -05:00
J. Nick Koston
f245c74520 fix byte ordering 2025-07-05 15:01:02 -05:00
J. Nick Koston
da1658e4f9 Merge branch 'ld2450_cpu_drain_spam' into integration 2025-07-05 14:50:51 -05:00
J. Nick Koston
80f9352a79 Merge branch 'light_ram' into integration 2025-07-05 14:50:47 -05:00
J. Nick Koston
9ded501402 clang-tidy 2025-07-05 14:50:17 -05:00
J. Nick Koston
3d6a1811c5 comments 2025-07-05 14:28:26 -05:00
J. Nick Koston
a5ee047efb Fix LD2450 excessive CPU usage and redundant sensor updates 2025-07-05 14:25:56 -05:00
J. Nick Koston
fb0090dcdc Merge branch 'light_ram' into integration 2025-07-05 13:53:20 -05:00
J. Nick Koston
294bd4d042 tweaks 2025-07-05 13:44:42 -05:00
J. Nick Koston
e99b8d2daf tweaks 2025-07-05 13:41:09 -05:00
J. Nick Koston
6dbdeeb59b tidy 2025-07-05 13:18:45 -05:00
J. Nick Koston
82fd62e9dd comments 2025-07-05 13:00:48 -05:00
J. Nick Koston
70f935d323 fixed a few missed ones 2025-07-05 12:39:05 -05:00
J. Nick Koston
0f3e6cccd9 Reduce light component memory usage by 50+ bytes per instance 2025-07-05 12:33:54 -05:00
J. Nick Koston
6ff323c56d Merge branch 'busy_disconnect_loop' into integration 2025-07-05 11:12:06 -05:00
J. Nick Koston
096ec79ef9 Fix bluetooth proxy busy loop when disconnecting pending BLE connections 2025-07-05 11:11:36 -05:00
J. Nick Koston
bf5ba65558 Merge branch 'ble_align' into integration 2025-07-05 09:05:20 -05:00
J. Nick Koston
62088dfaed Split LockFreeQueue into base and notifying variants to reduce memory usage 2025-07-05 09:02:33 -05:00
J. Nick Koston
dfcc3206f7 Split LockFreeQueue into base and notifying variants to reduce memory usage 2025-07-05 08:59:19 -05:00
J. Nick Koston
e173b7f0c2 Split LockFreeQueue into base and notifying variants to reduce memory usage 2025-07-05 08:58:41 -05:00
J. Nick Koston
f98e28a8a2 Split LockFreeQueue into base and notifying variants to reduce memory usage 2025-07-05 08:57:04 -05:00
J. Nick Koston
f63557f2e7 notes to the future 2025-07-05 07:34:46 -05:00
J. Nick Koston
a353598961 Merge branch 'ble_batching' into integration 2025-07-05 07:24:07 -05:00
J. Nick Koston
bc33b44648 Optimize Bluetooth proxy batching and increase scan buffer capacity 2025-07-05 07:23:31 -05:00
J. Nick Koston
1579779967 Merge branch 'defer_const' into integration 2025-07-04 20:54:41 -05:00
J. Nick Koston
cc6ea4cd14 cover 2025-07-04 20:51:50 -05:00
J. Nick Koston
303a8ff87a Merge branch 'defer_const' into integration 2025-07-04 20:33:07 -05:00
J. Nick Koston
7d3a11a735 Add const char overload for Component::defer() 2025-07-04 20:30:04 -05:00
J. Nick Koston
94b6344820 Merge branch 'reduce_main_loop' into integration 2025-07-04 13:56:19 -05:00
J. Nick Koston
40307c079c Merge remote-tracking branch 'origin/reduce_main_loop' into reduce_main_loop 2025-07-04 13:54:41 -05:00
J. Nick Koston
debef6fde4 address review comments 2025-07-04 13:54:07 -05:00
J. Nick Koston
0cda83d29c Update scheduler.cpp
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-04 13:46:39 -05:00
J. Nick Koston
32729c7ca7 Merge branch 'reduce_main_loop' into integration 2025-07-04 12:59:23 -05:00
J. Nick Koston
b7fca5488a lol 2025-07-04 12:59:11 -05:00
J. Nick Koston
9c22772758 fix scope issue 2025-07-04 11:40:11 -05:00
J. Nick Koston
1e72f07fdf Merge branch 'reduce_main_loop' into integration 2025-07-04 11:29:12 -05:00
J. Nick Koston
a592e96709 preen 2025-07-04 11:29:01 -05:00
J. Nick Koston
3980339868 Merge branch 'reduce_main_loop' into integration 2025-07-04 11:26:45 -05:00
J. Nick Koston
afa66c17bd preen 2025-07-04 11:26:33 -05:00
J. Nick Koston
be2988b1d7 Merge branch 'reduce_main_loop' into integration 2025-07-04 11:24:46 -05:00
J. Nick Koston
cf647f0c36 Merge remote-tracking branch 'upstream/dev' into integration 2025-07-04 11:24:40 -05:00
J. Nick Koston
385ed4ca0c Merge remote-tracking branch 'upstream/reduce_main_loop' into reduce_main_loop 2025-07-04 11:23:44 -05:00
J. Nick Koston
9188a8e326 preen 2025-07-04 11:23:33 -05:00
J. Nick Koston
0efb6d55c8 Merge branch 'dev' into reduce_main_loop 2025-07-04 11:20:27 -05:00
J. Nick Koston
f748047b7b Merge branch 'reduce_main_loop' into integration 2025-07-04 11:18:55 -05:00
J. Nick Koston
49bc767bf4 cleanup 2025-07-04 11:13:08 -05:00
J. Nick Koston
e12cc9a9a7 cleanup 2025-07-04 11:12:54 -05:00
J. Nick Koston
8e4470cdff Merge branch 'reduce_main_loop' into integration 2025-07-04 11:02:07 -05:00
J. Nick Koston
bdb7e19fd0 guard esp8266 2025-07-04 10:59:58 -05:00
J. Nick Koston
0fc3f0e162 guard esp8266 2025-07-04 10:57:39 -05:00
J. Nick Koston
6fac66e63b Merge branch 'reduce_main_loop' into integration 2025-07-04 10:46:38 -05:00
J. Nick Koston
71e06ea1b6 cleanup 2025-07-04 10:45:47 -05:00
J. Nick Koston
3df434fd55 improve test 2025-07-04 10:41:59 -05:00
J. Nick Koston
729b2b2873 remove debug 2025-07-04 10:35:29 -05:00
J. Nick Koston
bc2adb6b5a there was no locking on host! 2025-07-04 10:25:31 -05:00
J. Nick Koston
aaff086aeb there was no locking on host! 2025-07-04 10:24:04 -05:00
J. Nick Koston
e4c0f18ee3 fixes 2025-07-04 10:17:41 -05:00
J. Nick Koston
9c09a271f2 tweaks 2025-07-04 10:14:54 -05:00
J. Nick Koston
37578f3e22 fixes 2025-07-04 10:11:19 -05:00
J. Nick Koston
4649599592 fixes 2025-07-04 10:01:00 -05:00
J. Nick Koston
71f78e3a81 fixes 2025-07-04 10:00:25 -05:00
J. Nick Koston
f7ca26eef8 stress 2025-07-04 08:59:15 -05:00
J. Nick Koston
0665fcea9e stress test 2025-07-04 08:49:35 -05:00
J. Nick Koston
cd2b50c27f stress test 2025-07-04 08:49:12 -05:00
J. Nick Koston
ca70f17b3b make test race safe 2025-07-04 08:33:34 -05:00
J. Nick Koston
a5e08aaf74 make test race safe 2025-07-04 08:33:24 -05:00
J. Nick Koston
947db4605a Merge branch 'reduce_main_loop' into integration 2025-07-04 08:16:18 -05:00
J. Nick Koston
481a00a0b5 Merge remote-tracking branch 'upstream/dev' into integration 2025-07-04 08:16:16 -05:00
J. Nick Koston
465019e510 cover 2025-07-04 08:04:16 -05:00
J. Nick Koston
a4d5f39fb6 cover 2025-07-04 07:59:12 -05:00
J. Nick Koston
5dd76966c3 cover 2025-07-04 07:55:01 -05:00
J. Nick Koston
db86f87fc3 Make defer FIFO 2025-07-04 07:42:59 -05:00
J. Nick Koston
e21334b7fa Make defer FIFO 2025-07-04 07:42:37 -05:00
J. Nick Koston
ba4c268956 Make defer FIFO 2025-07-04 07:35:24 -05:00
J. Nick Koston
068594be5e Make defer FIFO 2025-07-04 07:29:37 -05:00
J. Nick Koston
0fd45fc86e fix 2025-07-03 20:15:45 -05:00
J. Nick Koston
257fb98113 Merge branch 'api_cleanup' into integration 2025-07-03 20:05:59 -05:00
J. Nick Koston
f8922b3cca Use std::span to eliminate heap allocation for single-packet API transmissions 2025-07-03 20:01:28 -05:00
J. Nick Koston
b0b08f317b Merge remote-tracking branch 'origin/dev' into integration 2025-07-03 17:47:02 -05:00
J. Nick Koston
2c4667fb46 Merge branch 'camera-platform' into integration 2025-07-03 15:14:49 -05:00
J. Nick Koston
9eadfa21d8 Merge branch 'webserver_busy_loop_dropped_connection' into integration 2025-07-03 14:45:38 -05:00
J. Nick Koston
953fd24458 Fix web_server busy loop with ungracefully disconnected clients 2025-07-03 14:43:11 -05:00
J. Nick Koston
1be171e084 Merge remote-tracking branch 'origin/webserver_string_lifetime' into integration 2025-07-03 14:06:25 -05:00
J. Nick Koston
5c83b99e0c do not need to rename as we changed design to not need it 2025-07-03 14:06:07 -05:00
J. Nick Koston
743e611735 Merge remote-tracking branch 'origin/webserver_string_lifetime' into integration 2025-07-03 13:59:22 -05:00
J. Nick Koston
35ff850894 make sure its bug for bug compat 2025-07-03 13:56:29 -05:00
Dieter Tschanz
b666295b53 Replace Windows-style with Unix-style directory separators in test 2025-07-03 20:53:00 +02:00
J. Nick Koston
96cf8d97ab Merge remote-tracking branch 'upstream/webserver_string_lifetime' into integration 2025-07-03 13:52:04 -05:00
J. Nick Koston
3c1a781a1c cleanups 2025-07-03 13:51:01 -05:00
J. Nick Koston
00bd1b0a02 cleanups 2025-07-03 13:49:22 -05:00
J. Nick Koston
b8482da421 fix defines 2025-07-03 13:38:23 -05:00
J. Nick Koston
756ece9ff3 Merge branch 'dev' into camera-platform 2025-07-03 13:37:54 -05:00
J. Nick Koston
4bb016fec3 Merge branch 'webserver_lock_churn' into integration 2025-07-03 13:35:15 -05:00
J. Nick Koston
32f0322dec Merge branch 'webserver_string_lifetime' into integration 2025-07-03 13:35:10 -05:00
J. Nick Koston
1a1c13b722 Fix web_server URL parsing lifetime issue 2025-07-03 13:27:02 -05:00
Dieter Tschanz
139453822b Add compile-time test to verify Camera interface implementation. 2025-07-03 20:26:10 +02:00
J. Nick Koston
7a33994666 Reduce web_server loop overhead on ESP32 by avoiding unnecessary semaphore operations 2025-07-03 12:50:59 -05:00
J. Nick Koston
f381d9011b Merge remote-tracking branch 'upstream/dev' into integration 2025-07-03 12:18:06 -05:00
J. Nick Koston
96352f047d Merge branch 'device_id_state' into integration 2025-07-03 12:17:48 -05:00
J. Nick Koston
5e7a1fea8c Add device_id to entity state messages for sub-device support 2025-07-03 10:21:12 -05:00
J. Nick Koston
64eb70444d Merge branch 'dev' into camera-platform 2025-07-02 19:52:06 -05:00
J. Nick Koston
0f39b1c49a merge 2025-07-02 14:06:59 -05:00
J. Nick Koston
e2d6363c68 merge 2025-07-02 14:06:32 -05:00
J. Nick Koston
cdeef700c2 Merge branch 'password_api' into integration 2025-07-02 14:05:12 -05:00
J. Nick Koston
86fd702841 Save flash and RAM by conditionally compiling unused API password code 2025-07-02 13:56:41 -05:00
J. Nick Koston
6c62d4a923 Merge branch 'fix_bytes_encoding' into integration 2025-07-01 22:44:43 -05:00
J. Nick Koston
6e42d009fb Fix bytes field encoding in protobuf code generator 2025-07-01 20:26:34 -05:00
J. Nick Koston
7d7769ea5d Merge branch 'component_memory' into integration 2025-07-01 14:11:58 -05:00
J. Nick Koston
3908677fe2 Merge branch 'ota_base_extract' into integration 2025-07-01 14:09:51 -05:00
J. Nick Koston
9799a2b636 test 2025-07-01 13:47:59 -05:00
Dieter Tschanz
55c8129423 Correction for failed component test. 2025-07-01 20:44:11 +02:00
J. Nick Koston
099474053e cleanuip 2025-07-01 13:38:47 -05:00
J. Nick Koston
efafabed97 fix rp2040 2025-07-01 13:23:24 -05:00
Dieter Tschanz
d209739f85 Introduce base Camera class to support alternative camera implementations
This commit introduces a new 'Camera' base class positioned between the
API layer and the existing 'ESP32Camera' implementation.
- No changes to functionality in 'ESP32Camera' or
'ESP32CameraWebServer'.
- This refactoring enables future camera implementations to integrate
with the existing API.
- The goal is to keep the commit as minimal and non-breaking as
possible.

This is the first step in a series of changes aimed at modernizing and
generalizing ESPHome's camera support.
2025-07-01 19:47:50 +02:00
J. Nick Koston
d463dd0f57 Merge branch 'bk7200_tagged_pointer_fix' into integration 2025-07-01 11:57:11 -05:00
J. Nick Koston
c33c14a46f tidy 2025-07-01 11:57:02 -05:00
J. Nick Koston
2d0c109dc1 Merge remote-tracking branch 'origin/dev' into ota_base_extract 2025-07-01 11:50:49 -05:00
J. Nick Koston
825d0bed88 fix esp8266 error handling 2025-07-01 11:29:38 -05:00
J. Nick Koston
cd1390916c md5 fixes 2025-07-01 11:09:08 -05:00
J. Nick Koston
149bdaf146 fixes 2025-07-01 10:50:17 -05:00
J. Nick Koston
ad628c9cba single ota path 2025-07-01 10:36:36 -05:00
J. Nick Koston
b88f87799e single ota path 2025-07-01 10:30:52 -05:00
J. Nick Koston
1ff7cf1125 single ota path 2025-07-01 10:28:48 -05:00
J. Nick Koston
31db6e51eb single ota path 2025-07-01 10:27:46 -05:00
J. Nick Koston
681d9236f9 single ota path 2025-07-01 10:26:55 -05:00
J. Nick Koston
8aa8af735d single ota path 2025-07-01 10:25:48 -05:00
J. Nick Koston
943d0f103d single ota path 2025-07-01 10:17:28 -05:00
J. Nick Koston
8b195d7f63 use ota backend 2025-07-01 10:11:41 -05:00
J. Nick Koston
649ad47e62 web_server_ support for ota backend idf 2025-07-01 10:05:23 -05:00
J. Nick Koston
93dc5765bb Merge upstream/dev into ota_base_extract 2025-07-01 09:57:09 -05:00
J. Nick Koston
b000b1b70c Fix regression: BK7231N devices not returning entities via API 2025-07-01 09:43:50 -05:00
J. Nick Koston
8707b6e01a lint 2025-07-01 07:31:45 -05:00
J. Nick Koston
34abd67f3e Merge branch 'component_memory' into integration 2025-06-30 21:21:07 -05:00
J. Nick Koston
45f1db9233 address review comments 2025-06-30 21:20:58 -05:00
J. Nick Koston
beb4d1511a Merge branch 'component_memory' into integration 2025-06-30 21:18:06 -05:00
J. Nick Koston
adeceee71f Reduce Component memory usage by 40% (8 bytes per component) 2025-06-30 21:15:20 -05:00
J. Nick Koston
7d4b11d112 Reduce Component memory usage by 40% (8 bytes per component) 2025-06-30 21:11:49 -05:00
J. Nick Koston
6733cd4ed1 Merge branch 'entity_name_must_be_unique' into integration 2025-06-30 18:31:40 -05:00
J. Nick Koston
07f361a404 empty name uses device name, use get_base_entity_object_id 2025-06-30 18:26:09 -05:00
J. Nick Koston
ae981ea7f2 Merge branch 'entity_name_must_be_unique' into integration 2025-06-30 17:48:49 -05:00
J. Nick Koston
b7d0f5e36b Fix entity hash collisions by enforcing unique names across devices per platform 2025-06-30 17:38:04 -05:00
J. Nick Koston
3cbce4df42 Merge branch 'esp32_touch_isr' into integration 2025-06-30 14:38:54 -05:00
J. Nick Koston
7e77e40bda cleanup 2025-06-30 14:37:30 -05:00
J. Nick Koston
305805256d dry 2025-06-30 14:34:07 -05:00
J. Nick Koston
e36c669dc0 dry 2025-06-30 14:32:13 -05:00
J. Nick Koston
71aff9bc60 dry 2025-06-30 14:30:07 -05:00
J. Nick Koston
36d11c969f dry 2025-06-30 14:29:57 -05:00
J. Nick Koston
f76ce5d3bb dry 2025-06-30 14:28:31 -05:00
J. Nick Koston
0df454481e safer 2025-06-30 14:15:26 -05:00
J. Nick Koston
83c1a30cfb Merge branch 'esp32_touch_isr' into integration 2025-06-30 13:46:54 -05:00
J. Nick Koston
6cbd1479c6 loop 2025-06-30 13:46:47 -05:00
J. Nick Koston
3e6e438920 Merge remote-tracking branch 'upstream/dev' into esp32_touch_isr 2025-06-30 13:43:18 -05:00
J. Nick Koston
560886eb90 clenaup 2025-06-30 13:32:59 -05:00
J. Nick Koston
340bb5cef6 clenaup 2025-06-30 13:31:55 -05:00
J. Nick Koston
44a7c1d4a5 cleanup 2025-06-30 13:14:55 -05:00
J. Nick Koston
519c49f175 Revert "fix"
This reverts commit c96ffefa42.
2025-06-30 13:11:27 -05:00
J. Nick Koston
c96ffefa42 fix 2025-06-30 13:02:26 -05:00
J. Nick Koston
490ca8ad5a relo 2025-06-30 12:53:41 -05:00
J. Nick Koston
e385f87d6c move more 2025-06-30 12:46:47 -05:00
J. Nick Koston
58de53123a move more 2025-06-30 12:41:55 -05:00
J. Nick Koston
4f365c1716 todo 2025-06-30 12:11:37 -05:00
J. Nick Koston
981177da23 todo 2025-06-30 12:09:07 -05:00
J. Nick Koston
088bea9ccd split 2025-06-30 10:50:26 -05:00
J. Nick Koston
36350f179e split 2025-06-30 10:49:59 -05:00
J. Nick Koston
902f08c1bc Extract OTA backend functionality into separate ota_base component 2025-06-30 10:38:31 -05:00
J. Nick Koston
47ad206ccd Extract OTA backend functionality into separate ota_base component 2025-06-30 10:35:19 -05:00
J. Nick Koston
9f51546023 Extract OTA backend functionality into separate ota_base component 2025-06-30 10:33:43 -05:00
J. Nick Koston
f6d679f056 Merge branch 'idf_webserver_ota' into integration 2025-06-30 10:04:39 -05:00
J. Nick Koston
93c45e88e7 revert ota backend changes 2025-06-30 10:04:23 -05:00
J. Nick Koston
da189da9ae Merge branch 'idf_webserver_ota' into integration 2025-06-30 09:56:30 -05:00
J. Nick Koston
c40a33cb48 revert ota backend changes 2025-06-30 09:56:20 -05:00
J. Nick Koston
9846beee7d revert ota backend changes 2025-06-30 09:55:02 -05:00
J. Nick Koston
81685f9132 Merge branch 'idf_webserver_ota' into integration 2025-06-30 09:50:56 -05:00
J. Nick Koston
14123d25c2 fixes 2025-06-30 09:50:46 -05:00
J. Nick Koston
928819ffbd fixes 2025-06-30 09:49:59 -05:00
J. Nick Koston
7f2f9636f5 make sure ota still works without ota loaded 2025-06-30 09:46:33 -05:00
J. Nick Koston
b49fe146ad make sure ota still works without ota loaded 2025-06-30 09:44:20 -05:00
J. Nick Koston
98bbd4136b Merge branch 'idf_webserver_ota' into integration 2025-06-30 09:24:05 -05:00
J. Nick Koston
d8d02f71ba cleanup 2025-06-30 09:23:57 -05:00
J. Nick Koston
26980df2b9 Merge branch 'idf_webserver_ota' into integration 2025-06-30 08:25:08 -05:00
J. Nick Koston
ffe39473d0 fixes 2025-06-30 08:18:34 -05:00
J. Nick Koston
6af8d152ee fixes 2025-06-30 08:18:18 -05:00
J. Nick Koston
de846a8f7a Merge branch 'idf_webserver_ota' into integration 2025-06-30 07:54:26 -05:00
J. Nick Koston
8e31316e3d Merge remote-tracking branch 'upstream/dev' into idf_webserver_ota 2025-06-30 07:51:22 -05:00
J. Nick Koston
fb6edb3243 fixes 2025-06-30 07:51:11 -05:00
J. Nick Koston
244bd9256f tidy 2025-06-30 06:55:08 -05:00
J. Nick Koston
1f61fd383c Merge branch 'dev' into esp32_touch_isr 2025-06-30 01:06:49 -05:00
J. Nick Koston
ce294ce0c1 Merge branch 'idf_webserver_ota' into integration 2025-06-30 01:01:29 -05:00
J. Nick Koston
dcbdc0ac51 Merge branch 'dev' into esp32_touch_isr 2025-06-30 00:59:07 -05:00
J. Nick Koston
daea06586d Merge branch 'update_libsodium' into integration 2025-06-30 00:54:49 -05:00
J. Nick Koston
9c8bf2587b Merge remote-tracking branch 'origin/update_libsodium' into update_libsodium 2025-06-30 00:50:31 -05:00
J. Nick Koston
9871cb04ea 0.1.10 2025-06-30 00:50:18 -05:00
J. Nick Koston
7dc093815f reduce 2025-06-29 23:40:09 -05:00
J. Nick Koston
087697106c remove debug 2025-06-29 23:32:59 -05:00
J. Nick Koston
9beebc7bfe Merge branch 'dev' into idf_webserver_ota 2025-06-29 23:22:34 -05:00
J. Nick Koston
4a948b7aae Merge branch 'dev' into update_libsodium 2025-06-29 23:20:30 -05:00
J. Nick Koston
0d3bc21e97 Merge branch 'dev' from upstream
Resolved conflicts in:
- esphome/components/api/list_entities.h
- esphome/components/api/subscribe_state.h

Both conflicts were about NOLINT comment style - chose upstream's inline comment format.
2025-06-29 23:18:41 -05:00
J. Nick Koston
7496894ae6 0.1.9 2025-06-29 23:16:20 -05:00
J. Nick Koston
918d7217a9 fix 2025-06-29 23:15:28 -05:00
J. Nick Koston
2103d583f9 bump to 0.1.8 2025-06-29 23:12:48 -05:00
J. Nick Koston
837c446926 Merge branch 'dev' from upstream 2025-06-29 23:10:20 -05:00
J. Nick Koston
480ea54ee0 Merge branch 'dev' into update_libsodium 2025-06-29 22:49:46 -05:00
J. Nick Koston
97e7c34cb6 Merge branch 'dev' into idf_webserver_ota 2025-06-29 22:41:30 -05:00
J. Nick Koston
fe65b149f5 tweak 2025-06-29 22:34:42 -05:00
J. Nick Koston
4106b97174 tweak 2025-06-29 22:31:47 -05:00
J. Nick Koston
8648954b94 tweak 2025-06-29 22:29:40 -05:00
J. Nick Koston
9f1fae0955 tweak 2025-06-29 22:27:36 -05:00
Jesse Hills
1d631c3c6d Update platformio.ini 2025-06-30 15:26:14 +12:00
J. Nick Koston
727161f1db tweak 2025-06-29 22:24:28 -05:00
Jesse Hills
bf5f628769 Update esphome/components/api/__init__.py 2025-06-30 15:23:40 +12:00
J. Nick Koston
8563a5785f tweak 2025-06-29 22:19:29 -05:00
J. Nick Koston
4082634e6d tweak 2025-06-29 22:14:15 -05:00
J. Nick Koston
a74adb5865 tweak 2025-06-29 22:13:56 -05:00
J. Nick Koston
2e4d7301f2 tweak 2025-06-29 22:12:36 -05:00
J. Nick Koston
94845222ad tweak 2025-06-29 22:12:20 -05:00
J. Nick Koston
7f6ac2deee tweak 2025-06-29 22:10:50 -05:00
J. Nick Koston
a054aa9c52 clean 2025-06-29 21:57:50 -05:00
J. Nick Koston
22cb59b88c clean 2025-06-29 21:55:13 -05:00
J. Nick Koston
6968772a31 preen 2025-06-29 21:48:35 -05:00
J. Nick Koston
004f4b51d1 preen 2025-06-29 21:41:57 -05:00
J. Nick Koston
8c8dd7b4bc preen 2025-06-29 21:40:20 -05:00
J. Nick Koston
9778289d33 revert 2025-06-29 21:36:25 -05:00
J. Nick Koston
a43caf08a6 cleanup 2025-06-29 21:31:54 -05:00
J. Nick Koston
01e550fac9 cleanup 2025-06-29 21:13:05 -05:00
J. Nick Koston
ad4dd6a060 cleanup 2025-06-29 21:07:39 -05:00
J. Nick Koston
849d99b0dc cleanup 2025-06-29 21:06:04 -05:00
J. Nick Koston
f5df5f71a3 cleanup 2025-06-29 21:04:45 -05:00
J. Nick Koston
429be0a5ae cleanup 2025-06-29 21:03:13 -05:00
J. Nick Koston
148e4ec555 cleanup 2025-06-29 20:59:51 -05:00
J. Nick Koston
bb22f4d6a3 cleanup 2025-06-29 20:54:36 -05:00
J. Nick Koston
f94703360b cleanup 2025-06-29 20:54:13 -05:00
J. Nick Koston
f26bec1a5a preen 2025-06-29 20:18:32 -05:00
J. Nick Koston
d065f4ae62 cleanup 2025-06-29 20:15:18 -05:00
J. Nick Koston
ed2c3e626b cleanup 2025-06-29 19:53:29 -05:00
J. Nick Koston
1927f92358 cleanup 2025-06-29 19:49:01 -05:00
J. Nick Koston
939144174c cleanup 2025-06-29 19:32:43 -05:00
J. Nick Koston
59bcbe7fef proper state machine 2025-06-29 19:31:01 -05:00
J. Nick Koston
8e00fedc67 rwatchdog 2025-06-29 19:24:40 -05:00
J. Nick Koston
0ac879ae0b remove 2025-06-29 19:22:13 -05:00
J. Nick Koston
22d1a18d22 Merge remote-tracking branch 'upstream/dev' into idf_webserver_ota 2025-06-29 19:19:32 -05:00
J. Nick Koston
ca203bff9b cleanup 2025-06-29 19:18:33 -05:00
J. Nick Koston
e01d16ce82 cleanup 2025-06-29 19:07:47 -05:00
J. Nick Koston
93b6b9835c cleanup 2025-06-29 19:04:54 -05:00
J. Nick Koston
d0ac5388d9 cleanup 2025-06-29 19:03:54 -05:00
J. Nick Koston
9097d646ca cleanup 2025-06-29 19:03:48 -05:00
J. Nick Koston
596a28e1fb cleanup 2025-06-29 19:00:07 -05:00
J. Nick Koston
5205ff5c43 cleanup 2025-06-29 18:59:09 -05:00
J. Nick Koston
c420bf5f4f cleanup 2025-06-29 18:55:46 -05:00
J. Nick Koston
18844e15dc cleanup 2025-06-29 18:54:48 -05:00
J. Nick Koston
af2f5b7348 cleanup 2025-06-29 18:54:14 -05:00
J. Nick Koston
bcbf0f0e26 cleanup 2025-06-29 18:53:43 -05:00
J. Nick Koston
4d460d4bc3 cleanup 2025-06-29 18:51:35 -05:00
J. Nick Koston
92f6f3ac0d cleanup 2025-06-29 18:48:30 -05:00
J. Nick Koston
bc63d246c8 cleanup 2025-06-29 18:46:15 -05:00
J. Nick Koston
b25f272d72 lint 2025-06-29 18:44:14 -05:00
J. Nick Koston
e3a3305adb delete 2025-06-29 18:44:02 -05:00
J. Nick Koston
c655c4e106 remove cruft 2025-06-29 18:39:17 -05:00
J. Nick Koston
7fe8cdaa34 remove cruft 2025-06-29 18:37:48 -05:00
J. Nick Koston
df97985048 Merge remote-tracking branch 'origin/integration' into integration 2025-06-29 18:33:46 -05:00
J. Nick Koston
3779675816 Merge branch 'binary_sensor_gpio_polling' into integration 2025-06-29 18:33:34 -05:00
J. Nick Koston
0005aad5b5 cleanup 2025-06-29 18:30:00 -05:00
J. Nick Koston
98c18517e2 Merge branch 'another_webserver_crash' into idf_webserver_ota 2025-06-29 18:23:25 -05:00
J. Nick Koston
e4dee935ce Fix thread-safe cleanup of event source connections in ESP-IDF web server 2025-06-29 18:21:24 -05:00
J. Nick Koston
f8cb44fb3c fixes 2025-06-29 17:54:11 -05:00
J. Nick Koston
101901fdb8 Merge remote-tracking branch 'upstream/loop_enable_disable_log_spam' into idf_webserver_ota 2025-06-29 17:41:04 -05:00
J. Nick Koston
b8579d2040 Reduce loop enable/disable log spam by using very verbose level 2025-06-29 17:39:48 -05:00
J. Nick Koston
3fca3df756 working 2025-06-29 17:22:33 -05:00
J. Nick Koston
2f5db85997 Merge remote-tracking branch 'upstream/last_Event_fix' into idf_webserver_ota 2025-06-29 16:54:00 -05:00
Jesse Hills
e0d4361875 Update esphome/components/gpio/binary_sensor/__init__.py 2025-06-30 09:53:54 +12:00
J. Nick Koston
30bafc43bd make bot happy 2025-06-29 16:52:55 -05:00
J. Nick Koston
3530437b48 Merge branch 'last_Event_fix' into idf_webserver_ota 2025-06-29 16:19:20 -05:00
J. Nick Koston
81db42942c Fix crash when event last_event_type is null in web_server 2025-06-29 16:16:53 -05:00
J. Nick Koston
6cb0d9e0b5 fixes 2025-06-29 16:11:33 -05:00
J. Nick Koston
19f7e36753 fixes 2025-06-29 16:10:58 -05:00
J. Nick Koston
a963f97520 fixes 2025-06-29 16:07:26 -05:00
J. Nick Koston
ad2d48e9b7 fixes 2025-06-29 16:03:05 -05:00
J. Nick Koston
5c0d67ca14 fixes 2025-06-29 15:50:12 -05:00
J. Nick Koston
3467329a7c cleanup 2025-06-29 15:43:41 -05:00
J. Nick Koston
d73fa370f3 cleanup 2025-06-29 15:35:59 -05:00
J. Nick Koston
78fd0a4870 cleanup 2025-06-29 15:23:32 -05:00
J. Nick Koston
3162bb475d cleanup 2025-06-29 15:08:27 -05:00
J. Nick Koston
c17503abd5 cleanup 2025-06-29 15:03:48 -05:00
J. Nick Koston
3433ee8171 cleanup 2025-06-29 14:59:41 -05:00
J. Nick Koston
344297b0a7 cleanup 2025-06-29 14:51:24 -05:00
J. Nick Koston
947456628e cleanup 2025-06-29 14:51:01 -05:00
J. Nick Koston
80dd6c111d cleanup 2025-06-29 14:44:47 -05:00
J. Nick Koston
b70188ba4b cleanup 2025-06-29 14:40:13 -05:00
J. Nick Koston
c6064aa2b4 Merge remote-tracking branch 'upstream/dev' into idf_webserver_ota 2025-06-29 14:35:44 -05:00
J. Nick Koston
6596f864be merg3 2025-06-29 14:35:38 -05:00
J. Nick Koston
f61a40efb8 fixes 2025-06-29 11:16:00 -05:00
J. Nick Koston
b049f0b480 fixes 2025-06-29 11:13:25 -05:00
J. Nick Koston
b2641d29c1 fixes 2025-06-29 11:12:40 -05:00
J. Nick Koston
7b8cfc768d fixes 2025-06-29 11:11:47 -05:00
J. Nick Koston
04860567f7 fixes 2025-06-29 11:10:29 -05:00
J. Nick Koston
b16edb5a99 fixes 2025-06-29 11:09:05 -05:00
J. Nick Koston
15a995b2e7 fixes 2025-06-29 11:07:48 -05:00
J. Nick Koston
f57e26c54e fixes 2025-06-29 11:07:25 -05:00
J. Nick Koston
2b7bc1cd9f fixes 2025-06-29 11:03:37 -05:00
J. Nick Koston
614a2f66a3 fixes 2025-06-29 10:57:00 -05:00
J. Nick Koston
9047b02c92 fixes 2025-06-29 10:53:29 -05:00
J. Nick Koston
e73d0477bb Merge branch 'api_conditional_memory' into integration 2025-06-29 10:42:24 -05:00
J. Nick Koston
2b1e623eb4 defines 2025-06-29 10:42:11 -05:00
J. Nick Koston
c366d555e9 Add OTA support to ESP-IDF webserver 2025-06-29 10:38:53 -05:00
J. Nick Koston
7efbd62730 Add OTA support to ESP-IDF webserver 2025-06-29 10:34:49 -05:00
J. Nick Koston
b77c1d0af8 Add OTA support to ESP-IDF webserver 2025-06-29 10:33:49 -05:00
J. Nick Koston
f8810ea6a8 Merge branch 'webserver_eliminate_heap_url_match' into integration 2025-06-29 10:26:55 -05:00
J. Nick Koston
40dd667211 fixes 2025-06-29 10:26:39 -05:00
J. Nick Koston
848b572864 Merge branch 'webserver_eliminate_heap_url_match' into integration 2025-06-29 10:17:49 -05:00
J. Nick Koston
7c858fbccd Optimize web_server UrlMatch to avoid heap allocations 2025-06-29 10:15:06 -05:00
J. Nick Koston
a1814ea37d Merge branch 'useless_default_data' into integration 2025-06-29 09:22:57 -05:00
J. Nick Koston
5892a1dbe2 tests 2025-06-29 08:40:26 -05:00
J. Nick Koston
29f524f432 tests 2025-06-29 08:37:53 -05:00
J. Nick Koston
4ec588ebd7 Merge remote-tracking branch 'origin/integration' into integration 2025-06-29 08:20:36 -05:00
J. Nick Koston
efdef61477 Merge branch 'api_reduce' into integration 2025-06-29 08:20:22 -05:00
J. Nick Koston
fe2b9f8c12 correct fix 2025-06-29 08:20:12 -05:00
J. Nick Koston
c6be55eb55 Merge branch 'api_conditional_memory' into integration 2025-06-29 08:14:25 -05:00
J. Nick Koston
4c69925b84 lint 2025-06-29 08:13:28 -05:00
J. Nick Koston
bc6407df0a Merge branch 'bitpack_api' into integration 2025-06-29 08:09:52 -05:00
J. Nick Koston
01982a8d0a reduce upper bound of batch delay as it did not make sense 2025-06-29 07:59:59 -05:00
J. Nick Koston
b995cd6257 Merge remote-tracking branch 'origin/integration' into integration 2025-06-29 07:47:28 -05:00
J. Nick Koston
b16d7b7a95 Merge branch 'api_conditional_memory' into integration 2025-06-29 07:47:17 -05:00
J. Nick Koston
42aea701d3 Reduce API component memory usage with conditional compilation 2025-06-29 07:45:48 -05:00
J. Nick Koston
5f56c85182 Merge remote-tracking branch 'origin/integration' into integration 2025-06-29 07:42:05 -05:00
J. Nick Koston
52b4eb8950 Merge branch 'api_reduce' into integration 2025-06-29 07:41:00 -05:00
J. Nick Koston
eeb2b42a0f fixes 2025-06-29 07:39:07 -05:00
J. Nick Koston
90772033d1 revert bad feedback 2025-06-29 06:10:55 -05:00
J. Nick Koston
dadeb4d2a9 Merge branch 'api_reduce' into integration 2025-06-28 23:24:40 -05:00
J. Nick Koston
60a5029c88 lint 2025-06-28 23:24:30 -05:00
J. Nick Koston
d7ba16b48b Merge branch 'api_reduce' into integration 2025-06-28 23:06:51 -05:00
J. Nick Koston
fca9befa63 cleanup 2025-06-28 23:06:40 -05:00
J. Nick Koston
187cbde0db cleanup 2025-06-28 23:06:34 -05:00
J. Nick Koston
f5ae5cade8 cleanup 2025-06-28 23:06:09 -05:00
J. Nick Koston
3e66c28aff Merge branch 'api_reduce' into integration 2025-06-28 23:05:15 -05:00
J. Nick Koston
89703a1aef cleanup 2025-06-28 23:05:02 -05:00
J. Nick Koston
cba31617e9 Merge branch 'api_reduce' into integration 2025-06-28 23:02:05 -05:00
J. Nick Koston
a3eeb46961 reduce 2025-06-28 23:01:48 -05:00
J. Nick Koston
128bd76f20 reduce 2025-06-28 22:45:00 -05:00
J. Nick Koston
c0355fd2c6 Merge branch 'webserver_helper_reduce_flash' into integration 2025-06-28 22:09:03 -05:00
J. Nick Koston
a5fd440e25 cleanup 2025-06-28 22:08:47 -05:00
J. Nick Koston
592ef8be2a Merge remote-tracking branch 'origin/integration' into integration 2025-06-28 22:04:41 -05:00
J. Nick Koston
3bcc1c7297 Merge branch 'add_message_object' into integration 2025-06-28 22:04:27 -05:00
J. Nick Koston
3b44c3acd1 Reduce flash usage by making add_message_object non-template 2025-06-28 22:03:04 -05:00
J. Nick Koston
ec4911643a Merge remote-tracking branch 'upstream/integration' into integration 2025-06-28 21:40:44 -05:00
J. Nick Koston
f4fedbab44 Merge branch 'webserver_helper_reduce_flash' into integration 2025-06-28 21:40:26 -05:00
J. Nick Koston
553d441ecc Reduce web_server code duplication by extracting detail parameter parsing 2025-06-28 21:38:40 -05:00
J. Nick Koston
1946116438 Merge branch 'api_read_message' into integration 2025-06-28 21:22:25 -05:00
J. Nick Koston
ab28515fba fix 2025-06-28 21:17:59 -05:00
J. Nick Koston
4dc11fb95e Merge branch 'api_read_message' into integration 2025-06-28 21:11:16 -05:00
J. Nick Koston
e27094e0f3 Remove unused return value from read_message and fix ifdef placement in generated API code 2025-06-28 21:09:33 -05:00
J. Nick Koston
88302201eb Merge remote-tracking branch 'origin/integration' into integration 2025-06-28 20:56:24 -05:00
J. Nick Koston
8afb172e83 Merge branch 'api_reduce' into integration 2025-06-28 20:56:00 -05:00
J. Nick Koston
562d024623 Remove single-use send_*_info wrappers in API connection 2025-06-28 20:49:09 -05:00
J. Nick Koston
50b094547c Remove single-use send_*_info wrappers in API connection 2025-06-28 20:47:57 -05:00
J. Nick Koston
a6c1e50985 Remove single-use send_*_info wrappers in API connection 2025-06-28 20:46:17 -05:00
J. Nick Koston
96772bdfc6 Merge remote-tracking branch 'origin/integration' into integration 2025-06-28 20:13:13 -05:00
J. Nick Koston
ed154d373c Merge remote-tracking branch 'origin/dev' into integration 2025-06-28 20:12:59 -05:00
J. Nick Koston
a5e862ce36 Remove redundant get_setup_priority() overrides returning default value 2025-06-28 17:21:20 -05:00
J. Nick Koston
ae55964bd9 Merge remote-tracking branch 'origin/bitpack_api' into integration 2025-06-28 16:47:43 -05:00
J. Nick Koston
c162309f41 Pack APIConnection members to reduce memory footprint 2025-06-28 16:46:17 -05:00
J. Nick Koston
62c667f1a0 Merge remote-tracking branch 'origin/dev' into integration 2025-06-28 16:11:15 -05:00
J. Nick Koston
3d08eae8e4 Merge branch 'scheduler_copy' into integration 2025-06-28 15:52:09 -05:00
J. Nick Koston
2af5a0a6dd Merge remote-tracking branch 'origin/scheduler_copy' into scheduler_copy 2025-06-28 15:52:03 -05:00
J. Nick Koston
6d24b04235 cover 2025-06-28 15:51:50 -05:00
J. Nick Koston
3ee8103353 Merge branch 'scheduler_copy' into integration 2025-06-28 15:49:33 -05:00
J. Nick Koston
1296165fce Merge branch 'dev' into scheduler_copy 2025-06-28 15:48:11 -05:00
J. Nick Koston
7100c22dc4 address copilot comments 2025-06-28 15:47:24 -05:00
J. Nick Koston
5718c0f5b8 Update test_scheduler_string_test.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-28 11:25:42 -05:00
J. Nick Koston
25ebddfa1c Update test_scheduler_string_test.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-28 11:25:36 -05:00
J. Nick Koston
2c0558fe23 Update test_scheduler_string_test.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-28 11:25:30 -05:00
J. Nick Koston
7192108fc1 Merge branch 'scheduler_copy' into integration 2025-06-28 10:32:21 -05:00
J. Nick Koston
847696c342 safer 2025-06-28 10:32:10 -05:00
J. Nick Koston
912ae1fc87 Merge remote-tracking branch 'origin/integration' into integration 2025-06-28 10:28:35 -05:00
J. Nick Koston
a86f75d31d Merge branch 'scheduler_copy' into integration 2025-06-28 10:28:22 -05:00
J. Nick Koston
fe1e25b5c7 Merge remote-tracking branch 'upstream/integration' into integration 2025-06-28 10:22:18 -05:00
J. Nick Koston
9b241b596a Merge branch 'scheduler_copy' into integration 2025-06-28 10:22:07 -05:00
J. Nick Koston
53b9c8d5bb cleanup 2025-06-28 10:15:05 -05:00
J. Nick Koston
2946bc9d72 cover 2025-06-28 10:10:43 -05:00
J. Nick Koston
67a20e212d safe 2025-06-28 09:59:50 -05:00
J. Nick Koston
a9ace366eb dry 2025-06-28 09:50:27 -05:00
J. Nick Koston
df3469efba dry 2025-06-28 09:48:58 -05:00
J. Nick Koston
0a3bbb8554 dry 2025-06-28 09:48:26 -05:00
J. Nick Koston
a15b9f5d3b dry 2025-06-28 09:45:59 -05:00
J. Nick Koston
e6334b0716 dry 2025-06-28 09:41:12 -05:00
J. Nick Koston
7a835baa5a Merge remote-tracking branch 'upstream/dev' into scheduler_copy 2025-06-28 09:36:32 -05:00
J. Nick Koston
c9c21a5728 Merge remote-tracking branch 'upstream/dev' into integration 2025-06-28 09:36:07 -05:00
J. Nick Koston
956959fc32 safety 2025-06-28 09:23:16 -05:00
J. Nick Koston
6f67f74638 Merge remote-tracking branch 'upstream/dev' into integration 2025-06-28 08:43:19 -05:00
J. Nick Koston
b3dd4543b7 Merge branch 'update_libsodium' into integration 2025-06-27 17:10:52 -05:00
J. Nick Koston
4f17a28ac5 Merge branch 'extract_helpers' into integration 2025-06-27 17:10:17 -05:00
J. Nick Koston
90736f367a release 2025-06-27 16:36:32 -05:00
J. Nick Koston
9af88bd482 DNM: Update libsodium
needs https://github.com/esphome/noise-c/pull/4
2025-06-27 14:07:27 -05:00
J. Nick Koston
13b89f4934 Merge branch 'libretiny_logconfig' into integration 2025-06-27 13:59:01 -05:00
J. Nick Koston
d00a00d142 Reduce libretiny logconfig messages
align with https://developers.esphome.io/architecture/logging
2025-06-27 13:58:33 -05:00
J. Nick Koston
e662c39e16 Merge branch 'extract_helpers' into integration 2025-06-27 13:18:50 -05:00
J. Nick Koston
95ef131285 address bot comments 2025-06-27 13:18:39 -05:00
J. Nick Koston
7476f170f6 Merge remote-tracking branch 'upstream/extract_helpers' into extract_helpers 2025-06-27 13:16:17 -05:00
J. Nick Koston
3b6bd55d1e address bot comments 2025-06-27 13:16:06 -05:00
J. Nick Koston
10dbc9e884 Merge remote-tracking branch 'origin/extract_helpers' into integration 2025-06-27 13:03:20 -05:00
J. Nick Koston
860f619dfe Merge branch 'dev' into extract_helpers 2025-06-27 20:02:57 +02:00
J. Nick Koston
17ddc9ee0c Merge branch 'extract_helpers' into integration 2025-06-27 12:56:28 -05:00
J. Nick Koston
949689c318 address bot review 2025-06-27 12:55:58 -05:00
J. Nick Koston
86a2aac011 Merge branch 'extract_helpers' into integration 2025-06-27 12:50:37 -05:00
J. Nick Koston
d0a402f201 Extract lock-free queue and event pool to core helpers 2025-06-27 12:49:44 -05:00
J. Nick Koston
05772d5365 Merge remote-tracking branch 'origin/integration' into integration 2025-06-27 10:19:35 -05:00
J. Nick Koston
c2a68f5147 Merge branch 'duplicate_webserver_code' into integration 2025-06-27 10:19:21 -05:00
J. Nick Koston
697ca1c7be simplify 2025-06-27 10:17:33 -05:00
J. Nick Koston
409346952f clang-format 2025-06-27 10:15:04 -05:00
J. Nick Koston
f4b3539d77 clang-format 2025-06-27 10:05:30 -05:00
J. Nick Koston
c12166c1a1 missed one 2025-06-27 10:04:29 -05:00
J. Nick Koston
8d20f003cb Merge branch 'duplicate_webserver_code' into integration 2025-06-27 09:45:00 -05:00
J. Nick Koston
88f857a2f0 defines 2025-06-27 09:44:50 -05:00
J. Nick Koston
fb7faadd99 reduce memory 2025-06-27 09:41:20 -05:00
J. Nick Koston
5c8d6752fb Merge branch 'dev' into duplicate_webserver_code 2025-06-27 16:01:32 +02:00
J. Nick Koston
dda81fbc2c Merge branch 'dev' into binary_sensor_gpio_polling 2025-06-27 15:58:14 +02:00
J. Nick Koston
c40dff5d63 cleanup 2025-06-27 06:30:51 -05:00
J. Nick Koston
6f07b54772 cleanup 2025-06-27 06:30:42 -05:00
J. Nick Koston
ce0f1dfcb6 Merge remote-tracking branch 'upstream/dev' into integration 2025-06-27 06:30:15 -05:00
J. Nick Koston
9a3a5d48eb Merge branch 'dynamic_logging' into integration 2025-06-26 20:47:40 -05:00
J. Nick Koston
4a759eda02 Disable dynamic log level control for ESP32 ESP-IDF builds 2025-06-26 20:47:02 -05:00
J. Nick Koston
26badf201d fixes 2025-06-27 01:17:26 +02:00
J. Nick Koston
384f27cd6d Merge branch 'wifi_memory' into integration 2025-06-27 01:13:11 +02:00
J. Nick Koston
ac1c5f9f58 Reduce WiFi component memory usage 2025-06-27 01:12:19 +02:00
J. Nick Koston
8ad058fdf4 Merge branch 'ethernet_padding' into integration 2025-06-27 01:00:27 +02:00
J. Nick Koston
9024c3c67a Reduce ethernet component memory usage by 8 bytes through struct optimization 2025-06-27 00:59:50 +02:00
J. Nick Koston
fc81a47499 Merge branch 'esp32_gpio_padding_waste' into integration 2025-06-27 00:43:41 +02:00
J. Nick Koston
a331452076 Reduce ESP32 GPIO memory usage by optimizing struct padding 2025-06-27 00:42:30 +02:00
J. Nick Koston
b1c6e8168e Merge remote-tracking branch 'origin/ota_memory_str' into integration 2025-06-27 00:34:36 +02:00
J. Nick Koston
b41cc0226e Optimize OTA password storage from std::string to const char 2025-06-27 00:24:45 +02:00
J. Nick Koston
450429ddd5 Merge branch 'safe_mode_padding' into integration 2025-06-27 00:22:40 +02:00
J. Nick Koston
f7b24f4b4b Optimize SafeModeComponent memory layout to reduce padding 2025-06-27 00:20:44 +02:00
J. Nick Koston
294c985380 Merge branch 'duplicate_webserver_code' into integration 2025-06-27 00:09:07 +02:00
J. Nick Koston
720964b901 Refactor web_server to extract duplicate sorting info code into helper method 2025-06-27 00:05:56 +02:00
J. Nick Koston
8895c8a987 bitpack api flags 2025-06-26 12:46:57 +02:00
J. Nick Koston
740dcd72a2 Merge branch 'duplicate_client_peername' into integration 2025-06-26 12:00:03 +02:00
J. Nick Koston
ffd442624f Optimize API connection memory usage by removing client_peername_ 2025-06-26 11:59:03 +02:00
J. Nick Koston
088fd85694 Merge branch 'batch_ping_fallback' into integration 2025-06-26 10:14:21 +02:00
J. Nick Koston
d5b68d69d3 tweak 2025-06-26 10:14:05 +02:00
J. Nick Koston
bb0f7bb393 Merge branch 'batch_ping_fallback' into integration 2025-06-26 10:10:12 +02:00
J. Nick Koston
d86a108f18 Merge remote-tracking branch 'upstream/dev' into batch_ping_fallback 2025-06-26 10:09:24 +02:00
J. Nick Koston
7828ed2d9e Merge branch 'batch_ping_fallback' into integration 2025-06-26 10:05:06 +02:00
J. Nick Koston
ebf14f50fb Merge branch 'dev' of https://github.com/esphome/esphome into batch_ping_fallback 2025-06-26 10:02:32 +02:00
J. Nick Koston
1546ff615b Merge branch 'app_padding' into integration 2025-06-26 02:47:46 +02:00
J. Nick Koston
46cf1fb597 comment 2025-06-26 02:47:33 +02:00
J. Nick Koston
8bf8655054 Merge branch 'app_padding' into integration 2025-06-26 02:45:13 +02:00
J. Nick Koston
a6d84948e2 Optimize Application class memory layout and reduce loop_interval size 2025-06-26 02:44:44 +02:00
J. Nick Koston
fac20a1f97 Merge branch 'batch_ping_fallback' into integration 2025-06-26 02:15:41 +02:00
J. Nick Koston
c65586b5e1 cleanup 2025-06-26 02:15:32 +02:00
J. Nick Koston
b27b018b06 Merge remote-tracking branch 'origin/integration' into integration 2025-06-26 02:13:42 +02:00
J. Nick Koston
403da1e632 Merge branch 'batch_ping_fallback' into integration 2025-06-26 02:12:53 +02:00
J. Nick Koston
2371ec1f9e Replace ping retry timer with batch queue fallback 2025-06-26 02:11:17 +02:00
J. Nick Koston
5e3ec2d34b lint 2025-06-26 00:24:53 +02:00
J. Nick Koston
78d84644c9 lint 2025-06-26 00:24:12 +02:00
J. Nick Koston
0cd0f8015a Merge branch 'message_creator_ram' into integration 2025-06-26 00:09:31 +02:00
J. Nick Koston
4b5424f695 nolint 2025-06-26 00:08:15 +02:00
J. Nick Koston
a1d59040f7 Merge remote-tracking branch 'origin/message_creator_ram' into integration 2025-06-25 23:54:37 +02:00
J. Nick Koston
0306398072 Merge remote-tracking branch 'origin/component_iterator' into integration 2025-06-25 23:54:33 +02:00
J. Nick Koston
a7e0bf9013 tweak 2025-06-25 23:53:22 +02:00
J. Nick Koston
ddb988cd83 Merge remote-tracking branch 'upstream/dev' into component_iterator 2025-06-25 23:39:45 +02:00
J. Nick Koston
04b54353f1 Merge remote-tracking branch 'upstream/dev' into scheduler_copy 2025-06-25 23:36:41 +02:00
J. Nick Koston
f058107c05 tweak 2025-06-25 23:33:54 +02:00
J. Nick Koston
6b5b0815d7 tidy issues 2025-06-25 23:26:57 +02:00
J. Nick Koston
8388497038 tidy issues 2025-06-25 23:18:50 +02:00
J. Nick Koston
825b1113b6 tweak 2025-06-25 23:17:41 +02:00
J. Nick Koston
9074ef792f Reduce component_iterator memory usage 2025-06-25 19:35:40 +02:00
J. Nick Koston
0946f28511 avoid string copy in scheduler for const strings 2025-06-25 19:08:18 +02:00
J. Nick Koston
23765cd4f5 Merge branch 'message_creator_ram' into integration 2025-06-25 18:28:56 +02:00
J. Nick Koston
e20c6468d0 fix missed one 2025-06-25 18:27:43 +02:00
J. Nick Koston
b90516de1d Merge branch 'template_value' into integration 2025-06-25 17:30:36 +02:00
J. Nick Koston
ec5cc0f00f Merge branch 'integration' of https://github.com/esphome/esphome into integration 2025-06-25 17:30:27 +02:00
J. Nick Koston
5dda5a976e Merge branch 'message_creator_ram' into integration 2025-06-25 17:22:41 +02:00
J. Nick Koston
915da9ae13 make the bot happy 2025-06-25 17:22:23 +02:00
J. Nick Koston
8652464f4e Merge branch 'dev' into message_creator_ram 2025-06-25 17:16:31 +02:00
J. Nick Koston
ce6ce1c1f8 Merge branch 'message_creator_ram' into integration 2025-06-25 17:10:41 +02:00
J. Nick Koston
39efe67e55 Optimize API connection memory with tagged pointers 2025-06-25 17:08:57 +02:00
J. Nick Koston
748ffa00f3 Optimize TemplatableValue memory 2025-06-25 14:49:01 +02:00
J. Nick Koston
e8d9df2b0e Merge branch 'sensor_memory' into integration 2025-06-25 14:32:47 +02:00
J. Nick Koston
17396d67de revert 2025-06-25 14:32:38 +02:00
J. Nick Koston
edd6a86714 Merge branch 'sensor_memory' into integration 2025-06-25 14:26:03 +02:00
J. Nick Koston
85b4012c56 Merge branch 'dev' into sensor_memory 2025-06-25 14:24:09 +02:00
J. Nick Koston
7d98433502 Update tests/integration/test_host_mode_sensor.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-25 14:23:59 +02:00
J. Nick Koston
23774ae03b Reduce memory required for sensor entities 2025-06-25 14:17:05 +02:00
J. Nick Koston
0dedbcdd71 Merge branch 'multi_device' into integration 2025-06-25 13:44:20 +02:00
J. Nick Koston
4bdd08887e use a common that does not have dupes on dev 2025-06-25 00:50:18 +02:00
J. Nick Koston
1fd8ebf386 update tests now that duplicate names are validated 2025-06-25 00:35:38 +02:00
J. Nick Koston
d2fc3e749c update tests now that duplicate names are validated 2025-06-25 00:34:50 +02:00
J. Nick Koston
71fbcbceaf update tests now that duplicate names are validated 2025-06-25 00:34:27 +02:00
J. Nick Koston
27347b2088 update tests now that duplicate names are validated 2025-06-25 00:34:04 +02:00
J. Nick Koston
599993d1a5 update tests now that duplicate names are validated 2025-06-25 00:22:51 +02:00
J. Nick Koston
bf359cb8e3 update tests now that duplicate names are validated 2025-06-25 00:20:51 +02:00
J. Nick Koston
509a704410 update tests now that duplicate names are validated 2025-06-25 00:19:32 +02:00
J. Nick Koston
1f48e2b01f update tests now that duplicate names are validated 2025-06-25 00:18:40 +02:00
J. Nick Koston
8b25b1eee6 update tests now that duplicate names are validated 2025-06-25 00:18:28 +02:00
J. Nick Koston
3bbf30ff5f Merge branch 'multi_device' into integration 2025-06-25 00:04:33 +02:00
J. Nick Koston
83613726d1 fix 2025-06-25 00:04:07 +02:00
J. Nick Koston
254b6a17f3 Merge remote-tracking branch 'dala318/multi_device' into integration 2025-06-24 23:54:40 +02:00
J. Nick Koston
796e12bd70 Merge branch 'dev' into multi_device 2025-06-24 23:54:24 +02:00
J. Nick Koston
ddbe17d3f6 fixes 2025-06-24 23:40:16 +02:00
J. Nick Koston
591ec36f4a fixes 2025-06-24 23:37:58 +02:00
J. Nick Koston
41eceb72ef preen 2025-06-24 23:28:06 +02:00
J. Nick Koston
0a5f094025 cleanup 2025-06-24 23:25:46 +02:00
J. Nick Koston
ca0f3ba262 cleanup 2025-06-24 23:23:59 +02:00
J. Nick Koston
30f4e782db cleanup 2025-06-24 23:23:35 +02:00
J. Nick Koston
192158ef1a cleanup 2025-06-24 23:22:18 +02:00
J. Nick Koston
602456db40 cleanup 2025-06-24 23:13:45 +02:00
J. Nick Koston
536e45668f migrate 2025-06-24 23:09:08 +02:00
J. Nick Koston
10bf05ab0d migrate 2025-06-24 22:59:46 +02:00
J. Nick Koston
5ad1af69e4 migrate 2025-06-24 22:57:10 +02:00
J. Nick Koston
48f2911434 raise 2025-06-24 22:18:29 +02:00
J. Nick Koston
dbb0d6349a Merge branch 'multi_device' into integration 2025-06-24 18:08:14 +02:00
J. Nick Koston
ac3598f12a cleanup 2025-06-24 18:07:58 +02:00
J. Nick Koston
66201be5ca preen 2025-06-24 18:00:10 +02:00
J. Nick Koston
ac0b0b652e cleanup 2025-06-24 17:55:58 +02:00
J. Nick Koston
d89ee2df42 Update esphome/core/application.h 2025-06-24 17:52:13 +02:00
J. Nick Koston
418e248e5e cleanup 2025-06-24 17:51:05 +02:00
J. Nick Koston
8c2b141049 cleanup 2025-06-24 17:41:40 +02:00
J. Nick Koston
2f8e07302b Update esphome/core/entity_base.cpp 2025-06-24 17:10:06 +02:00
J. Nick Koston
c3776240b6 fixes 2025-06-24 17:03:23 +02:00
J. Nick Koston
e370872ec1 fix conflicts 2025-06-24 16:13:34 +02:00
Jesse Hills
d4e978369a Store reference to device on EntityBase
This is so we can get the name of the device to use as part of the object id and to internally set the name for logging.
2025-06-24 19:56:30 +12:00
Jesse Hills
8d5d7f5237 Merge branch 'dev' into multi_device 2025-06-24 16:02:03 +12:00
J. Nick Koston
5cd498fbe9 Merge branch 'multi_device' into integration 2025-06-23 22:56:28 +02:00
J. Nick Koston
250f515f08 Merge branch 'api_opt' into integration 2025-06-23 12:20:40 +02:00
J. Nick Koston
0ec0a9e313 missing ifdef 2025-06-23 12:19:21 +02:00
J. Nick Koston
184f42ef03 Merge branch 'api_opt' into integration 2025-06-23 12:10:26 +02:00
J. Nick Koston
499517418d clang-tidy 2025-06-23 12:10:15 +02:00
J. Nick Koston
606b9c1a6d Merge branch 'api_opt' into integration 2025-06-23 12:00:34 +02:00
J. Nick Koston
971e954a54 follow logging guidelines 2025-06-23 11:59:07 +02:00
J. Nick Koston
e3aaf3219d speed up test 2025-06-23 11:58:16 +02:00
J. Nick Koston
0eea1c0e40 preen 2025-06-23 11:56:09 +02:00
J. Nick Koston
0773819778 cleanup 2025-06-23 11:45:58 +02:00
J. Nick Koston
170869b7db preen 2025-06-23 11:39:25 +02:00
J. Nick Koston
5dc54782e5 preen 2025-06-23 11:38:30 +02:00
J. Nick Koston
97b26fbefe preen 2025-06-23 11:38:10 +02:00
J. Nick Koston
686cc58d6c preen 2025-06-23 11:37:59 +02:00
J. Nick Koston
76a59759b2 preen 2025-06-23 11:37:27 +02:00
J. Nick Koston
93245a24b5 preen 2025-06-23 11:36:54 +02:00
J. Nick Koston
6a22ea1c7d preen 2025-06-23 11:35:41 +02:00
J. Nick Koston
56a02409c8 preen 2025-06-23 11:34:11 +02:00
J. Nick Koston
edeafd5a53 preen 2025-06-23 11:31:38 +02:00
J. Nick Koston
f67490b69b preen 2025-06-23 11:29:04 +02:00
J. Nick Koston
b76e34fb7b preen 2025-06-23 11:25:52 +02:00
J. Nick Koston
ddbda5032b preen 2025-06-23 11:25:24 +02:00
J. Nick Koston
5898d34b0a preen 2025-06-23 11:22:45 +02:00
J. Nick Koston
b0c02341ff preen 2025-06-23 11:22:08 +02:00
J. Nick Koston
19cbc8c33b preen 2025-06-23 11:21:37 +02:00
J. Nick Koston
02e61ef5d3 preen 2025-06-23 11:20:06 +02:00
J. Nick Koston
8d5d18064d preen 2025-06-23 11:19:56 +02:00
J. Nick Koston
c5ef7ebd27 preen 2025-06-23 11:19:07 +02:00
J. Nick Koston
047a3e0e8c preen 2025-06-23 11:18:47 +02:00
J. Nick Koston
13b23f840b preen 2025-06-23 11:17:17 +02:00
J. Nick Koston
147f6012b2 preen 2025-06-23 11:16:34 +02:00
J. Nick Koston
2c315595f0 preen 2025-06-23 11:12:04 +02:00
J. Nick Koston
20405c84ac preen 2025-06-23 11:10:07 +02:00
J. Nick Koston
0bc59b97de more api loop reductions 2025-06-23 11:06:51 +02:00
J. Nick Koston
a3a3bdc7eb more api loop reductions 2025-06-23 11:02:27 +02:00
J. Nick Koston
e767f30886 more api loop reductions 2025-06-23 10:59:49 +02:00
J. Nick Koston
e8c250a03c more api loop reductions 2025-06-23 10:59:00 +02:00
J. Nick Koston
d6725fc1ca more api loop reductions 2025-06-23 10:54:50 +02:00
J. Nick Koston
8ec998ff30 more api loop reductions 2025-06-23 10:52:34 +02:00
J. Nick Koston
23cc0c7f39 Merge remote-tracking branch 'upstream/dev' into api_reboot 2025-06-23 10:48:26 +02:00
J. Nick Koston
19b8bd6aa8 Merge remote-tracking branch 'upstream/logger_disable_loop' into integration 2025-06-23 09:03:16 +02:00
J. Nick Koston
ed57e7c6b0 Update esphome/components/logger/logger.cpp 2025-06-23 09:02:22 +02:00
J. Nick Koston
9f489c9f27 Update esphome/components/logger/logger.h 2025-06-23 09:01:21 +02:00
J. Nick Koston
f036989361 Update esphome/components/logger/logger.h 2025-06-23 09:01:01 +02:00
J. Nick Koston
6afa8141c0 Update esphome/components/logger/logger.cpp 2025-06-23 09:00:46 +02:00
J. Nick Koston
587964c6f1 Merge branch 'dev' into logger_disable_loop 2025-06-23 09:00:22 +02:00
Jesse Hills
7aea82a273 Move define 2025-06-23 14:15:10 +12:00
J. Nick Koston
20f946ccaf Merge branch 'dev' into multi_device 2025-06-23 00:32:09 +02:00
Jesse Hills
e5e972231c Update testing 2025-06-23 10:26:31 +12:00
J. Nick Koston
bfa80157f2 Merge branch 'scheduler_memory_opt' into integration 2025-06-23 00:07:43 +02:00
J. Nick Koston
99b1b079d0 Reduce RAM usage for scheduled tasks 2025-06-23 00:03:01 +02:00
J. Nick Koston
5697d549a8 Use scheduler for api reboot 2025-06-22 23:44:08 +02:00
Jesse Hills
754d2874e7 `this->` 2025-06-23 09:21:29 +12:00
Jesse Hills
06de58ff8b Dont need to warning about simple string area
A single device in a single area can have a simple string as the area
2025-06-23 09:20:53 +12:00
J. Nick Koston
a0b3527710 Merge branch 'logger_memory' into integration 2025-06-22 22:59:51 +02:00
J. Nick Koston
df24f48fa1 Merge branch 'pre_preserve_looping_components' into integration 2025-06-22 22:57:38 +02:00
J. Nick Koston
13d53590b2 Pre-reserve looping components vector to reduce memory allocations 2025-06-22 22:56:31 +02:00
J. Nick Koston
5857f7b9a7 Merge remote-tracking branch 'dala318/multi_device' into multi_device 2025-06-22 21:55:46 +02:00
J. Nick Koston
a5ea0cd41f remove unreachable code 2025-06-22 21:55:23 +02:00
J. Nick Koston
d677934417 Merge branch 'dev' into multi_device 2025-06-22 21:45:28 +02:00
J. Nick Koston
ba87a0b63c cleanups 2025-06-22 21:32:20 +02:00
J. Nick Koston
b725bb3dd1 lint 2025-06-22 21:28:16 +02:00
J. Nick Koston
c34ba3deb5 lint 2025-06-22 21:25:55 +02:00
J. Nick Koston
68b13340fb lint 2025-06-22 21:24:17 +02:00
J. Nick Koston
8831999ea6 lint 2025-06-22 21:23:41 +02:00
J. Nick Koston
c1853f8b84 document design decisions 2025-06-22 21:21:29 +02:00
J. Nick Koston
2b9b7e2853 validation should happen sooner 2025-06-22 21:18:04 +02:00
J. Nick Koston
d3b18debf9 validate sooner 2025-06-22 21:06:33 +02:00
J. Nick Koston
b01eb28d42 validate sooner 2025-06-22 21:05:15 +02:00
J. Nick Koston
02019dd16c validate sooner 2025-06-22 21:04:42 +02:00
J. Nick Koston
7be12f5ff6 validate sooner 2025-06-22 20:59:54 +02:00
J. Nick Koston
a90d59b6ba validate sooner 2025-06-22 20:59:07 +02:00
J. Nick Koston
e7fa156254 Merge remote-tracking branch 'upstream/dev' into integration 2025-06-22 20:15:02 +02:00
J. Nick Koston
a8ab6b1c43 Merge branch 'dev' into logger_disable_loop 2025-06-22 20:12:17 +02:00
J. Nick Koston
25ed7c890b cleanups 2025-06-22 20:03:02 +02:00
J. Nick Koston
85e3b63f05 adjust 2025-06-22 19:49:12 +02:00
J. Nick Koston
a37bac1956 add files 2025-06-22 19:47:19 +02:00
J. Nick Koston
818a978dfc units 2025-06-22 19:40:53 +02:00
J. Nick Koston
180aeb7d8e simplify 2025-06-22 13:50:29 +02:00
J. Nick Koston
0764fa7292 simplify 2025-06-22 13:48:27 +02:00
J. Nick Koston
17bf533ed7 simplify 2025-06-22 13:44:05 +02:00
J. Nick Koston
d7eae1c1a0 simplify 2025-06-22 13:43:52 +02:00
J. Nick Koston
7f2d979255 preen 2025-06-22 13:39:12 +02:00
J. Nick Koston
46b419ea8b preen 2025-06-22 13:38:14 +02:00
J. Nick Koston
b30b527ff9 one more place to check 2025-06-22 13:37:30 +02:00
J. Nick Koston
41b1bfc504 legacy test 2025-06-22 13:37:01 +02:00
J. Nick Koston
f4f14a7507 fixes 2025-06-22 13:29:49 +02:00
J. Nick Koston
61c29213a7 fixes 2025-06-22 13:29:41 +02:00
J. Nick Koston
e6d7639209 Merge branch 'dev' into multi_device 2025-06-22 13:03:16 +02:00
J. Nick Koston
3c07a186b2 Merge remote-tracking branch 'dala318/multi_device' into multi_device 2025-06-22 13:02:48 +02:00
J. Nick Koston
8a725250a9 Merge branch 'dev' into multi_device 2025-06-22 12:32:44 +02:00
J. Nick Koston
502b8a6073 fixes 2025-06-22 12:32:25 +02:00
J. Nick Koston
6212c6f80f Merge branch 'dev' into logger_disable_loop 2025-06-22 12:10:11 +02:00
J. Nick Koston
b03e3b8d4a fixes 2025-06-22 10:07:05 +02:00
J. Nick Koston
a98e34d190 handle collisions 2025-06-22 10:02:59 +02:00
J. Nick Koston
bf8d8b6e63 handle collisions 2025-06-22 10:01:53 +02:00
J. Nick Koston
57599f7a98 handle collisions 2025-06-22 10:00:31 +02:00
J. Nick Koston
ffccce7ffc handle collisions 2025-06-22 09:58:12 +02:00
J. Nick Koston
bbd5d050a9 Merge branch 'dev' into logger_disable_loop 2025-06-21 18:36:59 +02:00
J. Nick Koston
71a96fdcbf Merge branch 'dev' into logger_disable_loop 2025-06-21 18:11:19 +02:00
J. Nick Koston
221e3c6c9c preen 2025-06-21 18:09:16 +02:00
J. Nick Koston
fb1679d572 preen 2025-06-21 18:07:45 +02:00
J. Nick Koston
c19065f112 preen 2025-06-21 18:02:32 +02:00
J. Nick Koston
f2b04a077e preen 2025-06-21 18:01:12 +02:00
J. Nick Koston
8e7841c880 preen 2025-06-21 18:00:17 +02:00
J. Nick Koston
1873490b24 preen 2025-06-21 17:57:36 +02:00
J. Nick Koston
4d231953f4 preen 2025-06-21 17:57:10 +02:00
J. Nick Koston
aa4c399657 reverse space in vectors 2025-06-21 17:36:25 +02:00
J. Nick Koston
1f99d18982 reverse space in vectors 2025-06-21 17:34:08 +02:00
J. Nick Koston
be37178ef8 make areas and devices consistant 2025-06-21 17:32:11 +02:00
J. Nick Koston
fad86c655e make areas and devices consistant 2025-06-21 17:30:17 +02:00
J. Nick Koston
4a7958586e make areas and devices consistant 2025-06-21 17:19:16 +02:00
J. Nick Koston
f44ecd0891 make areas and devices consistant 2025-06-21 17:18:23 +02:00
J. Nick Koston
3d0392d668 make areas and devices consistant 2025-06-21 17:17:29 +02:00
J. Nick Koston
d300d2605b make areas and devices consistant 2025-06-21 17:13:04 +02:00
J. Nick Koston
66cce6a2f2 make areas and devices consistant 2025-06-21 17:12:25 +02:00
J. Nick Koston
65e3c6bfbb make areas and devices consistant 2025-06-21 17:12:00 +02:00
J. Nick Koston
2a39060912 Merge remote-tracking branch 'upstream/dev' into multi_device 2025-06-21 17:06:11 +02:00
J. Nick Koston
8714e80978 make areas and devices consistant 2025-06-21 17:05:46 +02:00
J. Nick Koston
98de53f60b migrate to using same area info for top level and sub devices 2025-06-21 16:47:03 +02:00
J. Nick Koston
41e11e9a0e migrate to using same area info for top level and sub devices 2025-06-21 16:43:48 +02:00
J. Nick Koston
e7a4eac8bd migrate to using same area info for top level and sub devices 2025-06-21 16:42:05 +02:00
J. Nick Koston
1589a131db migrate to using same area info for top level and sub devices 2025-06-21 16:39:07 +02:00
J. Nick Koston
7d84f0e650 migrate to using same area info for top level and sub devices 2025-06-21 16:37:21 +02:00
J. Nick Koston
86fb0e317f fixes 2025-06-21 15:22:35 +02:00
J. Nick Koston
32088d5ef7 revert 2025-06-21 13:35:32 +02:00
J. Nick Koston
63de88dd57 fixes 2025-06-21 13:33:29 +02:00
J. Nick Koston
153a6440dc cleanups to address review comments 2025-06-21 13:20:59 +02:00
J. Nick Koston
8937ed2269 cleanups to address review comments 2025-06-21 13:18:25 +02:00
J. Nick Koston
02e922b56f cleanups to address review comments 2025-06-21 13:16:42 +02:00
J. Nick Koston
bf9e901ab9 cleanups to address review comments 2025-06-21 13:13:44 +02:00
J. Nick Koston
1234ef8de2 Merge remote-tracking branch 'upstream/dev' into multi_device 2025-06-21 12:13:54 +02:00
J. Nick Koston
41697a7b1b Merge remote-tracking branch 'upstream/logger_disable_loop' into integration 2025-06-21 11:19:12 +02:00
J. Nick Koston
912e265bc0 Merge branch 'dev' into logger_disable_loop 2025-06-21 11:18:59 +02:00
J. Nick Koston
96ee6fb064 Merge branch 'logger_disable_loop' into integration 2025-06-21 11:17:13 +02:00
J. Nick Koston
788dba8ef3 define 2025-06-21 11:16:14 +02:00
J. Nick Koston
fdde9c4681 Reduce Logger memory usage by optimizing variable sizes 2025-06-21 00:27:05 +02:00
J. Nick Koston
f195e73d38 Merge branch 'logger_disable_loop' into integration 2025-06-20 22:54:40 +02:00
J. Nick Koston
b0d9ffc6a1 Reduce logger CPU usage by disabling loop when buffer is empty 2025-06-20 22:53:12 +02:00
J. Nick Koston
e17619841d fix last component being charged for stats 2025-06-20 22:03:53 +02:00
J. Nick Koston
eb6a7cf3b9 fix last component being charged for stats 2025-06-20 22:02:19 +02:00
J. Nick Koston
9901e2d72e Merge branch 'dev' into integration 2025-06-20 21:36:36 +02:00
J. Nick Koston
1be4e23b68 Merge branch 'dev' into binary_sensor_gpio_polling 2025-06-19 11:07:42 +02:00
J. Nick Koston
e78094cc0a Merge branch 'dev' into esp32_touch_isr 2025-06-19 10:49:17 +02:00
J. Nick Koston
bcf961c0b0 Merge branch 'dev' into integration 2025-06-19 04:05:25 +02:00
J. Nick Koston
f84a4c9753 Merge remote-tracking branch 'origin/disable_ethernet_loop' into integration 2025-06-19 03:42:53 +02:00
J. Nick Koston
df56ca0236 remove redundant enable_loop, it must already be enabled to get here 2025-06-19 03:41:25 +02:00
J. Nick Koston
de0cd0ec67 Merge branch 'dev' into disable_ethernet_loop 2025-06-19 03:39:15 +02:00
J. Nick Koston
67c30245c4 make copilot happy 2025-06-19 02:01:55 +02:00
J. Nick Koston
1f72757591 tidy 2025-06-19 01:35:45 +02:00
J. Nick Koston
35c2fdf6af dry 2025-06-19 01:31:11 +02:00
J. Nick Koston
d1ecd841be avoid auto 2025-06-19 01:28:17 +02:00
J. Nick Koston
828a49697c Merge branch 'gap_events' into integration 2025-06-19 01:18:36 +02:00
J. Nick Koston
0551495501 try another way 2025-06-19 01:18:26 +02:00
J. Nick Koston
2bbffe4a68 try another way 2025-06-19 01:18:11 +02:00
J. Nick Koston
281ad90e39 fixes 2025-06-19 01:16:46 +02:00
J. Nick Koston
ed50976a07 fixes 2025-06-19 01:16:22 +02:00
J. Nick Koston
a3400037d9 fixes 2025-06-19 01:14:15 +02:00
J. Nick Koston
f0d82f75bc fixes 2025-06-19 01:14:05 +02:00
J. Nick Koston
349cb80e90 Merge remote-tracking branch 'origin/integration' into integration 2025-06-19 01:12:20 +02:00
J. Nick Koston
c263ee39af Merge branch 'gap_events' into integration 2025-06-19 01:12:07 +02:00
J. Nick Koston
e99bc52756 Fix missing BLE GAP events causing RSSI sensor and beacon failures 2025-06-19 01:09:13 +02:00
J. Nick Koston
7944b2b8e9 Merge branch 'ota_perf' into integration 2025-06-19 00:40:07 +02:00
J. Nick Koston
ca6ae746c1 be explict 2025-06-19 00:39:19 +02:00
J. Nick Koston
deabac18b2 Merge branch 'disable_ethernet_loop' into integration 2025-06-18 21:39:35 +02:00
J. Nick Koston
5cf8681c61 Merge branch 'ota_perf' into integration 2025-06-18 21:35:14 +02:00
J. Nick Koston
ca7ede8f96 more cleanups 2025-06-18 21:35:04 +02:00
J. Nick Koston
4969682d52 Merge branch 'ota_perf' into integration 2025-06-18 21:27:51 +02:00
J. Nick Koston
8002fe0dd5 remove safety check 2025-06-18 21:27:30 +02:00
J. Nick Koston
7dfdf965b7 remove safety check 2025-06-18 21:26:32 +02:00
J. Nick Koston
b408795dd6 Merge branch 'api_reads' into integration 2025-06-18 19:24:32 +02:00
J. Nick Koston
a5a099336b one more 2025-06-18 19:22:23 +02:00
J. Nick Koston
4ae56fc004 Merge branch 'api_reads' into integration 2025-06-18 18:40:35 +02:00
J. Nick Koston
3f71c09b7b Fix slow noise handshake by reading multiple messages per loop 2025-06-18 18:36:55 +02:00
J. Nick Koston
bd50a7f1ab cleanup 2025-06-18 14:33:58 +02:00
J. Nick Koston
51e4c45e5c Merge branch 'loop_done_enable_isr' into disable_ethernet_loop 2025-06-18 14:27:18 +02:00
J. Nick Koston
e3fae49add Merge branch 'binary_sensor_gpio_polling' into integration 2025-06-18 14:24:42 +02:00
J. Nick Koston
610215ab60 updates 2025-06-18 14:24:31 +02:00
J. Nick Koston
74acbda435 Merge branch 'loop_done_enable_isr' into binary_sensor_gpio_polling 2025-06-18 14:19:03 +02:00
J. Nick Koston
25c4af777c Merge branch 'loop_done_enable_isr' into integration 2025-06-18 14:18:35 +02:00
J. Nick Koston
ec186e6324 rename 2025-06-18 14:17:45 +02:00
J. Nick Koston
150b7a98f3 Merge branch 'dev' into ota_perf 2025-06-18 13:57:20 +02:00
J. Nick Koston
8ae7c1cff0 Merge branch 'ota_perf' into integration 2025-06-18 13:46:36 +02:00
J. Nick Koston
7f1d0eef98 Optimize OTA loop to avoid unnecessary stack allocations 2025-06-18 13:44:07 +02:00
J. Nick Koston
1179ab33f2 tweaks 2025-06-18 12:52:18 +02:00
J. Nick Koston
a09faa1c10 Merge branch 'dev' into disable_ethernet_loop 2025-06-18 12:36:22 +02:00
J. Nick Koston
c0319d9b2f Merge branch 'binary_sensor_gpio_polling' into integration 2025-06-18 12:28:59 +02:00
J. Nick Koston
4870cd2921 use enable_loop_soon_from_isr 2025-06-18 12:28:49 +02:00
J. Nick Koston
d4280ec68b Merge branch 'loop_done_enable_isr' into binary_sensor_gpio_polling 2025-06-18 12:23:55 +02:00
J. Nick Koston
52cdc11927 Merge remote-tracking branch 'origin/proxy_memory' into integration 2025-06-18 12:21:31 +02:00
J. Nick Koston
8345b8c9ce Update esphome/components/esp32_ble_client/ble_client_base.h
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-18 12:21:10 +02:00
J. Nick Koston
c56f0677c3 Merge remote-tracking branch 'upstream/proxy_memory' into integration 2025-06-18 12:16:23 +02:00
J. Nick Koston
00e9e1421e Merge branch 'dev' into proxy_memory 2025-06-18 12:16:12 +02:00
J. Nick Koston
93c72c6e6c Merge branch 'loop_done_enable_isr' into integration 2025-06-18 12:15:24 +02:00
J. Nick Koston
9cea930dbd Merge remote-tracking branch 'upstream/dev' into integration 2025-06-18 12:15:18 +02:00
J. Nick Koston
7b9bd70729 Add enable_loop_soon_from_isr 2025-06-18 12:09:12 +02:00
J. Nick Koston
5115c7a100 Merge branch 'bump_ruff_precommit' into integration 2025-06-18 00:15:23 +02:00
J. Nick Koston
5634494e64 Bump ruff in pre-commit to 0.12.0
matches https://github.com/esphome/esphome/pull/9120
2025-06-18 00:11:40 +02:00
J. Nick Koston
aa8bd4abf1 Bump ruff in pre-commit to 0.12.0
matches https://github.com/esphome/esphome/pull/9120
2025-06-18 00:10:30 +02:00
J. Nick Koston
17fd69dd7f Bump ruff in pre-commit to 0.12.0
matches https://github.com/esphome/esphome/pull/9120
2025-06-18 00:09:18 +02:00
J. Nick Koston
1d9dae374b Merge branch 'loop_done' into integration 2025-06-17 23:45:20 +02:00
J. Nick Koston
cb2241ad91 make sure components that disable in setup are disabled at start 2025-06-17 23:45:16 +02:00
J. Nick Koston
d8a7e9abc8 make sure components that disable in setup are disabled at start 2025-06-17 23:44:32 +02:00
J. Nick Koston
969abc3f29 make sure components that disable in setup are disabled at start 2025-06-17 23:40:46 +02:00
J. Nick Koston
766fdc8a1f make sure components that disable in setup are disabled at start 2025-06-17 23:40:31 +02:00
J. Nick Koston
4c37c20d76 cleaner fix 2025-06-17 22:30:35 +02:00
J. Nick Koston
7d314398e1 cleaner fix 2025-06-17 22:30:31 +02:00
J. Nick Koston
b69191e3a8 cleaner fix 2025-06-17 22:29:21 +02:00
J. Nick Koston
b27c6b3596 cleaner fix 2025-06-17 22:27:24 +02:00
J. Nick Koston
5453835963 make ble client disable/enable smarter 2025-06-17 18:13:09 +02:00
J. Nick Koston
4d55ba057c make ble client disable/enable smarter 2025-06-17 18:09:53 +02:00
J. Nick Koston
325c01242c tweak 2025-06-17 16:16:20 +02:00
J. Nick Koston
45b32bca89 tweak 2025-06-17 16:08:28 +02:00
J. Nick Koston
7620049214 tweak 2025-06-17 16:05:48 +02:00
J. Nick Koston
3553495a60 Merge remote-tracking branch 'origin/integration' into integration 2025-06-17 15:55:51 +02:00
J. Nick Koston
3ce6db61d5 Merge branch 'binary_sensor_gpio_polling' into integration 2025-06-17 15:55:28 +02:00
J. Nick Koston
798ff32c40 cleanup 2025-06-17 15:55:10 +02:00
J. Nick Koston
430cee8bda Merge branch 'integration' of https://github.com/esphome/esphome into integration 2025-06-17 15:05:27 +02:00
J. Nick Koston
1fe3fb25a6 Merge branch 'binary_sensor_gpio_polling' into integration 2025-06-17 14:38:08 +02:00
J. Nick Koston
685ed87581 preen 2025-06-17 14:38:00 +02:00
J. Nick Koston
ea3ea1eee7 tweak 2025-06-17 14:17:35 +02:00
J. Nick Koston
c9edcb909b Merge branch 'binary_sensor_gpio_polling' into integration 2025-06-17 13:42:02 +02:00
J. Nick Koston
35bfc9f069 tweak 2025-06-17 13:41:57 +02:00
J. Nick Koston
c4aec194b9 Merge branch 'binary_sensor_gpio_polling' into integration 2025-06-17 13:31:44 +02:00
J. Nick Koston
e8547b16f6 Avoid polling for GPIO binary sensors when possible 2025-06-17 13:20:41 +02:00
J. Nick Koston
2bbe08cee0 Avoid polling for GPIO binary sensors when possible 2025-06-17 13:18:45 +02:00
J. Nick Koston
0a0c369b88 Avoid polling for GPIO binary sensors when possible 2025-06-17 13:17:35 +02:00
J. Nick Koston
5d2f454a94 Avoid polling for GPIO binary sensors when possible 2025-06-17 13:13:58 +02:00
J. Nick Koston
04bcc5c879 Avoid polling for GPIO binary sensors when possible 2025-06-17 13:02:00 +02:00
J. Nick Koston
d4db16665f Avoid polling for GPIO binary sensors when possible 2025-06-17 12:41:17 +02:00
J. Nick Koston
20b7a494f6 Merge remote-tracking branch 'origin/proxy_memory' into integration 2025-06-17 12:05:43 +02:00
J. Nick Koston
fbdce3ad89 Optimize bluetooth_proxy memory usage on ESP32 2025-06-17 12:04:49 +02:00
J. Nick Koston
4fc8807f02 Merge branch 'light_memory' into integration 2025-06-17 11:49:58 +02:00
J. Nick Koston
83075bfb5c Optimize LightState memory layout 2025-06-17 11:49:15 +02:00
J. Nick Koston
4074ec0425 Merge branch 'switch_memory' into integration 2025-06-17 11:27:45 +02:00
J. Nick Koston
8e1694dd0f Reduce Switch component memory usage by 8 bytes per instance 2025-06-17 11:27:11 +02:00
J. Nick Koston
911df18855 Merge branch 'api_memory' into integration 2025-06-17 11:10:17 +02:00
J. Nick Koston
6b049e93f8 Optimize API component memory usage by reordering class members to reduce padding 2025-06-17 11:09:22 +02:00
J. Nick Koston
a335dcc379 Merge remote-tracking branch 'upstream/dev' into integration 2025-06-17 10:40:41 +02:00
J. Nick Koston
c6478c8a79 Merge branch 'reduce_duplicate_gen_code_api' into integration 2025-06-17 10:40:23 +02:00
J. Nick Koston
cc9d40cb60 tweaks 2025-06-17 10:40:12 +02:00
J. Nick Koston
0a6b7f9a1b Update script/api_protobuf/api_protobuf.py 2025-06-17 10:39:49 +02:00
J. Nick Koston
daa1fb9a7a Merge remote-tracking branch 'swoboda1337/bump_libretiny' into integration 2025-06-17 04:33:57 +02:00
Jonathan Swoboda
b7d543290b Bump LibreTiny 2025-06-16 21:40:06 -04:00
J. Nick Koston
ea852b60ac Merge branch 'esp32_ble_tracker_reduce_memory' into integration 2025-06-16 22:07:13 +02:00
J. Nick Koston
ed341988ea Use smaller atomic types for ESP32 BLE Tracker ring buffer indices 2025-06-16 22:06:04 +02:00
J. Nick Koston
057b6c8e30 Merge branch 'api_reduce_millis' into integration 2025-06-16 19:34:07 +02:00
J. Nick Koston
44444fe071 Optimize API server performance by using cached loop time 2025-06-16 19:33:29 +02:00
J. Nick Koston
797330d6ab Disable Ethernet loop polling when connected and stable 2025-06-16 17:28:04 +02:00
J. Nick Koston
a630d5b5f5 Merge branch 'ble_pool' into integration 2025-06-16 15:45:50 +02:00
J. Nick Koston
eb3dc82b5d naming 2025-06-16 15:45:38 +02:00
J. Nick Koston
34ed18d562 Merge branch 'ble_pool' into integration 2025-06-16 15:43:59 +02:00
J. Nick Koston
1ce02ee313 naming 2025-06-16 15:43:43 +02:00
J. Nick Koston
2a26a0188c ble pool 2025-06-16 15:29:37 +02:00
J. Nick Koston
50cb05d1b1 ble pool 2025-06-16 15:28:03 +02:00
J. Nick Koston
6e739ac453 ble pool 2025-06-16 15:23:04 +02:00
J. Nick Koston
7aa2fd9f0e ble pool 2025-06-16 15:19:10 +02:00
J. Nick Koston
8e254e1b03 ble pool 2025-06-16 15:18:19 +02:00
J. Nick Koston
1ad9d717ff ble pool 2025-06-16 15:17:57 +02:00
J. Nick Koston
104658e43a ble pool 2025-06-16 15:16:15 +02:00
J. Nick Koston
e7e4b995bf ble pool 2025-06-16 15:15:26 +02:00
J. Nick Koston
b35b54f2c2 ble pool 2025-06-16 15:11:42 +02:00
J. Nick Koston
f80aeb1d1d cleanup 2025-06-16 15:10:27 +02:00
J. Nick Koston
6a756ab3b6 cleanup 2025-06-16 15:09:49 +02:00
J. Nick Koston
58a697bed1 cleanup 2025-06-16 15:07:23 +02:00
J. Nick Koston
280960ac18 cleanup 2025-06-16 15:06:02 +02:00
J. Nick Koston
0640ff13aa ble pool 2025-06-16 15:04:40 +02:00
J. Nick Koston
545505691f ble pool 2025-06-16 15:02:10 +02:00
J. Nick Koston
11fcf81321 ble pool 2025-06-16 15:00:58 +02:00
J. Nick Koston
c565b37dc8 ble pool 2025-06-16 15:00:07 +02:00
J. Nick Koston
3d18495270 ble pool 2025-06-16 14:55:15 +02:00
J. Nick Koston
419e4e63e9 ble pool 2025-06-16 14:53:50 +02:00
J. Nick Koston
724aa2bf65 ble pool 2025-06-16 14:52:38 +02:00
J. Nick Koston
573fa8aeb3 ble pool 2025-06-16 14:52:28 +02:00
J. Nick Koston
8a672e34c5 ble pool 2025-06-16 14:47:05 +02:00
J. Nick Koston
bc49211dab ble pool 2025-06-16 14:43:29 +02:00
J. Nick Koston
4ef9c3667e Merge branch 'reduce_duplicate_gen_code_api' into integration 2025-06-16 06:06:19 -05:00
J. Nick Koston
6babe516ac move to proto.h to have less generated code 2025-06-16 06:05:19 -05:00
J. Nick Koston
e0b258ef7e Merge branch 'empty_methods' into integration 2025-06-15 22:30:02 -05:00
J. Nick Koston
ff0c3a89b1 Remove empty generated protobuf methods 2025-06-15 22:25:21 -05:00
J. Nick Koston
2511b81048 Merge branch 'reduce_duplicate_gen_code_api' into integration 2025-06-15 22:09:15 -05:00
J. Nick Koston
6ffcd94edc early return was worse for simple functions 2025-06-15 22:00:40 -05:00
J. Nick Koston
2fcf73c812 Reduce code duplication in auto-generated API protocol code 2025-06-15 21:53:33 -05:00
J. Nick Koston
dee0608af9 adjust 2025-06-15 20:47:53 -05:00
J. Nick Koston
d11860a383 Merge remote-tracking branch 'origin/loop_done' into integration 2025-06-15 20:44:24 -05:00
J. Nick Koston
1c05115bf5 Merge branch 'dev' into loop_done 2025-06-15 20:44:09 -05:00
J. Nick Koston
d7e7382d0b tests, address review comments 2025-06-15 20:43:30 -05:00
J. Nick Koston
872388f6e3 tests, address review comments 2025-06-15 20:43:01 -05:00
J. Nick Koston
1215ef920b Merge branch 'loop_done' into integration 2025-06-15 20:36:08 -05:00
J. Nick Koston
d19d5a23ea speed up test a bit 2025-06-15 20:36:00 -05:00
J. Nick Koston
f49a779f1d speed up test a bit 2025-06-15 20:35:52 -05:00
J. Nick Koston
d8bf5b80e1 Merge branch 'loop_done' into integration 2025-06-15 20:34:22 -05:00
J. Nick Koston
69483b9353 speed up test a bit 2025-06-15 20:34:13 -05:00
J. Nick Koston
14e8548989 speed up test a bit 2025-06-15 20:33:52 -05:00
J. Nick Koston
4abd93b661 tests, address review comments 2025-06-15 20:32:36 -05:00
J. Nick Koston
5d925af76f tests, address review comments 2025-06-15 20:31:25 -05:00
J. Nick Koston
b999c6064a tests, address review comments 2025-06-15 20:30:54 -05:00
J. Nick Koston
94e3576978 tests, address review comments 2025-06-15 20:30:43 -05:00
J. Nick Koston
7a22406a2d Merge remote-tracking branch 'origin/integration' into integration 2025-06-15 20:29:36 -05:00
J. Nick Koston
e60684494f Merge branch 'loop_done' into integration 2025-06-15 20:29:25 -05:00
J. Nick Koston
9db28ed779 cover 2025-06-15 20:29:12 -05:00
J. Nick Koston
6fd8c5cee7 tests, address review comments 2025-06-15 20:22:49 -05:00
J. Nick Koston
787ec43266 tests, address review comments 2025-06-15 20:22:29 -05:00
J. Nick Koston
a4efc63bf2 test 2025-06-15 19:57:20 -05:00
J. Nick Koston
80a8f1437e tests 2025-06-15 19:38:13 -05:00
J. Nick Koston
fcca94169d Merge remote-tracking branch 'origin/integration' into integration 2025-06-15 19:12:32 -05:00
J. Nick Koston
d1924088e3 Merge remote-tracking branch 'origin/loop_done' into integration 2025-06-15 19:11:57 -05:00
J. Nick Koston
fd31afe09c tidy 2025-06-15 18:58:42 -05:00
J. Nick Koston
7a763712c5 tidy 2025-06-15 18:58:32 -05:00
J. Nick Koston
7216be5da7 Merge branch 'loop_done' into integration 2025-06-15 18:44:36 -05:00
J. Nick Koston
711b0a291b comments 2025-06-15 18:44:28 -05:00
J. Nick Koston
dfc96496c8 comments 2025-06-15 18:44:15 -05:00
J. Nick Koston
2a1c5ef333 Merge branch 'loop_done' into integration 2025-06-15 18:42:49 -05:00
J. Nick Koston
9755209499 comments 2025-06-15 18:42:40 -05:00
J. Nick Koston
0b26e537d4 Merge branch 'loop_done' into integration 2025-06-15 18:40:46 -05:00
J. Nick Koston
98c6233ec3 Merge branch 'dev' into loop_done 2025-06-15 18:40:33 -05:00
J. Nick Koston
f711706b1a Fix ESP32 Improv component to re-enable loop when service starts again 2025-06-15 18:40:08 -05:00
J. Nick Koston
cee7789ab6 tweak 2025-06-15 18:37:05 -05:00
J. Nick Koston
8a06c4380d partition 2025-06-15 18:32:36 -05:00
J. Nick Koston
72ecf7a288 Merge remote-tracking branch 'upstream/dev' into integration 2025-06-15 16:48:20 -05:00
J. Nick Koston
ef98c7502d Merge remote-tracking branch 'origin/dev' into integration 2025-06-15 13:42:11 -05:00
J. Nick Koston
03d0e74b65 Merge remote-tracking branch 'upstream/less_templates' into integration 2025-06-15 10:48:53 -05:00
J. Nick Koston
5b8fdc0364 Merge branch 'dev' into less_templates 2025-06-15 10:42:41 -05:00
J. Nick Koston
593b4bd137 Update script/api_protobuf/api_protobuf.py 2025-06-15 10:42:28 -05:00
J. Nick Koston
267e12d058 lint 2025-06-15 10:09:54 -05:00
J. Nick Koston
4a5e39b651 Add common base classes for entity protobuf messages to reduce duplicate code 2025-06-15 09:40:45 -05:00
J. Nick Koston
ea24fa5b78 Merge branch 'loop_done' into integration 2025-06-15 01:52:27 -05:00
J. Nick Koston
bb2bb128f7 remove trailing . 2025-06-15 01:52:17 -05:00
J. Nick Koston
94e8a856d7 Merge branch 'loop_done' into integration 2025-06-15 01:47:22 -05:00
J. Nick Koston
4c19fbf98e lint 2025-06-15 01:47:10 -05:00
J. Nick Koston
60f8938bfa Merge branch 'loop_done' into integration 2025-06-15 01:34:12 -05:00
J. Nick Koston
55679662b5 ordering 2025-06-15 01:34:03 -05:00
J. Nick Koston
53df959e49 Merge branch 'loop_done' into integration 2025-06-15 01:26:56 -05:00
J. Nick Koston
8e6ef9966f Merge remote-tracking branch 'upstream/loop_done' into loop_done 2025-06-15 01:26:45 -05:00
J. Nick Koston
1d52fceafa rename, cleanup 2025-06-15 01:26:25 -05:00
J. Nick Koston
99186ed864 rename, cleanup 2025-06-15 01:25:59 -05:00
J. Nick Koston
383931d484 Merge branch 'ble_events_ring_buffer' into integration 2025-06-15 00:31:34 -05:00
J. Nick Koston
0b49a54cb3 comments 2025-06-15 00:31:25 -05:00
J. Nick Koston
705c0f1891 Merge branch 'ble_events_ring_buffer' into integration 2025-06-15 00:27:13 -05:00
J. Nick Koston
544c3ffc95 comments 2025-06-15 00:26:06 -05:00
J. Nick Koston
33f252a45d Implement a lock free ring buffer for BLEEvents to avoid drops 2025-06-15 00:22:24 -05:00
J. Nick Koston
f55d82a015 Merge branch 'ble_queue_lock_free' into integration 2025-06-15 00:16:02 -05:00
J. Nick Koston
8cf33fdef0 preen 2025-06-15 00:15:48 -05:00
J. Nick Koston
f858d98811 Merge branch 'ble_queue_lock_free' into integration 2025-06-15 00:12:47 -05:00
J. Nick Koston
2a6165d440 simplify 2025-06-15 00:12:34 -05:00
J. Nick Koston
4586528c40 merge 2025-06-15 00:01:15 -05:00
J. Nick Koston
23a07baa19 Merge branch 'ble_queue_lock_free' into integration 2025-06-14 23:58:53 -05:00
J. Nick Koston
f9040ca932 cleanup 2025-06-14 23:54:42 -05:00
J. Nick Koston
4cea7f0237 Update esphome/components/esp32_ble/ble.cpp 2025-06-14 23:49:38 -05:00
J. Nick Koston
b1847d5e98 Make ble events queue lock free 2025-06-14 23:48:26 -05:00
J. Nick Koston
9ce4d2e952 Merge remote-tracking branch 'upstream/dev' into integration 2025-06-14 23:21:51 -05:00
J. Nick Koston
247078e06d Merge remote-tracking branch 'origin/loop_done' into integration 2025-06-14 23:20:20 -05:00
J. Nick Koston
a0cd72de28 revert 2025-06-14 23:19:43 -05:00
J. Nick Koston
e467f569f0 Merge branch 'loop_done' into integration 2025-06-14 23:15:49 -05:00
J. Nick Koston
e31c7b7dfc one more 2025-06-14 23:15:06 -05:00
J. Nick Koston
dc2e0c832b Merge branch 'loop_done' into integration 2025-06-14 22:37:11 -05:00
J. Nick Koston
7ddf51bb51 fix 2025-06-14 22:36:29 -05:00
J. Nick Koston
8fb3856665 small fix 2025-06-14 22:17:27 -05:00
J. Nick Koston
183dd74f3e one more 2025-06-14 22:17:27 -05:00
J. Nick Koston
4f29039b41 mark_loop_done 2025-06-14 22:17:24 -05:00
J. Nick Koston
102fcbec20 small fix 2025-06-14 22:09:19 -05:00
J. Nick Koston
d00e5212c7 one more 2025-06-14 22:04:33 -05:00
J. Nick Koston
0e6bfb62cd mark_loop_done 2025-06-14 21:58:18 -05:00
J. Nick Koston
f576e8f635 remove cap 2025-06-14 21:40:16 -05:00
J. Nick Koston
e6dc10a440 address review comments 2025-06-14 21:34:21 -05:00
J. Nick Koston
aa930fb6b6 Merge branch 'ble_queue_lock_free' into integration 2025-06-14 20:09:56 -05:00
J. Nick Koston
f327ed87e9 Make ble events queue lock free 2025-06-14 20:08:43 -05:00
J. Nick Koston
2de9be0589 Merge branch 'loop_runtime_stats' into integration 2025-06-14 19:43:45 -05:00
J. Nick Koston
345cde8645 Merge branch 'ble_events_ring_buffer' into integration 2025-06-14 19:25:57 -05:00
J. Nick Koston
cf152af9ae Implement a lock free ring buffer for BLEEvents to avoid drops 2025-06-14 19:24:57 -05:00
J. Nick Koston
d6333dcfd9 Revert "Reorder Application to reduce padding"
This reverts commit 82c39580df.
2025-06-14 18:18:45 -05:00
J. Nick Koston
0121f799f0 Merge branch 'reorder_app_reduce_padding' into integration 2025-06-14 18:16:39 -05:00
J. Nick Koston
82c39580df Reorder Application to reduce padding 2025-06-14 18:15:40 -05:00
J. Nick Koston
53a578a46f Merge branch 'area_str' into integration 2025-06-14 18:01:44 -05:00
J. Nick Koston
62612ef80b Optimize Application area_ from std::string to const char* 2025-06-14 18:00:32 -05:00
J. Nick Koston
61ac874c4c Merge branch 'parse_on_off_uint8t' into integration 2025-06-14 17:46:25 -05:00
J. Nick Koston
976b200ff6 Make ParseOnOffState enum uint8_t 2025-06-14 17:44:22 -05:00
J. Nick Koston
852343b6d8 Merge remote-tracking branch 'upstream/has_state_' into integration 2025-06-14 17:32:40 -05:00
J. Nick Koston
c56af9d52b Merge branch 'component_state_oversized' into integration 2025-06-14 17:03:31 -05:00
J. Nick Koston
05f18e2828 Optimize Component and Application state storage from uint32_t to uint8_t 2025-06-14 17:01:57 -05:00
J. Nick Koston
72804caab2 Merge branch 'warn_if_blocking_over_' into integration 2025-06-14 16:43:26 -05:00
J. Nick Koston
80cbe5c7c9 Reduce Component blocking threshold memory usage by 2 bytes per component 2025-06-14 16:42:08 -05:00
J. Nick Koston
21892d1236 Merge branch 'error_message_memory' into integration 2025-06-14 16:29:43 -05:00
J. Nick Koston
13824624f8 Reduce Component memory usage by 20 bytes per component 2025-06-14 16:27:45 -05:00
J. Nick Koston
0fd72ecbab Merge remote-tracking branch 'upstream/batch_exceeds_max_packet_size' into integration 2025-06-14 15:46:22 -05:00
J. Nick Koston
f848cb1546 Merge remote-tracking branch 'upstream/footer_not_reserved' into integration 2025-06-14 15:46:13 -05:00
J. Nick Koston
633854081a Merge remote-tracking branch 'upstream/guard_wrong_total_size' into integration 2025-06-14 15:45:35 -05:00
J. Nick Koston
4fed9a581b Merge remote-tracking branch 'upstream/missing_force' into integration 2025-06-14 15:45:28 -05:00
J. Nick Koston
e9c1202aaa Merge remote-tracking branch 'upstream/ble_events' into integration 2025-06-14 15:45:18 -05:00
J. Nick Koston
0a7ae279d0 preen 2025-06-14 11:40:50 -05:00
J. Nick Koston
0de2696543 Optimize memory usage by lazy-allocating raw callbacks in sensors 2025-06-14 11:37:11 -05:00
J. Nick Koston
a7dc239b71 cleanup 2025-06-14 10:49:23 -05:00
J. Nick Koston
fe0e6990f5 cover 2025-06-14 10:14:41 -05:00
J. Nick Koston
5ba65e92d9 cover 2025-06-14 10:12:12 -05:00
J. Nick Koston
a1452b52c9 Reduce entity memory usage by eliminating field shadowing and bit-packing 2025-06-14 10:00:49 -05:00
J. Nick Koston
dd2aa23a5f cover 2025-06-13 19:28:59 -05:00
J. Nick Koston
0e0359ba7d Fix protobuf encoding size mismatch by passing force parameter in encode_string 2025-06-13 19:20:08 -05:00
J. Nick Koston
93b1b7aded assert 2025-06-13 18:32:21 -05:00
J. Nick Koston
9472dc6a53 Fix API message encoding to return actual size instead of calculated size 2025-06-13 18:24:51 -05:00
J. Nick Koston
67b681854e Fix API message encoding to return actual size instead of calculated size 2025-06-13 18:20:01 -05:00
J. Nick Koston
7b5990833e Merge branch 'dev' into batch_exceeds_max_packet_size 2025-06-13 17:01:10 -05:00
J. Nick Koston
b6d5d04589 More coverage 2025-06-13 16:59:36 -05:00
J. Nick Koston
fdfbb3e944 Fix footer space not being reserved for batched messages
This only affects noise protocol, and its not a correctness issue, its only
fixing an inefficent reserve
2025-06-13 15:54:31 -05:00
J. Nick Koston
faa7a3e37f tweak 2025-06-13 15:14:14 -05:00
J. Nick Koston
23748b82bb Ensure api can send batches where the first message exceeds MAX_PACKET_SIZE 2025-06-13 14:58:09 -05:00
J. Nick Koston
bccb6f578a Ensure we can send batches where the first message exceeds MAX_PACKET_SIZE 2025-06-13 14:55:17 -05:00
J. Nick Koston
de8a5d6e9e Merge branch 'dev' into ble_events 2025-06-13 10:46:45 -05:00
J. Nick Koston
a8eb3f7961 lint 2025-06-13 10:46:09 -05:00
J. Nick Koston
2dc85f5a42 Merge remote-tracking branch 'upstream/esp32_touch_isr' into esp32_touch_isr 2025-06-13 10:11:49 -05:00
J. Nick Koston
82518b351d lint 2025-06-13 10:11:38 -05:00
J. Nick Koston
68f34a1683 Merge branch 'dev' into esp32_touch_isr 2025-06-12 20:19:29 -05:00
J. Nick Koston
bc6b72a422 tweaks 2025-06-12 20:16:12 -05:00
J. Nick Koston
599e28e1cb fixes 2025-06-12 20:02:39 -05:00
J. Nick Koston
ee6b2ba6c6 fixes 2025-06-12 19:56:12 -05:00
J. Nick Koston
0877b3e2af suppress unused events 2025-06-12 19:18:22 -05:00
J. Nick Koston
d1edb1e32a fix 2025-06-12 18:34:00 -05:00
J. Nick Koston
d1e6b8dd10 comment 2025-06-12 18:33:27 -05:00
J. Nick Koston
b32fc3bfdd lint 2025-06-12 18:30:53 -05:00
J. Nick Koston
1e24417db0 help with setup 2025-06-12 18:09:39 -05:00
J. Nick Koston
fb9387ecc5 help with setup 2025-06-12 17:55:21 -05:00
J. Nick Koston
6c5f4cdb70 help with setup 2025-06-12 17:49:01 -05:00
J. Nick Koston
aabacb7454 help with setup 2025-06-12 17:47:25 -05:00
J. Nick Koston
b5da84479e help with setup 2025-06-12 17:43:08 -05:00
J. Nick Koston
88d9361050 help with setup 2025-06-12 17:34:24 -05:00
J. Nick Koston
1d90388ffc help with setup 2025-06-12 17:27:09 -05:00
J. Nick Koston
b3c43ce31f help with setup 2025-06-12 17:23:10 -05:00
J. Nick Koston
6d9d22d422 help with setup 2025-06-12 17:17:16 -05:00
J. Nick Koston
86be1f56d0 preen 2025-06-12 17:14:00 -05:00
J. Nick Koston
a0c81ffd7a preen 2025-06-12 17:08:47 -05:00
J. Nick Koston
ec1dc42e58 Revert "preen"
This reverts commit 866eaed73d.
2025-06-12 17:05:06 -05:00
J. Nick Koston
866eaed73d preen 2025-06-12 16:58:24 -05:00
J. Nick Koston
a18374e1ad cleanup 2025-06-12 16:33:15 -05:00
J. Nick Koston
f7afcb3b24 cleanup 2025-06-12 16:30:41 -05:00
J. Nick Koston
3adcae783c cleanup 2025-06-12 16:19:27 -05:00
J. Nick Koston
73b40dd2e7 cleanup 2025-06-12 16:19:15 -05:00
J. Nick Koston
1e12614f9a cleanup 2025-06-12 16:14:37 -05:00
J. Nick Koston
aeaa7c699a Merge branch 'dev' into esp32_touch_isr 2025-06-12 15:57:26 -05:00
J. Nick Koston
f1c56b7254 cleanup 2025-06-12 15:56:32 -05:00
J. Nick Koston
e72e0d0646 cleanup 2025-06-12 15:56:19 -05:00
J. Nick Koston
5719d334aa cleanup 2025-06-12 15:56:04 -05:00
J. Nick Koston
bcb6b85333 cleanup 2025-06-12 15:54:15 -05:00
J. Nick Koston
5d765413ef cleanup 2025-06-12 15:53:42 -05:00
J. Nick Koston
efb2e5e7a8 cleanup 2025-06-12 15:52:38 -05:00
J. Nick Koston
5d5e346199 cleanup 2025-06-12 15:50:21 -05:00
J. Nick Koston
08a74890da cleanup 2025-06-12 15:48:29 -05:00
J. Nick Koston
0545b9c7f2 cleanup 2025-06-12 15:48:00 -05:00
J. Nick Koston
bbf7d32676 cleanup 2025-06-12 15:47:31 -05:00
J. Nick Koston
e83f4ae974 cleanup 2025-06-12 15:46:56 -05:00
J. Nick Koston
9b0d01e03f cleanup 2025-06-12 15:45:47 -05:00
J. Nick Koston
eae0d90a1e adjust 2025-06-12 15:41:41 -05:00
J. Nick Koston
90c09a7650 split 2025-06-12 13:29:12 -05:00
J. Nick Koston
aecf080211 touch ups 2025-06-12 13:16:48 -05:00
J. Nick Koston
8517420356 touch ups 2025-06-12 13:14:29 -05:00
J. Nick Koston
376be1f009 touch ups 2025-06-12 13:12:40 -05:00
J. Nick Koston
0021e76649 working 2025-06-12 13:07:25 -05:00
J. Nick Koston
d440c4bc43 derbug 2025-06-12 13:00:55 -05:00
J. Nick Koston
50840b2105 derbug 2025-06-12 13:00:39 -05:00
J. Nick Koston
7502c6b6c0 debug 2025-06-12 12:44:28 -05:00
J. Nick Koston
919c32f0cc tweak 2025-06-12 12:20:47 -05:00
J. Nick Koston
a28c951272 more debug 2025-06-12 12:13:46 -05:00
J. Nick Koston
13d7c5a9a9 more debug 2025-06-12 12:12:55 -05:00
J. Nick Koston
5f1383344d tweak 2025-06-12 12:10:50 -05:00
J. Nick Koston
48f43d3eb1 tweak 2025-06-12 11:58:21 -05:00
J. Nick Koston
4ac2141307 adjust 2025-06-12 11:52:29 -05:00
J. Nick Koston
719d8cac97 split it 2025-06-12 11:45:50 -05:00
J. Nick Koston
99cbe53a8e split it 2025-06-12 11:43:47 -05:00
J. Nick Koston
a36af1bfac s3 fixes 2025-06-12 10:59:40 -05:00
J. Nick Koston
8b6aa319bf s3 fixes 2025-06-12 10:57:46 -05:00
J. Nick Koston
a16d321e1a downgrade logging 2025-06-12 10:38:47 -05:00
J. Nick Koston
74e70278e2 fixes 2025-06-12 10:34:59 -05:00
J. Nick Koston
1332e24a2c fixes 2025-06-12 10:31:13 -05:00
J. Nick Koston
5ab78ec461 fixes 2025-06-12 10:30:58 -05:00
J. Nick Koston
ce701d3c31 fixes 2025-06-12 10:29:11 -05:00
J. Nick Koston
5fca1be44d fixes 2025-06-12 10:27:22 -05:00
J. Nick Koston
0bd4c333bd cleanup 2025-06-12 10:21:41 -05:00
J. Nick Koston
c6ed880732 fixes 2025-06-12 10:19:25 -05:00
J. Nick Koston
da0f3c6cce fixes 2025-06-12 10:12:56 -05:00
J. Nick Koston
e5d12d346a fixes 2025-06-12 10:08:29 -05:00
J. Nick Koston
478e2e726b fixes 2025-06-12 10:01:35 -05:00
J. Nick Koston
dbdac3707b fixes 2025-06-12 10:00:49 -05:00
J. Nick Koston
bd89a88e34 fixes 2025-06-12 09:23:38 -05:00
J. Nick Koston
d322d83745 fixes 2025-06-12 09:21:03 -05:00
J. Nick Koston
463a581ab9 DEBUG! 2025-06-12 00:56:42 -05:00
J. Nick Koston
eae4bd222a track pads 2025-06-11 23:29:00 -05:00
J. Nick Koston
a7bb7fc14d fix 2025-06-11 22:55:15 -05:00
J. Nick Koston
c047aa47eb use ll for all 2025-06-11 22:46:40 -05:00
J. Nick Koston
61bca56316 try touch_ll_read_raw_data 2025-06-11 22:43:41 -05:00
J. Nick Koston
9a37323eb8 Use interrupt based approach for esp32_touch 2025-06-11 22:32:25 -05:00
J. Nick Koston
99a54369bf Merge remote-tracking branch 'upstream/dev' into loop_runtime_stats 2025-06-11 22:01:22 -05:00
J. Nick Koston
f7533dfc5c review 2025-06-11 16:25:31 -05:00
J. Nick Koston
ee7d95272d lets be sure 2025-06-11 13:32:55 -05:00
J. Nick Koston
2b9b1d12e6 lets be sure 2025-06-11 13:32:47 -05:00
J. Nick Koston
2cbb5c7d8e fix error 2025-06-11 13:16:44 -05:00
J. Nick Koston
9686c7babe Merge branch 'dev' into ble_events 2025-06-11 13:09:32 -05:00
J. Nick Koston
66bd4c96c4 safety 2025-06-11 13:05:30 -05:00
J. Nick Koston
dc47faa4b6 safety 2025-06-11 13:05:01 -05:00
J. Nick Koston
55ee0b116d lint 2025-06-11 13:03:50 -05:00
J. Nick Koston
c6957c08bc lint 2025-06-11 13:02:08 -05:00
J. Nick Koston
8fe6a323d8 remove workaround 2025-06-11 13:00:55 -05:00
J. Nick Koston
8e51590c32 remove workaround 2025-06-11 12:59:57 -05:00
J. Nick Koston
ae066d5627 cleanup 2025-06-11 11:55:28 -05:00
J. Nick Koston
6760279916 cleanup compacted code 2025-06-11 11:51:43 -05:00
J. Nick Koston
3c208050b0 comments 2025-06-11 11:47:34 -05:00
J. Nick Koston
bbc7c9fb37 dry 2025-06-11 11:46:17 -05:00
J. Nick Koston
e1c3862586 preen 2025-06-11 11:36:50 -05:00
J. Nick Koston
c24b7cb7bd v->d 2025-06-11 11:34:30 -05:00
J. Nick Koston
c91e16549d lint 2025-06-11 11:27:13 -05:00
J. Nick Koston
6e70aca458 wip 2025-06-11 11:23:13 -05:00
J. Nick Koston
d9ffd0ac8e wip 2025-06-11 11:22:01 -05:00
J. Nick Koston
4641f73d19 comments 2025-06-11 11:19:36 -05:00
J. Nick Koston
9f0051c21f cleanup 2025-06-11 11:17:10 -05:00
J. Nick Koston
0331cb09e8 reduce 2025-06-11 11:17:01 -05:00
J. Nick Koston
2f8946f86c cleanup 2025-06-11 11:14:10 -05:00
J. Nick Koston
88a3df4008 cleanup 2025-06-11 11:13:34 -05:00
J. Nick Koston
0adf514bd6 preen 2025-06-11 11:09:19 -05:00
J. Nick Koston
a1b5a2abcb tweak 2025-06-11 10:58:56 -05:00
J. Nick Koston
068c62c6fe adjust 2025-06-11 10:43:48 -05:00
J. Nick Koston
0e9f14f969 wip 2025-06-11 10:20:18 -05:00
J. Nick Koston
78315fd388 preen 2025-06-11 10:08:30 -05:00
J. Nick Koston
0ab69002df preen 2025-06-11 10:05:15 -05:00
J. Nick Koston
1eec1239ec wip 2025-06-11 09:56:02 -05:00
J. Nick Koston
60cc4c4ed0 Merge branch 'dev' into drop_unique_id 2025-06-11 08:26:41 -05:00
J. Nick Koston
34c100e997 Remove legacy unique_id field from entities
These are no longer used in Home Assistant. This will be a breaking
change for MQTT for the sensors that defined custom unique ids.
2025-06-07 22:17:07 -05:00
Daniel Vikstrom
57f4067fbf Move fnv1a_32bit_hash to helpers 2025-06-02 14:42:39 +02:00
Daniel Vikstrom
f4a9221232 Change hash method 2025-06-02 08:31:06 +02:00
J. Nick Koston
3d4a75148d Merge branch 'dev' into multi_device 2025-05-31 10:27:31 -05:00
J. Nick Koston
c2c5bd844d Merge branch 'dev' into multi_device 2025-05-29 13:43:21 -05:00
J. Nick Koston
98a2f23024 Merge remote-tracking branch 'upstream/dev' into loop_runtime_stats 2025-05-29 11:04:14 -05:00
J. Nick Koston
c955897d1b Merge remote-tracking branch 'upstream/dev' into loop_runtime_stats 2025-05-27 11:39:45 -05:00
Daniel Vikstrom
9624efa21e Fix proto generation and clang 2025-05-22 14:18:46 +02:00
DanielV
831638210d Merge branch 'dev' into multi_device 2025-05-22 08:41:54 +02:00
J. Nick Koston
cfdb0925ce Merge branch 'dev' into loop_runtime_stats 2025-05-13 23:42:19 -05:00
J. Nick Koston
83db3eddd9 revert ota 2025-05-13 01:07:43 -05:00
J. Nick Koston
cc2c5a544e revert ota 2025-05-13 01:07:38 -05:00
J. Nick Koston
8fba8c2800 revert ota 2025-05-13 01:05:37 -05:00
J. Nick Koston
51d1da8460 revert ota 2025-05-13 01:04:09 -05:00
J. Nick Koston
2f1257056d revert 2025-05-13 01:02:00 -05:00
J. Nick Koston
2f8f6967bf fix ota 2025-05-13 00:55:19 -05:00
J. Nick Koston
246527e618 runtime stats 2025-05-13 00:54:05 -05:00
J. Nick Koston
3857cc9c83 runtime stats 2025-05-13 00:51:14 -05:00
Daniel Vikström
a59a8c563e Attempt fixing circular import by lazy import 2025-05-06 12:30:04 +02:00
Daniel Vikström
856829bcbb More namespace and import fixes 2025-05-06 12:05:45 +02:00
Daniel Vikström
dd2b931f61 Fix namespace error 2025-05-06 11:46:23 +02:00
Daniel Vikström
39beccbbb0 remove from CODEOWNERS 2025-05-06 10:50:09 +02:00
Daniel Vikström
ff626b428f Attempt moving it to esphome config section 2025-05-06 10:48:26 +02:00
Daniel Vikström
3915e1f012 Revert "Improve stability for unrelated test"
This reverts commit 3922950951.
2025-05-06 03:36:03 +02:00
Daniel Vikström
7b460b6224 Restore ci-api-proto.yml 2025-05-06 03:34:33 +02:00
Daniel Vikström
8fb8e79730 Fix clang 2025-05-06 03:20:22 +02:00
Daniel Vikström
79bbc475f4 Fix generated files and revert entity config to device_id 2025-05-06 03:05:00 +02:00
Daniel Vikström
cef023283b Fix generated files 2025-05-06 02:55:44 +02:00
Daniel Vikström
d4fda79ada Attempt to replace device_id:str with device_uid:uint32 2025-05-06 02:07:59 +02:00
DanielV
ff0bdcf4cd Merge branch 'dev' into multi_device 2025-05-06 00:48:23 +02:00
DanielV
bfbc313144 Merge branch 'dev' into multi_device 2025-04-22 14:28:51 +02:00
Daniel Vikström
31f2376f15 Rename ref in codegen 2025-04-22 14:03:07 +02:00
DanielV
f76ecb6604 Merge pull request #10 from dala318/multi_device_2
Sub Devices (all entities)
2025-04-22 08:49:28 +02:00
Daniel Vikström
298cc58433 Activate the rest of entities 2025-04-19 23:18:26 +02:00
Daniel Vikström
825c0593e1 Fix generated code after merge 2025-04-19 19:07:50 +02:00
DanielV
87ed1dc3e3 Merge branch 'dev' into multi_device 2025-04-19 18:58:09 +02:00
DanielV
67e9db021c Merge branch 'dev' into multi_device 2025-04-14 22:21:50 +02:00
Daniel Vikström
3922950951 Improve stability for unrelated test 2025-04-14 21:37:27 +02:00
DanielV
9c4aa0ba53 Merge branch 'dev' into multi_device 2025-04-11 13:19:52 +02:00
Daniel Vikström
f5f1651b31 Fix clang 2025-04-10 09:35:08 +02:00
Daniel Vikström
32f4e4ca13 Cleaning up 2025-04-09 19:20:28 +02:00
Daniel Vikström
962e0c4c33 Make it a Class but only use the id in entities 2025-04-09 19:09:31 +02:00
Daniel Vikström
2c01bc5795 Fix clang-tidy 2025-04-09 15:22:40 +02:00
Daniel Vikström
0651f7cb3c Work on sub-device creation 2025-04-09 01:39:24 +02:00
Daniel Vikström
01ac59ce2a Store proto with all additions but commented out 2025-04-09 01:18:42 +02:00
Daniel Vikström
c1fd597757 Add CODEOWNER 2025-04-09 01:12:14 +02:00
Daniel Vikström
e79e244eee Fix generated proto-files 2025-04-09 01:09:45 +02:00
Daniel Vikström
68ecc08111 Register device_id to entity and separate struct for all device info 2025-04-09 00:11:05 +02:00
Daniel Vikström
3b5fbc359f Formating updates 2025-04-08 22:21:11 +02:00
Daniel Vikström
583e5ea47f Add code-owner tag 2025-04-08 22:21:08 +02:00
Daniel Vikström
7b647c3fae Add a single test 2025-04-08 22:21:07 +02:00
Daniel Vikström
a8b76c617c Some basic chain working 2025-04-08 22:07:09 +02:00
Daniel Vikström
1bd8985dff Add a device component 2025-04-08 22:00:09 +02:00
Daniel Vikström
25b5a6c4ae Add device_id to entity_base 2025-04-08 22:00:06 +02:00
331 changed files with 12074 additions and 8683 deletions

View File

@@ -1 +1 @@
a3cdfc378d28b53b416a1d5bf0ab9077ee18867f0d39436ea8013cf5a4ead87a
07f621354fe1350ba51953c80273cd44a04aa44f15cc30bd7b8fe2a641427b7a

View File

@@ -1,4 +1,4 @@
[run]
omit =
omit =
esphome/components/*
tests/integration/*

92
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,92 @@
name: Report an issue with ESPHome
description: Report an issue with ESPHome.
body:
- type: markdown
attributes:
value: |
This issue form is for reporting bugs only!
If you have a feature request or enhancement, please [request them here instead][fr].
[fr]: https://github.com/orgs/esphome/discussions
- type: textarea
validations:
required: true
id: problem
attributes:
label: The problem
description: >-
Describe the issue you are experiencing here to communicate to the
maintainers. Tell us what you were trying to do and what happened.
Provide a clear and concise description of what the problem is.
- type: markdown
attributes:
value: |
## Environment
- type: input
id: version
validations:
required: true
attributes:
label: Which version of ESPHome has the issue?
description: >
ESPHome version like 1.19, 2025.6.0 or 2025.XX.X-dev.
- type: dropdown
validations:
required: true
id: installation
attributes:
label: What type of installation are you using?
options:
- Home Assistant Add-on
- Docker
- pip
- type: dropdown
validations:
required: true
id: platform
attributes:
label: What platform are you using?
options:
- ESP8266
- ESP32
- RP2040
- BK72XX
- RTL87XX
- LN882X
- Host
- Other
- type: input
id: component_name
attributes:
label: Component causing the issue
description: >
The name of the component or platform. For example, api/i2c or ultrasonic.
- type: markdown
attributes:
value: |
# Details
- type: textarea
id: config
attributes:
label: YAML Config
description: |
Include a complete YAML configuration file demonstrating the problem here. Preferably post the *entire* file - don't make assumptions about what is unimportant. However, if it's a large or complicated config then you will need to reduce it to the smallest possible file *that still demonstrates the problem*. If you don't provide enough information to *easily* reproduce the problem, it's unlikely your bug report will get any attention. Logs do not belong here, attach them below.
render: yaml
- type: textarea
id: logs
attributes:
label: Anything in the logs that might be useful for us?
description: For example, error message, or stack traces. Serial or USB logs are much more useful than WiFi logs.
render: txt
- type: textarea
id: additional
attributes:
label: Additional information
description: >
If you have any additional information for us, use the field below.
Please note, you can attach screenshots or screen recordings here, by
dragging and dropping files in the field below.

View File

@@ -1,15 +1,21 @@
---
blank_issues_enabled: false
contact_links:
- name: Issue Tracker
url: https://github.com/esphome/issues
about: Please create bug reports in the dedicated issue tracker.
- name: Feature Request Tracker
url: https://github.com/esphome/feature-requests
about: |
Please create feature requests in the dedicated feature request tracker.
- name: Report an issue with the ESPHome documentation
url: https://github.com/esphome/esphome-docs/issues/new/choose
about: Report an issue with the ESPHome documentation.
- name: Report an issue with the ESPHome web server
url: https://github.com/esphome/esphome-webserver/issues/new/choose
about: Report an issue with the ESPHome web server.
- name: Report an issue with the ESPHome Builder / Dashboard
url: https://github.com/esphome/dashboard/issues/new/choose
about: Report an issue with the ESPHome Builder / Dashboard.
- name: Report an issue with the ESPHome API client
url: https://github.com/esphome/aioesphomeapi/issues/new/choose
about: Report an issue with the ESPHome API client.
- name: Make a Feature Request
url: https://github.com/orgs/esphome/discussions
about: Please create feature requests in the dedicated feature request tracker.
- name: Frequently Asked Question
url: https://esphome.io/guides/faq.html
about: |
Please view the FAQ for common questions and what
to include in a bug report.
about: Please view the FAQ for common questions and what to include in a bug report.

View File

@@ -73,4 +73,3 @@ jobs:
});
}
}

View File

@@ -47,7 +47,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5.6.0
with:
python-version: "3.10"
python-version: "3.11"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.11.1

View File

@@ -20,8 +20,8 @@ permissions:
contents: read
env:
DEFAULT_PYTHON: "3.10"
PYUPGRADE_TARGET: "--py310-plus"
DEFAULT_PYTHON: "3.11"
PYUPGRADE_TARGET: "--py311-plus"
concurrency:
# yamllint disable-line rule:line-length
@@ -39,7 +39,7 @@ jobs:
uses: actions/checkout@v4.2.2
- name: Generate cache-key
id: cache-key
run: echo key="${{ hashFiles('requirements.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT
run: echo key="${{ hashFiles('requirements.txt', 'requirements_test.txt', '.pre-commit-config.yaml') }}" >> $GITHUB_OUTPUT
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python
uses: actions/setup-python@v5.6.0
@@ -58,56 +58,16 @@ jobs:
python -m venv venv
. venv/bin/activate
python --version
pip install -r requirements.txt -r requirements_test.txt
pip install -r requirements.txt -r requirements_test.txt pre-commit
pip install -e .
ruff:
name: Check ruff
runs-on: ubuntu-24.04
needs:
- common
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
- name: Restore Python
uses: ./.github/actions/restore-python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Run Ruff
run: |
. venv/bin/activate
ruff format esphome tests
- name: Suggested changes
run: script/ci-suggest-changes
if: always()
flake8:
name: Check flake8
runs-on: ubuntu-24.04
needs:
- common
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
- name: Restore Python
uses: ./.github/actions/restore-python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Run flake8
run: |
. venv/bin/activate
flake8 esphome
- name: Suggested changes
run: script/ci-suggest-changes
if: always()
pylint:
name: Check pylint
runs-on: ubuntu-24.04
needs:
- common
- determine-jobs
if: needs.determine-jobs.outputs.python-linters == 'true'
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
@@ -124,27 +84,6 @@ jobs:
run: script/ci-suggest-changes
if: always()
pyupgrade:
name: Check pyupgrade
runs-on: ubuntu-24.04
needs:
- common
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
- name: Restore Python
uses: ./.github/actions/restore-python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Run pyupgrade
run: |
. venv/bin/activate
pyupgrade ${{ env.PYUPGRADE_TARGET }} `find esphome -name "*.py" -type f`
- name: Suggested changes
run: script/ci-suggest-changes
if: always()
ci-custom:
name: Run script/ci-custom
runs-on: ubuntu-24.04
@@ -173,7 +112,6 @@ jobs:
fail-fast: false
matrix:
python-version:
- "3.10"
- "3.11"
- "3.12"
- "3.13"
@@ -189,14 +127,10 @@ jobs:
os: windows-latest
- python-version: "3.12"
os: windows-latest
- python-version: "3.10"
os: windows-latest
- python-version: "3.13"
os: macOS-latest
- python-version: "3.12"
os: macOS-latest
- python-version: "3.10"
os: macOS-latest
runs-on: ${{ matrix.os }}
needs:
- common
@@ -232,11 +166,52 @@ jobs:
path: venv
key: ${{ runner.os }}-${{ steps.restore-python.outputs.python-version }}-venv-${{ needs.common.outputs.cache-key }}
determine-jobs:
name: Determine which jobs to run
runs-on: ubuntu-24.04
needs:
- common
outputs:
integration-tests: ${{ steps.determine.outputs.integration-tests }}
clang-tidy: ${{ steps.determine.outputs.clang-tidy }}
python-linters: ${{ steps.determine.outputs.python-linters }}
changed-components: ${{ steps.determine.outputs.changed-components }}
component-test-count: ${{ steps.determine.outputs.component-test-count }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
with:
# Fetch enough history to find the merge base
fetch-depth: 2
- name: Restore Python
uses: ./.github/actions/restore-python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Determine which tests to run
id: determine
env:
GH_TOKEN: ${{ github.token }}
run: |
. venv/bin/activate
output=$(python script/determine-jobs.py)
echo "Test determination output:"
echo "$output" | jq
# Extract individual fields
echo "integration-tests=$(echo "$output" | jq -r '.integration_tests')" >> $GITHUB_OUTPUT
echo "clang-tidy=$(echo "$output" | jq -r '.clang_tidy')" >> $GITHUB_OUTPUT
echo "python-linters=$(echo "$output" | jq -r '.python_linters')" >> $GITHUB_OUTPUT
echo "changed-components=$(echo "$output" | jq -c '.changed_components')" >> $GITHUB_OUTPUT
echo "component-test-count=$(echo "$output" | jq -r '.component_test_count')" >> $GITHUB_OUTPUT
integration-tests:
name: Run integration tests
runs-on: ubuntu-latest
needs:
- common
- determine-jobs
if: needs.determine-jobs.outputs.integration-tests == 'true'
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
@@ -266,44 +241,13 @@ jobs:
. venv/bin/activate
pytest -vv --no-cov --tb=native -n auto tests/integration/
clang-format:
name: Check clang-format
runs-on: ubuntu-24.04
needs:
- common
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
- name: Restore Python
uses: ./.github/actions/restore-python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Install clang-format
run: |
. venv/bin/activate
pip install clang-format -c requirements_dev.txt
- name: Run clang-format
run: |
. venv/bin/activate
script/clang-format -i
git diff-index --quiet HEAD --
- name: Suggested changes
run: script/ci-suggest-changes
if: always()
clang-tidy:
name: ${{ matrix.name }}
runs-on: ubuntu-24.04
needs:
- common
- ruff
- ci-custom
- clang-format
- flake8
- pylint
- pytest
- pyupgrade
- determine-jobs
if: needs.determine-jobs.outputs.clang-tidy == 'true'
env:
GH_TOKEN: ${{ github.token }}
strategy:
@@ -411,50 +355,18 @@ jobs:
# yamllint disable-line rule:line-length
if: always()
list-components:
runs-on: ubuntu-24.04
needs:
- common
if: github.event_name == 'pull_request'
env:
GH_TOKEN: ${{ github.token }}
outputs:
components: ${{ steps.list-components.outputs.components }}
count: ${{ steps.list-components.outputs.count }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
- name: Restore Python
uses: ./.github/actions/restore-python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Find changed components
id: list-components
run: |
. venv/bin/activate
components=$(script/list-components.py --changed)
output_components=$(echo "$components" | jq -R -s -c 'split("\n")[:-1] | map(select(length > 0))')
count=$(echo "$output_components" | jq length)
echo "components=$output_components" >> $GITHUB_OUTPUT
echo "count=$count" >> $GITHUB_OUTPUT
echo "$count Components:"
echo "$output_components" | jq
test-build-components:
name: Component test ${{ matrix.file }}
runs-on: ubuntu-24.04
needs:
- common
- list-components
if: github.event_name == 'pull_request' && fromJSON(needs.list-components.outputs.count) > 0 && fromJSON(needs.list-components.outputs.count) < 100
- determine-jobs
if: github.event_name == 'pull_request' && fromJSON(needs.determine-jobs.outputs.component-test-count) > 0 && fromJSON(needs.determine-jobs.outputs.component-test-count) < 100
strategy:
fail-fast: false
max-parallel: 2
matrix:
file: ${{ fromJson(needs.list-components.outputs.components) }}
file: ${{ fromJson(needs.determine-jobs.outputs.changed-components) }}
steps:
- name: Install dependencies
run: |
@@ -482,8 +394,8 @@ jobs:
runs-on: ubuntu-24.04
needs:
- common
- list-components
if: github.event_name == 'pull_request' && fromJSON(needs.list-components.outputs.count) >= 100
- determine-jobs
if: github.event_name == 'pull_request' && fromJSON(needs.determine-jobs.outputs.component-test-count) >= 100
outputs:
matrix: ${{ steps.split.outputs.components }}
steps:
@@ -492,7 +404,7 @@ jobs:
- name: Split components into 20 groups
id: split
run: |
components=$(echo '${{ needs.list-components.outputs.components }}' | jq -c '.[]' | shuf | jq -s -c '[_nwise(20) | join(" ")]')
components=$(echo '${{ needs.determine-jobs.outputs.changed-components }}' | jq -c '.[]' | shuf | jq -s -c '[_nwise(20) | join(" ")]')
echo "components=$components" >> $GITHUB_OUTPUT
test-build-components-split:
@@ -500,9 +412,9 @@ jobs:
runs-on: ubuntu-24.04
needs:
- common
- list-components
- determine-jobs
- test-build-components-splitter
if: github.event_name == 'pull_request' && fromJSON(needs.list-components.outputs.count) >= 100
if: github.event_name == 'pull_request' && fromJSON(needs.determine-jobs.outputs.component-test-count) >= 100
strategy:
fail-fast: false
max-parallel: 4
@@ -539,24 +451,41 @@ jobs:
./script/test_build_components -e compile -c $component
done
pre-commit-ci-lite:
name: pre-commit.ci lite
runs-on: ubuntu-latest
needs:
- common
if: github.event_name == 'pull_request' && github.base_ref != 'beta' && github.base_ref != 'release'
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
- name: Restore Python
uses: ./.github/actions/restore-python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- uses: pre-commit/action@v3.0.1
env:
SKIP: pylint,clang-tidy-hash
- uses: pre-commit-ci/lite-action@v1.1.0
if: always()
ci-status:
name: CI Status
runs-on: ubuntu-24.04
needs:
- common
- ruff
- ci-custom
- clang-format
- flake8
- pylint
- pytest
- integration-tests
- pyupgrade
- clang-tidy
- list-components
- determine-jobs
- test-build-components
- test-build-components-splitter
- test-build-components-split
- pre-commit-ci-lite
if: always()
steps:
- name: Success

View File

@@ -96,7 +96,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5.6.0
with:
python-version: "3.10"
python-version: "3.11"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.11.1

View File

@@ -1,25 +0,0 @@
---
name: YAML lint
on:
push:
branches: [dev, beta, release]
paths:
- "**.yaml"
- "**.yml"
pull_request:
paths:
- "**.yaml"
- "**.yml"
jobs:
yamllint:
name: yamllint
runs-on: ubuntu-latest
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.2.2
- name: Run yamllint
uses: frenck/action-yamllint@v1.5.0
with:
strict: true

View File

@@ -1,10 +1,17 @@
---
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
ci:
autoupdate_commit_msg: 'pre-commit: autoupdate'
autoupdate_schedule: off # Disabled until ruff versions are synced between deps and pre-commit
# Skip hooks that have issues in pre-commit CI environment
skip: [pylint, clang-tidy-hash]
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.12.2
rev: v0.12.3
hooks:
# Run the linter.
- id: ruff
@@ -20,22 +27,25 @@ repos:
- pydocstyle==5.1.1
files: ^(esphome|tests)/.+\.py$
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.4.0
rev: v5.0.0
hooks:
- id: no-commit-to-branch
args:
- --branch=dev
- --branch=release
- --branch=beta
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/asottile/pyupgrade
rev: v3.20.0
hooks:
- id: pyupgrade
args: [--py310-plus]
args: [--py311-plus]
- repo: https://github.com/adrienverge/yamllint.git
rev: v1.37.1
hooks:
- id: yamllint
exclude: ^(\.clang-format|\.clang-tidy)$
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v13.0.1
hooks:

View File

@@ -324,6 +324,7 @@ esphome/components/nextion/text_sensor/* @senexcrenshaw
esphome/components/nfc/* @jesserockz @kbx81
esphome/components/noblex/* @AGalfra
esphome/components/npi19/* @bakerkj
esphome/components/nrf52/* @tomaszduda23
esphome/components/number/* @esphome/core
esphome/components/one_wire/* @ssieb
esphome/components/online_image/* @clydebarrow @guillempages
@@ -378,6 +379,7 @@ esphome/components/rp2040_pwm/* @jesserockz
esphome/components/rpi_dpi_rgb/* @clydebarrow
esphome/components/rtl87xx/* @kuba2k2
esphome/components/rtttl/* @glmnet
esphome/components/runtime_stats/* @bdraco
esphome/components/safe_mode/* @jsuanet @kbx81 @paulmonigatti
esphome/components/scd4x/* @martgras @sjtrny
esphome/components/script/* @esphome/core
@@ -535,5 +537,6 @@ esphome/components/xiaomi_xmwsdj04mmc/* @medusalix
esphome/components/xl9535/* @mreditor97
esphome/components/xpt2046/touchscreen/* @nielsnl68 @numo68
esphome/components/xxtea/* @clydebarrow
esphome/components/zephyr/* @tomaszduda23
esphome/components/zhlt01/* @cfeenstra1024
esphome/components/zio_ultrasonic/* @kahrendt

View File

@@ -458,13 +458,6 @@ def command_vscode(args):
def command_compile(args, config):
# Set memory analysis options in config
if args.analyze_memory:
config.setdefault(CONF_ESPHOME, {})["analyze_memory"] = True
if args.memory_report:
config.setdefault(CONF_ESPHOME, {})["memory_report_file"] = args.memory_report
exit_code = write_cpp(config)
if exit_code != 0:
return exit_code
@@ -844,17 +837,6 @@ def parse_args(argv):
help="Only generate source code, do not compile.",
action="store_true",
)
parser_compile.add_argument(
"--analyze-memory",
help="Analyze and display memory usage by component after compilation.",
action="store_true",
)
parser_compile.add_argument(
"--memory-report",
help="Save memory analysis report to a file (supports .json or .txt).",
type=str,
metavar="FILE",
)
parser_upload = subparsers.add_parser(
"upload",

File diff suppressed because it is too large Load Diff

View File

@@ -51,82 +51,83 @@ SAMPLING_MODES = {
"max": sampling_mode.MAX,
}
adc1_channel_t = cg.global_ns.enum("adc1_channel_t")
adc2_channel_t = cg.global_ns.enum("adc2_channel_t")
adc_unit_t = cg.global_ns.enum("adc_unit_t", is_class=True)
adc_channel_t = cg.global_ns.enum("adc_channel_t", is_class=True)
# pin to adc1 channel mapping
# https://github.com/espressif/esp-idf/blob/v4.4.8/components/driver/include/driver/adc.h
ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = {
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32/include/soc/adc_channel.h
VARIANT_ESP32: {
36: adc1_channel_t.ADC1_CHANNEL_0,
37: adc1_channel_t.ADC1_CHANNEL_1,
38: adc1_channel_t.ADC1_CHANNEL_2,
39: adc1_channel_t.ADC1_CHANNEL_3,
32: adc1_channel_t.ADC1_CHANNEL_4,
33: adc1_channel_t.ADC1_CHANNEL_5,
34: adc1_channel_t.ADC1_CHANNEL_6,
35: adc1_channel_t.ADC1_CHANNEL_7,
36: adc_channel_t.ADC_CHANNEL_0,
37: adc_channel_t.ADC_CHANNEL_1,
38: adc_channel_t.ADC_CHANNEL_2,
39: adc_channel_t.ADC_CHANNEL_3,
32: adc_channel_t.ADC_CHANNEL_4,
33: adc_channel_t.ADC_CHANNEL_5,
34: adc_channel_t.ADC_CHANNEL_6,
35: adc_channel_t.ADC_CHANNEL_7,
},
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c2/include/soc/adc_channel.h
VARIANT_ESP32C2: {
0: adc1_channel_t.ADC1_CHANNEL_0,
1: adc1_channel_t.ADC1_CHANNEL_1,
2: adc1_channel_t.ADC1_CHANNEL_2,
3: adc1_channel_t.ADC1_CHANNEL_3,
4: adc1_channel_t.ADC1_CHANNEL_4,
0: adc_channel_t.ADC_CHANNEL_0,
1: adc_channel_t.ADC_CHANNEL_1,
2: adc_channel_t.ADC_CHANNEL_2,
3: adc_channel_t.ADC_CHANNEL_3,
4: adc_channel_t.ADC_CHANNEL_4,
},
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c3/include/soc/adc_channel.h
VARIANT_ESP32C3: {
0: adc1_channel_t.ADC1_CHANNEL_0,
1: adc1_channel_t.ADC1_CHANNEL_1,
2: adc1_channel_t.ADC1_CHANNEL_2,
3: adc1_channel_t.ADC1_CHANNEL_3,
4: adc1_channel_t.ADC1_CHANNEL_4,
0: adc_channel_t.ADC_CHANNEL_0,
1: adc_channel_t.ADC_CHANNEL_1,
2: adc_channel_t.ADC_CHANNEL_2,
3: adc_channel_t.ADC_CHANNEL_3,
4: adc_channel_t.ADC_CHANNEL_4,
},
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c6/include/soc/adc_channel.h
VARIANT_ESP32C6: {
0: adc1_channel_t.ADC1_CHANNEL_0,
1: adc1_channel_t.ADC1_CHANNEL_1,
2: adc1_channel_t.ADC1_CHANNEL_2,
3: adc1_channel_t.ADC1_CHANNEL_3,
4: adc1_channel_t.ADC1_CHANNEL_4,
5: adc1_channel_t.ADC1_CHANNEL_5,
6: adc1_channel_t.ADC1_CHANNEL_6,
0: adc_channel_t.ADC_CHANNEL_0,
1: adc_channel_t.ADC_CHANNEL_1,
2: adc_channel_t.ADC_CHANNEL_2,
3: adc_channel_t.ADC_CHANNEL_3,
4: adc_channel_t.ADC_CHANNEL_4,
5: adc_channel_t.ADC_CHANNEL_5,
6: adc_channel_t.ADC_CHANNEL_6,
},
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32h2/include/soc/adc_channel.h
VARIANT_ESP32H2: {
1: adc1_channel_t.ADC1_CHANNEL_0,
2: adc1_channel_t.ADC1_CHANNEL_1,
3: adc1_channel_t.ADC1_CHANNEL_2,
4: adc1_channel_t.ADC1_CHANNEL_3,
5: adc1_channel_t.ADC1_CHANNEL_4,
1: adc_channel_t.ADC_CHANNEL_0,
2: adc_channel_t.ADC_CHANNEL_1,
3: adc_channel_t.ADC_CHANNEL_2,
4: adc_channel_t.ADC_CHANNEL_3,
5: adc_channel_t.ADC_CHANNEL_4,
},
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32s2/include/soc/adc_channel.h
VARIANT_ESP32S2: {
1: adc1_channel_t.ADC1_CHANNEL_0,
2: adc1_channel_t.ADC1_CHANNEL_1,
3: adc1_channel_t.ADC1_CHANNEL_2,
4: adc1_channel_t.ADC1_CHANNEL_3,
5: adc1_channel_t.ADC1_CHANNEL_4,
6: adc1_channel_t.ADC1_CHANNEL_5,
7: adc1_channel_t.ADC1_CHANNEL_6,
8: adc1_channel_t.ADC1_CHANNEL_7,
9: adc1_channel_t.ADC1_CHANNEL_8,
10: adc1_channel_t.ADC1_CHANNEL_9,
1: adc_channel_t.ADC_CHANNEL_0,
2: adc_channel_t.ADC_CHANNEL_1,
3: adc_channel_t.ADC_CHANNEL_2,
4: adc_channel_t.ADC_CHANNEL_3,
5: adc_channel_t.ADC_CHANNEL_4,
6: adc_channel_t.ADC_CHANNEL_5,
7: adc_channel_t.ADC_CHANNEL_6,
8: adc_channel_t.ADC_CHANNEL_7,
9: adc_channel_t.ADC_CHANNEL_8,
10: adc_channel_t.ADC_CHANNEL_9,
},
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32s3/include/soc/adc_channel.h
VARIANT_ESP32S3: {
1: adc1_channel_t.ADC1_CHANNEL_0,
2: adc1_channel_t.ADC1_CHANNEL_1,
3: adc1_channel_t.ADC1_CHANNEL_2,
4: adc1_channel_t.ADC1_CHANNEL_3,
5: adc1_channel_t.ADC1_CHANNEL_4,
6: adc1_channel_t.ADC1_CHANNEL_5,
7: adc1_channel_t.ADC1_CHANNEL_6,
8: adc1_channel_t.ADC1_CHANNEL_7,
9: adc1_channel_t.ADC1_CHANNEL_8,
10: adc1_channel_t.ADC1_CHANNEL_9,
1: adc_channel_t.ADC_CHANNEL_0,
2: adc_channel_t.ADC_CHANNEL_1,
3: adc_channel_t.ADC_CHANNEL_2,
4: adc_channel_t.ADC_CHANNEL_3,
5: adc_channel_t.ADC_CHANNEL_4,
6: adc_channel_t.ADC_CHANNEL_5,
7: adc_channel_t.ADC_CHANNEL_6,
8: adc_channel_t.ADC_CHANNEL_7,
9: adc_channel_t.ADC_CHANNEL_8,
10: adc_channel_t.ADC_CHANNEL_9,
},
}
@@ -135,24 +136,24 @@ ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = {
ESP32_VARIANT_ADC2_PIN_TO_CHANNEL = {
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32/include/soc/adc_channel.h
VARIANT_ESP32: {
4: adc2_channel_t.ADC2_CHANNEL_0,
0: adc2_channel_t.ADC2_CHANNEL_1,
2: adc2_channel_t.ADC2_CHANNEL_2,
15: adc2_channel_t.ADC2_CHANNEL_3,
13: adc2_channel_t.ADC2_CHANNEL_4,
12: adc2_channel_t.ADC2_CHANNEL_5,
14: adc2_channel_t.ADC2_CHANNEL_6,
27: adc2_channel_t.ADC2_CHANNEL_7,
25: adc2_channel_t.ADC2_CHANNEL_8,
26: adc2_channel_t.ADC2_CHANNEL_9,
4: adc_channel_t.ADC_CHANNEL_0,
0: adc_channel_t.ADC_CHANNEL_1,
2: adc_channel_t.ADC_CHANNEL_2,
15: adc_channel_t.ADC_CHANNEL_3,
13: adc_channel_t.ADC_CHANNEL_4,
12: adc_channel_t.ADC_CHANNEL_5,
14: adc_channel_t.ADC_CHANNEL_6,
27: adc_channel_t.ADC_CHANNEL_7,
25: adc_channel_t.ADC_CHANNEL_8,
26: adc_channel_t.ADC_CHANNEL_9,
},
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c2/include/soc/adc_channel.h
VARIANT_ESP32C2: {
5: adc2_channel_t.ADC2_CHANNEL_0,
5: adc_channel_t.ADC_CHANNEL_0,
},
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c3/include/soc/adc_channel.h
VARIANT_ESP32C3: {
5: adc2_channel_t.ADC2_CHANNEL_0,
5: adc_channel_t.ADC_CHANNEL_0,
},
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c6/include/soc/adc_channel.h
VARIANT_ESP32C6: {}, # no ADC2
@@ -160,29 +161,29 @@ ESP32_VARIANT_ADC2_PIN_TO_CHANNEL = {
VARIANT_ESP32H2: {}, # no ADC2
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32s2/include/soc/adc_channel.h
VARIANT_ESP32S2: {
11: adc2_channel_t.ADC2_CHANNEL_0,
12: adc2_channel_t.ADC2_CHANNEL_1,
13: adc2_channel_t.ADC2_CHANNEL_2,
14: adc2_channel_t.ADC2_CHANNEL_3,
15: adc2_channel_t.ADC2_CHANNEL_4,
16: adc2_channel_t.ADC2_CHANNEL_5,
17: adc2_channel_t.ADC2_CHANNEL_6,
18: adc2_channel_t.ADC2_CHANNEL_7,
19: adc2_channel_t.ADC2_CHANNEL_8,
20: adc2_channel_t.ADC2_CHANNEL_9,
11: adc_channel_t.ADC_CHANNEL_0,
12: adc_channel_t.ADC_CHANNEL_1,
13: adc_channel_t.ADC_CHANNEL_2,
14: adc_channel_t.ADC_CHANNEL_3,
15: adc_channel_t.ADC_CHANNEL_4,
16: adc_channel_t.ADC_CHANNEL_5,
17: adc_channel_t.ADC_CHANNEL_6,
18: adc_channel_t.ADC_CHANNEL_7,
19: adc_channel_t.ADC_CHANNEL_8,
20: adc_channel_t.ADC_CHANNEL_9,
},
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32s3/include/soc/adc_channel.h
VARIANT_ESP32S3: {
11: adc2_channel_t.ADC2_CHANNEL_0,
12: adc2_channel_t.ADC2_CHANNEL_1,
13: adc2_channel_t.ADC2_CHANNEL_2,
14: adc2_channel_t.ADC2_CHANNEL_3,
15: adc2_channel_t.ADC2_CHANNEL_4,
16: adc2_channel_t.ADC2_CHANNEL_5,
17: adc2_channel_t.ADC2_CHANNEL_6,
18: adc2_channel_t.ADC2_CHANNEL_7,
19: adc2_channel_t.ADC2_CHANNEL_8,
20: adc2_channel_t.ADC2_CHANNEL_9,
11: adc_channel_t.ADC_CHANNEL_0,
12: adc_channel_t.ADC_CHANNEL_1,
13: adc_channel_t.ADC_CHANNEL_2,
14: adc_channel_t.ADC_CHANNEL_3,
15: adc_channel_t.ADC_CHANNEL_4,
16: adc_channel_t.ADC_CHANNEL_5,
17: adc_channel_t.ADC_CHANNEL_6,
18: adc_channel_t.ADC_CHANNEL_7,
19: adc_channel_t.ADC_CHANNEL_8,
20: adc_channel_t.ADC_CHANNEL_9,
},
}

View File

@@ -3,12 +3,15 @@
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/voltage_sampler/voltage_sampler.h"
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/hal.h"
#ifdef USE_ESP32
#include <esp_adc_cal.h>
#include "driver/adc.h"
#endif // USE_ESP32
#include "esp_adc/adc_cali.h"
#include "esp_adc/adc_cali_scheme.h"
#include "esp_adc/adc_oneshot.h"
#include "hal/adc_types.h" // This defines ADC_CHANNEL_MAX
#endif // USE_ESP32
namespace esphome {
namespace adc {
@@ -49,36 +52,71 @@ class Aggregator {
class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler {
public:
#ifdef USE_ESP32
/// Set the attenuation for this pin. Only available on the ESP32.
void set_attenuation(adc_atten_t attenuation) { this->attenuation_ = attenuation; }
void set_channel1(adc1_channel_t channel) {
this->channel1_ = channel;
this->channel2_ = ADC2_CHANNEL_MAX;
}
void set_channel2(adc2_channel_t channel) {
this->channel2_ = channel;
this->channel1_ = ADC1_CHANNEL_MAX;
}
void set_autorange(bool autorange) { this->autorange_ = autorange; }
#endif // USE_ESP32
/// Update ADC values
/// Update the sensor's state by reading the current ADC value.
/// This method is called periodically based on the update interval.
void update() override;
/// Setup ADC
/// Set up the ADC sensor by initializing hardware and calibration parameters.
/// This method is called once during device initialization.
void setup() override;
/// Output the configuration details of the ADC sensor for debugging purposes.
/// This method is called during the ESPHome setup process to log the configuration.
void dump_config() override;
/// `HARDWARE_LATE` setup priority
/// 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;
/// Set the GPIO pin to be used by the ADC sensor.
/// @param pin Pointer to an InternalGPIOPin representing the ADC input pin.
void set_pin(InternalGPIOPin *pin) { this->pin_ = pin; }
/// Enable or disable the output of raw ADC values (unprocessed data).
/// @param output_raw Boolean indicating whether to output raw ADC values (true) or processed values (false).
void set_output_raw(bool output_raw) { this->output_raw_ = output_raw; }
/// Set the number of samples to be taken for ADC readings to improve accuracy.
/// A higher sample count reduces noise but increases the reading time.
/// @param sample_count The number of samples (e.g., 1, 4, 8).
void set_sample_count(uint8_t sample_count);
/// Set the sampling mode for how multiple ADC samples are combined into a single measurement.
///
/// When multiple samples are taken (controlled by set_sample_count), they can be combined
/// in one of three ways:
/// - SamplingMode::AVG: Compute the average (default)
/// - SamplingMode::MIN: Use the lowest sample value
/// - SamplingMode::MAX: Use the highest sample value
/// @param sampling_mode The desired sampling mode to use for aggregating ADC samples.
void set_sampling_mode(SamplingMode sampling_mode);
/// Perform a single ADC sampling operation and return the measured value.
/// This function handles raw readings, calibration, and averaging as needed.
/// @return The sampled value as a float.
float sample() override;
#ifdef USE_ESP8266
std::string unique_id() override;
#endif // USE_ESP8266
#ifdef USE_ESP32
/// Set the ADC attenuation level to adjust the input voltage range.
/// This determines how the ADC interprets input voltages, allowing for greater precision
/// or the ability to measure higher voltages depending on the chosen attenuation level.
/// @param attenuation The desired ADC attenuation level (e.g., ADC_ATTEN_DB_0, ADC_ATTEN_DB_11).
void set_attenuation(adc_atten_t attenuation) { this->attenuation_ = attenuation; }
/// Configure the ADC to use a specific channel on ADC1.
/// This sets the channel for single-shot or continuous ADC measurements.
/// @param channel The ADC1 channel to configure, such as ADC_CHANNEL_0, ADC_CHANNEL_3, etc.
void set_channel(adc_unit_t unit, adc_channel_t channel) {
this->adc_unit_ = unit;
this->channel_ = channel;
}
/// Set whether autoranging should be enabled for the ADC.
/// Autoranging automatically adjusts the attenuation level to handle a wide range of input voltages.
/// @param autorange Boolean indicating whether to enable autoranging.
void set_autorange(bool autorange) { this->autorange_ = autorange; }
#endif // USE_ESP32
#ifdef USE_RP2040
void set_is_temperature() { this->is_temperature_ = true; }
@@ -90,17 +128,28 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
InternalGPIOPin *pin_;
SamplingMode sampling_mode_{SamplingMode::AVG};
#ifdef USE_ESP32
float sample_autorange_();
float sample_fixed_attenuation_();
bool autorange_{false};
adc_oneshot_unit_handle_t adc_handle_{nullptr};
adc_cali_handle_t calibration_handle_{nullptr};
adc_atten_t attenuation_{ADC_ATTEN_DB_0};
adc_channel_t channel_;
adc_unit_t adc_unit_;
struct SetupFlags {
uint8_t init_complete : 1;
uint8_t config_complete : 1;
uint8_t handle_init_complete : 1;
uint8_t calibration_complete : 1;
uint8_t reserved : 4;
} setup_flags_{};
static adc_oneshot_unit_handle_t shared_adc_handles[2];
#endif // USE_ESP32
#ifdef USE_RP2040
bool is_temperature_{false};
#endif // USE_RP2040
#ifdef USE_ESP32
adc_atten_t attenuation_{ADC_ATTEN_DB_0};
adc1_channel_t channel1_{ADC1_CHANNEL_MAX};
adc2_channel_t channel2_{ADC2_CHANNEL_MAX};
bool autorange_{false};
esp_adc_cal_characteristics_t cal_characteristics_[SOC_ADC_ATTEN_NUM] = {};
#endif // USE_ESP32
};
} // namespace adc

View File

@@ -8,145 +8,308 @@ namespace adc {
static const char *const TAG = "adc.esp32";
static const adc_bits_width_t ADC_WIDTH_MAX_SOC_BITS = static_cast<adc_bits_width_t>(ADC_WIDTH_MAX - 1);
adc_oneshot_unit_handle_t ADCSensor::shared_adc_handles[2] = {nullptr, nullptr};
#ifndef SOC_ADC_RTC_MAX_BITWIDTH
#if USE_ESP32_VARIANT_ESP32S2
static const int32_t SOC_ADC_RTC_MAX_BITWIDTH = 13;
#else
static const int32_t SOC_ADC_RTC_MAX_BITWIDTH = 12;
#endif // USE_ESP32_VARIANT_ESP32S2
#endif // SOC_ADC_RTC_MAX_BITWIDTH
static const int ADC_MAX = (1 << SOC_ADC_RTC_MAX_BITWIDTH) - 1;
static const int ADC_HALF = (1 << SOC_ADC_RTC_MAX_BITWIDTH) >> 1;
void ADCSensor::setup() {
ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->get_name().c_str());
if (this->channel1_ != ADC1_CHANNEL_MAX) {
adc1_config_width(ADC_WIDTH_MAX_SOC_BITS);
if (!this->autorange_) {
adc1_config_channel_atten(this->channel1_, this->attenuation_);
}
} else if (this->channel2_ != ADC2_CHANNEL_MAX) {
if (!this->autorange_) {
adc2_config_channel_atten(this->channel2_, this->attenuation_);
}
}
for (int32_t i = 0; i <= ADC_ATTEN_DB_12_COMPAT; i++) {
auto adc_unit = this->channel1_ != ADC1_CHANNEL_MAX ? ADC_UNIT_1 : ADC_UNIT_2;
auto cal_value = esp_adc_cal_characterize(adc_unit, (adc_atten_t) i, ADC_WIDTH_MAX_SOC_BITS,
1100, // default vref
&this->cal_characteristics_[i]);
switch (cal_value) {
case ESP_ADC_CAL_VAL_EFUSE_VREF:
ESP_LOGV(TAG, "Using eFuse Vref for calibration");
break;
case ESP_ADC_CAL_VAL_EFUSE_TP:
ESP_LOGV(TAG, "Using two-point eFuse Vref for calibration");
break;
case ESP_ADC_CAL_VAL_DEFAULT_VREF:
default:
break;
}
const LogString *attenuation_to_str(adc_atten_t attenuation) {
switch (attenuation) {
case ADC_ATTEN_DB_0:
return LOG_STR("0 dB");
case ADC_ATTEN_DB_2_5:
return LOG_STR("2.5 dB");
case ADC_ATTEN_DB_6:
return LOG_STR("6 dB");
case ADC_ATTEN_DB_12_COMPAT:
return LOG_STR("12 dB");
default:
return LOG_STR("Unknown Attenuation");
}
}
void ADCSensor::dump_config() {
static const char *const ATTEN_AUTO_STR = "auto";
static const char *const ATTEN_0DB_STR = "0 db";
static const char *const ATTEN_2_5DB_STR = "2.5 db";
static const char *const ATTEN_6DB_STR = "6 db";
static const char *const ATTEN_12DB_STR = "12 db";
const char *atten_str = ATTEN_AUTO_STR;
const LogString *adc_unit_to_str(adc_unit_t unit) {
switch (unit) {
case ADC_UNIT_1:
return LOG_STR("ADC1");
case ADC_UNIT_2:
return LOG_STR("ADC2");
default:
return LOG_STR("Unknown ADC Unit");
}
}
LOG_SENSOR("", "ADC Sensor", this);
LOG_PIN(" Pin: ", this->pin_);
if (!this->autorange_) {
switch (this->attenuation_) {
case ADC_ATTEN_DB_0:
atten_str = ATTEN_0DB_STR;
break;
case ADC_ATTEN_DB_2_5:
atten_str = ATTEN_2_5DB_STR;
break;
case ADC_ATTEN_DB_6:
atten_str = ATTEN_6DB_STR;
break;
case ADC_ATTEN_DB_12_COMPAT:
atten_str = ATTEN_12DB_STR;
break;
default: // This is to satisfy the unused ADC_ATTEN_MAX
break;
void ADCSensor::setup() {
ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->get_name().c_str());
// Check if another sensor already initialized this ADC unit
if (ADCSensor::shared_adc_handles[this->adc_unit_] == nullptr) {
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_ESP32C6 || USE_ESP32_VARIANT_ESP32H2
init_config.clk_src = ADC_DIGI_CLK_SRC_DEFAULT;
#endif // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || 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);
this->mark_failed();
return;
}
}
this->adc_handle_ = ADCSensor::shared_adc_handles[this->adc_unit_];
this->setup_flags_.handle_init_complete = true;
adc_oneshot_chan_cfg_t config = {
.atten = this->attenuation_,
.bitwidth = ADC_BITWIDTH_DEFAULT,
};
esp_err_t err = adc_oneshot_config_channel(this->adc_handle_, this->channel_, &config);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error configuring channel: %d", err);
this->mark_failed();
return;
}
this->setup_flags_.config_complete = true;
// Initialize ADC calibration
if (this->calibration_handle_ == nullptr) {
adc_cali_handle_t handle = nullptr;
esp_err_t err;
#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32H2
// RISC-V variants 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_;
#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
cali_config.unit_id = this->adc_unit_;
cali_config.atten = this->attenuation_;
cali_config.bitwidth = ADC_BITWIDTH_DEFAULT;
err = adc_cali_create_scheme_curve_fitting(&cali_config, &handle);
if (err == ESP_OK) {
this->calibration_handle_ = handle;
this->setup_flags_.calibration_complete = true;
ESP_LOGV(TAG, "Using curve fitting calibration");
} else {
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
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)
.default_vref = 1100, // Default reference voltage in mV
#endif // !defined(USE_ESP32_VARIANT_ESP32S2)
};
err = adc_cali_create_scheme_line_fitting(&cali_config, &handle);
if (err == ESP_OK) {
this->calibration_handle_ = handle;
this->setup_flags_.calibration_complete = true;
ESP_LOGV(TAG, "Using line fitting calibration");
} else {
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 || ESP32C6 || ESP32S3 || ESP32H2
}
this->setup_flags_.init_complete = true;
}
void ADCSensor::dump_config() {
LOG_SENSOR("", "ADC Sensor", this);
LOG_PIN(" Pin: ", this->pin_);
ESP_LOGCONFIG(TAG,
" Attenuation: %s\n"
" Samples: %i\n"
" Channel: %d\n"
" Unit: %s\n"
" Attenuation: %s\n"
" Samples: %i\n"
" Sampling mode: %s",
atten_str, this->sample_count_, LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
this->channel_, LOG_STR_ARG(adc_unit_to_str(this->adc_unit_)),
this->autorange_ ? "Auto" : LOG_STR_ARG(attenuation_to_str(this->attenuation_)), this->sample_count_,
LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
ESP_LOGCONFIG(
TAG,
" Setup Status:\n"
" Handle Init: %s\n"
" Config: %s\n"
" Calibration: %s\n"
" Overall Init: %s",
this->setup_flags_.handle_init_complete ? "OK" : "FAILED", this->setup_flags_.config_complete ? "OK" : "FAILED",
this->setup_flags_.calibration_complete ? "OK" : "FAILED", this->setup_flags_.init_complete ? "OK" : "FAILED");
LOG_UPDATE_INTERVAL(this);
}
float ADCSensor::sample() {
if (!this->autorange_) {
auto aggr = Aggregator(this->sampling_mode_);
if (this->autorange_) {
return this->sample_autorange_();
} else {
return this->sample_fixed_attenuation_();
}
}
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
int raw = -1;
if (this->channel1_ != ADC1_CHANNEL_MAX) {
raw = adc1_get_raw(this->channel1_);
} else if (this->channel2_ != ADC2_CHANNEL_MAX) {
adc2_get_raw(this->channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw);
}
if (raw == -1) {
return NAN;
}
float ADCSensor::sample_fixed_attenuation_() {
auto aggr = Aggregator(this->sampling_mode_);
aggr.add_sample(raw);
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
int raw;
esp_err_t err = adc_oneshot_read(this->adc_handle_, this->channel_, &raw);
if (err != ESP_OK) {
ESP_LOGW(TAG, "ADC read failed with error %d", err);
continue;
}
if (this->output_raw_) {
return aggr.aggregate();
if (raw == -1) {
ESP_LOGW(TAG, "Invalid ADC reading");
continue;
}
uint32_t mv =
esp_adc_cal_raw_to_voltage(aggr.aggregate(), &this->cal_characteristics_[(int32_t) this->attenuation_]);
return mv / 1000.0f;
aggr.add_sample(raw);
}
int raw12 = ADC_MAX, raw6 = ADC_MAX, raw2 = ADC_MAX, raw0 = ADC_MAX;
uint32_t final_value = aggr.aggregate();
if (this->channel1_ != ADC1_CHANNEL_MAX) {
adc1_config_channel_atten(this->channel1_, ADC_ATTEN_DB_12_COMPAT);
raw12 = adc1_get_raw(this->channel1_);
if (raw12 < ADC_MAX) {
adc1_config_channel_atten(this->channel1_, ADC_ATTEN_DB_6);
raw6 = adc1_get_raw(this->channel1_);
if (raw6 < ADC_MAX) {
adc1_config_channel_atten(this->channel1_, ADC_ATTEN_DB_2_5);
raw2 = adc1_get_raw(this->channel1_);
if (raw2 < ADC_MAX) {
adc1_config_channel_atten(this->channel1_, ADC_ATTEN_DB_0);
raw0 = adc1_get_raw(this->channel1_);
}
if (this->output_raw_) {
return final_value;
}
if (this->calibration_handle_ != nullptr) {
int voltage_mv;
esp_err_t err = adc_cali_raw_to_voltage(this->calibration_handle_, final_value, &voltage_mv);
if (err == ESP_OK) {
return voltage_mv / 1000.0f;
} else {
ESP_LOGW(TAG, "ADC calibration conversion failed with error %d, disabling calibration", err);
if (this->calibration_handle_ != nullptr) {
#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32H2
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 || ESP32C6 || ESP32S3 || ESP32H2
this->calibration_handle_ = nullptr;
}
}
} else if (this->channel2_ != ADC2_CHANNEL_MAX) {
adc2_config_channel_atten(this->channel2_, ADC_ATTEN_DB_12_COMPAT);
adc2_get_raw(this->channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw12);
if (raw12 < ADC_MAX) {
adc2_config_channel_atten(this->channel2_, ADC_ATTEN_DB_6);
adc2_get_raw(this->channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw6);
if (raw6 < ADC_MAX) {
adc2_config_channel_atten(this->channel2_, ADC_ATTEN_DB_2_5);
adc2_get_raw(this->channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw2);
if (raw2 < ADC_MAX) {
adc2_config_channel_atten(this->channel2_, ADC_ATTEN_DB_0);
adc2_get_raw(this->channel2_, ADC_WIDTH_MAX_SOC_BITS, &raw0);
}
}
return final_value * 3.3f / 4095.0f;
}
float ADCSensor::sample_autorange_() {
// Auto-range mode
auto read_atten = [this](adc_atten_t atten) -> std::pair<int, float> {
// First reconfigure the attenuation for this reading
adc_oneshot_chan_cfg_t config = {
.atten = atten,
.bitwidth = ADC_BITWIDTH_DEFAULT,
};
esp_err_t err = adc_oneshot_config_channel(this->adc_handle_, this->channel_, &config);
if (err != ESP_OK) {
ESP_LOGW(TAG, "Error configuring ADC channel for autorange: %d", err);
return {-1, 0.0f};
}
// Need to recalibrate for the new attenuation
if (this->calibration_handle_ != nullptr) {
// Delete old calibration handle
#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32H2
adc_cali_delete_scheme_curve_fitting(this->calibration_handle_);
#else
adc_cali_delete_scheme_line_fitting(this->calibration_handle_);
#endif
this->calibration_handle_ = nullptr;
}
// Create new calibration handle for this attenuation
adc_cali_handle_t handle = nullptr;
#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32H2
adc_cali_curve_fitting_config_t cali_config = {};
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
cali_config.chan = this->channel_;
#endif
cali_config.unit_id = this->adc_unit_;
cali_config.atten = atten;
cali_config.bitwidth = ADC_BITWIDTH_DEFAULT;
err = adc_cali_create_scheme_curve_fitting(&cali_config, &handle);
#else
adc_cali_line_fitting_config_t cali_config = {
.unit_id = this->adc_unit_,
.atten = atten,
.bitwidth = ADC_BITWIDTH_DEFAULT,
#if !defined(USE_ESP32_VARIANT_ESP32S2)
.default_vref = 1100,
#endif
};
err = adc_cali_create_scheme_line_fitting(&cali_config, &handle);
#endif
int raw;
err = adc_oneshot_read(this->adc_handle_, this->channel_, &raw);
if (err != ESP_OK) {
ESP_LOGW(TAG, "ADC read failed in autorange with error %d", err);
if (handle != nullptr) {
#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32H2
adc_cali_delete_scheme_curve_fitting(handle);
#else
adc_cali_delete_scheme_line_fitting(handle);
#endif
}
return {-1, 0.0f};
}
float voltage = 0.0f;
if (handle != nullptr) {
int voltage_mv;
err = adc_cali_raw_to_voltage(handle, raw, &voltage_mv);
if (err == ESP_OK) {
voltage = voltage_mv / 1000.0f;
} else {
voltage = raw * 3.3f / 4095.0f;
}
// Clean up calibration handle
#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32H2
adc_cali_delete_scheme_curve_fitting(handle);
#else
adc_cali_delete_scheme_line_fitting(handle);
#endif
} else {
voltage = raw * 3.3f / 4095.0f;
}
return {raw, voltage};
};
auto [raw12, mv12] = read_atten(ADC_ATTEN_DB_12);
if (raw12 == -1) {
ESP_LOGE(TAG, "Failed to read ADC in autorange mode");
return NAN;
}
int raw6 = 4095, raw2 = 4095, raw0 = 4095;
float mv6 = 0, mv2 = 0, mv0 = 0;
if (raw12 < 4095) {
auto [raw6_val, mv6_val] = read_atten(ADC_ATTEN_DB_6);
raw6 = raw6_val;
mv6 = mv6_val;
if (raw6 < 4095 && raw6 != -1) {
auto [raw2_val, mv2_val] = read_atten(ADC_ATTEN_DB_2_5);
raw2 = raw2_val;
mv2 = mv2_val;
if (raw2 < 4095 && raw2 != -1) {
auto [raw0_val, mv0_val] = read_atten(ADC_ATTEN_DB_0);
raw0 = raw0_val;
mv0 = mv0_val;
}
}
}
@@ -155,19 +318,19 @@ float ADCSensor::sample() {
return NAN;
}
uint32_t mv12 = esp_adc_cal_raw_to_voltage(raw12, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_12_COMPAT]);
uint32_t mv6 = esp_adc_cal_raw_to_voltage(raw6, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_6]);
uint32_t mv2 = esp_adc_cal_raw_to_voltage(raw2, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_2_5]);
uint32_t mv0 = esp_adc_cal_raw_to_voltage(raw0, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_0]);
uint32_t c12 = std::min(raw12, ADC_HALF);
uint32_t c6 = ADC_HALF - std::abs(raw6 - ADC_HALF);
uint32_t c2 = ADC_HALF - std::abs(raw2 - ADC_HALF);
uint32_t c0 = std::min(ADC_MAX - raw0, ADC_HALF);
const int adc_half = 2048;
uint32_t c12 = std::min(raw12, adc_half);
uint32_t c6 = adc_half - std::abs(raw6 - adc_half);
uint32_t c2 = adc_half - std::abs(raw2 - adc_half);
uint32_t c0 = std::min(4095 - raw0, adc_half);
uint32_t csum = c12 + c6 + c2 + c0;
uint32_t mv_scaled = (mv12 * c12) + (mv6 * c6) + (mv2 * c2) + (mv0 * c0);
return mv_scaled / (float) (csum * 1000U);
if (csum == 0) {
ESP_LOGE(TAG, "Invalid weight sum in autorange calculation");
return NAN;
}
return (mv12 * c12 + mv6 * c6 + mv2 * c2 + mv0 * c0) / csum;
}
} // namespace adc

View File

@@ -56,8 +56,6 @@ float ADCSensor::sample() {
return aggr.aggregate() / 1024.0f;
}
std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; }
} // namespace adc
} // namespace esphome

View File

@@ -10,13 +10,11 @@ from esphome.const import (
CONF_NUMBER,
CONF_PIN,
CONF_RAW,
CONF_WIFI,
DEVICE_CLASS_VOLTAGE,
STATE_CLASS_MEASUREMENT,
UNIT_VOLT,
)
from esphome.core import CORE
import esphome.final_validate as fv
from . import (
ATTENUATION_MODES,
@@ -24,6 +22,7 @@ from . import (
ESP32_VARIANT_ADC2_PIN_TO_CHANNEL,
SAMPLING_MODES,
adc_ns,
adc_unit_t,
validate_adc_pin,
)
@@ -57,21 +56,6 @@ def validate_config(config):
return config
def final_validate_config(config):
if CORE.is_esp32:
variant = get_esp32_variant()
if (
CONF_WIFI in fv.full_config.get()
and config[CONF_PIN][CONF_NUMBER]
in ESP32_VARIANT_ADC2_PIN_TO_CHANNEL[variant]
):
raise cv.Invalid(
f"{variant} doesn't support ADC on this pin when Wi-Fi is configured"
)
return config
ADCSensor = adc_ns.class_(
"ADCSensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler
)
@@ -99,8 +83,6 @@ CONFIG_SCHEMA = cv.All(
validate_config,
)
FINAL_VALIDATE_SCHEMA = final_validate_config
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
@@ -119,13 +101,13 @@ async def to_code(config):
cg.add(var.set_sample_count(config[CONF_SAMPLES]))
cg.add(var.set_sampling_mode(config[CONF_SAMPLING_MODE]))
if attenuation := config.get(CONF_ATTENUATION):
if attenuation == "auto":
cg.add(var.set_autorange(cg.global_ns.true))
else:
cg.add(var.set_attenuation(attenuation))
if CORE.is_esp32:
if attenuation := config.get(CONF_ATTENUATION):
if attenuation == "auto":
cg.add(var.set_autorange(cg.global_ns.true))
else:
cg.add(var.set_attenuation(attenuation))
variant = get_esp32_variant()
pin_num = config[CONF_PIN][CONF_NUMBER]
if (
@@ -133,10 +115,10 @@ async def to_code(config):
and pin_num in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL[variant]
):
chan = ESP32_VARIANT_ADC1_PIN_TO_CHANNEL[variant][pin_num]
cg.add(var.set_channel1(chan))
cg.add(var.set_channel(adc_unit_t.ADC_UNIT_1, chan))
elif (
variant in ESP32_VARIANT_ADC2_PIN_TO_CHANNEL
and pin_num in ESP32_VARIANT_ADC2_PIN_TO_CHANNEL[variant]
):
chan = ESP32_VARIANT_ADC2_PIN_TO_CHANNEL[variant][pin_num]
cg.add(var.set_channel2(chan))
cg.add(var.set_channel(adc_unit_t.ADC_UNIT_2, chan))

View File

@@ -24,8 +24,9 @@ from esphome.const import (
CONF_TRIGGER_ID,
CONF_VARIABLES,
)
from esphome.core import coroutine_with_priority
from esphome.core import CORE, coroutine_with_priority
DOMAIN = "api"
DEPENDENCIES = ["network"]
AUTO_LOAD = ["socket"]
CODEOWNERS = ["@OttoWinter"]
@@ -51,6 +52,7 @@ SERVICE_ARG_NATIVE_TYPES = {
}
CONF_ENCRYPTION = "encryption"
CONF_BATCH_DELAY = "batch_delay"
CONF_CUSTOM_SERVICES = "custom_services"
def validate_encryption_key(value):
@@ -115,6 +117,7 @@ CONFIG_SCHEMA = cv.All(
cv.positive_time_period_milliseconds,
cv.Range(max=cv.TimePeriod(milliseconds=65535)),
),
cv.Optional(CONF_CUSTOM_SERVICES, default=False): cv.boolean,
cv.Optional(CONF_ON_CLIENT_CONNECTED): automation.validate_automation(
single=True
),
@@ -139,8 +142,11 @@ async def to_code(config):
cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT]))
cg.add(var.set_batch_delay(config[CONF_BATCH_DELAY]))
# Set USE_API_SERVICES if any services are enabled
if config.get(CONF_ACTIONS) or config[CONF_CUSTOM_SERVICES]:
cg.add_define("USE_API_SERVICES")
if actions := config.get(CONF_ACTIONS, []):
cg.add_define("USE_API_YAML_SERVICES")
for conf in actions:
template_args = []
func_args = []
@@ -317,7 +323,10 @@ async def api_connected_to_code(config, condition_id, template_arg, args):
def FILTER_SOURCE_FILES() -> list[str]:
"""Filter out api_pb2_dump.cpp when proto message dumping is not enabled."""
"""Filter out api_pb2_dump.cpp when proto message dumping is not enabled
and user_services.cpp when no services are defined."""
files_to_filter = []
# api_pb2_dump.cpp is only needed when HAS_PROTO_MESSAGE_DUMP is defined
# This is a particularly large file that still needs to be opened and read
# all the way to the end even when ifdef'd out
@@ -325,6 +334,11 @@ def FILTER_SOURCE_FILES() -> list[str]:
# HAS_PROTO_MESSAGE_DUMP is defined when ESPHOME_LOG_HAS_VERY_VERBOSE is set,
# which happens when the logger level is VERY_VERBOSE
if get_logger_level() != "VERY_VERBOSE":
return ["api_pb2_dump.cpp"]
files_to_filter.append("api_pb2_dump.cpp")
return []
# user_services.cpp is only needed when services are defined
config = CORE.config.get(DOMAIN, {})
if config and not config.get(CONF_ACTIONS) and not config[CONF_CUSTOM_SERVICES]:
files_to_filter.append("user_services.cpp")
return files_to_filter

View File

@@ -222,37 +222,37 @@ message DeviceInfoResponse {
// The model of the board. For example NodeMCU
string model = 6;
bool has_deep_sleep = 7;
bool has_deep_sleep = 7 [(field_ifdef) = "USE_DEEP_SLEEP"];
// The esphome project details if set
string project_name = 8;
string project_version = 9;
string project_name = 8 [(field_ifdef) = "ESPHOME_PROJECT_NAME"];
string project_version = 9 [(field_ifdef) = "ESPHOME_PROJECT_NAME"];
uint32 webserver_port = 10;
uint32 webserver_port = 10 [(field_ifdef) = "USE_WEBSERVER"];
uint32 legacy_bluetooth_proxy_version = 11;
uint32 bluetooth_proxy_feature_flags = 15;
uint32 legacy_bluetooth_proxy_version = 11 [(field_ifdef) = "USE_BLUETOOTH_PROXY"];
uint32 bluetooth_proxy_feature_flags = 15 [(field_ifdef) = "USE_BLUETOOTH_PROXY"];
string manufacturer = 12;
string friendly_name = 13;
uint32 legacy_voice_assistant_version = 14;
uint32 voice_assistant_feature_flags = 17;
uint32 legacy_voice_assistant_version = 14 [(field_ifdef) = "USE_VOICE_ASSISTANT"];
uint32 voice_assistant_feature_flags = 17 [(field_ifdef) = "USE_VOICE_ASSISTANT"];
string suggested_area = 16;
string suggested_area = 16 [(field_ifdef) = "USE_AREAS"];
// The Bluetooth mac address of the device. For example "AC:BC:32:89:0E:AA"
string bluetooth_mac_address = 18;
string bluetooth_mac_address = 18 [(field_ifdef) = "USE_BLUETOOTH_PROXY"];
// Supports receiving and saving api encryption key
bool api_encryption_supported = 19;
bool api_encryption_supported = 19 [(field_ifdef) = "USE_API_NOISE"];
repeated DeviceInfo devices = 20;
repeated AreaInfo areas = 21;
repeated DeviceInfo devices = 20 [(field_ifdef) = "USE_DEVICES"];
repeated AreaInfo areas = 21 [(field_ifdef) = "USE_AREAS"];
// Top-level area info to phase out suggested_area
AreaInfo area = 22;
AreaInfo area = 22 [(field_ifdef) = "USE_AREAS"];
}
message ListEntitiesRequest {
@@ -290,14 +290,14 @@ message ListEntitiesBinarySensorResponse {
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
reserved 4; // Deprecated: was string unique_id
string device_class = 5;
bool is_status_binary_sensor = 6;
bool disabled_by_default = 7;
string icon = 8;
string icon = 8 [(field_ifdef) = "USE_ENTITY_ICON"];
EntityCategory entity_category = 9;
uint32 device_id = 10;
uint32 device_id = 10 [(field_ifdef) = "USE_DEVICES"];
}
message BinarySensorStateResponse {
option (id) = 21;
@@ -311,7 +311,7 @@ message BinarySensorStateResponse {
// If the binary sensor does not have a valid state yet.
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
bool missing_state = 3;
uint32 device_id = 4;
uint32 device_id = 4 [(field_ifdef) = "USE_DEVICES"];
}
// ==================== COVER ====================
@@ -324,17 +324,17 @@ message ListEntitiesCoverResponse {
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
reserved 4; // Deprecated: was string unique_id
bool assumed_state = 5;
bool supports_position = 6;
bool supports_tilt = 7;
string device_class = 8;
bool disabled_by_default = 9;
string icon = 10;
string icon = 10 [(field_ifdef) = "USE_ENTITY_ICON"];
EntityCategory entity_category = 11;
bool supports_stop = 12;
uint32 device_id = 13;
uint32 device_id = 13 [(field_ifdef) = "USE_DEVICES"];
}
enum LegacyCoverState {
@@ -361,7 +361,7 @@ message CoverStateResponse {
float position = 3;
float tilt = 4;
CoverOperation current_operation = 5;
uint32 device_id = 6;
uint32 device_id = 6 [(field_ifdef) = "USE_DEVICES"];
}
enum LegacyCoverCommand {
@@ -374,6 +374,7 @@ message CoverCommandRequest {
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_COVER";
option (no_delay) = true;
option (base_class) = "CommandProtoMessage";
fixed32 key = 1;
@@ -387,6 +388,7 @@ message CoverCommandRequest {
bool has_tilt = 6;
float tilt = 7;
bool stop = 8;
uint32 device_id = 9 [(field_ifdef) = "USE_DEVICES"];
}
// ==================== FAN ====================
@@ -399,17 +401,17 @@ message ListEntitiesFanResponse {
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
reserved 4; // Deprecated: was string unique_id
bool supports_oscillation = 5;
bool supports_speed = 6;
bool supports_direction = 7;
int32 supported_speed_count = 8;
bool disabled_by_default = 9;
string icon = 10;
string icon = 10 [(field_ifdef) = "USE_ENTITY_ICON"];
EntityCategory entity_category = 11;
repeated string supported_preset_modes = 12;
uint32 device_id = 13;
uint32 device_id = 13 [(field_ifdef) = "USE_DEVICES"];
}
enum FanSpeed {
FAN_SPEED_LOW = 0;
@@ -434,13 +436,14 @@ message FanStateResponse {
FanDirection direction = 5;
int32 speed_level = 6;
string preset_mode = 7;
uint32 device_id = 8;
uint32 device_id = 8 [(field_ifdef) = "USE_DEVICES"];
}
message FanCommandRequest {
option (id) = 31;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_FAN";
option (no_delay) = true;
option (base_class) = "CommandProtoMessage";
fixed32 key = 1;
bool has_state = 2;
@@ -455,6 +458,7 @@ message FanCommandRequest {
int32 speed_level = 11;
bool has_preset_mode = 12;
string preset_mode = 13;
uint32 device_id = 14 [(field_ifdef) = "USE_DEVICES"];
}
// ==================== LIGHT ====================
@@ -480,7 +484,7 @@ message ListEntitiesLightResponse {
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
reserved 4; // Deprecated: was string unique_id
repeated ColorMode supported_color_modes = 12;
// next four supports_* are for legacy clients, newer clients should use color modes
@@ -492,9 +496,9 @@ message ListEntitiesLightResponse {
float max_mireds = 10;
repeated string effects = 11;
bool disabled_by_default = 13;
string icon = 14;
string icon = 14 [(field_ifdef) = "USE_ENTITY_ICON"];
EntityCategory entity_category = 15;
uint32 device_id = 16;
uint32 device_id = 16 [(field_ifdef) = "USE_DEVICES"];
}
message LightStateResponse {
option (id) = 24;
@@ -516,13 +520,14 @@ message LightStateResponse {
float cold_white = 12;
float warm_white = 13;
string effect = 9;
uint32 device_id = 14;
uint32 device_id = 14 [(field_ifdef) = "USE_DEVICES"];
}
message LightCommandRequest {
option (id) = 32;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_LIGHT";
option (no_delay) = true;
option (base_class) = "CommandProtoMessage";
fixed32 key = 1;
bool has_state = 2;
@@ -551,6 +556,7 @@ message LightCommandRequest {
uint32 flash_length = 17;
bool has_effect = 18;
string effect = 19;
uint32 device_id = 28 [(field_ifdef) = "USE_DEVICES"];
}
// ==================== SENSOR ====================
@@ -576,9 +582,9 @@ message ListEntitiesSensorResponse {
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
reserved 4; // Deprecated: was string unique_id
string icon = 5;
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"];
string unit_of_measurement = 6;
int32 accuracy_decimals = 7;
bool force_update = 8;
@@ -588,7 +594,7 @@ message ListEntitiesSensorResponse {
SensorLastResetType legacy_last_reset_type = 11;
bool disabled_by_default = 12;
EntityCategory entity_category = 13;
uint32 device_id = 14;
uint32 device_id = 14 [(field_ifdef) = "USE_DEVICES"];
}
message SensorStateResponse {
option (id) = 25;
@@ -602,7 +608,7 @@ message SensorStateResponse {
// If the sensor does not have a valid state yet.
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
bool missing_state = 3;
uint32 device_id = 4;
uint32 device_id = 4 [(field_ifdef) = "USE_DEVICES"];
}
// ==================== SWITCH ====================
@@ -615,14 +621,14 @@ message ListEntitiesSwitchResponse {
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
reserved 4; // Deprecated: was string unique_id
string icon = 5;
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"];
bool assumed_state = 6;
bool disabled_by_default = 7;
EntityCategory entity_category = 8;
string device_class = 9;
uint32 device_id = 10;
uint32 device_id = 10 [(field_ifdef) = "USE_DEVICES"];
}
message SwitchStateResponse {
option (id) = 26;
@@ -633,16 +639,18 @@ message SwitchStateResponse {
fixed32 key = 1;
bool state = 2;
uint32 device_id = 3;
uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"];
}
message SwitchCommandRequest {
option (id) = 33;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_SWITCH";
option (no_delay) = true;
option (base_class) = "CommandProtoMessage";
fixed32 key = 1;
bool state = 2;
uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"];
}
// ==================== TEXT SENSOR ====================
@@ -655,13 +663,13 @@ message ListEntitiesTextSensorResponse {
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
reserved 4; // Deprecated: was string unique_id
string icon = 5;
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"];
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
string device_class = 8;
uint32 device_id = 9;
uint32 device_id = 9 [(field_ifdef) = "USE_DEVICES"];
}
message TextSensorStateResponse {
option (id) = 27;
@@ -675,7 +683,7 @@ message TextSensorStateResponse {
// If the text sensor does not have a valid state yet.
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
bool missing_state = 3;
uint32 device_id = 4;
uint32 device_id = 4 [(field_ifdef) = "USE_DEVICES"];
}
// ==================== SUBSCRIBE LOGS ====================
@@ -799,18 +807,21 @@ enum ServiceArgType {
SERVICE_ARG_TYPE_STRING_ARRAY = 7;
}
message ListEntitiesServicesArgument {
option (ifdef) = "USE_API_SERVICES";
string name = 1;
ServiceArgType type = 2;
}
message ListEntitiesServicesResponse {
option (id) = 41;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_API_SERVICES";
string name = 1;
fixed32 key = 2;
repeated ListEntitiesServicesArgument args = 3;
}
message ExecuteServiceArgument {
option (ifdef) = "USE_API_SERVICES";
bool bool_ = 1;
int32 legacy_int = 2;
float float_ = 3;
@@ -826,6 +837,7 @@ message ExecuteServiceRequest {
option (id) = 42;
option (source) = SOURCE_CLIENT;
option (no_delay) = true;
option (ifdef) = "USE_API_SERVICES";
fixed32 key = 1;
repeated ExecuteServiceArgument args = 2;
@@ -841,21 +853,23 @@ message ListEntitiesCameraResponse {
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
reserved 4; // Deprecated: was string unique_id
bool disabled_by_default = 5;
string icon = 6;
string icon = 6 [(field_ifdef) = "USE_ENTITY_ICON"];
EntityCategory entity_category = 7;
uint32 device_id = 8;
uint32 device_id = 8 [(field_ifdef) = "USE_DEVICES"];
}
message CameraImageResponse {
option (id) = 44;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_CAMERA";
fixed32 key = 1;
bytes data = 2;
bool done = 3;
uint32 device_id = 4 [(field_ifdef) = "USE_DEVICES"];
}
message CameraImageRequest {
option (id) = 45;
@@ -923,7 +937,7 @@ message ListEntitiesClimateResponse {
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
reserved 4; // Deprecated: was string unique_id
bool supports_current_temperature = 5;
bool supports_two_point_target_temperature = 6;
@@ -941,14 +955,14 @@ message ListEntitiesClimateResponse {
repeated ClimatePreset supported_presets = 16;
repeated string supported_custom_presets = 17;
bool disabled_by_default = 18;
string icon = 19;
string icon = 19 [(field_ifdef) = "USE_ENTITY_ICON"];
EntityCategory entity_category = 20;
float visual_current_temperature_step = 21;
bool supports_current_humidity = 22;
bool supports_target_humidity = 23;
float visual_min_humidity = 24;
float visual_max_humidity = 25;
uint32 device_id = 26;
uint32 device_id = 26 [(field_ifdef) = "USE_DEVICES"];
}
message ClimateStateResponse {
option (id) = 47;
@@ -973,13 +987,14 @@ message ClimateStateResponse {
string custom_preset = 13;
float current_humidity = 14;
float target_humidity = 15;
uint32 device_id = 16;
uint32 device_id = 16 [(field_ifdef) = "USE_DEVICES"];
}
message ClimateCommandRequest {
option (id) = 48;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_CLIMATE";
option (no_delay) = true;
option (base_class) = "CommandProtoMessage";
fixed32 key = 1;
bool has_mode = 2;
@@ -1005,6 +1020,7 @@ message ClimateCommandRequest {
string custom_preset = 21;
bool has_target_humidity = 22;
float target_humidity = 23;
uint32 device_id = 24 [(field_ifdef) = "USE_DEVICES"];
}
// ==================== NUMBER ====================
@@ -1022,9 +1038,9 @@ message ListEntitiesNumberResponse {
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
reserved 4; // Deprecated: was string unique_id
string icon = 5;
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"];
float min_value = 6;
float max_value = 7;
float step = 8;
@@ -1033,7 +1049,7 @@ message ListEntitiesNumberResponse {
string unit_of_measurement = 11;
NumberMode mode = 12;
string device_class = 13;
uint32 device_id = 14;
uint32 device_id = 14 [(field_ifdef) = "USE_DEVICES"];
}
message NumberStateResponse {
option (id) = 50;
@@ -1047,16 +1063,18 @@ message NumberStateResponse {
// If the number does not have a valid state yet.
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
bool missing_state = 3;
uint32 device_id = 4;
uint32 device_id = 4 [(field_ifdef) = "USE_DEVICES"];
}
message NumberCommandRequest {
option (id) = 51;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_NUMBER";
option (no_delay) = true;
option (base_class) = "CommandProtoMessage";
fixed32 key = 1;
float state = 2;
uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"];
}
// ==================== SELECT ====================
@@ -1069,13 +1087,13 @@ message ListEntitiesSelectResponse {
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
reserved 4; // Deprecated: was string unique_id
string icon = 5;
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"];
repeated string options = 6;
bool disabled_by_default = 7;
EntityCategory entity_category = 8;
uint32 device_id = 9;
uint32 device_id = 9 [(field_ifdef) = "USE_DEVICES"];
}
message SelectStateResponse {
option (id) = 53;
@@ -1089,16 +1107,18 @@ message SelectStateResponse {
// If the select does not have a valid state yet.
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
bool missing_state = 3;
uint32 device_id = 4;
uint32 device_id = 4 [(field_ifdef) = "USE_DEVICES"];
}
message SelectCommandRequest {
option (id) = 54;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_SELECT";
option (no_delay) = true;
option (base_class) = "CommandProtoMessage";
fixed32 key = 1;
string state = 2;
uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"];
}
// ==================== SIREN ====================
@@ -1111,15 +1131,15 @@ message ListEntitiesSirenResponse {
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
reserved 4; // Deprecated: was string unique_id
string icon = 5;
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"];
bool disabled_by_default = 6;
repeated string tones = 7;
bool supports_duration = 8;
bool supports_volume = 9;
EntityCategory entity_category = 10;
uint32 device_id = 11;
uint32 device_id = 11 [(field_ifdef) = "USE_DEVICES"];
}
message SirenStateResponse {
option (id) = 56;
@@ -1130,13 +1150,14 @@ message SirenStateResponse {
fixed32 key = 1;
bool state = 2;
uint32 device_id = 3;
uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"];
}
message SirenCommandRequest {
option (id) = 57;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_SIREN";
option (no_delay) = true;
option (base_class) = "CommandProtoMessage";
fixed32 key = 1;
bool has_state = 2;
@@ -1147,6 +1168,7 @@ message SirenCommandRequest {
uint32 duration = 7;
bool has_volume = 8;
float volume = 9;
uint32 device_id = 10 [(field_ifdef) = "USE_DEVICES"];
}
// ==================== LOCK ====================
@@ -1172,9 +1194,9 @@ message ListEntitiesLockResponse {
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
reserved 4; // Deprecated: was string unique_id
string icon = 5;
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"];
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
bool assumed_state = 8;
@@ -1184,7 +1206,7 @@ message ListEntitiesLockResponse {
// Not yet implemented:
string code_format = 11;
uint32 device_id = 12;
uint32 device_id = 12 [(field_ifdef) = "USE_DEVICES"];
}
message LockStateResponse {
option (id) = 59;
@@ -1194,19 +1216,21 @@ message LockStateResponse {
option (no_delay) = true;
fixed32 key = 1;
LockState state = 2;
uint32 device_id = 3;
uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"];
}
message LockCommandRequest {
option (id) = 60;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_LOCK";
option (no_delay) = true;
option (base_class) = "CommandProtoMessage";
fixed32 key = 1;
LockCommand command = 2;
// Not yet implemented:
bool has_code = 3;
string code = 4;
uint32 device_id = 5 [(field_ifdef) = "USE_DEVICES"];
}
// ==================== BUTTON ====================
@@ -1219,21 +1243,23 @@ message ListEntitiesButtonResponse {
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
reserved 4; // Deprecated: was string unique_id
string icon = 5;
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"];
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
string device_class = 8;
uint32 device_id = 9;
uint32 device_id = 9 [(field_ifdef) = "USE_DEVICES"];
}
message ButtonCommandRequest {
option (id) = 62;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_BUTTON";
option (no_delay) = true;
option (base_class) = "CommandProtoMessage";
fixed32 key = 1;
uint32 device_id = 2 [(field_ifdef) = "USE_DEVICES"];
}
// ==================== MEDIA PLAYER ====================
@@ -1272,9 +1298,9 @@ message ListEntitiesMediaPlayerResponse {
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
reserved 4; // Deprecated: was string unique_id
string icon = 5;
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"];
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
@@ -1282,7 +1308,7 @@ message ListEntitiesMediaPlayerResponse {
repeated MediaPlayerSupportedFormat supported_formats = 9;
uint32 device_id = 10;
uint32 device_id = 10 [(field_ifdef) = "USE_DEVICES"];
}
message MediaPlayerStateResponse {
option (id) = 64;
@@ -1294,13 +1320,14 @@ message MediaPlayerStateResponse {
MediaPlayerState state = 2;
float volume = 3;
bool muted = 4;
uint32 device_id = 5;
uint32 device_id = 5 [(field_ifdef) = "USE_DEVICES"];
}
message MediaPlayerCommandRequest {
option (id) = 65;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_MEDIA_PLAYER";
option (no_delay) = true;
option (base_class) = "CommandProtoMessage";
fixed32 key = 1;
@@ -1315,6 +1342,7 @@ message MediaPlayerCommandRequest {
bool has_announcement = 8;
bool announcement = 9;
uint32 device_id = 10 [(field_ifdef) = "USE_DEVICES"];
}
// ==================== BLUETOOTH ====================
@@ -1817,14 +1845,14 @@ message ListEntitiesAlarmControlPanelResponse {
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
string icon = 5;
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"];
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
uint32 supported_features = 8;
bool requires_code = 9;
bool requires_code_to_arm = 10;
uint32 device_id = 11;
uint32 device_id = 11 [(field_ifdef) = "USE_DEVICES"];
}
message AlarmControlPanelStateResponse {
@@ -1835,7 +1863,7 @@ message AlarmControlPanelStateResponse {
option (no_delay) = true;
fixed32 key = 1;
AlarmControlPanelState state = 2;
uint32 device_id = 3;
uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"];
}
message AlarmControlPanelCommandRequest {
@@ -1843,9 +1871,11 @@ message AlarmControlPanelCommandRequest {
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_ALARM_CONTROL_PANEL";
option (no_delay) = true;
option (base_class) = "CommandProtoMessage";
fixed32 key = 1;
AlarmControlPanelStateCommand command = 2;
string code = 3;
uint32 device_id = 4 [(field_ifdef) = "USE_DEVICES"];
}
// ===================== TEXT =====================
@@ -1862,8 +1892,8 @@ message ListEntitiesTextResponse {
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
string icon = 5;
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"];
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
@@ -1871,7 +1901,7 @@ message ListEntitiesTextResponse {
uint32 max_length = 9;
string pattern = 10;
TextMode mode = 11;
uint32 device_id = 12;
uint32 device_id = 12 [(field_ifdef) = "USE_DEVICES"];
}
message TextStateResponse {
option (id) = 98;
@@ -1885,16 +1915,18 @@ message TextStateResponse {
// If the Text does not have a valid state yet.
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
bool missing_state = 3;
uint32 device_id = 4;
uint32 device_id = 4 [(field_ifdef) = "USE_DEVICES"];
}
message TextCommandRequest {
option (id) = 99;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_TEXT";
option (no_delay) = true;
option (base_class) = "CommandProtoMessage";
fixed32 key = 1;
string state = 2;
uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"];
}
@@ -1908,12 +1940,12 @@ message ListEntitiesDateResponse {
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
reserved 4; // Deprecated: was string unique_id
string icon = 5;
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"];
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
uint32 device_id = 8;
uint32 device_id = 8 [(field_ifdef) = "USE_DEVICES"];
}
message DateStateResponse {
option (id) = 101;
@@ -1929,18 +1961,20 @@ message DateStateResponse {
uint32 year = 3;
uint32 month = 4;
uint32 day = 5;
uint32 device_id = 6;
uint32 device_id = 6 [(field_ifdef) = "USE_DEVICES"];
}
message DateCommandRequest {
option (id) = 102;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_DATETIME_DATE";
option (no_delay) = true;
option (base_class) = "CommandProtoMessage";
fixed32 key = 1;
uint32 year = 2;
uint32 month = 3;
uint32 day = 4;
uint32 device_id = 5 [(field_ifdef) = "USE_DEVICES"];
}
// ==================== DATETIME TIME ====================
@@ -1953,12 +1987,12 @@ message ListEntitiesTimeResponse {
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
reserved 4; // Deprecated: was string unique_id
string icon = 5;
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"];
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
uint32 device_id = 8;
uint32 device_id = 8 [(field_ifdef) = "USE_DEVICES"];
}
message TimeStateResponse {
option (id) = 104;
@@ -1974,18 +2008,20 @@ message TimeStateResponse {
uint32 hour = 3;
uint32 minute = 4;
uint32 second = 5;
uint32 device_id = 6;
uint32 device_id = 6 [(field_ifdef) = "USE_DEVICES"];
}
message TimeCommandRequest {
option (id) = 105;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_DATETIME_TIME";
option (no_delay) = true;
option (base_class) = "CommandProtoMessage";
fixed32 key = 1;
uint32 hour = 2;
uint32 minute = 3;
uint32 second = 4;
uint32 device_id = 5 [(field_ifdef) = "USE_DEVICES"];
}
// ==================== EVENT ====================
@@ -1998,15 +2034,15 @@ message ListEntitiesEventResponse {
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
reserved 4; // Deprecated: was string unique_id
string icon = 5;
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"];
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
string device_class = 8;
repeated string event_types = 9;
uint32 device_id = 10;
uint32 device_id = 10 [(field_ifdef) = "USE_DEVICES"];
}
message EventResponse {
option (id) = 108;
@@ -2016,7 +2052,7 @@ message EventResponse {
fixed32 key = 1;
string event_type = 2;
uint32 device_id = 3;
uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"];
}
// ==================== VALVE ====================
@@ -2029,9 +2065,9 @@ message ListEntitiesValveResponse {
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
reserved 4; // Deprecated: was string unique_id
string icon = 5;
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"];
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
string device_class = 8;
@@ -2039,7 +2075,7 @@ message ListEntitiesValveResponse {
bool assumed_state = 9;
bool supports_position = 10;
bool supports_stop = 11;
uint32 device_id = 12;
uint32 device_id = 12 [(field_ifdef) = "USE_DEVICES"];
}
enum ValveOperation {
@@ -2057,7 +2093,7 @@ message ValveStateResponse {
fixed32 key = 1;
float position = 2;
ValveOperation current_operation = 3;
uint32 device_id = 4;
uint32 device_id = 4 [(field_ifdef) = "USE_DEVICES"];
}
message ValveCommandRequest {
@@ -2065,11 +2101,13 @@ message ValveCommandRequest {
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_VALVE";
option (no_delay) = true;
option (base_class) = "CommandProtoMessage";
fixed32 key = 1;
bool has_position = 2;
float position = 3;
bool stop = 4;
uint32 device_id = 5 [(field_ifdef) = "USE_DEVICES"];
}
// ==================== DATETIME DATETIME ====================
@@ -2082,12 +2120,12 @@ message ListEntitiesDateTimeResponse {
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
reserved 4; // Deprecated: was string unique_id
string icon = 5;
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"];
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
uint32 device_id = 8;
uint32 device_id = 8 [(field_ifdef) = "USE_DEVICES"];
}
message DateTimeStateResponse {
option (id) = 113;
@@ -2101,16 +2139,18 @@ message DateTimeStateResponse {
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
bool missing_state = 2;
fixed32 epoch_seconds = 3;
uint32 device_id = 4;
uint32 device_id = 4 [(field_ifdef) = "USE_DEVICES"];
}
message DateTimeCommandRequest {
option (id) = 114;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_DATETIME_DATETIME";
option (no_delay) = true;
option (base_class) = "CommandProtoMessage";
fixed32 key = 1;
fixed32 epoch_seconds = 2;
uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"];
}
// ==================== UPDATE ====================
@@ -2123,13 +2163,13 @@ message ListEntitiesUpdateResponse {
string object_id = 1;
fixed32 key = 2;
string name = 3;
string unique_id = 4;
reserved 4; // Deprecated: was string unique_id
string icon = 5;
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"];
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
string device_class = 8;
uint32 device_id = 9;
uint32 device_id = 9 [(field_ifdef) = "USE_DEVICES"];
}
message UpdateStateResponse {
option (id) = 117;
@@ -2148,7 +2188,7 @@ message UpdateStateResponse {
string title = 8;
string release_summary = 9;
string release_url = 10;
uint32 device_id = 11;
uint32 device_id = 11 [(field_ifdef) = "USE_DEVICES"];
}
enum UpdateCommand {
UPDATE_COMMAND_NONE = 0;
@@ -2160,7 +2200,9 @@ message UpdateCommandRequest {
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_UPDATE";
option (no_delay) = true;
option (base_class) = "CommandProtoMessage";
fixed32 key = 1;
UpdateCommand command = 2;
uint32 device_id = 3 [(field_ifdef) = "USE_DEVICES"];
}

View File

@@ -86,8 +86,8 @@ void APIConnection::start() {
APIError err = this->helper_->init();
if (err != APIError::OK) {
on_fatal_error();
ESP_LOGW(TAG, "%s: Helper init failed: %s errno=%d", this->get_client_combined_info().c_str(),
api_error_to_str(err), errno);
ESP_LOGW(TAG, "%s: Helper init failed %s errno=%d", this->get_client_combined_info().c_str(), api_error_to_str(err),
errno);
return;
}
this->client_info_ = helper_->getpeername();
@@ -119,7 +119,7 @@ void APIConnection::loop() {
APIError err = this->helper_->loop();
if (err != APIError::OK) {
on_fatal_error();
ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", this->get_client_combined_info().c_str(),
ESP_LOGW(TAG, "%s: Socket operation failed %s errno=%d", this->get_client_combined_info().c_str(),
api_error_to_str(err), errno);
return;
}
@@ -136,14 +136,8 @@ void APIConnection::loop() {
break;
} else if (err != APIError::OK) {
on_fatal_error();
if (err == APIError::SOCKET_READ_FAILED && errno == ECONNRESET) {
ESP_LOGW(TAG, "%s: Connection reset", this->get_client_combined_info().c_str());
} else if (err == APIError::CONNECTION_CLOSED) {
ESP_LOGW(TAG, "%s: Connection closed", this->get_client_combined_info().c_str());
} else {
ESP_LOGW(TAG, "%s: Reading failed: %s errno=%d", this->get_client_combined_info().c_str(),
api_error_to_str(err), errno);
}
ESP_LOGW(TAG, "%s: Reading failed %s errno=%d", this->get_client_combined_info().c_str(), api_error_to_str(err),
errno);
return;
} else {
this->last_traffic_ = now;
@@ -186,7 +180,8 @@ void APIConnection::loop() {
on_fatal_error();
ESP_LOGW(TAG, "%s is unresponsive; disconnecting", this->get_client_combined_info().c_str());
}
} else if (now - this->last_traffic_ > KEEPALIVE_TIMEOUT_MS) {
} else if (now - this->last_traffic_ > KEEPALIVE_TIMEOUT_MS && !this->flags_.remove) {
// Only send ping if we're not disconnecting
ESP_LOGVV(TAG, "Sending keepalive PING");
this->flags_.sent_ping = this->send_message(PingRequest());
if (!this->flags_.sent_ping) {
@@ -201,7 +196,7 @@ void APIConnection::loop() {
#ifdef USE_CAMERA
if (this->image_reader_ && this->image_reader_->available() && this->helper_->can_write_without_blocking()) {
uint32_t to_send = std::min((size_t) MAX_PACKET_SIZE, this->image_reader_->available());
uint32_t to_send = std::min((size_t) MAX_BATCH_PACKET_SIZE, this->image_reader_->available());
bool done = this->image_reader_->available() == to_send;
uint32_t msg_size = 0;
ProtoSize::add_fixed_field<4>(msg_size, 1, true);
@@ -246,10 +241,6 @@ void APIConnection::loop() {
}
}
std::string get_default_unique_id(const std::string &component_type, EntityBase *entity) {
return App.get_name() + component_type + entity->get_object_id();
}
DisconnectResponse APIConnection::disconnect(const DisconnectRequest &msg) {
// remote initiated disconnect_client
// don't close yet, we still need to send the disconnect response
@@ -326,8 +317,8 @@ uint16_t APIConnection::try_send_binary_sensor_state(EntityBase *entity, APIConn
BinarySensorStateResponse resp;
resp.state = binary_sensor->state;
resp.missing_state = !binary_sensor->has_state();
fill_entity_state_base(binary_sensor, resp);
return encode_message_to_buffer(resp, BinarySensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_state(binary_sensor, resp, BinarySensorStateResponse::MESSAGE_TYPE, conn,
remaining_size, is_single);
}
uint16_t APIConnection::try_send_binary_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -336,9 +327,8 @@ uint16_t APIConnection::try_send_binary_sensor_info(EntityBase *entity, APIConne
ListEntitiesBinarySensorResponse msg;
msg.device_class = binary_sensor->get_device_class();
msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor();
msg.unique_id = get_default_unique_id("binary_sensor", binary_sensor);
fill_entity_info_base(binary_sensor, msg);
return encode_message_to_buffer(msg, ListEntitiesBinarySensorResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_info(binary_sensor, msg, ListEntitiesBinarySensorResponse::MESSAGE_TYPE, conn,
remaining_size, is_single);
}
#endif
@@ -358,8 +348,7 @@ uint16_t APIConnection::try_send_cover_state(EntityBase *entity, APIConnection *
if (traits.get_supports_tilt())
msg.tilt = cover->tilt;
msg.current_operation = static_cast<enums::CoverOperation>(cover->current_operation);
fill_entity_state_base(cover, msg);
return encode_message_to_buffer(msg, CoverStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_state(cover, msg, CoverStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
uint16_t APIConnection::try_send_cover_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
@@ -371,9 +360,8 @@ uint16_t APIConnection::try_send_cover_info(EntityBase *entity, APIConnection *c
msg.supports_tilt = traits.get_supports_tilt();
msg.supports_stop = traits.get_supports_stop();
msg.device_class = cover->get_device_class();
msg.unique_id = get_default_unique_id("cover", cover);
fill_entity_info_base(cover, msg);
return encode_message_to_buffer(msg, ListEntitiesCoverResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_info(cover, msg, ListEntitiesCoverResponse::MESSAGE_TYPE, conn, remaining_size,
is_single);
}
void APIConnection::cover_command(const CoverCommandRequest &msg) {
ENTITY_COMMAND_MAKE_CALL(cover::Cover, cover, cover)
@@ -420,8 +408,7 @@ uint16_t APIConnection::try_send_fan_state(EntityBase *entity, APIConnection *co
msg.direction = static_cast<enums::FanDirection>(fan->direction);
if (traits.supports_preset_modes())
msg.preset_mode = fan->preset_mode;
fill_entity_state_base(fan, msg);
return encode_message_to_buffer(msg, FanStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_state(fan, msg, FanStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
uint16_t APIConnection::try_send_fan_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
@@ -434,9 +421,7 @@ uint16_t APIConnection::try_send_fan_info(EntityBase *entity, APIConnection *con
msg.supported_speed_count = traits.supported_speed_count();
for (auto const &preset : traits.supported_preset_modes())
msg.supported_preset_modes.push_back(preset);
msg.unique_id = get_default_unique_id("fan", fan);
fill_entity_info_base(fan, msg);
return encode_message_to_buffer(msg, ListEntitiesFanResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_info(fan, msg, ListEntitiesFanResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::fan_command(const FanCommandRequest &msg) {
ENTITY_COMMAND_MAKE_CALL(fan::Fan, fan, fan)
@@ -481,8 +466,7 @@ uint16_t APIConnection::try_send_light_state(EntityBase *entity, APIConnection *
resp.warm_white = values.get_warm_white();
if (light->supports_effects())
resp.effect = light->get_effect_name();
fill_entity_state_base(light, resp);
return encode_message_to_buffer(resp, LightStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_state(light, resp, LightStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
uint16_t APIConnection::try_send_light_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
@@ -508,9 +492,8 @@ uint16_t APIConnection::try_send_light_info(EntityBase *entity, APIConnection *c
msg.effects.push_back(effect->get_name());
}
}
msg.unique_id = get_default_unique_id("light", light);
fill_entity_info_base(light, msg);
return encode_message_to_buffer(msg, ListEntitiesLightResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_info(light, msg, ListEntitiesLightResponse::MESSAGE_TYPE, conn, remaining_size,
is_single);
}
void APIConnection::light_command(const LightCommandRequest &msg) {
ENTITY_COMMAND_MAKE_CALL(light::LightState, light, light)
@@ -557,8 +540,7 @@ uint16_t APIConnection::try_send_sensor_state(EntityBase *entity, APIConnection
SensorStateResponse resp;
resp.state = sensor->state;
resp.missing_state = !sensor->has_state();
fill_entity_state_base(sensor, resp);
return encode_message_to_buffer(resp, SensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_state(sensor, resp, SensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
uint16_t APIConnection::try_send_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -570,11 +552,8 @@ uint16_t APIConnection::try_send_sensor_info(EntityBase *entity, APIConnection *
msg.force_update = sensor->get_force_update();
msg.device_class = sensor->get_device_class();
msg.state_class = static_cast<enums::SensorStateClass>(sensor->get_state_class());
msg.unique_id = sensor->unique_id();
if (msg.unique_id.empty())
msg.unique_id = get_default_unique_id("sensor", sensor);
fill_entity_info_base(sensor, msg);
return encode_message_to_buffer(msg, ListEntitiesSensorResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_info(sensor, msg, ListEntitiesSensorResponse::MESSAGE_TYPE, conn, remaining_size,
is_single);
}
#endif
@@ -589,8 +568,8 @@ uint16_t APIConnection::try_send_switch_state(EntityBase *entity, APIConnection
auto *a_switch = static_cast<switch_::Switch *>(entity);
SwitchStateResponse resp;
resp.state = a_switch->state;
fill_entity_state_base(a_switch, resp);
return encode_message_to_buffer(resp, SwitchStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_state(a_switch, resp, SwitchStateResponse::MESSAGE_TYPE, conn, remaining_size,
is_single);
}
uint16_t APIConnection::try_send_switch_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -599,9 +578,8 @@ uint16_t APIConnection::try_send_switch_info(EntityBase *entity, APIConnection *
ListEntitiesSwitchResponse msg;
msg.assumed_state = a_switch->assumed_state();
msg.device_class = a_switch->get_device_class();
msg.unique_id = get_default_unique_id("switch", a_switch);
fill_entity_info_base(a_switch, msg);
return encode_message_to_buffer(msg, ListEntitiesSwitchResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_info(a_switch, msg, ListEntitiesSwitchResponse::MESSAGE_TYPE, conn, remaining_size,
is_single);
}
void APIConnection::switch_command(const SwitchCommandRequest &msg) {
ENTITY_COMMAND_GET(switch_::Switch, a_switch, switch)
@@ -626,19 +604,16 @@ uint16_t APIConnection::try_send_text_sensor_state(EntityBase *entity, APIConnec
TextSensorStateResponse resp;
resp.state = text_sensor->state;
resp.missing_state = !text_sensor->has_state();
fill_entity_state_base(text_sensor, resp);
return encode_message_to_buffer(resp, TextSensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_state(text_sensor, resp, TextSensorStateResponse::MESSAGE_TYPE, conn, remaining_size,
is_single);
}
uint16_t APIConnection::try_send_text_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *text_sensor = static_cast<text_sensor::TextSensor *>(entity);
ListEntitiesTextSensorResponse msg;
msg.device_class = text_sensor->get_device_class();
msg.unique_id = text_sensor->unique_id();
if (msg.unique_id.empty())
msg.unique_id = get_default_unique_id("text_sensor", text_sensor);
fill_entity_info_base(text_sensor, msg);
return encode_message_to_buffer(msg, ListEntitiesTextSensorResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_info(text_sensor, msg, ListEntitiesTextSensorResponse::MESSAGE_TYPE, conn,
remaining_size, is_single);
}
#endif
@@ -651,7 +626,6 @@ uint16_t APIConnection::try_send_climate_state(EntityBase *entity, APIConnection
bool is_single) {
auto *climate = static_cast<climate::Climate *>(entity);
ClimateStateResponse resp;
fill_entity_state_base(climate, resp);
auto traits = climate->get_traits();
resp.mode = static_cast<enums::ClimateMode>(climate->mode);
resp.action = static_cast<enums::ClimateAction>(climate->action);
@@ -678,7 +652,8 @@ uint16_t APIConnection::try_send_climate_state(EntityBase *entity, APIConnection
resp.current_humidity = climate->current_humidity;
if (traits.get_supports_target_humidity())
resp.target_humidity = climate->target_humidity;
return encode_message_to_buffer(resp, ClimateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_state(climate, resp, ClimateStateResponse::MESSAGE_TYPE, conn, remaining_size,
is_single);
}
uint16_t APIConnection::try_send_climate_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
@@ -709,9 +684,8 @@ uint16_t APIConnection::try_send_climate_info(EntityBase *entity, APIConnection
msg.supported_custom_presets.push_back(custom_preset);
for (auto swing_mode : traits.get_supported_swing_modes())
msg.supported_swing_modes.push_back(static_cast<enums::ClimateSwingMode>(swing_mode));
msg.unique_id = get_default_unique_id("climate", climate);
fill_entity_info_base(climate, msg);
return encode_message_to_buffer(msg, ListEntitiesClimateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_info(climate, msg, ListEntitiesClimateResponse::MESSAGE_TYPE, conn, remaining_size,
is_single);
}
void APIConnection::climate_command(const ClimateCommandRequest &msg) {
ENTITY_COMMAND_MAKE_CALL(climate::Climate, climate, climate)
@@ -751,8 +725,7 @@ uint16_t APIConnection::try_send_number_state(EntityBase *entity, APIConnection
NumberStateResponse resp;
resp.state = number->state;
resp.missing_state = !number->has_state();
fill_entity_state_base(number, resp);
return encode_message_to_buffer(resp, NumberStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_state(number, resp, NumberStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
uint16_t APIConnection::try_send_number_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -765,9 +738,8 @@ uint16_t APIConnection::try_send_number_info(EntityBase *entity, APIConnection *
msg.min_value = number->traits.get_min_value();
msg.max_value = number->traits.get_max_value();
msg.step = number->traits.get_step();
msg.unique_id = get_default_unique_id("number", number);
fill_entity_info_base(number, msg);
return encode_message_to_buffer(msg, ListEntitiesNumberResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_info(number, msg, ListEntitiesNumberResponse::MESSAGE_TYPE, conn, remaining_size,
is_single);
}
void APIConnection::number_command(const NumberCommandRequest &msg) {
ENTITY_COMMAND_MAKE_CALL(number::Number, number, number)
@@ -789,16 +761,14 @@ uint16_t APIConnection::try_send_date_state(EntityBase *entity, APIConnection *c
resp.year = date->year;
resp.month = date->month;
resp.day = date->day;
fill_entity_state_base(date, resp);
return encode_message_to_buffer(resp, DateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_state(date, resp, DateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
uint16_t APIConnection::try_send_date_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *date = static_cast<datetime::DateEntity *>(entity);
ListEntitiesDateResponse msg;
msg.unique_id = get_default_unique_id("date", date);
fill_entity_info_base(date, msg);
return encode_message_to_buffer(msg, ListEntitiesDateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_info(date, msg, ListEntitiesDateResponse::MESSAGE_TYPE, conn, remaining_size,
is_single);
}
void APIConnection::date_command(const DateCommandRequest &msg) {
ENTITY_COMMAND_MAKE_CALL(datetime::DateEntity, date, date)
@@ -820,16 +790,14 @@ uint16_t APIConnection::try_send_time_state(EntityBase *entity, APIConnection *c
resp.hour = time->hour;
resp.minute = time->minute;
resp.second = time->second;
fill_entity_state_base(time, resp);
return encode_message_to_buffer(resp, TimeStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_state(time, resp, TimeStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
uint16_t APIConnection::try_send_time_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *time = static_cast<datetime::TimeEntity *>(entity);
ListEntitiesTimeResponse msg;
msg.unique_id = get_default_unique_id("time", time);
fill_entity_info_base(time, msg);
return encode_message_to_buffer(msg, ListEntitiesTimeResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_info(time, msg, ListEntitiesTimeResponse::MESSAGE_TYPE, conn, remaining_size,
is_single);
}
void APIConnection::time_command(const TimeCommandRequest &msg) {
ENTITY_COMMAND_MAKE_CALL(datetime::TimeEntity, time, time)
@@ -852,16 +820,15 @@ uint16_t APIConnection::try_send_datetime_state(EntityBase *entity, APIConnectio
ESPTime state = datetime->state_as_esptime();
resp.epoch_seconds = state.timestamp;
}
fill_entity_state_base(datetime, resp);
return encode_message_to_buffer(resp, DateTimeStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_state(datetime, resp, DateTimeStateResponse::MESSAGE_TYPE, conn, remaining_size,
is_single);
}
uint16_t APIConnection::try_send_datetime_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *datetime = static_cast<datetime::DateTimeEntity *>(entity);
ListEntitiesDateTimeResponse msg;
msg.unique_id = get_default_unique_id("datetime", datetime);
fill_entity_info_base(datetime, msg);
return encode_message_to_buffer(msg, ListEntitiesDateTimeResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_info(datetime, msg, ListEntitiesDateTimeResponse::MESSAGE_TYPE, conn, remaining_size,
is_single);
}
void APIConnection::datetime_command(const DateTimeCommandRequest &msg) {
ENTITY_COMMAND_MAKE_CALL(datetime::DateTimeEntity, datetime, datetime)
@@ -882,8 +849,7 @@ uint16_t APIConnection::try_send_text_state(EntityBase *entity, APIConnection *c
TextStateResponse resp;
resp.state = text->state;
resp.missing_state = !text->has_state();
fill_entity_state_base(text, resp);
return encode_message_to_buffer(resp, TextStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_state(text, resp, TextStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
uint16_t APIConnection::try_send_text_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -894,9 +860,8 @@ uint16_t APIConnection::try_send_text_info(EntityBase *entity, APIConnection *co
msg.min_length = text->traits.get_min_length();
msg.max_length = text->traits.get_max_length();
msg.pattern = text->traits.get_pattern();
msg.unique_id = get_default_unique_id("text", text);
fill_entity_info_base(text, msg);
return encode_message_to_buffer(msg, ListEntitiesTextResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_info(text, msg, ListEntitiesTextResponse::MESSAGE_TYPE, conn, remaining_size,
is_single);
}
void APIConnection::text_command(const TextCommandRequest &msg) {
ENTITY_COMMAND_MAKE_CALL(text::Text, text, text)
@@ -917,8 +882,7 @@ uint16_t APIConnection::try_send_select_state(EntityBase *entity, APIConnection
SelectStateResponse resp;
resp.state = select->state;
resp.missing_state = !select->has_state();
fill_entity_state_base(select, resp);
return encode_message_to_buffer(resp, SelectStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_state(select, resp, SelectStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
uint16_t APIConnection::try_send_select_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -927,9 +891,8 @@ uint16_t APIConnection::try_send_select_info(EntityBase *entity, APIConnection *
ListEntitiesSelectResponse msg;
for (const auto &option : select->traits.get_options())
msg.options.push_back(option);
msg.unique_id = get_default_unique_id("select", select);
fill_entity_info_base(select, msg);
return encode_message_to_buffer(msg, ListEntitiesSelectResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_info(select, msg, ListEntitiesSelectResponse::MESSAGE_TYPE, conn, remaining_size,
is_single);
}
void APIConnection::select_command(const SelectCommandRequest &msg) {
ENTITY_COMMAND_MAKE_CALL(select::Select, select, select)
@@ -944,9 +907,8 @@ uint16_t APIConnection::try_send_button_info(EntityBase *entity, APIConnection *
auto *button = static_cast<button::Button *>(entity);
ListEntitiesButtonResponse msg;
msg.device_class = button->get_device_class();
msg.unique_id = get_default_unique_id("button", button);
fill_entity_info_base(button, msg);
return encode_message_to_buffer(msg, ListEntitiesButtonResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_info(button, msg, ListEntitiesButtonResponse::MESSAGE_TYPE, conn, remaining_size,
is_single);
}
void esphome::api::APIConnection::button_command(const ButtonCommandRequest &msg) {
ENTITY_COMMAND_GET(button::Button, button, button)
@@ -965,8 +927,7 @@ uint16_t APIConnection::try_send_lock_state(EntityBase *entity, APIConnection *c
auto *a_lock = static_cast<lock::Lock *>(entity);
LockStateResponse resp;
resp.state = static_cast<enums::LockState>(a_lock->state);
fill_entity_state_base(a_lock, resp);
return encode_message_to_buffer(resp, LockStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_state(a_lock, resp, LockStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
uint16_t APIConnection::try_send_lock_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -976,9 +937,8 @@ uint16_t APIConnection::try_send_lock_info(EntityBase *entity, APIConnection *co
msg.assumed_state = a_lock->traits.get_assumed_state();
msg.supports_open = a_lock->traits.get_supports_open();
msg.requires_code = a_lock->traits.get_requires_code();
msg.unique_id = get_default_unique_id("lock", a_lock);
fill_entity_info_base(a_lock, msg);
return encode_message_to_buffer(msg, ListEntitiesLockResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_info(a_lock, msg, ListEntitiesLockResponse::MESSAGE_TYPE, conn, remaining_size,
is_single);
}
void APIConnection::lock_command(const LockCommandRequest &msg) {
ENTITY_COMMAND_GET(lock::Lock, a_lock, lock)
@@ -1008,8 +968,7 @@ uint16_t APIConnection::try_send_valve_state(EntityBase *entity, APIConnection *
ValveStateResponse resp;
resp.position = valve->position;
resp.current_operation = static_cast<enums::ValveOperation>(valve->current_operation);
fill_entity_state_base(valve, resp);
return encode_message_to_buffer(resp, ValveStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_state(valve, resp, ValveStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
uint16_t APIConnection::try_send_valve_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
@@ -1020,9 +979,8 @@ uint16_t APIConnection::try_send_valve_info(EntityBase *entity, APIConnection *c
msg.assumed_state = traits.get_is_assumed_state();
msg.supports_position = traits.get_supports_position();
msg.supports_stop = traits.get_supports_stop();
msg.unique_id = get_default_unique_id("valve", valve);
fill_entity_info_base(valve, msg);
return encode_message_to_buffer(msg, ListEntitiesValveResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_info(valve, msg, ListEntitiesValveResponse::MESSAGE_TYPE, conn, remaining_size,
is_single);
}
void APIConnection::valve_command(const ValveCommandRequest &msg) {
ENTITY_COMMAND_MAKE_CALL(valve::Valve, valve, valve)
@@ -1049,8 +1007,8 @@ uint16_t APIConnection::try_send_media_player_state(EntityBase *entity, APIConne
resp.state = static_cast<enums::MediaPlayerState>(report_state);
resp.volume = media_player->volume;
resp.muted = media_player->is_muted();
fill_entity_state_base(media_player, resp);
return encode_message_to_buffer(resp, MediaPlayerStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_state(media_player, resp, MediaPlayerStateResponse::MESSAGE_TYPE, conn, remaining_size,
is_single);
}
uint16_t APIConnection::try_send_media_player_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
@@ -1067,9 +1025,8 @@ uint16_t APIConnection::try_send_media_player_info(EntityBase *entity, APIConnec
media_format.sample_bytes = supported_format.sample_bytes;
msg.supported_formats.push_back(media_format);
}
msg.unique_id = get_default_unique_id("media_player", media_player);
fill_entity_info_base(media_player, msg);
return encode_message_to_buffer(msg, ListEntitiesMediaPlayerResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_info(media_player, msg, ListEntitiesMediaPlayerResponse::MESSAGE_TYPE, conn,
remaining_size, is_single);
}
void APIConnection::media_player_command(const MediaPlayerCommandRequest &msg) {
ENTITY_COMMAND_MAKE_CALL(media_player::MediaPlayer, media_player, media_player)
@@ -1104,9 +1061,8 @@ uint16_t APIConnection::try_send_camera_info(EntityBase *entity, APIConnection *
bool is_single) {
auto *camera = static_cast<camera::Camera *>(entity);
ListEntitiesCameraResponse msg;
msg.unique_id = get_default_unique_id("camera", camera);
fill_entity_info_base(camera, msg);
return encode_message_to_buffer(msg, ListEntitiesCameraResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_info(camera, msg, ListEntitiesCameraResponse::MESSAGE_TYPE, conn, remaining_size,
is_single);
}
void APIConnection::camera_image(const CameraImageRequest &msg) {
if (camera::Camera::instance() == nullptr)
@@ -1284,8 +1240,8 @@ uint16_t APIConnection::try_send_alarm_control_panel_state(EntityBase *entity, A
auto *a_alarm_control_panel = static_cast<alarm_control_panel::AlarmControlPanel *>(entity);
AlarmControlPanelStateResponse resp;
resp.state = static_cast<enums::AlarmControlPanelState>(a_alarm_control_panel->get_state());
fill_entity_state_base(a_alarm_control_panel, resp);
return encode_message_to_buffer(resp, AlarmControlPanelStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_state(a_alarm_control_panel, resp, AlarmControlPanelStateResponse::MESSAGE_TYPE, conn,
remaining_size, is_single);
}
uint16_t APIConnection::try_send_alarm_control_panel_info(EntityBase *entity, APIConnection *conn,
uint32_t remaining_size, bool is_single) {
@@ -1294,10 +1250,8 @@ uint16_t APIConnection::try_send_alarm_control_panel_info(EntityBase *entity, AP
msg.supported_features = a_alarm_control_panel->get_supported_features();
msg.requires_code = a_alarm_control_panel->get_requires_code();
msg.requires_code_to_arm = a_alarm_control_panel->get_requires_code_to_arm();
msg.unique_id = get_default_unique_id("alarm_control_panel", a_alarm_control_panel);
fill_entity_info_base(a_alarm_control_panel, msg);
return encode_message_to_buffer(msg, ListEntitiesAlarmControlPanelResponse::MESSAGE_TYPE, conn, remaining_size,
is_single);
return fill_and_encode_entity_info(a_alarm_control_panel, msg, ListEntitiesAlarmControlPanelResponse::MESSAGE_TYPE,
conn, remaining_size, is_single);
}
void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) {
ENTITY_COMMAND_MAKE_CALL(alarm_control_panel::AlarmControlPanel, a_alarm_control_panel, alarm_control_panel)
@@ -1338,8 +1292,7 @@ uint16_t APIConnection::try_send_event_response(event::Event *event, const std::
uint32_t remaining_size, bool is_single) {
EventResponse resp;
resp.event_type = event_type;
fill_entity_state_base(event, resp);
return encode_message_to_buffer(resp, EventResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_state(event, resp, EventResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
uint16_t APIConnection::try_send_event_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -1349,9 +1302,8 @@ uint16_t APIConnection::try_send_event_info(EntityBase *entity, APIConnection *c
msg.device_class = event->get_device_class();
for (const auto &event_type : event->get_event_types())
msg.event_types.push_back(event_type);
msg.unique_id = get_default_unique_id("event", event);
fill_entity_info_base(event, msg);
return encode_message_to_buffer(msg, ListEntitiesEventResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_info(event, msg, ListEntitiesEventResponse::MESSAGE_TYPE, conn, remaining_size,
is_single);
}
#endif
@@ -1377,17 +1329,15 @@ uint16_t APIConnection::try_send_update_state(EntityBase *entity, APIConnection
resp.release_summary = update->update_info.summary;
resp.release_url = update->update_info.release_url;
}
fill_entity_state_base(update, resp);
return encode_message_to_buffer(resp, UpdateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_state(update, resp, UpdateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
uint16_t APIConnection::try_send_update_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *update = static_cast<update::UpdateEntity *>(entity);
ListEntitiesUpdateResponse msg;
msg.device_class = update->get_device_class();
msg.unique_id = get_default_unique_id("update", update);
fill_entity_info_base(update, msg);
return encode_message_to_buffer(msg, ListEntitiesUpdateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
return fill_and_encode_entity_info(update, msg, ListEntitiesUpdateResponse::MESSAGE_TYPE, conn, remaining_size,
is_single);
}
void APIConnection::update_command(const UpdateCommandRequest &msg) {
ENTITY_COMMAND_GET(update::UpdateEntity, update, update)
@@ -1410,9 +1360,6 @@ void APIConnection::update_command(const UpdateCommandRequest &msg) {
#endif
bool APIConnection::try_send_log_message(int level, const char *tag, const char *line, size_t message_len) {
if (this->flags_.log_subscription < level)
return false;
// Pre-calculate message size to avoid reallocations
uint32_t msg_size = 0;
@@ -1435,6 +1382,24 @@ bool APIConnection::try_send_log_message(int level, const char *tag, const char
return this->send_buffer(buffer, SubscribeLogsResponse::MESSAGE_TYPE);
}
void APIConnection::complete_authentication_() {
// Early return if already authenticated
if (this->flags_.connection_state == static_cast<uint8_t>(ConnectionState::AUTHENTICATED)) {
return;
}
this->flags_.connection_state = static_cast<uint8_t>(ConnectionState::AUTHENTICATED);
ESP_LOGD(TAG, "%s connected", this->get_client_combined_info().c_str());
#ifdef USE_API_CLIENT_CONNECTED_TRIGGER
this->parent_->get_client_connected_trigger()->trigger(this->client_info_, this->client_peername_);
#endif
#ifdef USE_HOMEASSISTANT_TIME
if (homeassistant::global_homeassistant_time != nullptr) {
this->send_time_request();
}
#endif
}
HelloResponse APIConnection::hello(const HelloRequest &msg) {
this->client_info_ = msg.client_info;
this->client_peername_ = this->helper_->getpeername();
@@ -1450,7 +1415,14 @@ HelloResponse APIConnection::hello(const HelloRequest &msg) {
resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
resp.name = App.get_name();
#ifdef USE_API_PASSWORD
// Password required - wait for authentication
this->flags_.connection_state = static_cast<uint8_t>(ConnectionState::CONNECTED);
#else
// No password configured - auto-authenticate
this->complete_authentication_();
#endif
return resp;
}
ConnectResponse APIConnection::connect(const ConnectRequest &msg) {
@@ -1463,29 +1435,22 @@ ConnectResponse APIConnection::connect(const ConnectRequest &msg) {
// bool invalid_password = 1;
resp.invalid_password = !correct;
if (correct) {
ESP_LOGD(TAG, "%s connected", this->get_client_combined_info().c_str());
this->flags_.connection_state = static_cast<uint8_t>(ConnectionState::AUTHENTICATED);
#ifdef USE_API_CLIENT_CONNECTED_TRIGGER
this->parent_->get_client_connected_trigger()->trigger(this->client_info_, this->client_peername_);
#endif
#ifdef USE_HOMEASSISTANT_TIME
if (homeassistant::global_homeassistant_time != nullptr) {
this->send_time_request();
}
#endif
this->complete_authentication_();
}
return resp;
}
DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
DeviceInfoResponse resp{};
#ifdef USE_API_PASSWORD
resp.uses_password = this->parent_->uses_password();
resp.uses_password = true;
#else
resp.uses_password = false;
#endif
resp.name = App.get_name();
resp.friendly_name = App.get_friendly_name();
#ifdef USE_AREAS
resp.suggested_area = App.get_area();
#endif
resp.mac_address = get_mac_address_pretty();
resp.esphome_version = ESPHOME_VERSION;
resp.compilation_time = App.get_compilation_time();
@@ -1551,8 +1516,8 @@ void APIConnection::on_home_assistant_state_response(const HomeAssistantStateRes
}
}
}
#ifdef USE_API_SERVICES
void APIConnection::execute_service(const ExecuteServiceRequest &msg) {
ESP_LOGD(TAG, "execute_service called with key: %u, args count: %zu", msg.key, msg.args.size());
bool found = false;
for (auto *service : this->parent_->get_user_services()) {
if (service->execute_service(msg)) {
@@ -1563,6 +1528,7 @@ void APIConnection::execute_service(const ExecuteServiceRequest &msg) {
ESP_LOGV(TAG, "Could not find service");
}
}
#endif
#ifdef USE_API_NOISE
NoiseEncryptionSetKeyResponse APIConnection::noise_encryption_set_key(const NoiseEncryptionSetKeyRequest &msg) {
psk_t psk{};
@@ -1595,7 +1561,7 @@ bool APIConnection::try_to_clear_buffer(bool log_out_of_space) {
APIError err = this->helper_->loop();
if (err != APIError::OK) {
on_fatal_error();
ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", this->get_client_combined_info().c_str(),
ESP_LOGW(TAG, "%s: Socket operation failed %s errno=%d", this->get_client_combined_info().c_str(),
api_error_to_str(err), errno);
return false;
}
@@ -1616,12 +1582,8 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) {
return false;
if (err != APIError::OK) {
on_fatal_error();
if (err == APIError::SOCKET_WRITE_FAILED && errno == ECONNRESET) {
ESP_LOGW(TAG, "%s: Connection reset", this->get_client_combined_info().c_str());
} else {
ESP_LOGW(TAG, "%s: Packet write failed %s errno=%d", this->get_client_combined_info().c_str(),
api_error_to_str(err), errno);
}
ESP_LOGW(TAG, "%s: Packet write failed %s errno=%d", this->get_client_combined_info().c_str(),
api_error_to_str(err), errno);
return false;
}
// Do not set last_traffic_ on send
@@ -1629,11 +1591,11 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) {
}
void APIConnection::on_unauthenticated_access() {
this->on_fatal_error();
ESP_LOGD(TAG, "%s requested access without authentication", this->get_client_combined_info().c_str());
ESP_LOGD(TAG, "%s access without authentication", this->get_client_combined_info().c_str());
}
void APIConnection::on_no_setup_connection() {
this->on_fatal_error();
ESP_LOGD(TAG, "%s requested access without full connection", this->get_client_combined_info().c_str());
ESP_LOGD(TAG, "%s access without full connection", this->get_client_combined_info().c_str());
}
void APIConnection::on_fatal_error() {
this->helper_->close();
@@ -1651,7 +1613,6 @@ void APIConnection::DeferredBatch::add_item(EntityBase *entity, MessageCreator c
item.creator.cleanup(message_type);
// Move assign the new creator
item.creator = std::move(creator);
item.estimated_size = estimated_size; // Update estimated size too
return;
}
}
@@ -1662,8 +1623,15 @@ void APIConnection::DeferredBatch::add_item(EntityBase *entity, MessageCreator c
void APIConnection::DeferredBatch::add_item_front(EntityBase *entity, MessageCreator creator, uint8_t message_type,
uint8_t estimated_size) {
// Insert at front for high priority messages (no deduplication check)
items.insert(items.begin(), BatchItem(entity, std::move(creator), message_type, estimated_size));
// Add high priority message and swap to front
// This avoids expensive vector::insert which shifts all elements
// Note: We only ever have one high-priority message at a time (ping OR disconnect)
// If we're disconnecting, pings are blocked, so this simple swap is sufficient
items.emplace_back(entity, std::move(creator), message_type, estimated_size);
if (items.size() > 1) {
// Swap the new high-priority item to the front
std::swap(items.front(), items.back());
}
}
bool APIConnection::schedule_batch_() {
@@ -1731,7 +1699,7 @@ void APIConnection::process_batch_() {
// Initialize buffer and tracking variables
this->parent_->get_shared_buffer_ref().clear();
// Pre-calculate exact buffer size needed based on stored estimated sizes
// Pre-calculate exact buffer size needed based on message types
uint32_t total_estimated_size = 0;
for (size_t i = 0; i < this->deferred_batch_.size(); i++) {
const auto &item = this->deferred_batch_[i];
@@ -1773,9 +1741,9 @@ void APIConnection::process_batch_() {
// Update tracking variables
items_processed++;
// After first message, set remaining size to MAX_PACKET_SIZE to avoid fragmentation
// After first message, set remaining size to MAX_BATCH_PACKET_SIZE to avoid fragmentation
if (items_processed == 1) {
remaining_size = MAX_PACKET_SIZE;
remaining_size = MAX_BATCH_PACKET_SIZE;
}
remaining_size -= payload_size;
// Calculate where the next message's header padding will start
@@ -1799,12 +1767,8 @@ void APIConnection::process_batch_() {
this->helper_->write_protobuf_packets(ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, packet_info);
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
on_fatal_error();
if (err == APIError::SOCKET_WRITE_FAILED && errno == ECONNRESET) {
ESP_LOGW(TAG, "%s: Connection reset during batch write", this->get_client_combined_info().c_str());
} else {
ESP_LOGW(TAG, "%s: Batch write failed %s errno=%d", this->get_client_combined_info().c_str(),
api_error_to_str(err), errno);
}
ESP_LOGW(TAG, "%s: Batch write failed %s errno=%d", this->get_client_combined_info().c_str(), api_error_to_str(err),
errno);
}
#ifdef HAS_PROTO_MESSAGE_DUMP

View File

@@ -195,7 +195,9 @@ class APIConnection : public APIServerConnection {
// TODO
return {};
}
#ifdef USE_API_SERVICES
void execute_service(const ExecuteServiceRequest &msg) override;
#endif
#ifdef USE_API_NOISE
NoiseEncryptionSetKeyResponse noise_encryption_set_key(const NoiseEncryptionSetKeyRequest &msg) override;
#endif
@@ -207,6 +209,7 @@ class APIConnection : public APIServerConnection {
return static_cast<ConnectionState>(this->flags_.connection_state) == ConnectionState::CONNECTED ||
this->is_authenticated();
}
uint8_t get_log_subscription_level() const { return this->flags_.log_subscription; }
void on_fatal_error() override;
void on_unauthenticated_access() override;
void on_no_setup_connection() override;
@@ -271,36 +274,45 @@ class APIConnection : public APIServerConnection {
ProtoWriteBuffer allocate_batch_message_buffer(uint16_t size);
protected:
// Helper function to fill common entity info fields
static void fill_entity_info_base(esphome::EntityBase *entity, InfoResponseProtoMessage &response) {
// Set common fields that are shared by all entity types
response.key = entity->get_object_id_hash();
response.object_id = entity->get_object_id();
if (entity->has_own_name())
response.name = entity->get_name();
// Set common EntityBase properties
response.icon = entity->get_icon();
response.disabled_by_default = entity->is_disabled_by_default();
response.entity_category = static_cast<enums::EntityCategory>(entity->get_entity_category());
#ifdef USE_DEVICES
response.device_id = entity->get_device_id();
#endif
}
// Helper function to fill common entity state fields
static void fill_entity_state_base(esphome::EntityBase *entity, StateResponseProtoMessage &response) {
response.key = entity->get_object_id_hash();
#ifdef USE_DEVICES
response.device_id = entity->get_device_id();
#endif
}
// Helper function to handle authentication completion
void complete_authentication_();
// 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);
// 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) {
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);
}
// 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) {
// Set common fields that are shared by all entity types
msg.key = entity->get_object_id_hash();
msg.object_id = entity->get_object_id();
if (entity->has_own_name())
msg.name = entity->get_name();
// Set common EntityBase properties
#ifdef USE_ENTITY_ICON
msg.icon = entity->get_icon();
#endif
msg.disabled_by_default = entity->is_disabled_by_default();
msg.entity_category = static_cast<enums::EntityCategory>(entity->get_entity_category());
#ifdef USE_DEVICES
msg.device_id = entity->get_device_id();
#endif
return encode_message_to_buffer(msg, message_type, conn, remaining_size, is_single);
}
#ifdef USE_VOICE_ASSISTANT
// Helper to check voice assistant validity and connection ownership
inline bool check_voice_assistant_api_connection_() const;
@@ -628,7 +640,7 @@ class APIConnection : public APIServerConnection {
// to send in one go. This is the maximum size of a single packet
// that can be sent over the network.
// This is to avoid fragmentation of the packet.
static constexpr size_t MAX_PACKET_SIZE = 1390; // MTU
static constexpr size_t MAX_BATCH_PACKET_SIZE = 1390; // MTU
bool schedule_batch_();
void process_batch_();
@@ -641,7 +653,7 @@ class APIConnection : public APIServerConnection {
// Helper to log a proto message from a MessageCreator object
void log_proto_message_(EntityBase *entity, const MessageCreator &creator, uint8_t message_type) {
this->flags_.log_only_mode = true;
creator(entity, this, MAX_PACKET_SIZE, true, message_type);
creator(entity, this, MAX_BATCH_PACKET_SIZE, true, message_type);
this->flags_.log_only_mode = false;
}
@@ -661,7 +673,7 @@ class APIConnection : public APIServerConnection {
if (this->flags_.should_try_send_immediately && this->get_batch_delay_ms_() == 0 &&
this->helper_->can_write_without_blocking()) {
// Now actually encode and send
if (creator(entity, this, MAX_PACKET_SIZE, true) &&
if (creator(entity, this, MAX_BATCH_PACKET_SIZE, true) &&
this->send_buffer(ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, message_type)) {
#ifdef HAS_PROTO_MESSAGE_DUMP
// Log the message in verbose mode

View File

@@ -5,7 +5,6 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include "proto.h"
#include "api_pb2_size.h"
#include <cstring>
#include <cinttypes>
@@ -613,7 +612,7 @@ APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) {
buffer->type = type;
return APIError::OK;
}
APIError APINoiseFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) {
APIError APINoiseFrameHelper::write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) {
// Resize to include MAC space (required for Noise encryption)
buffer.get_buffer()->resize(buffer.get_buffer()->size() + frame_footer_size_);
PacketInfo packet{type, 0,
@@ -1002,7 +1001,7 @@ APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
buffer->type = rx_header_parsed_type_;
return APIError::OK;
}
APIError APIPlaintextFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) {
APIError APIPlaintextFrameHelper::write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) {
PacketInfo packet{type, 0, static_cast<uint16_t>(buffer.get_buffer()->size() - frame_header_padding_)};
return write_protobuf_packets(buffer, std::span<const PacketInfo>(&packet, 1));
}

View File

@@ -30,13 +30,11 @@ struct ReadPacketBuffer {
// Packed packet info structure to minimize memory usage
struct PacketInfo {
uint16_t message_type; // 2 bytes
uint16_t offset; // 2 bytes (sufficient for packet size ~1460 bytes)
uint16_t payload_size; // 2 bytes (up to 65535 bytes)
uint16_t padding; // 2 byte (for alignment)
uint16_t offset; // Offset in buffer where message starts
uint16_t payload_size; // Size of the message payload
uint8_t message_type; // Message type (0-255)
PacketInfo(uint16_t type, uint16_t off, uint16_t size)
: message_type(type), offset(off), payload_size(size), padding(0) {}
PacketInfo(uint8_t type, uint16_t off, uint16_t size) : offset(off), payload_size(size), message_type(type) {}
};
enum class APIError : uint16_t {
@@ -98,7 +96,7 @@ class APIFrameHelper {
}
// Give this helper a name for logging
void set_log_info(std::string info) { info_ = std::move(info); }
virtual APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) = 0;
virtual APIError write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) = 0;
// Write multiple protobuf packets in a single operation
// packets contains (message_type, offset, length) for each message in the buffer
// The buffer contains all messages with appropriate padding before each
@@ -197,7 +195,7 @@ class APINoiseFrameHelper : public APIFrameHelper {
APIError init() override;
APIError loop() override;
APIError read_packet(ReadPacketBuffer *buffer) override;
APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) override;
APIError write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) override;
APIError write_protobuf_packets(ProtoWriteBuffer buffer, std::span<const PacketInfo> packets) override;
// Get the frame header padding required by this protocol
uint8_t frame_header_padding() override { return frame_header_padding_; }
@@ -251,7 +249,7 @@ class APIPlaintextFrameHelper : public APIFrameHelper {
APIError init() override;
APIError loop() override;
APIError read_packet(ReadPacketBuffer *buffer) override;
APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) override;
APIError write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) override;
APIError write_protobuf_packets(ProtoWriteBuffer buffer, std::span<const PacketInfo> packets) override;
uint8_t frame_header_padding() override { return frame_header_padding_; }
// Get the frame footer size required by this protocol

View File

@@ -23,3 +23,7 @@ extend google.protobuf.MessageOptions {
optional bool no_delay = 1040 [default=false];
optional string base_class = 1041;
}
extend google.protobuf.FieldOptions {
optional string field_ifdef = 1042;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -195,6 +195,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
this->on_home_assistant_state_response(msg);
break;
}
#ifdef USE_API_SERVICES
case 42: {
ExecuteServiceRequest msg;
msg.decode(msg_data, msg_size);
@@ -204,6 +205,7 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
this->on_execute_service_request(msg);
break;
}
#endif
#ifdef USE_CAMERA
case 45: {
CameraImageRequest msg;
@@ -660,11 +662,13 @@ void APIServerConnection::on_get_time_request(const GetTimeRequest &msg) {
}
}
}
#ifdef USE_API_SERVICES
void APIServerConnection::on_execute_service_request(const ExecuteServiceRequest &msg) {
if (this->check_authenticated_()) {
this->execute_service(msg);
}
}
#endif
#ifdef USE_API_NOISE
void APIServerConnection::on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &msg) {
if (this->check_authenticated_()) {

View File

@@ -69,7 +69,9 @@ class APIServerConnectionBase : public ProtoService {
virtual void on_get_time_request(const GetTimeRequest &value){};
virtual void on_get_time_response(const GetTimeResponse &value){};
#ifdef USE_API_SERVICES
virtual void on_execute_service_request(const ExecuteServiceRequest &value){};
#endif
#ifdef USE_CAMERA
virtual void on_camera_image_request(const CameraImageRequest &value){};
@@ -216,7 +218,9 @@ class APIServerConnection : public APIServerConnectionBase {
virtual void subscribe_homeassistant_services(const SubscribeHomeassistantServicesRequest &msg) = 0;
virtual void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) = 0;
virtual GetTimeResponse get_time(const GetTimeRequest &msg) = 0;
#ifdef USE_API_SERVICES
virtual void execute_service(const ExecuteServiceRequest &msg) = 0;
#endif
#ifdef USE_API_NOISE
virtual NoiseEncryptionSetKeyResponse noise_encryption_set_key(const NoiseEncryptionSetKeyRequest &msg) = 0;
#endif
@@ -333,7 +337,9 @@ class APIServerConnection : public APIServerConnectionBase {
void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &msg) override;
void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) override;
void on_get_time_request(const GetTimeRequest &msg) override;
#ifdef USE_API_SERVICES
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

View File

@@ -1,365 +0,0 @@
#pragma once
#include "proto.h"
#include <cstdint>
#include <string>
namespace esphome {
namespace api {
class ProtoSize {
public:
/**
* @brief ProtoSize class for Protocol Buffer serialization size calculation
*
* This class provides static methods to calculate the exact byte counts needed
* for encoding various Protocol Buffer field types. All methods are designed to be
* efficient for the common case where many fields have default values.
*
* Implements Protocol Buffer encoding size calculation according to:
* https://protobuf.dev/programming-guides/encoding/
*
* Key features:
* - Early-return optimization for zero/default values
* - Direct total_size updates to avoid unnecessary additions
* - Specialized handling for different field types according to protobuf spec
* - Templated helpers for repeated fields and messages
*/
/**
* @brief Calculates the size in bytes needed to encode a uint32_t value as a varint
*
* @param value The uint32_t value to calculate size for
* @return The number of bytes needed to encode the value
*/
static inline uint32_t varint(uint32_t value) {
// Optimized varint size calculation using leading zeros
// Each 7 bits requires one byte in the varint encoding
if (value < 128)
return 1; // 7 bits, common case for small values
// For larger values, count bytes needed based on the position of the highest bit set
if (value < 16384) {
return 2; // 14 bits
} else if (value < 2097152) {
return 3; // 21 bits
} else if (value < 268435456) {
return 4; // 28 bits
} else {
return 5; // 32 bits (maximum for uint32_t)
}
}
/**
* @brief Calculates the size in bytes needed to encode a uint64_t value as a varint
*
* @param value The uint64_t value to calculate size for
* @return The number of bytes needed to encode the value
*/
static inline uint32_t varint(uint64_t value) {
// Handle common case of values fitting in uint32_t (vast majority of use cases)
if (value <= UINT32_MAX) {
return varint(static_cast<uint32_t>(value));
}
// For larger values, determine size based on highest bit position
if (value < (1ULL << 35)) {
return 5; // 35 bits
} else if (value < (1ULL << 42)) {
return 6; // 42 bits
} else if (value < (1ULL << 49)) {
return 7; // 49 bits
} else if (value < (1ULL << 56)) {
return 8; // 56 bits
} else if (value < (1ULL << 63)) {
return 9; // 63 bits
} else {
return 10; // 64 bits (maximum for uint64_t)
}
}
/**
* @brief Calculates the size in bytes needed to encode an int32_t value as a varint
*
* Special handling is needed for negative values, which are sign-extended to 64 bits
* in Protocol Buffers, resulting in a 10-byte varint.
*
* @param value The int32_t value to calculate size for
* @return The number of bytes needed to encode the value
*/
static inline uint32_t varint(int32_t value) {
// Negative values are sign-extended to 64 bits in protocol buffers,
// which always results in a 10-byte varint for negative int32
if (value < 0) {
return 10; // Negative int32 is always 10 bytes long
}
// For non-negative values, use the uint32_t implementation
return varint(static_cast<uint32_t>(value));
}
/**
* @brief Calculates the size in bytes needed to encode an int64_t value as a varint
*
* @param value The int64_t value to calculate size for
* @return The number of bytes needed to encode the value
*/
static inline uint32_t varint(int64_t value) {
// For int64_t, we convert to uint64_t and calculate the size
// This works because the bit pattern determines the encoding size,
// and we've handled negative int32 values as a special case above
return varint(static_cast<uint64_t>(value));
}
/**
* @brief Calculates the size in bytes needed to encode a field ID and wire type
*
* @param field_id The field identifier
* @param type The wire type value (from the WireType enum in the protobuf spec)
* @return The number of bytes needed to encode the field ID and wire type
*/
static inline uint32_t field(uint32_t field_id, uint32_t type) {
uint32_t tag = (field_id << 3) | (type & 0b111);
return varint(tag);
}
/**
* @brief Common parameters for all add_*_field methods
*
* All add_*_field methods follow these common patterns:
*
* @param total_size Reference to the total message size to update
* @param precalced_field_id_size Pre-calculated size of the field ID in bytes
* @param value The value to calculate size for (type varies)
* @param force Whether to calculate size even if the value is default/zero/empty
*
* Each method follows this implementation pattern:
* 1. Skip calculation if value is default (0, false, empty) and not forced
* 2. Calculate the size based on the field's encoding rules
* 3. Add the precalced_field_id_size + calculated value size to total_size
*/
/**
* @brief Calculates and adds the size of an int32 field to the total message size
*/
static inline void add_int32_field(uint32_t &total_size, uint32_t precalced_field_id_size, int32_t value,
bool force = false) {
// Skip calculation if value is zero and not forced
if (value == 0 && !force) {
return; // No need to update total_size
}
// Calculate and directly add to total_size
if (value < 0) {
// Negative values are encoded as 10-byte varints in protobuf
total_size += precalced_field_id_size + 10;
} else {
// For non-negative values, use the standard varint size
total_size += precalced_field_id_size + varint(static_cast<uint32_t>(value));
}
}
/**
* @brief Calculates and adds the size of a uint32 field to the total message size
*/
static inline void add_uint32_field(uint32_t &total_size, uint32_t precalced_field_id_size, uint32_t value,
bool force = false) {
// Skip calculation if value is zero and not forced
if (value == 0 && !force) {
return; // No need to update total_size
}
// Calculate and directly add to total_size
total_size += precalced_field_id_size + varint(value);
}
/**
* @brief Calculates and adds the size of a boolean field to the total message size
*/
static inline void add_bool_field(uint32_t &total_size, uint32_t precalced_field_id_size, bool value,
bool force = false) {
// Skip calculation if value is false and not forced
if (!value && !force) {
return; // No need to update total_size
}
// Boolean fields always use 1 byte when true
total_size += precalced_field_id_size + 1;
}
/**
* @brief Calculates and adds the size of a fixed field to the total message size
*
* Fixed fields always take exactly N bytes (4 for fixed32/float, 8 for fixed64/double).
*
* @tparam NumBytes The number of bytes for this fixed field (4 or 8)
* @param is_nonzero Whether the value is non-zero
*/
template<uint32_t NumBytes>
static inline void add_fixed_field(uint32_t &total_size, uint32_t precalced_field_id_size, bool is_nonzero,
bool force = false) {
// Skip calculation if value is zero and not forced
if (!is_nonzero && !force) {
return; // No need to update total_size
}
// Fixed fields always take exactly NumBytes
total_size += precalced_field_id_size + NumBytes;
}
/**
* @brief Calculates and adds the size of an enum field to the total message size
*
* Enum fields are encoded as uint32 varints.
*/
static inline void add_enum_field(uint32_t &total_size, uint32_t precalced_field_id_size, uint32_t value,
bool force = false) {
// Skip calculation if value is zero and not forced
if (value == 0 && !force) {
return; // No need to update total_size
}
// Enums are encoded as uint32
total_size += precalced_field_id_size + varint(value);
}
/**
* @brief Calculates and adds the size of a sint32 field to the total message size
*
* Sint32 fields use ZigZag encoding, which is more efficient for negative values.
*/
static inline void add_sint32_field(uint32_t &total_size, uint32_t precalced_field_id_size, int32_t value,
bool force = false) {
// Skip calculation if value is zero and not forced
if (value == 0 && !force) {
return; // No need to update total_size
}
// ZigZag encoding for sint32: (n << 1) ^ (n >> 31)
uint32_t zigzag = (static_cast<uint32_t>(value) << 1) ^ (static_cast<uint32_t>(value >> 31));
total_size += precalced_field_id_size + varint(zigzag);
}
/**
* @brief Calculates and adds the size of an int64 field to the total message size
*/
static inline void add_int64_field(uint32_t &total_size, uint32_t precalced_field_id_size, int64_t value,
bool force = false) {
// Skip calculation if value is zero and not forced
if (value == 0 && !force) {
return; // No need to update total_size
}
// Calculate and directly add to total_size
total_size += precalced_field_id_size + varint(value);
}
/**
* @brief Calculates and adds the size of a uint64 field to the total message size
*/
static inline void add_uint64_field(uint32_t &total_size, uint32_t precalced_field_id_size, uint64_t value,
bool force = false) {
// Skip calculation if value is zero and not forced
if (value == 0 && !force) {
return; // No need to update total_size
}
// Calculate and directly add to total_size
total_size += precalced_field_id_size + varint(value);
}
/**
* @brief Calculates and adds the size of a sint64 field to the total message size
*
* Sint64 fields use ZigZag encoding, which is more efficient for negative values.
*/
static inline void add_sint64_field(uint32_t &total_size, uint32_t precalced_field_id_size, int64_t value,
bool force = false) {
// Skip calculation if value is zero and not forced
if (value == 0 && !force) {
return; // No need to update total_size
}
// ZigZag encoding for sint64: (n << 1) ^ (n >> 63)
uint64_t zigzag = (static_cast<uint64_t>(value) << 1) ^ (static_cast<uint64_t>(value >> 63));
total_size += precalced_field_id_size + varint(zigzag);
}
/**
* @brief Calculates and adds the size of a string/bytes field to the total message size
*/
static inline void add_string_field(uint32_t &total_size, uint32_t precalced_field_id_size, const std::string &str,
bool force = false) {
// Skip calculation if string is empty and not forced
if (str.empty() && !force) {
return; // No need to update total_size
}
// Calculate and directly add to total_size
const uint32_t str_size = static_cast<uint32_t>(str.size());
total_size += precalced_field_id_size + varint(str_size) + str_size;
}
/**
* @brief Calculates and adds the size of a nested message field to the total message size
*
* This helper function directly updates the total_size reference if the nested size
* is greater than zero or force is true.
*
* @param nested_size The pre-calculated size of the nested message
*/
static inline void add_message_field(uint32_t &total_size, uint32_t precalced_field_id_size, uint32_t nested_size,
bool force = false) {
// Skip calculation if nested message is empty and not forced
if (nested_size == 0 && !force) {
return; // No need to update total_size
}
// Calculate and directly add to total_size
// Field ID + length varint + nested message content
total_size += precalced_field_id_size + varint(nested_size) + nested_size;
}
/**
* @brief Calculates and adds the size of a nested message field to the total message size
*
* This version takes a ProtoMessage object, calculates its size internally,
* and updates the total_size reference. This eliminates the need for a temporary variable
* at the call site.
*
* @param message The nested message object
*/
static inline void add_message_object(uint32_t &total_size, uint32_t precalced_field_id_size,
const ProtoMessage &message, bool force = false) {
uint32_t nested_size = 0;
message.calculate_size(nested_size);
// Use the base implementation with the calculated nested_size
add_message_field(total_size, precalced_field_id_size, nested_size, force);
}
/**
* @brief Calculates and adds the sizes of all messages in a repeated field to the total message size
*
* This helper processes a vector of message objects, calculating the size for each message
* and adding it to the total size.
*
* @tparam MessageType The type of the nested messages in the vector
* @param messages Vector of message objects
*/
template<typename MessageType>
static inline void add_repeated_message(uint32_t &total_size, uint32_t precalced_field_id_size,
const std::vector<MessageType> &messages) {
// Skip if the vector is empty
if (messages.empty()) {
return;
}
// For repeated fields, always use force=true
for (const auto &message : messages) {
add_message_object(total_size, precalced_field_id_size, message, true);
}
}
};
} // namespace api
} // namespace esphome

View File

@@ -24,14 +24,6 @@ static const char *const TAG = "api";
// APIServer
APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
#ifndef USE_API_YAML_SERVICES
// Global empty vector to avoid guard variables (saves 8 bytes)
// This is initialized at program startup before any threads
static const std::vector<UserServiceDescriptor *> empty_user_services{};
const std::vector<UserServiceDescriptor *> &get_empty_user_services_instance() { return empty_user_services; }
#endif
APIServer::APIServer() {
global_api_server = this;
// Pre-allocate shared write buffer
@@ -39,7 +31,6 @@ APIServer::APIServer() {
}
void APIServer::setup() {
ESP_LOGCONFIG(TAG, "Running setup");
this->setup_controller();
#ifdef USE_API_NOISE
@@ -113,7 +104,7 @@ void APIServer::setup() {
return;
}
for (auto &c : this->clients_) {
if (!c->flags_.remove)
if (!c->flags_.remove && c->get_log_subscription_level() >= level)
c->try_send_log_message(level, tag, message, message_len);
}
});
@@ -213,22 +204,20 @@ void APIServer::loop() {
void APIServer::dump_config() {
ESP_LOGCONFIG(TAG,
"API Server:\n"
"Server:\n"
" Address: %s:%u",
network::get_use_address().c_str(), this->port_);
#ifdef USE_API_NOISE
ESP_LOGCONFIG(TAG, " Using noise encryption: %s", YESNO(this->noise_ctx_->has_psk()));
ESP_LOGCONFIG(TAG, " Noise encryption: %s", YESNO(this->noise_ctx_->has_psk()));
if (!this->noise_ctx_->has_psk()) {
ESP_LOGCONFIG(TAG, " Supports noise encryption: YES");
ESP_LOGCONFIG(TAG, " Supports encryption: YES");
}
#else
ESP_LOGCONFIG(TAG, " Using noise encryption: NO");
ESP_LOGCONFIG(TAG, " Noise encryption: NO");
#endif
}
#ifdef USE_API_PASSWORD
bool APIServer::uses_password() const { return !this->password_.empty(); }
bool APIServer::check_password(const std::string &password) const {
// depend only on input password length
const char *a = this->password_.c_str();
@@ -436,7 +425,7 @@ bool APIServer::save_noise_psk(psk_t psk, bool make_active) {
ESP_LOGD(TAG, "Noise PSK saved");
if (make_active) {
this->set_timeout(100, [this, psk]() {
ESP_LOGW(TAG, "Disconnecting all clients to reset connections");
ESP_LOGW(TAG, "Disconnecting all clients to reset PSK");
this->set_noise_psk(psk);
for (auto &c : this->clients_) {
c->send_message(DisconnectRequest());

View File

@@ -12,7 +12,9 @@
#include "esphome/core/log.h"
#include "list_entities.h"
#include "subscribe_state.h"
#ifdef USE_API_SERVICES
#include "user_services.h"
#endif
#include <vector>
@@ -25,11 +27,6 @@ struct SavedNoisePsk {
} PACKED; // NOLINT
#endif
#ifndef USE_API_YAML_SERVICES
// Forward declaration of helper function
const std::vector<UserServiceDescriptor *> &get_empty_user_services_instance();
#endif
class APIServer : public Component, public Controller {
public:
APIServer();
@@ -42,7 +39,6 @@ class APIServer : public Component, public Controller {
bool teardown() override;
#ifdef USE_API_PASSWORD
bool check_password(const std::string &password) const;
bool uses_password() const;
void set_password(const std::string &password);
#endif
void set_port(uint16_t port);
@@ -112,18 +108,9 @@ class APIServer : public Component, public Controller {
void on_media_player_update(media_player::MediaPlayer *obj) override;
#endif
void send_homeassistant_service_call(const HomeassistantServiceResponse &call);
void register_user_service(UserServiceDescriptor *descriptor) {
#ifdef USE_API_YAML_SERVICES
// Vector is pre-allocated when services are defined in YAML
this->user_services_.push_back(descriptor);
#else
// Lazy allocate vector on first use for CustomAPIDevice
if (!this->user_services_) {
this->user_services_ = std::make_unique<std::vector<UserServiceDescriptor *>>();
}
this->user_services_->push_back(descriptor);
#ifdef USE_API_SERVICES
void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); }
#endif
}
#ifdef USE_HOMEASSISTANT_TIME
void request_time();
#endif
@@ -152,17 +139,9 @@ class APIServer : public Component, public Controller {
void get_home_assistant_state(std::string entity_id, optional<std::string> attribute,
std::function<void(std::string)> f);
const std::vector<HomeAssistantStateSubscription> &get_state_subs() const;
const std::vector<UserServiceDescriptor *> &get_user_services() const {
#ifdef USE_API_YAML_SERVICES
return this->user_services_;
#else
if (this->user_services_) {
return *this->user_services_;
}
// Return reference to global empty instance (no guard needed)
return get_empty_user_services_instance();
#ifdef USE_API_SERVICES
const std::vector<UserServiceDescriptor *> &get_user_services() const { return this->user_services_; }
#endif
}
#ifdef USE_API_CLIENT_CONNECTED_TRIGGER
Trigger<std::string, std::string> *get_client_connected_trigger() const { return this->client_connected_trigger_; }
@@ -194,14 +173,8 @@ class APIServer : public Component, public Controller {
#endif
std::vector<uint8_t> shared_write_buffer_; // Shared proto write buffer for all connections
std::vector<HomeAssistantStateSubscription> state_subs_;
#ifdef USE_API_YAML_SERVICES
// When services are defined in YAML, we know at compile time that services will be registered
#ifdef USE_API_SERVICES
std::vector<UserServiceDescriptor *> user_services_;
#else
// Services can still be registered at runtime by CustomAPIDevice components even when not
// defined in YAML. Using unique_ptr allows lazy allocation, saving 12 bytes in the common
// case where no services (YAML or custom) are used.
std::unique_ptr<std::vector<UserServiceDescriptor *>> user_services_;
#endif
// Group smaller types together

View File

@@ -3,10 +3,13 @@
#include <map>
#include "api_server.h"
#ifdef USE_API
#ifdef USE_API_SERVICES
#include "user_services.h"
#endif
namespace esphome {
namespace api {
#ifdef USE_API_SERVICES
template<typename T, typename... Ts> class CustomAPIDeviceService : public UserServiceBase<Ts...> {
public:
CustomAPIDeviceService(const std::string &name, const std::array<std::string, sizeof...(Ts)> &arg_names, T *obj,
@@ -19,6 +22,7 @@ template<typename T, typename... Ts> class CustomAPIDeviceService : public UserS
T *obj_;
void (T::*callback_)(Ts...);
};
#endif // USE_API_SERVICES
class CustomAPIDevice {
public:
@@ -46,12 +50,14 @@ class CustomAPIDevice {
* @param name The name of the service to register.
* @param arg_names The name of the arguments for the service, must match the arguments of the function.
*/
#ifdef USE_API_SERVICES
template<typename T, typename... Ts>
void register_service(void (T::*callback)(Ts...), const std::string &name,
const std::array<std::string, sizeof...(Ts)> &arg_names) {
auto *service = new CustomAPIDeviceService<T, Ts...>(name, arg_names, (T *) this, callback); // NOLINT
global_api_server->register_user_service(service);
}
#endif
/** Register a custom native API service that will show up in Home Assistant.
*
@@ -71,10 +77,12 @@ class CustomAPIDevice {
* @param callback The member function to call when the service is triggered.
* @param name The name of the arguments for the service, must match the arguments of the function.
*/
#ifdef USE_API_SERVICES
template<typename T> void register_service(void (T::*callback)(), const std::string &name) {
auto *service = new CustomAPIDeviceService<T>(name, {}, (T *) this, callback); // NOLINT
global_api_server->register_user_service(service);
}
#endif
/** Subscribe to the state (or attribute state) of an entity from Home Assistant.
*

View File

@@ -83,10 +83,12 @@ bool ListEntitiesIterator::on_end() { return this->client_->send_list_info_done(
ListEntitiesIterator::ListEntitiesIterator(APIConnection *client) : client_(client) {}
#ifdef USE_API_SERVICES
bool ListEntitiesIterator::on_service(UserServiceDescriptor *service) {
auto resp = service->encode_list_service_response();
return this->client_->send_message(resp);
}
#endif
} // namespace api
} // namespace esphome

View File

@@ -44,7 +44,9 @@ class ListEntitiesIterator : public ComponentIterator {
#ifdef USE_TEXT_SENSOR
bool on_text_sensor(text_sensor::TextSensor *entity) override;
#endif
#ifdef USE_API_SERVICES
bool on_service(UserServiceDescriptor *service) override;
#endif
#ifdef USE_CAMERA
bool on_camera(camera::Camera *entity) override;
#endif

View File

@@ -2,14 +2,84 @@
#include <cinttypes>
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include "api_pb2_size.h"
namespace esphome {
namespace api {
static const char *const TAG = "api.proto";
// Message handler registries are defined in api_pb2.cpp
void ProtoMessage::decode(const uint8_t *buffer, size_t length) {
uint32_t i = 0;
bool error = false;
while (i < length) {
uint32_t consumed;
auto res = ProtoVarInt::parse(&buffer[i], length - i, &consumed);
if (!res.has_value()) {
ESP_LOGV(TAG, "Invalid field start at %" PRIu32, i);
break;
}
uint32_t field_type = (res->as_uint32()) & 0b111;
uint32_t field_id = (res->as_uint32()) >> 3;
i += consumed;
switch (field_type) {
case 0: { // VarInt
res = ProtoVarInt::parse(&buffer[i], length - i, &consumed);
if (!res.has_value()) {
ESP_LOGV(TAG, "Invalid VarInt at %" PRIu32, i);
error = true;
break;
}
if (!this->decode_varint(field_id, *res)) {
ESP_LOGV(TAG, "Cannot decode VarInt field %" PRIu32 " with value %" PRIu32 "!", field_id, res->as_uint32());
}
i += consumed;
break;
}
case 2: { // Length-delimited
res = ProtoVarInt::parse(&buffer[i], length - i, &consumed);
if (!res.has_value()) {
ESP_LOGV(TAG, "Invalid Length Delimited at %" PRIu32, i);
error = true;
break;
}
uint32_t field_length = res->as_uint32();
i += consumed;
if (field_length > length - i) {
ESP_LOGV(TAG, "Out-of-bounds Length Delimited at %" PRIu32, i);
error = true;
break;
}
if (!this->decode_length(field_id, ProtoLengthDelimited(&buffer[i], field_length))) {
ESP_LOGV(TAG, "Cannot decode Length Delimited field %" PRIu32 "!", field_id);
}
i += field_length;
break;
}
case 5: { // 32-bit
if (length - i < 4) {
ESP_LOGV(TAG, "Out-of-bounds Fixed32-bit at %" PRIu32, i);
error = true;
break;
}
uint32_t val = encode_uint32(buffer[i + 3], buffer[i + 2], buffer[i + 1], buffer[i]);
if (!this->decode_32bit(field_id, Proto32Bit(val))) {
ESP_LOGV(TAG, "Cannot decode 32-bit field %" PRIu32 " with value %" PRIu32 "!", field_id, val);
}
i += 4;
break;
}
default:
ESP_LOGV(TAG, "Invalid field type at %" PRIu32, i);
error = true;
break;
}
if (error) {
break;
}
}
}
#ifdef HAS_PROTO_MESSAGE_DUMP
std::string ProtoMessage::dump() const {
@@ -19,554 +89,5 @@ std::string ProtoMessage::dump() const {
}
#endif
// Helper to get vector size for any repeated field type
static inline size_t get_vector_size(ProtoFieldType type, const void *field_addr) {
switch (type) {
case ProtoFieldType::TYPE_BOOL:
return static_cast<const std::vector<bool> *>(field_addr)->size();
case ProtoFieldType::TYPE_INT32:
case ProtoFieldType::TYPE_SINT32:
case ProtoFieldType::TYPE_SFIXED32:
return static_cast<const std::vector<int32_t> *>(field_addr)->size();
case ProtoFieldType::TYPE_UINT32:
case ProtoFieldType::TYPE_ENUM:
case ProtoFieldType::TYPE_FIXED32:
return static_cast<const std::vector<uint32_t> *>(field_addr)->size();
case ProtoFieldType::TYPE_INT64:
case ProtoFieldType::TYPE_SINT64:
return static_cast<const std::vector<int64_t> *>(field_addr)->size();
case ProtoFieldType::TYPE_UINT64:
return static_cast<const std::vector<uint64_t> *>(field_addr)->size();
case ProtoFieldType::TYPE_FLOAT:
return static_cast<const std::vector<float> *>(field_addr)->size();
case ProtoFieldType::TYPE_STRING:
case ProtoFieldType::TYPE_BYTES:
return static_cast<const std::vector<std::string> *>(field_addr)->size();
default:
return 0;
}
}
// Helper to get a pointer to the nth element in a vector (const version)
static inline const void *get_vector_element(ProtoFieldType type, const void *field_addr, size_t index) {
switch (type) {
case ProtoFieldType::TYPE_BOOL: {
// std::vector<bool> is special - we need to handle it differently
static bool temp_bool;
temp_bool = (*static_cast<const std::vector<bool> *>(field_addr))[index];
return &temp_bool;
}
case ProtoFieldType::TYPE_INT32:
case ProtoFieldType::TYPE_SINT32:
case ProtoFieldType::TYPE_SFIXED32:
return &(*static_cast<const std::vector<int32_t> *>(field_addr))[index];
case ProtoFieldType::TYPE_UINT32:
case ProtoFieldType::TYPE_ENUM:
case ProtoFieldType::TYPE_FIXED32:
return &(*static_cast<const std::vector<uint32_t> *>(field_addr))[index];
case ProtoFieldType::TYPE_INT64:
case ProtoFieldType::TYPE_SINT64:
return &(*static_cast<const std::vector<int64_t> *>(field_addr))[index];
case ProtoFieldType::TYPE_UINT64:
return &(*static_cast<const std::vector<uint64_t> *>(field_addr))[index];
case ProtoFieldType::TYPE_FLOAT:
return &(*static_cast<const std::vector<float> *>(field_addr))[index];
case ProtoFieldType::TYPE_STRING:
case ProtoFieldType::TYPE_BYTES:
return &(*static_cast<const std::vector<std::string> *>(field_addr))[index];
default:
return nullptr;
}
}
// Unified encode function that works for both single fields and repeated fields
static inline void encode_field(ProtoWriteBuffer &buffer, ProtoFieldType type, uint8_t field_num,
const void *field_addr, bool force) {
switch (type) {
case ProtoFieldType::TYPE_BOOL:
buffer.encode_bool(field_num, *static_cast<const bool *>(field_addr), force);
break;
case ProtoFieldType::TYPE_INT32:
buffer.encode_int32(field_num, *static_cast<const int32_t *>(field_addr), force);
break;
case ProtoFieldType::TYPE_UINT32:
case ProtoFieldType::TYPE_ENUM:
buffer.encode_uint32(field_num, *static_cast<const uint32_t *>(field_addr), force);
break;
case ProtoFieldType::TYPE_INT64:
buffer.encode_int64(field_num, *static_cast<const int64_t *>(field_addr), force);
break;
case ProtoFieldType::TYPE_UINT64:
buffer.encode_uint64(field_num, *static_cast<const uint64_t *>(field_addr), force);
break;
case ProtoFieldType::TYPE_SINT32:
buffer.encode_sint32(field_num, *static_cast<const int32_t *>(field_addr), force);
break;
case ProtoFieldType::TYPE_SINT64:
buffer.encode_sint64(field_num, *static_cast<const int64_t *>(field_addr), force);
break;
case ProtoFieldType::TYPE_STRING:
buffer.encode_string(field_num, *static_cast<const std::string *>(field_addr), force);
break;
case ProtoFieldType::TYPE_BYTES: {
const auto *str = static_cast<const std::string *>(field_addr);
buffer.encode_bytes(field_num, reinterpret_cast<const uint8_t *>(str->data()), str->size(), force);
break;
}
case ProtoFieldType::TYPE_FLOAT:
buffer.encode_float(field_num, *static_cast<const float *>(field_addr), force);
break;
case ProtoFieldType::TYPE_FIXED32:
buffer.encode_fixed32(field_num, *static_cast<const uint32_t *>(field_addr), force);
break;
case ProtoFieldType::TYPE_SFIXED32:
buffer.encode_sfixed32(field_num, *static_cast<const int32_t *>(field_addr), force);
break;
default:
break;
}
}
// Unified size calculation
static inline void calculate_field_size(uint32_t &total_size, ProtoFieldType type, uint8_t precalc_size,
const void *field_addr, bool force) {
switch (type) {
case ProtoFieldType::TYPE_BOOL:
ProtoSize::add_bool_field(total_size, precalc_size, *static_cast<const bool *>(field_addr), force);
break;
case ProtoFieldType::TYPE_INT32:
ProtoSize::add_int32_field(total_size, precalc_size, *static_cast<const int32_t *>(field_addr), force);
break;
case ProtoFieldType::TYPE_UINT32:
case ProtoFieldType::TYPE_ENUM:
ProtoSize::add_uint32_field(total_size, precalc_size, *static_cast<const uint32_t *>(field_addr), force);
break;
case ProtoFieldType::TYPE_INT64:
ProtoSize::add_int64_field(total_size, precalc_size, *static_cast<const int64_t *>(field_addr), force);
break;
case ProtoFieldType::TYPE_UINT64:
ProtoSize::add_uint64_field(total_size, precalc_size, *static_cast<const uint64_t *>(field_addr), force);
break;
case ProtoFieldType::TYPE_SINT32:
ProtoSize::add_sint32_field(total_size, precalc_size, *static_cast<const int32_t *>(field_addr), force);
break;
case ProtoFieldType::TYPE_SINT64:
ProtoSize::add_sint64_field(total_size, precalc_size, *static_cast<const int64_t *>(field_addr), force);
break;
case ProtoFieldType::TYPE_STRING:
case ProtoFieldType::TYPE_BYTES:
ProtoSize::add_string_field(total_size, precalc_size, *static_cast<const std::string *>(field_addr), force);
break;
case ProtoFieldType::TYPE_FLOAT:
case ProtoFieldType::TYPE_FIXED32:
case ProtoFieldType::TYPE_SFIXED32: {
// All 32-bit fixed types use the same size calculation
// Check if the 4-byte value is non-zero by treating as uint32_t
uint32_t val = *static_cast<const uint32_t *>(field_addr);
ProtoSize::add_fixed_field<4>(total_size, precalc_size, val != 0, force);
break;
}
default:
break;
}
}
// Decode varint for single fields
static inline bool decode_varint_field(ProtoFieldType type, void *field_addr, const ProtoVarInt &value) {
switch (type) {
case ProtoFieldType::TYPE_BOOL:
*static_cast<bool *>(field_addr) = value.as_bool();
return true;
case ProtoFieldType::TYPE_INT32:
*static_cast<int32_t *>(field_addr) = value.as_int32();
return true;
case ProtoFieldType::TYPE_UINT32:
case ProtoFieldType::TYPE_ENUM:
*static_cast<uint32_t *>(field_addr) = value.as_uint32();
return true;
case ProtoFieldType::TYPE_INT64:
*static_cast<int64_t *>(field_addr) = value.as_int64();
return true;
case ProtoFieldType::TYPE_UINT64:
*static_cast<uint64_t *>(field_addr) = value.as_uint64();
return true;
case ProtoFieldType::TYPE_SINT32:
*static_cast<int32_t *>(field_addr) = value.as_sint32();
return true;
case ProtoFieldType::TYPE_SINT64:
*static_cast<int64_t *>(field_addr) = value.as_sint64();
return true;
default:
return false;
}
}
// Decode varint for repeated fields
static inline bool decode_repeated_varint_field(ProtoFieldType type, void *field_addr, const ProtoVarInt &value) {
switch (type) {
case ProtoFieldType::TYPE_BOOL:
static_cast<std::vector<bool> *>(field_addr)->push_back(value.as_bool());
return true;
case ProtoFieldType::TYPE_INT32:
static_cast<std::vector<int32_t> *>(field_addr)->push_back(value.as_int32());
return true;
case ProtoFieldType::TYPE_UINT32:
case ProtoFieldType::TYPE_ENUM:
static_cast<std::vector<uint32_t> *>(field_addr)->push_back(value.as_uint32());
return true;
case ProtoFieldType::TYPE_INT64:
static_cast<std::vector<int64_t> *>(field_addr)->push_back(value.as_int64());
return true;
case ProtoFieldType::TYPE_UINT64:
static_cast<std::vector<uint64_t> *>(field_addr)->push_back(value.as_uint64());
return true;
case ProtoFieldType::TYPE_SINT32:
static_cast<std::vector<int32_t> *>(field_addr)->push_back(value.as_sint32());
return true;
case ProtoFieldType::TYPE_SINT64:
static_cast<std::vector<int64_t> *>(field_addr)->push_back(value.as_sint64());
return true;
default:
return false;
}
}
// Decode 32-bit for single fields
static inline bool decode_32bit_field(ProtoFieldType type, void *field_addr, const Proto32Bit &value) {
switch (type) {
case ProtoFieldType::TYPE_FLOAT:
*static_cast<float *>(field_addr) = value.as_float();
return true;
case ProtoFieldType::TYPE_FIXED32:
*static_cast<uint32_t *>(field_addr) = value.as_fixed32();
return true;
case ProtoFieldType::TYPE_SFIXED32:
*static_cast<int32_t *>(field_addr) = value.as_sfixed32();
return true;
default:
return false;
}
}
// Decode 32-bit for repeated fields
static inline bool decode_repeated_32bit_field(ProtoFieldType type, void *field_addr, const Proto32Bit &value) {
switch (type) {
case ProtoFieldType::TYPE_FLOAT:
static_cast<std::vector<float> *>(field_addr)->push_back(value.as_float());
return true;
case ProtoFieldType::TYPE_FIXED32:
static_cast<std::vector<uint32_t> *>(field_addr)->push_back(value.as_fixed32());
return true;
case ProtoFieldType::TYPE_SFIXED32:
static_cast<std::vector<int32_t> *>(field_addr)->push_back(value.as_sfixed32());
return true;
default:
return false;
}
}
// Decode length-delimited for single fields
static inline bool decode_length_field(ProtoFieldType type, void *field_addr, const ProtoLengthDelimited &value,
uint8_t message_type_id) {
switch (type) {
case ProtoFieldType::TYPE_STRING:
case ProtoFieldType::TYPE_BYTES:
*static_cast<std::string *>(field_addr) = value.as_string();
return true;
case ProtoFieldType::TYPE_MESSAGE:
if (message_type_id < MESSAGE_HANDLER_COUNT && MESSAGE_HANDLERS[message_type_id].decode != nullptr) {
return MESSAGE_HANDLERS[message_type_id].decode(field_addr, value);
}
return false;
default:
return false;
}
}
// Decode length-delimited for repeated fields
static inline bool decode_repeated_length_field(ProtoFieldType type, void *field_addr,
const ProtoLengthDelimited &value, uint8_t message_type_id) {
switch (type) {
case ProtoFieldType::TYPE_STRING:
case ProtoFieldType::TYPE_BYTES:
static_cast<std::vector<std::string> *>(field_addr)->push_back(value.as_string());
return true;
case ProtoFieldType::TYPE_MESSAGE:
if (message_type_id < REPEATED_MESSAGE_HANDLER_COUNT &&
REPEATED_MESSAGE_HANDLERS[message_type_id].decode != nullptr) {
return REPEATED_MESSAGE_HANDLERS[message_type_id].decode(field_addr, value);
}
return false;
default:
return false;
}
}
void ProtoMessage::decode(const uint8_t *buffer, size_t length) {
uint8_t *base = reinterpret_cast<uint8_t *>(this);
// Get metadata
const FieldMeta *fields = get_field_metadata();
uint8_t field_count = get_field_count();
const RepeatedFieldMeta *repeated_fields = get_repeated_field_metadata();
uint8_t repeated_count = get_repeated_field_count();
// Handle empty message
if (field_count == 0 && repeated_count == 0) {
if (length > 0) {
ESP_LOGW(TAG, "Received data for message with no fields");
}
return;
}
uint32_t i = 0;
while (i < length) {
// Parse field tag
uint32_t consumed;
auto tag_res = ProtoVarInt::parse(&buffer[i], length - i, &consumed);
if (!tag_res.has_value()) {
ESP_LOGV(TAG, "Invalid field tag at position %u", i);
break;
}
uint32_t tag = tag_res->as_uint32();
uint32_t field_id = tag >> 3;
uint32_t wire_type = tag & 0x07;
i += consumed;
bool decoded = false;
switch (wire_type) {
case 0: { // Varint
auto value_res = ProtoVarInt::parse(&buffer[i], length - i, &consumed);
if (!value_res.has_value()) {
ESP_LOGV(TAG, "Invalid varint at position %u", i);
return;
}
ProtoVarInt value = *value_res;
// Try regular fields first using binary search
if (const FieldMeta *field = find_field_binary(fields, field_count, field_id)) {
decoded = decode_varint_field(field->get_type(), base + field->get_offset(), value);
}
// If not found, try repeated fields (linear search - usually only 1-2 fields)
if (!decoded) {
for (uint8_t j = 0; j < repeated_count; j++) {
if (repeated_fields[j].field_num == field_id) {
decoded = decode_repeated_varint_field(repeated_fields[j].get_type(),
base + repeated_fields[j].get_offset(), value);
break;
}
}
}
i += consumed;
break;
}
case 2: { // Length-delimited
auto len_res = ProtoVarInt::parse(&buffer[i], length - i, &consumed);
if (!len_res.has_value()) {
ESP_LOGV(TAG, "Invalid length at position %u", i);
return;
}
uint32_t field_length = len_res->as_uint32();
i += consumed;
if (i + field_length > length) {
ESP_LOGV(TAG, "Length-delimited field exceeds buffer at position %u", i);
return;
}
ProtoLengthDelimited value(&buffer[i], field_length);
// Try regular fields first using binary search
if (const FieldMeta *field = find_field_binary(fields, field_count, field_id)) {
decoded =
decode_length_field(field->get_type(), base + field->get_offset(), value, field->get_message_type_id());
}
// If not found, try repeated fields (linear search - usually only 1-2 fields)
if (!decoded) {
for (uint8_t j = 0; j < repeated_count; j++) {
if (repeated_fields[j].field_num == field_id) {
decoded =
decode_repeated_length_field(repeated_fields[j].get_type(), base + repeated_fields[j].get_offset(),
value, repeated_fields[j].get_message_type_id());
break;
}
}
}
i += field_length;
break;
}
case 5: { // 32-bit
if (i + 4 > length) {
ESP_LOGV(TAG, "32-bit field exceeds buffer at position %u", i);
return;
}
uint32_t raw = 0;
raw |= uint32_t(buffer[i]) << 0;
raw |= uint32_t(buffer[i + 1]) << 8;
raw |= uint32_t(buffer[i + 2]) << 16;
raw |= uint32_t(buffer[i + 3]) << 24;
Proto32Bit value(raw);
// Try regular fields first using binary search
if (const FieldMeta *field = find_field_binary(fields, field_count, field_id)) {
decoded = decode_32bit_field(field->get_type(), base + field->get_offset(), value);
}
// If not found, try repeated fields (linear search - usually only 1-2 fields)
if (!decoded) {
for (uint8_t j = 0; j < repeated_count; j++) {
if (repeated_fields[j].field_num == field_id) {
decoded = decode_repeated_32bit_field(repeated_fields[j].get_type(),
base + repeated_fields[j].get_offset(), value);
break;
}
}
}
i += 4;
break;
}
default:
ESP_LOGV(TAG, "Unknown wire type %u at position %u", wire_type, i);
return;
}
if (!decoded) {
ESP_LOGV(TAG, "Skipping unknown field %u with wire type %u", field_id, wire_type);
}
}
}
void ProtoMessage::encode(ProtoWriteBuffer buffer) const {
const uint8_t *base = reinterpret_cast<const uint8_t *>(this);
// Encode regular fields
uint8_t field_count = get_field_count();
if (field_count) {
const FieldMeta *fields = get_field_metadata();
for (uint8_t i = 0; i < field_count; i++) {
const void *field_addr = base + fields[i].get_offset();
if (fields[i].get_type() == ProtoFieldType::TYPE_MESSAGE) {
uint8_t handler_id = fields[i].get_message_type_id();
if (handler_id < MESSAGE_HANDLER_COUNT && MESSAGE_HANDLERS[handler_id].encode != nullptr) {
MESSAGE_HANDLERS[handler_id].encode(buffer, field_addr, fields[i].field_num);
}
} else {
encode_field(buffer, fields[i].get_type(), fields[i].field_num, field_addr, false);
}
}
}
// Encode repeated fields - reuse the same encode_field function!
uint8_t repeated_count = get_repeated_field_count();
if (repeated_count == 0) {
return; // No repeated fields to process
}
const RepeatedFieldMeta *repeated_fields = get_repeated_field_metadata();
for (uint8_t i = 0; i < repeated_count; i++) {
const void *field_addr = base + repeated_fields[i].get_offset();
if (repeated_fields[i].get_type() == ProtoFieldType::TYPE_MESSAGE) {
uint8_t handler_id = repeated_fields[i].get_message_type_id();
if (handler_id < REPEATED_MESSAGE_HANDLER_COUNT && REPEATED_MESSAGE_HANDLERS[handler_id].encode != nullptr) {
REPEATED_MESSAGE_HANDLERS[handler_id].encode(buffer, field_addr, repeated_fields[i].field_num);
}
} else {
// Early exit for empty vectors
size_t count = get_vector_size(repeated_fields[i].get_type(), field_addr);
if (count == 0) {
continue;
}
// Iterate through the vector and encode each element using the same function!
for (size_t j = 0; j < count; j++) {
const void *element = get_vector_element(repeated_fields[i].get_type(), field_addr, j);
if (element != nullptr) {
encode_field(buffer, repeated_fields[i].get_type(), repeated_fields[i].field_num, element, true);
}
}
}
}
}
void ProtoMessage::calculate_size(uint32_t &total_size) const {
const uint8_t *base = reinterpret_cast<const uint8_t *>(this);
// Calculate size for regular fields
uint8_t field_count = get_field_count();
if (field_count) {
const FieldMeta *fields = get_field_metadata();
for (uint8_t i = 0; i < field_count; i++) {
const void *field_addr = base + fields[i].get_offset();
if (fields[i].get_type() == ProtoFieldType::TYPE_MESSAGE) {
uint8_t handler_id = fields[i].get_message_type_id();
if (handler_id < MESSAGE_HANDLER_COUNT && MESSAGE_HANDLERS[handler_id].size != nullptr) {
MESSAGE_HANDLERS[handler_id].size(total_size, field_addr, fields[i].get_precalced_size(), false);
}
} else {
calculate_field_size(total_size, fields[i].get_type(), fields[i].get_precalced_size(), field_addr, false);
}
}
}
// Calculate size for repeated fields - reuse the same calculate_field_size function!
uint8_t repeated_count = get_repeated_field_count();
if (repeated_count == 0) {
return; // No repeated fields to process
}
const RepeatedFieldMeta *repeated_fields = get_repeated_field_metadata();
for (uint8_t i = 0; i < repeated_count; i++) {
const void *field_addr = base + repeated_fields[i].get_offset();
if (repeated_fields[i].get_type() == ProtoFieldType::TYPE_MESSAGE) {
uint8_t handler_id = repeated_fields[i].get_message_type_id();
if (handler_id < REPEATED_MESSAGE_HANDLER_COUNT && REPEATED_MESSAGE_HANDLERS[handler_id].size != nullptr) {
REPEATED_MESSAGE_HANDLERS[handler_id].size(total_size, field_addr, repeated_fields[i].get_precalced_size());
}
} else {
// Special optimization for fixed-size types
ProtoFieldType type = repeated_fields[i].get_type();
size_t count = get_vector_size(type, field_addr);
// Early exit for empty vectors
if (count == 0) {
continue;
}
// For fixed-size types, we can calculate size more efficiently
if (type == ProtoFieldType::TYPE_FIXED32 || type == ProtoFieldType::TYPE_SFIXED32 ||
type == ProtoFieldType::TYPE_FLOAT) {
total_size += count * (repeated_fields[i].get_precalced_size() + 4);
} else if (type == ProtoFieldType::TYPE_BOOL) {
// Booleans are always 1 byte when encoded
total_size += count * (repeated_fields[i].get_precalced_size() + 1);
} else {
// For variable-size types, calculate each element
for (size_t j = 0; j < count; j++) {
const void *element = get_vector_element(type, field_addr, j);
if (element != nullptr) {
calculate_field_size(total_size, type, repeated_fields[i].get_precalced_size(), element, true);
}
}
}
}
}
}
} // namespace api
} // namespace esphome

View File

@@ -4,6 +4,7 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include <cassert>
#include <vector>
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
@@ -13,64 +14,6 @@
namespace esphome {
namespace api {
// Forward declarations
class ProtoWriteBuffer;
// Enum for all supported field types
enum class ProtoFieldType : uint8_t {
// Varint types (wire type 0)
TYPE_BOOL = 0,
TYPE_INT32 = 1,
TYPE_UINT32 = 2,
TYPE_INT64 = 3,
TYPE_UINT64 = 4,
TYPE_SINT32 = 5,
TYPE_SINT64 = 6,
TYPE_ENUM = 7,
// Length-delimited types (wire type 2)
TYPE_STRING = 8,
TYPE_BYTES = 9,
TYPE_MESSAGE = 10,
// 32-bit types (wire type 5)
TYPE_FLOAT = 11,
TYPE_FIXED32 = 12,
TYPE_SFIXED32 = 13,
};
// Helper to get wire type from field type
inline constexpr uint8_t get_wire_type(ProtoFieldType type) {
switch (type) {
case ProtoFieldType::TYPE_BOOL:
case ProtoFieldType::TYPE_INT32:
case ProtoFieldType::TYPE_UINT32:
case ProtoFieldType::TYPE_INT64:
case ProtoFieldType::TYPE_UINT64:
case ProtoFieldType::TYPE_SINT32:
case ProtoFieldType::TYPE_SINT64:
case ProtoFieldType::TYPE_ENUM:
return 0; // varint
case ProtoFieldType::TYPE_STRING:
case ProtoFieldType::TYPE_BYTES:
case ProtoFieldType::TYPE_MESSAGE:
return 2; // length-delimited
case ProtoFieldType::TYPE_FLOAT:
case ProtoFieldType::TYPE_FIXED32:
case ProtoFieldType::TYPE_SFIXED32:
return 5; // 32-bit
}
return 0;
}
// Macro to calculate field offset without triggering -Winvalid-offsetof
// This uses the same approach as offsetof but with explicit reinterpret_cast
#define PROTO_FIELD_OFFSET(Type, Member) \
static_cast<uint16_t>(reinterpret_cast<size_t>(&reinterpret_cast<Type *>(16)->Member) - \
16) /* NOLINT(bugprone-macro-parentheses) */
/// Representation of a VarInt - in ProtoBuf should be 64bit but we only use 32bit
class ProtoVarInt {
public:
@@ -113,20 +56,19 @@ class ProtoVarInt {
return {}; // Incomplete or invalid varint
}
inline uint16_t as_uint16() const { return this->value_; }
inline uint32_t as_uint32() const { return this->value_; }
inline uint64_t as_uint64() const { return this->value_; }
inline bool as_bool() const { return this->value_; }
template<typename T> inline T as_enum() const { return static_cast<T>(this->as_uint32()); }
inline int32_t as_int32() const {
uint16_t as_uint16() const { return this->value_; }
uint32_t as_uint32() const { return this->value_; }
uint64_t as_uint64() const { return this->value_; }
bool as_bool() const { return this->value_; }
int32_t as_int32() const {
// Not ZigZag encoded
return static_cast<int32_t>(this->as_int64());
}
inline int64_t as_int64() const {
int64_t as_int64() const {
// Not ZigZag encoded
return static_cast<int64_t>(this->value_);
}
inline int32_t as_sint32() const {
int32_t as_sint32() const {
// with ZigZag encoding
if (this->value_ & 1) {
return static_cast<int32_t>(~(this->value_ >> 1));
@@ -134,7 +76,7 @@ class ProtoVarInt {
return static_cast<int32_t>(this->value_ >> 1);
}
}
inline int64_t as_sint64() const {
int64_t as_sint64() const {
// with ZigZag encoding
if (this->value_ & 1) {
return static_cast<int64_t>(~(this->value_ >> 1));
@@ -176,18 +118,6 @@ class ProtoVarInt {
out.push_back(val);
return;
}
// Optimize for 2-byte varints (0x80-0x3FFF)
if (val <= 0x3FFF) {
out.push_back((val & 0x7F) | 0x80);
out.push_back(val >> 7);
return;
}
// For 3+ byte varints, write first 2 bytes directly
out.push_back((val & 0x7F) | 0x80);
out.push_back(((val >> 7) & 0x7F) | 0x80);
val >>= 14;
// Continue with remaining bytes
while (val) {
uint8_t temp = val & 0x7F;
val >>= 7;
@@ -203,17 +133,24 @@ class ProtoVarInt {
uint64_t value_;
};
// Forward declaration for decode_to_message and encode_to_writer
class ProtoMessage;
class ProtoLengthDelimited {
public:
explicit ProtoLengthDelimited(const uint8_t *value, size_t length) : value_(value), length_(length) {}
inline std::string as_string() const {
return std::string(reinterpret_cast<const char *>(this->value_), this->length_);
}
template<class C> inline C as_message() const {
auto msg = C();
msg.decode(this->value_, this->length_);
return msg;
}
std::string as_string() const { return std::string(reinterpret_cast<const char *>(this->value_), this->length_); }
/**
* Decode the length-delimited data into an existing ProtoMessage instance.
*
* This method allows decoding without templates, enabling use in contexts
* where the message type is not known at compile time. The ProtoMessage's
* decode() method will be called with the raw data and length.
*
* @param msg The ProtoMessage instance to decode into
*/
void decode_to_message(ProtoMessage &msg) const;
protected:
const uint8_t *const value_;
@@ -223,9 +160,9 @@ class ProtoLengthDelimited {
class Proto32Bit {
public:
explicit Proto32Bit(uint32_t value) : value_(value) {}
inline uint32_t as_fixed32() const { return this->value_; }
inline int32_t as_sfixed32() const { return static_cast<int32_t>(this->value_); }
inline float as_float() const {
uint32_t as_fixed32() const { return this->value_; }
int32_t as_sfixed32() const { return static_cast<int32_t>(this->value_); }
float as_float() const {
union {
uint32_t raw;
float value;
@@ -238,133 +175,31 @@ class Proto32Bit {
const uint32_t value_;
};
// Function pointer types used by V2 structures
using EncodeFunc = void (*)(ProtoWriteBuffer &, const void *field_ptr, uint8_t field_num);
using SizeFunc = void (*)(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size, bool force);
using DecodeLengthFunc = bool (*)(void *field_ptr, ProtoLengthDelimited value);
// Function pointer types for repeated fields used by V2 structures
using RepeatedEncodeFunc = void (*)(ProtoWriteBuffer &, const void *field_ptr, uint8_t field_num);
using RepeatedSizeFunc = void (*)(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size);
using RepeatedDecodeLengthFunc = bool (*)(void *field_ptr, ProtoLengthDelimited value);
// Message handler registry entry
struct MessageHandler {
EncodeFunc encode;
SizeFunc size;
DecodeLengthFunc decode;
};
// Repeated message handler registry entry
struct RepeatedMessageHandler {
RepeatedEncodeFunc encode;
RepeatedSizeFunc size;
RepeatedDecodeLengthFunc decode;
};
// Global message handler registries (defined in api_pb2.cpp)
extern const MessageHandler MESSAGE_HANDLERS[];
extern const uint8_t MESSAGE_HANDLER_COUNT;
extern const RepeatedMessageHandler REPEATED_MESSAGE_HANDLERS[];
extern const uint8_t REPEATED_MESSAGE_HANDLER_COUNT;
// Optimized metadata structure (4 bytes - no padding on 32-bit architectures)
struct FieldMeta {
uint8_t field_num; // Protobuf field number (1-255)
uint8_t type_and_size; // bits 0-4: ProtoFieldType, bits 5-6: precalced_field_id_size-1, bit 7: reserved
union {
uint16_t offset; // For non-message types: offset in class (0-65535)
struct {
uint8_t offset_low; // For TYPE_MESSAGE: low byte of offset (bits 0-7)
uint8_t message_type_id; // For TYPE_MESSAGE: bits 0-1: offset high (bits 8-9), bits 2-7: handler index (0-63)
};
};
// Helper methods
inline ProtoFieldType get_type() const { return static_cast<ProtoFieldType>(type_and_size & 0x1F); }
inline uint8_t get_precalced_size() const { return ((type_and_size >> 5) & 0x03) + 1; }
inline uint16_t get_offset() const {
if (get_type() == ProtoFieldType::TYPE_MESSAGE) {
// Reconstruct full offset from packed fields (10-bit offset)
// Bits 0-7 from offset_low, bits 8-9 from lower 2 bits of message_type_id
return static_cast<uint16_t>(offset_low) | (static_cast<uint16_t>(message_type_id & 0x03) << 8);
}
return offset;
}
inline uint8_t get_message_type_id() const { return message_type_id >> 2; } // Upper 6 bits for type ID (0-63)
};
// Optimized repeated field metadata (4 bytes - no padding on 32-bit architectures)
struct RepeatedFieldMeta {
uint8_t field_num; // Protobuf field number (1-255)
uint8_t type_and_size; // bits 0-4: ProtoFieldType, bits 5-6: precalced_field_id_size-1, bit 7: reserved
union {
uint16_t offset; // For non-message types: offset in class (0-65535)
struct {
uint8_t offset_low; // For TYPE_MESSAGE: low byte of offset (bits 0-7)
uint8_t message_type_id; // For TYPE_MESSAGE: bits 0-1: offset high (bits 8-9), bits 2-7: handler index (0-63)
};
};
// Helper methods
inline ProtoFieldType get_type() const { return static_cast<ProtoFieldType>(type_and_size & 0x1F); }
inline uint8_t get_precalced_size() const { return ((type_and_size >> 5) & 0x03) + 1; }
inline uint16_t get_offset() const {
if (get_type() == ProtoFieldType::TYPE_MESSAGE) {
// Reconstruct full offset from packed fields (10-bit offset)
// Bits 0-7 from offset_low, bits 8-9 from lower 2 bits of message_type_id
return static_cast<uint16_t>(offset_low) | (static_cast<uint16_t>(message_type_id & 0x03) << 8);
}
return offset;
}
inline uint8_t get_message_type_id() const { return message_type_id >> 2; } // Upper 6 bits for type ID (0-63)
};
// Binary search for field lookup - optimized for performance
inline const FieldMeta *find_field_binary(const FieldMeta *fields, uint8_t count, uint8_t field_id) {
uint8_t left = 0;
uint8_t right = count;
while (left < right) {
uint8_t mid = (left + right) / 2;
uint8_t mid_field = fields[mid].field_num;
if (mid_field < field_id) {
left = mid + 1;
} else if (mid_field > field_id) {
right = mid;
} else {
// Found field_id
return &fields[mid];
}
}
return nullptr; // Field not found
}
// NOTE: Proto64Bit class removed - wire type 1 (64-bit fixed) not supported
class ProtoWriteBuffer {
public:
ProtoWriteBuffer(std::vector<uint8_t> *buffer) : buffer_(buffer) {}
inline void write(uint8_t value) { this->buffer_->push_back(value); }
inline void encode_varint_raw(ProtoVarInt value) { value.encode(*this->buffer_); }
inline void encode_varint_raw(uint32_t value) { this->encode_varint_raw(ProtoVarInt(value)); }
void write(uint8_t value) { this->buffer_->push_back(value); }
void encode_varint_raw(ProtoVarInt value) { value.encode(*this->buffer_); }
void encode_varint_raw(uint32_t value) { this->encode_varint_raw(ProtoVarInt(value)); }
/**
* Encode a field key (tag/wire type combination).
*
* @param field_id Field number (tag) in the protobuf message
* @param type Wire type value:
* - 0: Varint (int32, int64, uint32, uint64, sint32, sint64, bool, enum)
* - 1: 64-bit (fixed64, sfixed64, double)
* - 2: Length-delimited (string, bytes, embedded messages, packed repeated fields)
* - 5: 32-bit (fixed32, sfixed32, float)
* - Note: Wire type 1 (64-bit fixed) is not supported
*
* Following https://protobuf.dev/programming-guides/encoding/#structure
*/
inline void encode_field_raw(uint32_t field_id, uint32_t type) {
void encode_field_raw(uint32_t field_id, uint32_t type) {
uint32_t val = (field_id << 3) | (type & 0b111);
this->encode_varint_raw(val);
}
inline void encode_string(uint32_t field_id, const char *string, size_t len, bool force = false) {
void encode_string(uint32_t field_id, const char *string, size_t len, bool force = false) {
if (len == 0 && !force)
return;
@@ -373,25 +208,25 @@ class ProtoWriteBuffer {
auto *data = reinterpret_cast<const uint8_t *>(string);
this->buffer_->insert(this->buffer_->end(), data, data + len);
}
inline void encode_string(uint32_t field_id, const std::string &value, bool force = false) {
void encode_string(uint32_t field_id, const std::string &value, bool force = false) {
this->encode_string(field_id, value.data(), value.size(), force);
}
inline void encode_bytes(uint32_t field_id, const uint8_t *data, size_t len, bool force = false) {
void encode_bytes(uint32_t field_id, const uint8_t *data, size_t len, bool force = false) {
this->encode_string(field_id, reinterpret_cast<const char *>(data), len, force);
}
inline void encode_uint32(uint32_t field_id, uint32_t value, bool force = false) {
void encode_uint32(uint32_t field_id, uint32_t value, bool force = false) {
if (value == 0 && !force)
return;
this->encode_field_raw(field_id, 0); // type 0: Varint - uint32
this->encode_varint_raw(value);
}
inline void encode_uint64(uint32_t field_id, uint64_t value, bool force = false) {
void encode_uint64(uint32_t field_id, uint64_t value, bool force = false) {
if (value == 0 && !force)
return;
this->encode_field_raw(field_id, 0); // type 0: Varint - uint64
this->encode_varint_raw(ProtoVarInt(value));
}
inline void encode_bool(uint32_t field_id, bool value, bool force = false) {
void encode_bool(uint32_t field_id, bool value, bool force = false) {
if (!value && !force)
return;
this->encode_field_raw(field_id, 0); // type 0: Varint - bool
@@ -402,15 +237,16 @@ class ProtoWriteBuffer {
return;
this->encode_field_raw(field_id, 5); // type 5: 32-bit fixed32
// Reserve space and write all 4 bytes at once for better performance
size_t pos = this->buffer_->size();
this->buffer_->resize(pos + 4);
(*this->buffer_)[pos] = (value >> 0) & 0xFF;
(*this->buffer_)[pos + 1] = (value >> 8) & 0xFF;
(*this->buffer_)[pos + 2] = (value >> 16) & 0xFF;
(*this->buffer_)[pos + 3] = (value >> 24) & 0xFF;
this->write((value >> 0) & 0xFF);
this->write((value >> 8) & 0xFF);
this->write((value >> 16) & 0xFF);
this->write((value >> 24) & 0xFF);
}
inline void encode_float(uint32_t field_id, float value, bool force = false) {
// NOTE: Wire type 1 (64-bit fixed: double, fixed64, sfixed64) is intentionally
// not supported to reduce overhead on embedded systems. All ESPHome devices are
// 32-bit microcontrollers where 64-bit operations are expensive. If 64-bit support
// is needed in the future, the necessary encoding/decoding functions must be added.
void encode_float(uint32_t field_id, float value, bool force = false) {
if (value == 0.0f && !force)
return;
@@ -421,7 +257,7 @@ class ProtoWriteBuffer {
val.value = value;
this->encode_fixed32(field_id, val.raw);
}
inline void encode_int32(uint32_t field_id, int32_t value, bool force = false) {
void encode_int32(uint32_t field_id, int32_t value, bool force = false) {
if (value < 0) {
// negative int32 is always 10 byte long
this->encode_int64(field_id, value, force);
@@ -429,10 +265,10 @@ class ProtoWriteBuffer {
}
this->encode_uint32(field_id, static_cast<uint32_t>(value), force);
}
inline void encode_int64(uint32_t field_id, int64_t value, bool force = false) {
void encode_int64(uint32_t field_id, int64_t value, bool force = false) {
this->encode_uint64(field_id, static_cast<uint64_t>(value), force);
}
inline void encode_sint32(uint32_t field_id, int32_t value, bool force = false) {
void encode_sint32(uint32_t field_id, int32_t value, bool force = false) {
uint32_t uvalue;
if (value < 0) {
uvalue = ~(value << 1);
@@ -441,7 +277,7 @@ class ProtoWriteBuffer {
}
this->encode_uint32(field_id, uvalue, force);
}
inline void encode_sint64(uint32_t field_id, int64_t value, bool force = false) {
void encode_sint64(uint32_t field_id, int64_t value, bool force = false) {
uint64_t uvalue;
if (value < 0) {
uvalue = ~(value << 1);
@@ -450,23 +286,7 @@ class ProtoWriteBuffer {
}
this->encode_uint64(field_id, uvalue, force);
}
inline void encode_sfixed32(uint32_t field_id, int32_t value, bool force = false) {
if (!force && value == 0)
return;
this->encode_fixed32(field_id, static_cast<uint32_t>(value), force);
}
template<class C> void encode_message(uint32_t field_id, const C &value, bool force = false) {
this->encode_field_raw(field_id, 2); // type 2: Length-delimited message
size_t begin = this->buffer_->size();
value.encode(*this);
const uint32_t nested_length = this->buffer_->size() - begin;
// add size varint
std::vector<uint8_t> var;
ProtoVarInt(nested_length).encode(var);
this->buffer_->insert(this->buffer_->begin() + begin, var.begin(), var.end());
}
void encode_message(uint32_t field_id, const ProtoMessage &value, bool force = false);
std::vector<uint8_t> *get_buffer() const { return buffer_; }
protected:
@@ -476,18 +296,11 @@ class ProtoWriteBuffer {
class ProtoMessage {
public:
virtual ~ProtoMessage() = default;
// Metadata getters - must be implemented by derived classes
virtual const FieldMeta *get_field_metadata() const { return nullptr; }
virtual uint8_t get_field_count() const { return 0; }
virtual const RepeatedFieldMeta *get_repeated_field_metadata() const { return nullptr; }
virtual uint8_t get_repeated_field_count() const { return 0; }
// Encode/decode/calculate_size using V2 metadata (will check for V3 first)
void encode(ProtoWriteBuffer buffer) const;
// Default implementation for messages with no fields
virtual void encode(ProtoWriteBuffer buffer) const {}
void decode(const uint8_t *buffer, size_t length);
void calculate_size(uint32_t &total_size) const;
// Default implementation for messages with no fields
virtual void calculate_size(uint32_t &total_size) const {}
#ifdef HAS_PROTO_MESSAGE_DUMP
std::string dump() const;
virtual void dump_to(std::string &out) const = 0;
@@ -495,8 +308,511 @@ class ProtoMessage {
#endif
protected:
virtual bool decode_varint(uint32_t field_id, ProtoVarInt value) { return false; }
virtual bool decode_length(uint32_t field_id, ProtoLengthDelimited value) { return false; }
virtual bool decode_32bit(uint32_t field_id, Proto32Bit value) { return false; }
// NOTE: decode_64bit removed - wire type 1 not supported
};
class ProtoSize {
public:
/**
* @brief ProtoSize class for Protocol Buffer serialization size calculation
*
* This class provides static methods to calculate the exact byte counts needed
* for encoding various Protocol Buffer field types. All methods are designed to be
* efficient for the common case where many fields have default values.
*
* Implements Protocol Buffer encoding size calculation according to:
* https://protobuf.dev/programming-guides/encoding/
*
* Key features:
* - Early-return optimization for zero/default values
* - Direct total_size updates to avoid unnecessary additions
* - Specialized handling for different field types according to protobuf spec
* - Templated helpers for repeated fields and messages
*/
/**
* @brief Calculates the size in bytes needed to encode a uint32_t value as a varint
*
* @param value The uint32_t value to calculate size for
* @return The number of bytes needed to encode the value
*/
static inline uint32_t varint(uint32_t value) {
// Optimized varint size calculation using leading zeros
// Each 7 bits requires one byte in the varint encoding
if (value < 128)
return 1; // 7 bits, common case for small values
// For larger values, count bytes needed based on the position of the highest bit set
if (value < 16384) {
return 2; // 14 bits
} else if (value < 2097152) {
return 3; // 21 bits
} else if (value < 268435456) {
return 4; // 28 bits
} else {
return 5; // 32 bits (maximum for uint32_t)
}
}
/**
* @brief Calculates the size in bytes needed to encode a uint64_t value as a varint
*
* @param value The uint64_t value to calculate size for
* @return The number of bytes needed to encode the value
*/
static inline uint32_t varint(uint64_t value) {
// Handle common case of values fitting in uint32_t (vast majority of use cases)
if (value <= UINT32_MAX) {
return varint(static_cast<uint32_t>(value));
}
// For larger values, determine size based on highest bit position
if (value < (1ULL << 35)) {
return 5; // 35 bits
} else if (value < (1ULL << 42)) {
return 6; // 42 bits
} else if (value < (1ULL << 49)) {
return 7; // 49 bits
} else if (value < (1ULL << 56)) {
return 8; // 56 bits
} else if (value < (1ULL << 63)) {
return 9; // 63 bits
} else {
return 10; // 64 bits (maximum for uint64_t)
}
}
/**
* @brief Calculates the size in bytes needed to encode an int32_t value as a varint
*
* Special handling is needed for negative values, which are sign-extended to 64 bits
* in Protocol Buffers, resulting in a 10-byte varint.
*
* @param value The int32_t value to calculate size for
* @return The number of bytes needed to encode the value
*/
static inline uint32_t varint(int32_t value) {
// Negative values are sign-extended to 64 bits in protocol buffers,
// which always results in a 10-byte varint for negative int32
if (value < 0) {
return 10; // Negative int32 is always 10 bytes long
}
// For non-negative values, use the uint32_t implementation
return varint(static_cast<uint32_t>(value));
}
/**
* @brief Calculates the size in bytes needed to encode an int64_t value as a varint
*
* @param value The int64_t value to calculate size for
* @return The number of bytes needed to encode the value
*/
static inline uint32_t varint(int64_t value) {
// For int64_t, we convert to uint64_t and calculate the size
// This works because the bit pattern determines the encoding size,
// and we've handled negative int32 values as a special case above
return varint(static_cast<uint64_t>(value));
}
/**
* @brief Calculates the size in bytes needed to encode a field ID and wire type
*
* @param field_id The field identifier
* @param type The wire type value (from the WireType enum in the protobuf spec)
* @return The number of bytes needed to encode the field ID and wire type
*/
static inline uint32_t field(uint32_t field_id, uint32_t type) {
uint32_t tag = (field_id << 3) | (type & 0b111);
return varint(tag);
}
/**
* @brief Common parameters for all add_*_field methods
*
* All add_*_field methods follow these common patterns:
*
* @param total_size Reference to the total message size to update
* @param field_id_size Pre-calculated size of the field ID in bytes
* @param value The value to calculate size for (type varies)
* @param force Whether to calculate size even if the value is default/zero/empty
*
* Each method follows this implementation pattern:
* 1. Skip calculation if value is default (0, false, empty) and not forced
* 2. Calculate the size based on the field's encoding rules
* 3. Add the field_id_size + calculated value size to total_size
*/
/**
* @brief Calculates and adds the size of an int32 field to the total message size
*/
static inline void add_int32_field(uint32_t &total_size, uint32_t field_id_size, int32_t value) {
// Skip calculation if value is zero
if (value == 0) {
return; // No need to update total_size
}
// Calculate and directly add to total_size
if (value < 0) {
// Negative values are encoded as 10-byte varints in protobuf
total_size += field_id_size + 10;
} else {
// For non-negative values, use the standard varint size
total_size += field_id_size + varint(static_cast<uint32_t>(value));
}
}
/**
* @brief Calculates and adds the size of an int32 field to the total message size (repeated field version)
*/
static inline void add_int32_field_repeated(uint32_t &total_size, uint32_t field_id_size, int32_t value) {
// Always calculate size for repeated fields
if (value < 0) {
// Negative values are encoded as 10-byte varints in protobuf
total_size += field_id_size + 10;
} else {
// For non-negative values, use the standard varint size
total_size += field_id_size + varint(static_cast<uint32_t>(value));
}
}
/**
* @brief Calculates and adds the size of a uint32 field to the total message size
*/
static inline void add_uint32_field(uint32_t &total_size, uint32_t field_id_size, uint32_t value) {
// Skip calculation if value is zero
if (value == 0) {
return; // No need to update total_size
}
// Calculate and directly add to total_size
total_size += field_id_size + varint(value);
}
/**
* @brief Calculates and adds the size of a uint32 field to the total message size (repeated field version)
*/
static inline void add_uint32_field_repeated(uint32_t &total_size, uint32_t field_id_size, uint32_t value) {
// Always calculate size for repeated fields
total_size += field_id_size + varint(value);
}
/**
* @brief Calculates and adds the size of a boolean field to the total message size
*/
static inline void add_bool_field(uint32_t &total_size, uint32_t field_id_size, bool value) {
// Skip calculation if value is false
if (!value) {
return; // No need to update total_size
}
// Boolean fields always use 1 byte when true
total_size += field_id_size + 1;
}
/**
* @brief Calculates and adds the size of a boolean field to the total message size (repeated field version)
*/
static inline void add_bool_field_repeated(uint32_t &total_size, uint32_t field_id_size, bool value) {
// Always calculate size for repeated fields
// Boolean fields always use 1 byte
total_size += field_id_size + 1;
}
/**
* @brief Calculates and adds the size of a fixed field to the total message size
*
* Fixed fields always take exactly N bytes (4 for fixed32/float, 8 for fixed64/double).
*
* @tparam NumBytes The number of bytes for this fixed field (4 or 8)
* @param is_nonzero Whether the value is non-zero
*/
template<uint32_t NumBytes>
static inline void add_fixed_field(uint32_t &total_size, uint32_t field_id_size, bool is_nonzero) {
// Skip calculation if value is zero
if (!is_nonzero) {
return; // No need to update total_size
}
// Fixed fields always take exactly NumBytes
total_size += field_id_size + NumBytes;
}
/**
* @brief Calculates and adds the size of a float field to the total message size
*/
static inline void add_float_field(uint32_t &total_size, uint32_t field_id_size, float value) {
if (value != 0.0f) {
total_size += field_id_size + 4;
}
}
// NOTE: add_double_field removed - wire type 1 (64-bit: double) not supported
// to reduce overhead on embedded systems
/**
* @brief Calculates and adds the size of a fixed32 field to the total message size
*/
static inline void add_fixed32_field(uint32_t &total_size, uint32_t field_id_size, uint32_t value) {
if (value != 0) {
total_size += field_id_size + 4;
}
}
// NOTE: add_fixed64_field removed - wire type 1 (64-bit: fixed64) not supported
// to reduce overhead on embedded systems
/**
* @brief Calculates and adds the size of a sfixed32 field to the total message size
*/
static inline void add_sfixed32_field(uint32_t &total_size, uint32_t field_id_size, int32_t value) {
if (value != 0) {
total_size += field_id_size + 4;
}
}
// NOTE: add_sfixed64_field removed - wire type 1 (64-bit: sfixed64) not supported
// to reduce overhead on embedded systems
/**
* @brief Calculates and adds the size of an enum field to the total message size
*
* Enum fields are encoded as uint32 varints.
*/
static inline void add_enum_field(uint32_t &total_size, uint32_t field_id_size, uint32_t value) {
// Skip calculation if value is zero
if (value == 0) {
return; // No need to update total_size
}
// Enums are encoded as uint32
total_size += field_id_size + varint(value);
}
/**
* @brief Calculates and adds the size of an enum field to the total message size (repeated field version)
*
* Enum fields are encoded as uint32 varints.
*/
static inline void add_enum_field_repeated(uint32_t &total_size, uint32_t field_id_size, uint32_t value) {
// Always calculate size for repeated fields
// Enums are encoded as uint32
total_size += field_id_size + varint(value);
}
/**
* @brief Calculates and adds the size of a sint32 field to the total message size
*
* Sint32 fields use ZigZag encoding, which is more efficient for negative values.
*/
static inline void add_sint32_field(uint32_t &total_size, uint32_t field_id_size, int32_t value) {
// Skip calculation if value is zero
if (value == 0) {
return; // No need to update total_size
}
// ZigZag encoding for sint32: (n << 1) ^ (n >> 31)
uint32_t zigzag = (static_cast<uint32_t>(value) << 1) ^ (static_cast<uint32_t>(value >> 31));
total_size += field_id_size + varint(zigzag);
}
/**
* @brief Calculates and adds the size of a sint32 field to the total message size (repeated field version)
*
* Sint32 fields use ZigZag encoding, which is more efficient for negative values.
*/
static inline void add_sint32_field_repeated(uint32_t &total_size, uint32_t field_id_size, int32_t value) {
// Always calculate size for repeated fields
// ZigZag encoding for sint32: (n << 1) ^ (n >> 31)
uint32_t zigzag = (static_cast<uint32_t>(value) << 1) ^ (static_cast<uint32_t>(value >> 31));
total_size += field_id_size + varint(zigzag);
}
/**
* @brief Calculates and adds the size of an int64 field to the total message size
*/
static inline void add_int64_field(uint32_t &total_size, uint32_t field_id_size, int64_t value) {
// Skip calculation if value is zero
if (value == 0) {
return; // No need to update total_size
}
// Calculate and directly add to total_size
total_size += field_id_size + varint(value);
}
/**
* @brief Calculates and adds the size of an int64 field to the total message size (repeated field version)
*/
static inline void add_int64_field_repeated(uint32_t &total_size, uint32_t field_id_size, int64_t value) {
// Always calculate size for repeated fields
total_size += field_id_size + varint(value);
}
/**
* @brief Calculates and adds the size of a uint64 field to the total message size
*/
static inline void add_uint64_field(uint32_t &total_size, uint32_t field_id_size, uint64_t value) {
// Skip calculation if value is zero
if (value == 0) {
return; // No need to update total_size
}
// Calculate and directly add to total_size
total_size += field_id_size + varint(value);
}
/**
* @brief Calculates and adds the size of a uint64 field to the total message size (repeated field version)
*/
static inline void add_uint64_field_repeated(uint32_t &total_size, uint32_t field_id_size, uint64_t value) {
// Always calculate size for repeated fields
total_size += field_id_size + varint(value);
}
// NOTE: sint64 support functions (add_sint64_field, add_sint64_field_repeated) removed
// sint64 type is not supported by ESPHome API to reduce overhead on embedded systems
/**
* @brief Calculates and adds the size of a string/bytes field to the total message size
*/
static inline void add_string_field(uint32_t &total_size, uint32_t field_id_size, const std::string &str) {
// Skip calculation if string is empty
if (str.empty()) {
return; // No need to update total_size
}
// Calculate and directly add to total_size
const uint32_t str_size = static_cast<uint32_t>(str.size());
total_size += field_id_size + varint(str_size) + str_size;
}
/**
* @brief Calculates and adds the size of a string/bytes field to the total message size (repeated field version)
*/
static inline void add_string_field_repeated(uint32_t &total_size, uint32_t field_id_size, const std::string &str) {
// Always calculate size for repeated fields
const uint32_t str_size = static_cast<uint32_t>(str.size());
total_size += field_id_size + varint(str_size) + str_size;
}
/**
* @brief Calculates and adds the size of a nested message field to the total message size
*
* This helper function directly updates the total_size reference if the nested size
* is greater than zero.
*
* @param nested_size The pre-calculated size of the nested message
*/
static inline void add_message_field(uint32_t &total_size, uint32_t field_id_size, uint32_t nested_size) {
// Skip calculation if nested message is empty
if (nested_size == 0) {
return; // No need to update total_size
}
// Calculate and directly add to total_size
// Field ID + length varint + nested message content
total_size += field_id_size + varint(nested_size) + nested_size;
}
/**
* @brief Calculates and adds the size of a nested message field to the total message size (repeated field version)
*
* @param nested_size The pre-calculated size of the nested message
*/
static inline void add_message_field_repeated(uint32_t &total_size, uint32_t field_id_size, uint32_t nested_size) {
// Always calculate size for repeated fields
// Field ID + length varint + nested message content
total_size += field_id_size + varint(nested_size) + nested_size;
}
/**
* @brief Calculates and adds the size of a nested message field to the total message size
*
* This version takes a ProtoMessage object, calculates its size internally,
* and updates the total_size reference. This eliminates the need for a temporary variable
* at the call site.
*
* @param message The nested message object
*/
static inline void add_message_object(uint32_t &total_size, uint32_t field_id_size, const ProtoMessage &message) {
uint32_t nested_size = 0;
message.calculate_size(nested_size);
// Use the base implementation with the calculated nested_size
add_message_field(total_size, field_id_size, nested_size);
}
/**
* @brief Calculates and adds the size of a nested message field to the total message size (repeated field version)
*
* @param message The nested message object
*/
static inline void add_message_object_repeated(uint32_t &total_size, uint32_t field_id_size,
const ProtoMessage &message) {
uint32_t nested_size = 0;
message.calculate_size(nested_size);
// Use the base implementation with the calculated nested_size
add_message_field_repeated(total_size, field_id_size, nested_size);
}
/**
* @brief Calculates and adds the sizes of all messages in a repeated field to the total message size
*
* This helper processes a vector of message objects, calculating the size for each message
* and adding it to the total size.
*
* @tparam MessageType The type of the nested messages in the vector
* @param messages Vector of message objects
*/
template<typename MessageType>
static inline void add_repeated_message(uint32_t &total_size, uint32_t field_id_size,
const std::vector<MessageType> &messages) {
// Skip if the vector is empty
if (messages.empty()) {
return;
}
// Use the repeated field version for all messages
for (const auto &message : messages) {
add_message_object_repeated(total_size, field_id_size, message);
}
}
};
// Implementation of encode_message - must be after ProtoMessage is defined
inline void ProtoWriteBuffer::encode_message(uint32_t field_id, const ProtoMessage &value, bool force) {
this->encode_field_raw(field_id, 2); // type 2: Length-delimited message
// Calculate the message size first
uint32_t msg_length_bytes = 0;
value.calculate_size(msg_length_bytes);
// Calculate how many bytes the length varint needs
uint32_t varint_length_bytes = ProtoSize::varint(msg_length_bytes);
// Reserve exact space for the length varint
size_t begin = this->buffer_->size();
this->buffer_->resize(this->buffer_->size() + varint_length_bytes);
// Write the length varint directly
ProtoVarInt(msg_length_bytes).encode_to_buffer_unchecked(this->buffer_->data() + begin, varint_length_bytes);
// Now encode the message content - it will append to the buffer
value.encode(*this);
// Verify that the encoded size matches what we calculated
assert(this->buffer_->size() == begin + varint_length_bytes + msg_length_bytes);
}
// Implementation of decode_to_message - must be after ProtoMessage is defined
inline void ProtoLengthDelimited::decode_to_message(ProtoMessage &msg) const {
msg.decode(this->value_, this->length_);
}
template<typename T> const char *proto_enum_to_string(T value);
class ProtoService {
@@ -553,17 +869,6 @@ class ProtoService {
return true;
}
};
template<typename MessageType>
void encode_repeated_message_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
template<typename MessageType>
void size_repeated_message_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size);
template<typename MessageType>
void encode_message_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
template<typename MessageType>
void size_message_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size, bool force);
} // namespace api
} // namespace esphome

View File

@@ -1,53 +0,0 @@
#pragma once
#include "proto.h"
#include "api_pb2_size.h"
namespace esphome {
namespace api {
// Template functions for message field handling (regular fields)
template<typename MessageType>
inline void encode_message_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num) {
const MessageType *msg = static_cast<const MessageType *>(field_ptr);
buffer.encode_message<MessageType>(field_num, *msg);
}
template<typename MessageType>
inline void size_message_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size,
bool force) {
const MessageType *msg = static_cast<const MessageType *>(field_ptr);
ProtoSize::add_message_object(total_size, precalced_field_id_size, *msg, force);
}
template<typename MessageType> inline bool decode_message_field(void *field_ptr, ProtoLengthDelimited value) {
MessageType *msg = static_cast<MessageType *>(field_ptr);
*msg = value.as_message<MessageType>();
return true;
}
// Template repeated field functions (must be in header for instantiation)
template<typename MessageType>
inline void encode_repeated_message_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num) {
const auto *vec = static_cast<const std::vector<MessageType> *>(field_ptr);
for (const auto &msg : *vec) {
buffer.encode_message<MessageType>(field_num, msg, true);
}
}
// Template size functions for repeated fields (must be in header for instantiation)
template<typename MessageType>
inline void size_repeated_message_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size) {
const auto *vec = static_cast<const std::vector<MessageType> *>(field_ptr);
ProtoSize::add_repeated_message<MessageType>(total_size, precalced_field_id_size, *vec);
}
// Template decode functions for repeated fields
template<typename MessageType> inline bool decode_repeated_message_field(void *field_ptr, ProtoLengthDelimited value) {
auto *vec = static_cast<std::vector<MessageType> *>(field_ptr);
vec->push_back(value.as_message<MessageType>());
return true;
}
} // namespace api
} // namespace esphome

View File

@@ -7,6 +7,7 @@
#include "esphome/core/automation.h"
#include "api_pb2.h"
#ifdef USE_API_SERVICES
namespace esphome {
namespace api {
@@ -15,6 +16,8 @@ class UserServiceDescriptor {
virtual ListEntitiesServicesResponse encode_list_service_response() = 0;
virtual bool execute_service(const ExecuteServiceRequest &req) = 0;
bool is_internal() { return false; }
};
template<typename T> T get_execute_arg_value(const ExecuteServiceArgument &arg);
@@ -73,3 +76,4 @@ template<typename... Ts> class UserServiceTrigger : public UserServiceBase<Ts...
} // namespace api
} // namespace esphome
#endif // USE_API_SERVICES

View File

@@ -3,8 +3,6 @@
#include "esphome/core/component.h"
#include "esphome/components/as3935/as3935.h"
#include "esphome/components/spi/spi.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/binary_sensor/binary_sensor.h"
namespace esphome {
namespace as3935_spi {

View File

@@ -31,7 +31,7 @@ CONFIG_SCHEMA = cv.All(
async def to_code(config):
if CORE.is_esp32 or CORE.is_libretiny:
# https://github.com/ESP32Async/AsyncTCP
cg.add_library("ESP32Async/AsyncTCP", "3.4.4")
cg.add_library("ESP32Async/AsyncTCP", "3.4.5")
elif CORE.is_esp8266:
# https://github.com/ESP32Async/ESPAsyncTCP
cg.add_library("ESP32Async/ESPAsyncTCP", "2.0.0")

View File

@@ -85,13 +85,13 @@ async def to_code(config):
await cg.register_component(var, config)
cg.add(var.set_active(config[CONF_ACTIVE]))
await esp32_ble_tracker.register_ble_device(var, config)
await esp32_ble_tracker.register_raw_ble_device(var, config)
for connection_conf in config.get(CONF_CONNECTIONS, []):
connection_var = cg.new_Pvariable(connection_conf[CONF_ID])
await cg.register_component(connection_var, connection_conf)
cg.add(var.register_connection(connection_var))
await esp32_ble_tracker.register_client(connection_var, connection_conf)
await esp32_ble_tracker.register_raw_client(connection_var, connection_conf)
if config.get(CONF_CACHE_SERVICES):
add_idf_sdkconfig_option("CONFIG_BT_GATTC_CACHE_NVS_FLASH", True)

View File

@@ -42,15 +42,13 @@ void BluetoothProxy::send_bluetooth_scanner_state_(esp32_ble_tracker::ScannerSta
this->api_connection_->send_message(resp);
}
#ifdef USE_ESP32_BLE_DEVICE
bool BluetoothProxy::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr || this->raw_advertisements_)
return false;
ESP_LOGV(TAG, "Proxying packet from %s - %s. RSSI: %d dB", device.get_name().c_str(), device.address_str().c_str(),
device.get_rssi());
this->send_api_packet_(device);
return true;
// This method should never be called since bluetooth_proxy always uses raw advertisements
// but we need to provide an implementation to satisfy the virtual method requirement
return false;
}
#endif
// Batch size for BLE advertisements to maximize WiFi efficiency
// Each advertisement is up to 80 bytes when packaged (including protocol overhead)
@@ -69,7 +67,7 @@ std::vector<api::BluetoothLERawAdvertisement> batch_buffer;
static std::vector<api::BluetoothLERawAdvertisement> &get_batch_buffer() { return batch_buffer; }
bool BluetoothProxy::parse_devices(const esp32_ble::BLEScanResult *scan_results, size_t count) {
if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr || !this->raw_advertisements_)
if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr)
return false;
// Get the batch buffer reference
@@ -116,6 +114,7 @@ void BluetoothProxy::flush_pending_advertisements() {
this->api_connection_->send_message(resp);
}
#ifdef USE_ESP32_BLE_DEVICE
void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device) {
api::BluetoothLEAdvertisementResponse resp;
resp.address = device.address_uint64();
@@ -153,14 +152,14 @@ void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &devi
this->api_connection_->send_message(resp);
}
#endif // USE_ESP32_BLE_DEVICE
void BluetoothProxy::dump_config() {
ESP_LOGCONFIG(TAG, "Bluetooth Proxy:");
ESP_LOGCONFIG(TAG,
" Active: %s\n"
" Connections: %d\n"
" Raw advertisements: %s",
YESNO(this->active_), this->connections_.size(), YESNO(this->raw_advertisements_));
" Connections: %d",
YESNO(this->active_), this->connections_.size());
}
int BluetoothProxy::get_bluetooth_connections_free() {
@@ -188,15 +187,13 @@ void BluetoothProxy::loop() {
}
// Flush any pending BLE advertisements that have been accumulated but not yet sent
if (this->raw_advertisements_) {
static uint32_t last_flush_time = 0;
uint32_t now = App.get_loop_component_start_time();
static uint32_t last_flush_time = 0;
uint32_t now = App.get_loop_component_start_time();
// Flush accumulated advertisements every 100ms
if (now - last_flush_time >= 100) {
this->flush_pending_advertisements();
last_flush_time = now;
}
// Flush accumulated advertisements every 100ms
if (now - last_flush_time >= 100) {
this->flush_pending_advertisements();
last_flush_time = now;
}
for (auto *connection : this->connections_) {
if (connection->send_service_ == connection->service_count_) {
@@ -318,9 +315,7 @@ void BluetoothProxy::loop() {
}
esp32_ble_tracker::AdvertisementParserType BluetoothProxy::get_advertisement_parser_type() {
if (this->raw_advertisements_)
return esp32_ble_tracker::AdvertisementParserType::RAW_ADVERTISEMENTS;
return esp32_ble_tracker::AdvertisementParserType::PARSED_ADVERTISEMENTS;
return esp32_ble_tracker::AdvertisementParserType::RAW_ADVERTISEMENTS;
}
BluetoothConnection *BluetoothProxy::get_connection_(uint64_t address, bool reserve) {
@@ -565,7 +560,6 @@ void BluetoothProxy::subscribe_api_connection(api::APIConnection *api_connection
return;
}
this->api_connection_ = api_connection;
this->raw_advertisements_ = flags & BluetoothProxySubscriptionFlag::SUBSCRIPTION_RAW_ADVERTISEMENTS;
this->parent_->recalculate_advertisement_parser_types();
this->send_bluetooth_scanner_state_(this->parent_->get_scanner_state());
@@ -577,7 +571,6 @@ void BluetoothProxy::unsubscribe_api_connection(api::APIConnection *api_connecti
return;
}
this->api_connection_ = nullptr;
this->raw_advertisements_ = false;
this->parent_->recalculate_advertisement_parser_types();
}

View File

@@ -51,7 +51,9 @@ enum BluetoothProxySubscriptionFlag : uint32_t {
class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Component {
public:
BluetoothProxy();
#ifdef USE_ESP32_BLE_DEVICE
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
#endif
bool parse_devices(const esp32_ble::BLEScanResult *scan_results, size_t count) override;
void dump_config() override;
void setup() override;
@@ -129,7 +131,9 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com
}
protected:
#ifdef USE_ESP32_BLE_DEVICE
void send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device);
#endif
void send_bluetooth_scanner_state_(esp32_ble_tracker::ScannerState state);
BluetoothConnection *get_connection_(uint64_t address, bool reserve);
@@ -143,8 +147,7 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com
// Group 3: 1-byte types grouped together
bool active_;
bool raw_advertisements_{false};
// 2 bytes used, 2 bytes padding
// 1 byte used, 3 bytes padding
};
extern BluetoothProxy *global_bluetooth_proxy; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)

View File

@@ -3,6 +3,7 @@
CODEOWNERS = ["@esphome/core"]
CONF_BYTE_ORDER = "byte_order"
CONF_COLOR_DEPTH = "color_depth"
CONF_DRAW_ROUNDING = "draw_rounding"
CONF_ON_STATE_CHANGE = "on_state_change"
CONF_REQUEST_HEADERS = "request_headers"

View File

@@ -53,6 +53,7 @@ void DebugComponent::on_shutdown() {
auto pref = global_preferences->make_preference(REBOOT_MAX_LEN, fnv1_hash(REBOOT_KEY + App.get_name()));
if (component != nullptr) {
strncpy(buffer, component->get_component_source(), REBOOT_MAX_LEN - 1);
buffer[REBOOT_MAX_LEN - 1] = '\0';
}
ESP_LOGD(TAG, "Storing reboot source: %s", buffer);
pref.save(&buffer);
@@ -68,6 +69,7 @@ std::string DebugComponent::get_reset_reason_() {
auto pref = global_preferences->make_preference(REBOOT_MAX_LEN, fnv1_hash(REBOOT_KEY + App.get_name()));
char buffer[REBOOT_MAX_LEN]{};
if (pref.load(&buffer)) {
buffer[REBOOT_MAX_LEN - 1] = '\0';
reset_reason = "Reboot request from " + std::string(buffer);
}
}

View File

@@ -114,7 +114,6 @@ void ESP32InternalGPIOPin::setup() {
if (flags_ & gpio::FLAG_OUTPUT) {
gpio_set_drive_capability(pin_, drive_strength_);
}
ESP_LOGD(TAG, "rtc: %d", SOC_GPIO_SUPPORT_RTC_INDEPENDENT);
}
void ESP32InternalGPIOPin::pin_mode(gpio::Flags flags) {

View File

@@ -105,6 +105,7 @@ void BLEClientBase::dump_config() {
}
}
#ifdef USE_ESP32_BLE_DEVICE
bool BLEClientBase::parse_device(const espbt::ESPBTDevice &device) {
if (!this->auto_connect_)
return false;
@@ -122,6 +123,7 @@ bool BLEClientBase::parse_device(const espbt::ESPBTDevice &device) {
this->remote_addr_type_ = device.get_address_type();
return true;
}
#endif
void BLEClientBase::connect() {
ESP_LOGI(TAG, "[%d] [%s] 0x%02x Attempting BLE connection", this->connection_index_, this->address_str_.c_str(),

View File

@@ -31,7 +31,9 @@ class BLEClientBase : public espbt::ESPBTClient, public Component {
void dump_config() override;
void run_later(std::function<void()> &&f); // NOLINT
#ifdef USE_ESP32_BLE_DEVICE
bool parse_device(const espbt::ESPBTDevice &device) override;
#endif
void on_scan_end() override {}
bool gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) override;

View File

@@ -31,6 +31,8 @@ from esphome.const import (
CONF_TRIGGER_ID,
)
from esphome.core import CORE
from esphome.enum import StrEnum
from esphome.types import ConfigType
AUTO_LOAD = ["esp32_ble"]
DEPENDENCIES = ["esp32"]
@@ -50,6 +52,25 @@ IDF_MAX_CONNECTIONS = 9
_LOGGER = logging.getLogger(__name__)
# Enum for BLE features
class BLEFeatures(StrEnum):
ESP_BT_DEVICE = "ESP_BT_DEVICE"
# Set to track which features are needed by components
_required_features: set[BLEFeatures] = set()
def register_ble_features(features: set[BLEFeatures]) -> None:
"""Register BLE features that a component needs.
Args:
features: Set of BLEFeatures enum members
"""
_required_features.update(features)
esp32_ble_tracker_ns = cg.esphome_ns.namespace("esp32_ble_tracker")
ESP32BLETracker = esp32_ble_tracker_ns.class_(
"ESP32BLETracker",
@@ -277,6 +298,15 @@ async def to_code(config):
cg.add(var.set_scan_window(int(params[CONF_WINDOW].total_milliseconds / 0.625)))
cg.add(var.set_scan_active(params[CONF_ACTIVE]))
cg.add(var.set_scan_continuous(params[CONF_CONTINUOUS]))
# Register ESP_BT_DEVICE feature if any of the automation triggers are used
if (
config.get(CONF_ON_BLE_ADVERTISE)
or config.get(CONF_ON_BLE_SERVICE_DATA_ADVERTISE)
or config.get(CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE)
):
register_ble_features({BLEFeatures.ESP_BT_DEVICE})
for conf in config.get(CONF_ON_BLE_ADVERTISE, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
if CONF_MAC_ADDRESS in conf:
@@ -334,6 +364,11 @@ async def to_code(config):
cg.add_define("USE_OTA_STATE_CALLBACK") # To be notified when an OTA update starts
cg.add_define("USE_ESP32_BLE_CLIENT")
# Add feature-specific defines based on what's needed
if BLEFeatures.ESP_BT_DEVICE in _required_features:
cg.add_define("USE_ESP32_BLE_DEVICE")
if config.get(CONF_SOFTWARE_COEXISTENCE):
cg.add_define("USE_ESP32_BLE_SOFTWARE_COEXISTENCE")
@@ -382,13 +417,43 @@ async def esp32_ble_tracker_stop_scan_action_to_code(
return var
async def register_ble_device(var, config):
async def register_ble_device(
var: cg.SafeExpType, config: ConfigType
) -> cg.SafeExpType:
register_ble_features({BLEFeatures.ESP_BT_DEVICE})
paren = await cg.get_variable(config[CONF_ESP32_BLE_ID])
cg.add(paren.register_listener(var))
return var
async def register_client(var, config):
async def register_client(var: cg.SafeExpType, config: ConfigType) -> cg.SafeExpType:
register_ble_features({BLEFeatures.ESP_BT_DEVICE})
paren = await cg.get_variable(config[CONF_ESP32_BLE_ID])
cg.add(paren.register_client(var))
return var
async def register_raw_ble_device(
var: cg.SafeExpType, config: ConfigType
) -> cg.SafeExpType:
"""Register a BLE device listener that only needs raw advertisement data.
This does NOT register the ESP_BT_DEVICE feature, meaning ESPBTDevice
will not be compiled in if this is the only registration method used.
"""
paren = await cg.get_variable(config[CONF_ESP32_BLE_ID])
cg.add(paren.register_listener(var))
return var
async def register_raw_client(
var: cg.SafeExpType, config: ConfigType
) -> cg.SafeExpType:
"""Register a BLE client that only needs raw advertisement data.
This does NOT register the ESP_BT_DEVICE feature, meaning ESPBTDevice
will not be compiled in if this is the only registration method used.
"""
paren = await cg.get_variable(config[CONF_ESP32_BLE_ID])
cg.add(paren.register_client(var))
return var

View File

@@ -7,6 +7,7 @@
namespace esphome {
namespace esp32_ble_tracker {
#ifdef USE_ESP32_BLE_DEVICE
class ESPBTAdvertiseTrigger : public Trigger<const ESPBTDevice &>, public ESPBTDeviceListener {
public:
explicit ESPBTAdvertiseTrigger(ESP32BLETracker *parent) { parent->register_listener(this); }
@@ -87,6 +88,7 @@ class BLEEndOfScanTrigger : public Trigger<>, public ESPBTDeviceListener {
bool parse_device(const ESPBTDevice &device) override { return false; }
void on_scan_end() override { this->trigger(); }
};
#endif // USE_ESP32_BLE_DEVICE
template<typename... Ts> class ESP32BLEStartScanAction : public Action<Ts...> {
public:

View File

@@ -141,6 +141,7 @@ void ESP32BLETracker::loop() {
}
if (this->parse_advertisements_) {
#ifdef USE_ESP32_BLE_DEVICE
ESPBTDevice device;
device.parse_scan_rst(scan_result);
@@ -162,6 +163,7 @@ void ESP32BLETracker::loop() {
if (!found && !this->scan_continuous_) {
this->print_bt_device_info(device);
}
#endif // USE_ESP32_BLE_DEVICE
}
// Move to next entry in ring buffer
@@ -511,6 +513,7 @@ void ESP32BLETracker::set_scanner_state_(ScannerState state) {
this->scanner_state_callbacks_.call(state);
}
#ifdef USE_ESP32_BLE_DEVICE
ESPBLEiBeacon::ESPBLEiBeacon(const uint8_t *data) { memcpy(&this->beacon_data_, data, sizeof(beacon_data_)); }
optional<ESPBLEiBeacon> ESPBLEiBeacon::from_manufacturer_data(const ServiceData &data) {
if (!data.uuid.contains(0x4C, 0x00))
@@ -751,13 +754,16 @@ void ESPBTDevice::parse_adv_(const uint8_t *payload, uint8_t len) {
}
}
}
std::string ESPBTDevice::address_str() const {
char mac[24];
snprintf(mac, sizeof(mac), "%02X:%02X:%02X:%02X:%02X:%02X", this->address_[0], this->address_[1], this->address_[2],
this->address_[3], this->address_[4], this->address_[5]);
return mac;
}
uint64_t ESPBTDevice::address_uint64() const { return esp32_ble::ble_addr_to_uint64(this->address_); }
#endif // USE_ESP32_BLE_DEVICE
void ESP32BLETracker::dump_config() {
ESP_LOGCONFIG(TAG, "BLE Tracker:");
@@ -796,6 +802,7 @@ void ESP32BLETracker::dump_config() {
}
}
#ifdef USE_ESP32_BLE_DEVICE
void ESP32BLETracker::print_bt_device_info(const ESPBTDevice &device) {
const uint64_t address = device.address_uint64();
for (auto &disc : this->already_discovered_) {
@@ -866,8 +873,9 @@ bool ESPBTDevice::resolve_irk(const uint8_t *irk) const {
return ecb_ciphertext[15] == (addr64 & 0xff) && ecb_ciphertext[14] == ((addr64 >> 8) & 0xff) &&
ecb_ciphertext[13] == ((addr64 >> 16) & 0xff);
}
#endif // USE_ESP32_BLE_DEVICE
} // namespace esp32_ble_tracker
} // namespace esphome
#endif
#endif // USE_ESP32

View File

@@ -39,6 +39,7 @@ struct ServiceData {
adv_data_t data;
};
#ifdef USE_ESP32_BLE_DEVICE
class ESPBLEiBeacon {
public:
ESPBLEiBeacon() { memset(&this->beacon_data_, 0, sizeof(this->beacon_data_)); }
@@ -116,13 +117,16 @@ class ESPBTDevice {
std::vector<ServiceData> service_datas_{};
const BLEScanResult *scan_result_{nullptr};
};
#endif // USE_ESP32_BLE_DEVICE
class ESP32BLETracker;
class ESPBTDeviceListener {
public:
virtual void on_scan_end() {}
#ifdef USE_ESP32_BLE_DEVICE
virtual bool parse_device(const ESPBTDevice &device) = 0;
#endif
virtual bool parse_devices(const BLEScanResult *scan_results, size_t count) { return false; };
virtual AdvertisementParserType get_advertisement_parser_type() {
return AdvertisementParserType::PARSED_ADVERTISEMENTS;
@@ -237,7 +241,9 @@ class ESP32BLETracker : public Component,
void register_client(ESPBTClient *client);
void recalculate_advertisement_parser_types();
#ifdef USE_ESP32_BLE_DEVICE
void print_bt_device_info(const ESPBTDevice &device);
#endif
void start_scan();
void stop_scan();

View File

@@ -308,7 +308,7 @@ async def to_code(config):
cg.add(var.set_frame_buffer_count(config[CONF_FRAME_BUFFER_COUNT]))
cg.add(var.set_frame_size(config[CONF_RESOLUTION]))
cg.add_define("USE_ESP32_CAMERA")
cg.add_define("USE_CAMERA")
if CORE.using_esp_idf:
add_idf_component(name="espressif/esp32-camera", ref="2.0.15")

View File

@@ -20,14 +20,16 @@ adjusted_ids = set()
CONFIG_SCHEMA = cv.All(
cv.ensure_list(
{
cv.GenerateID(): cv.declare_id(EspLdo),
cv.Required(CONF_VOLTAGE): cv.All(
cv.voltage, cv.float_range(min=0.5, max=2.7)
),
cv.Required(CONF_CHANNEL): cv.one_of(*CHANNELS, int=True),
cv.Optional(CONF_ADJUSTABLE, default=False): cv.boolean,
}
cv.COMPONENT_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(EspLdo),
cv.Required(CONF_VOLTAGE): cv.All(
cv.voltage, cv.float_range(min=0.5, max=2.7)
),
cv.Required(CONF_CHANNEL): cv.one_of(*CHANNELS, int=True),
cv.Optional(CONF_ADJUSTABLE, default=False): cv.boolean,
}
)
),
cv.only_with_esp_idf,
only_on_variant(supported=[VARIANT_ESP32P4]),

View File

@@ -17,6 +17,9 @@ class EspLdo : public Component {
void set_adjustable(bool adjustable) { this->adjustable_ = adjustable; }
void set_voltage(float voltage) { this->voltage_ = voltage; }
void adjust_voltage(float voltage);
float get_setup_priority() const override {
return setup_priority::BUS; // LDO setup should be done early
}
protected:
int channel_;

View File

@@ -342,5 +342,11 @@ async def to_code(config):
cg.add_define("USE_ETHERNET")
# Disable WiFi when using Ethernet to save memory
if CORE.using_esp_idf:
add_idf_sdkconfig_option("CONFIG_ESP_WIFI_ENABLED", False)
# Also disable WiFi/BT coexistence since WiFi is disabled
add_idf_sdkconfig_option("CONFIG_SW_COEXIST_ENABLE", False)
if CORE.using_arduino:
cg.add_library("WiFi", None)

View File

@@ -29,7 +29,6 @@ class IPAddressEthernetInfo : public PollingComponent, public text_sensor::TextS
}
float get_setup_priority() const override { return setup_priority::ETHERNET; }
std::string unique_id() override { return get_mac_address() + "-ethernetinfo"; }
void dump_config() override;
void add_ip_sensors(uint8_t index, text_sensor::TextSensor *s) { this->ip_sensors_[index] = s; }
@@ -52,7 +51,6 @@ class DNSAddressEthernetInfo : public PollingComponent, public text_sensor::Text
}
}
float get_setup_priority() const override { return setup_priority::ETHERNET; }
std::string unique_id() override { return get_mac_address() + "-ethernetinfo-dns"; }
void dump_config() override;
protected:
@@ -63,7 +61,6 @@ class MACAddressEthernetInfo : public Component, public text_sensor::TextSensor
public:
void setup() override { this->publish_state(ethernet::global_eth_component->get_eth_mac_address_pretty()); }
float get_setup_priority() const override { return setup_priority::ETHERNET; }
std::string unique_id() override { return get_mac_address() + "-ethernetinfo-mac"; }
void dump_config() override;
};

View File

@@ -177,6 +177,10 @@ optional<FanRestoreState> Fan::restore_state_() {
return {};
}
void Fan::save_state_() {
if (this->restore_mode_ == FanRestoreMode::NO_RESTORE) {
return;
}
FanRestoreState state{};
state.state = this->state;
state.oscillating = this->oscillating;

View File

@@ -1,6 +1,6 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor
import esphome.config_validation as cv
from esphome.const import (
CONF_ID,
DEVICE_CLASS_DISTANCE,

View File

@@ -1,11 +1,16 @@
import logging
from esphome import pins
import esphome.codegen as cg
from esphome.components import binary_sensor
import esphome.config_validation as cv
from esphome.const import CONF_PIN
from esphome.const import CONF_ID, CONF_NAME, CONF_NUMBER, CONF_PIN
from esphome.core import CORE
from .. import gpio_ns
_LOGGER = logging.getLogger(__name__)
GPIOBinarySensor = gpio_ns.class_(
"GPIOBinarySensor", binary_sensor.BinarySensor, cg.Component
)
@@ -41,6 +46,22 @@ async def to_code(config):
pin = await cg.gpio_pin_expression(config[CONF_PIN])
cg.add(var.set_pin(pin))
cg.add(var.set_use_interrupt(config[CONF_USE_INTERRUPT]))
if config[CONF_USE_INTERRUPT]:
# Check for ESP8266 GPIO16 interrupt limitation
# GPIO16 on ESP8266 is a special pin that doesn't support interrupts through
# the Arduino attachInterrupt() function. This is the only known GPIO pin
# across all supported platforms that has this limitation, so we handle it
# here instead of in the platform-specific code.
use_interrupt = config[CONF_USE_INTERRUPT]
if use_interrupt and CORE.is_esp8266 and config[CONF_PIN][CONF_NUMBER] == 16:
_LOGGER.warning(
"GPIO binary_sensor '%s': GPIO16 on ESP8266 doesn't support interrupts. "
"Falling back to polling mode (same as in ESPHome <2025.7). "
"The sensor will work exactly as before, but other pins have better "
"performance with interrupts.",
config.get(CONF_NAME, config[CONF_ID]),
)
use_interrupt = False
cg.add(var.set_use_interrupt(use_interrupt))
if use_interrupt:
cg.add(var.set_interrupt_type(config[CONF_INTERRUPT_TYPE]))

View File

@@ -83,7 +83,7 @@ void HttpRequestUpdate::update_task(void *params) {
container.reset(); // Release ownership of the container's shared_ptr
valid = json::parse_json(response, [this_update](JsonObject root) -> bool {
if (!root.containsKey("name") || !root.containsKey("version") || !root.containsKey("builds")) {
if (!root["name"].is<const char *>() || !root["version"].is<const char *>() || !root["builds"].is<JsonArray>()) {
ESP_LOGE(TAG, "Manifest does not contain required fields");
return false;
}
@@ -91,26 +91,26 @@ void HttpRequestUpdate::update_task(void *params) {
this_update->update_info_.latest_version = root["version"].as<std::string>();
for (auto build : root["builds"].as<JsonArray>()) {
if (!build.containsKey("chipFamily")) {
if (!build["chipFamily"].is<const char *>()) {
ESP_LOGE(TAG, "Manifest does not contain required fields");
return false;
}
if (build["chipFamily"] == ESPHOME_VARIANT) {
if (!build.containsKey("ota")) {
if (!build["ota"].is<JsonObject>()) {
ESP_LOGE(TAG, "Manifest does not contain required fields");
return false;
}
auto ota = build["ota"];
if (!ota.containsKey("path") || !ota.containsKey("md5")) {
JsonObject ota = build["ota"].as<JsonObject>();
if (!ota["path"].is<const char *>() || !ota["md5"].is<const char *>()) {
ESP_LOGE(TAG, "Manifest does not contain required fields");
return false;
}
this_update->update_info_.firmware_url = ota["path"].as<std::string>();
this_update->update_info_.md5 = ota["md5"].as<std::string>();
if (ota.containsKey("summary"))
if (ota["summary"].is<const char *>())
this_update->update_info_.summary = ota["summary"].as<std::string>();
if (ota.containsKey("release_url"))
if (ota["release_url"].is<const char *>())
this_update->update_info_.release_url = ota["release_url"].as<std::string>();
return true;

View File

@@ -7,6 +7,7 @@
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include <driver/gpio.h>
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0)
#define SOC_HP_I2C_NUM SOC_I2C_NUM
@@ -20,21 +21,72 @@ static const char *const TAG = "i2c.idf";
void IDFI2CBus::setup() {
ESP_LOGCONFIG(TAG, "Running setup");
static i2c_port_t next_port = I2C_NUM_0;
port_ = next_port;
this->port_ = next_port;
if (this->port_ == I2C_NUM_MAX) {
ESP_LOGE(TAG, "No more than %u buses supported", I2C_NUM_MAX);
this->mark_failed();
return;
}
if (this->timeout_ > 13000) {
ESP_LOGW(TAG, "Using max allowed timeout: 13 ms");
this->timeout_ = 13000;
}
this->recover_();
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2)
next_port = (i2c_port_t) (next_port + 1);
i2c_master_bus_config_t bus_conf{};
memset(&bus_conf, 0, sizeof(bus_conf));
bus_conf.sda_io_num = gpio_num_t(sda_pin_);
bus_conf.scl_io_num = gpio_num_t(scl_pin_);
bus_conf.i2c_port = this->port_;
bus_conf.glitch_ignore_cnt = 7;
#if SOC_LP_I2C_SUPPORTED
if (this->port_ < SOC_HP_I2C_NUM) {
bus_conf.clk_source = I2C_CLK_SRC_DEFAULT;
} else {
bus_conf.lp_source_clk = LP_I2C_SCLK_DEFAULT;
}
#else
bus_conf.clk_source = I2C_CLK_SRC_DEFAULT;
#endif
bus_conf.flags.enable_internal_pullup = sda_pullup_enabled_ || scl_pullup_enabled_;
esp_err_t err = i2c_new_master_bus(&bus_conf, &this->bus_);
if (err != ESP_OK) {
ESP_LOGW(TAG, "i2c_new_master_bus failed: %s", esp_err_to_name(err));
this->mark_failed();
return;
}
i2c_device_config_t dev_conf{};
memset(&dev_conf, 0, sizeof(dev_conf));
dev_conf.dev_addr_length = I2C_ADDR_BIT_LEN_7;
dev_conf.device_address = I2C_DEVICE_ADDRESS_NOT_USED;
dev_conf.scl_speed_hz = this->frequency_;
dev_conf.scl_wait_us = this->timeout_;
err = i2c_master_bus_add_device(this->bus_, &dev_conf, &this->dev_);
if (err != ESP_OK) {
ESP_LOGW(TAG, "i2c_master_bus_add_device failed: %s", esp_err_to_name(err));
this->mark_failed();
return;
}
this->initialized_ = true;
if (this->scan_) {
ESP_LOGV(TAG, "Scanning for devices");
this->i2c_scan_();
}
#else
#if SOC_HP_I2C_NUM > 1
next_port = (next_port == I2C_NUM_0) ? I2C_NUM_1 : I2C_NUM_MAX;
#else
next_port = I2C_NUM_MAX;
#endif
if (port_ == I2C_NUM_MAX) {
ESP_LOGE(TAG, "No more than %u buses supported", SOC_HP_I2C_NUM);
this->mark_failed();
return;
}
recover_();
i2c_config_t conf{};
memset(&conf, 0, sizeof(conf));
conf.mode = I2C_MODE_MASTER;
@@ -53,11 +105,7 @@ void IDFI2CBus::setup() {
this->mark_failed();
return;
}
if (timeout_ > 0) { // if timeout specified in yaml:
if (timeout_ > 13000) {
ESP_LOGW(TAG, "i2c timeout of %" PRIu32 "us greater than max of 13ms on esp-idf, setting to max", timeout_);
timeout_ = 13000;
}
if (timeout_ > 0) {
err = i2c_set_timeout(port_, timeout_ * 80); // unit: APB 80MHz clock cycle
if (err != ESP_OK) {
ESP_LOGW(TAG, "i2c_set_timeout failed: %s", esp_err_to_name(err));
@@ -73,12 +121,15 @@ void IDFI2CBus::setup() {
this->mark_failed();
return;
}
initialized_ = true;
if (this->scan_) {
ESP_LOGV(TAG, "Scanning bus for active devices");
this->i2c_scan_();
}
#endif
}
void IDFI2CBus::dump_config() {
ESP_LOGCONFIG(TAG, "I2C Bus:");
ESP_LOGCONFIG(TAG,
@@ -123,6 +174,74 @@ ErrorCode IDFI2CBus::readv(uint8_t address, ReadBuffer *buffers, size_t cnt) {
ESP_LOGVV(TAG, "i2c bus not initialized!");
return ERROR_NOT_INITIALIZED;
}
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2)
i2c_operation_job_t jobs[cnt + 4];
uint8_t read = (address << 1) | I2C_MASTER_READ;
size_t last = 0, num = 0;
jobs[num].command = I2C_MASTER_CMD_START;
num++;
jobs[num].command = I2C_MASTER_CMD_WRITE;
jobs[num].write.ack_check = true;
jobs[num].write.data = &read;
jobs[num].write.total_bytes = 1;
num++;
// find the last valid index
for (size_t i = 0; i < cnt; i++) {
const auto &buf = buffers[i];
if (buf.len == 0) {
continue;
}
last = i;
}
for (size_t i = 0; i < cnt; i++) {
const auto &buf = buffers[i];
if (buf.len == 0) {
continue;
}
if (i == last) {
// the last byte read before stop should always be a nack,
// split the last read if len is larger than 1
if (buf.len > 1) {
jobs[num].command = I2C_MASTER_CMD_READ;
jobs[num].read.ack_value = I2C_ACK_VAL;
jobs[num].read.data = (uint8_t *) buf.data;
jobs[num].read.total_bytes = buf.len - 1;
num++;
}
jobs[num].command = I2C_MASTER_CMD_READ;
jobs[num].read.ack_value = I2C_NACK_VAL;
jobs[num].read.data = (uint8_t *) buf.data + buf.len - 1;
jobs[num].read.total_bytes = 1;
num++;
} else {
jobs[num].command = I2C_MASTER_CMD_READ;
jobs[num].read.ack_value = I2C_ACK_VAL;
jobs[num].read.data = (uint8_t *) buf.data;
jobs[num].read.total_bytes = buf.len;
num++;
}
}
jobs[num].command = I2C_MASTER_CMD_STOP;
num++;
esp_err_t err = i2c_master_execute_defined_operations(this->dev_, jobs, num, 20);
if (err == ESP_ERR_INVALID_STATE) {
ESP_LOGVV(TAG, "RX from %02X failed: not acked", address);
return ERROR_NOT_ACKNOWLEDGED;
} else if (err == ESP_ERR_TIMEOUT) {
ESP_LOGVV(TAG, "RX from %02X failed: timeout", address);
return ERROR_TIMEOUT;
} else if (err != ESP_OK) {
ESP_LOGVV(TAG, "RX from %02X failed: %s", address, esp_err_to_name(err));
return ERROR_UNKNOWN;
}
#else
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
esp_err_t err = i2c_master_start(cmd);
if (err != ESP_OK) {
@@ -168,6 +287,7 @@ ErrorCode IDFI2CBus::readv(uint8_t address, ReadBuffer *buffers, size_t cnt) {
ESP_LOGVV(TAG, "RX from %02X failed: %s", address, esp_err_to_name(err));
return ERROR_UNKNOWN;
}
#endif
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
char debug_buf[4];
@@ -185,6 +305,7 @@ ErrorCode IDFI2CBus::readv(uint8_t address, ReadBuffer *buffers, size_t cnt) {
return ERROR_OK;
}
ErrorCode IDFI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cnt, bool stop) {
// logging is only enabled with vv level, if warnings are shown the caller
// should log them
@@ -207,6 +328,49 @@ ErrorCode IDFI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cnt, b
ESP_LOGVV(TAG, "0x%02X TX %s", address, debug_hex.c_str());
#endif
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2)
i2c_operation_job_t jobs[cnt + 3];
uint8_t write = (address << 1) | I2C_MASTER_WRITE;
size_t num = 0;
jobs[num].command = I2C_MASTER_CMD_START;
num++;
jobs[num].command = I2C_MASTER_CMD_WRITE;
jobs[num].write.ack_check = true;
jobs[num].write.data = &write;
jobs[num].write.total_bytes = 1;
num++;
for (size_t i = 0; i < cnt; i++) {
const auto &buf = buffers[i];
if (buf.len == 0) {
continue;
}
jobs[num].command = I2C_MASTER_CMD_WRITE;
jobs[num].write.ack_check = true;
jobs[num].write.data = (uint8_t *) buf.data;
jobs[num].write.total_bytes = buf.len;
num++;
}
if (stop) {
jobs[num].command = I2C_MASTER_CMD_STOP;
num++;
}
esp_err_t err = i2c_master_execute_defined_operations(this->dev_, jobs, num, 20);
if (err == ESP_ERR_INVALID_STATE) {
ESP_LOGVV(TAG, "TX to %02X failed: not acked", address);
return ERROR_NOT_ACKNOWLEDGED;
} else if (err == ESP_ERR_TIMEOUT) {
ESP_LOGVV(TAG, "TX to %02X failed: timeout", address);
return ERROR_TIMEOUT;
} else if (err != ESP_OK) {
ESP_LOGVV(TAG, "TX to %02X failed: %s", address, esp_err_to_name(err));
return ERROR_UNKNOWN;
}
#else
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
esp_err_t err = i2c_master_start(cmd);
if (err != ESP_OK) {
@@ -252,6 +416,7 @@ ErrorCode IDFI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cnt, b
ESP_LOGVV(TAG, "TX to %02X failed: %s", address, esp_err_to_name(err));
return ERROR_UNKNOWN;
}
#endif
return ERROR_OK;
}

View File

@@ -2,9 +2,14 @@
#ifdef USE_ESP_IDF
#include <driver/i2c.h>
#include "esphome/core/component.h"
#include "i2c_bus.h"
#include "esp_idf_version.h"
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2)
#include <driver/i2c_master.h>
#else
#include <driver/i2c.h>
#endif
namespace esphome {
namespace i2c {
@@ -38,6 +43,10 @@ class IDFI2CBus : public InternalI2CBus, public Component {
RecoveryCode recovery_result_;
protected:
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2)
i2c_master_dev_handle_t dev_;
i2c_master_bus_handle_t bus_;
#endif
i2c_port_t port_;
uint8_t sda_pin_;
bool sda_pullup_enabled_;

View File

@@ -180,7 +180,7 @@ async def to_code(config):
await speaker.register_speaker(var, config)
if config[CONF_DAC_TYPE] == "internal":
cg.add(var.set_internal_dac_mode(config[CONF_CHANNEL]))
cg.add(var.set_internal_dac_mode(config[CONF_MODE]))
else:
cg.add(var.set_dout_pin(config[CONF_I2S_DOUT_PIN]))
if use_legacy():

View File

@@ -12,6 +12,6 @@ CONFIG_SCHEMA = cv.All(
@coroutine_with_priority(1.0)
async def to_code(config):
cg.add_library("bblanchon/ArduinoJson", "6.18.5")
cg.add_library("bblanchon/ArduinoJson", "7.4.2")
cg.add_define("USE_JSON")
cg.add_global(json_ns.using)

View File

@@ -1,83 +1,76 @@
#include "json_util.h"
#include "esphome/core/log.h"
// ArduinoJson::Allocator is included via ArduinoJson.h in json_util.h
namespace esphome {
namespace json {
static const char *const TAG = "json";
static std::vector<char> global_json_build_buffer; // NOLINT
static const auto ALLOCATOR = RAMAllocator<uint8_t>(RAMAllocator<uint8_t>::ALLOC_INTERNAL);
// Build an allocator for the JSON Library using the RAMAllocator class
struct SpiRamAllocator : ArduinoJson::Allocator {
void *allocate(size_t size) override { return this->allocator_.allocate(size); }
void deallocate(void *pointer) override {
// ArduinoJson's Allocator interface doesn't provide the size parameter in deallocate.
// RAMAllocator::deallocate() requires the size, which we don't have access to here.
// RAMAllocator::deallocate implementation just calls free() regardless of whether
// the memory was allocated with heap_caps_malloc or malloc.
// This is safe because ESP-IDF's heap implementation internally tracks the memory region
// and routes free() to the appropriate heap.
free(pointer); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc)
}
void *reallocate(void *ptr, size_t new_size) override {
return this->allocator_.reallocate(static_cast<uint8_t *>(ptr), new_size);
}
protected:
RAMAllocator<uint8_t> allocator_{RAMAllocator<uint8_t>(RAMAllocator<uint8_t>::NONE)};
};
std::string build_json(const json_build_t &f) {
// Here we are allocating up to 5kb of memory,
// with the heap size minus 2kb to be safe if less than 5kb
// as we can not have a true dynamic sized document.
// The excess memory is freed below with `shrinkToFit()`
auto free_heap = ALLOCATOR.get_max_free_block_size();
size_t request_size = std::min(free_heap, (size_t) 512);
while (true) {
ESP_LOGV(TAG, "Attempting to allocate %zu bytes for JSON serialization", request_size);
DynamicJsonDocument json_document(request_size);
if (json_document.capacity() == 0) {
ESP_LOGE(TAG, "Could not allocate memory for document! Requested %zu bytes, largest free heap block: %zu bytes",
request_size, free_heap);
return "{}";
}
JsonObject root = json_document.to<JsonObject>();
f(root);
if (json_document.overflowed()) {
if (request_size == free_heap) {
ESP_LOGE(TAG, "Could not allocate memory for document! Overflowed largest free heap block: %zu bytes",
free_heap);
return "{}";
}
request_size = std::min(request_size * 2, free_heap);
continue;
}
json_document.shrinkToFit();
ESP_LOGV(TAG, "Size after shrink %zu bytes", json_document.capacity());
std::string output;
serializeJson(json_document, output);
return output;
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
auto doc_allocator = SpiRamAllocator();
JsonDocument json_document(&doc_allocator);
if (json_document.overflowed()) {
ESP_LOGE(TAG, "Could not allocate memory for JSON document!");
return "{}";
}
JsonObject root = json_document.to<JsonObject>();
f(root);
if (json_document.overflowed()) {
ESP_LOGE(TAG, "Could not allocate memory for JSON document!");
return "{}";
}
std::string output;
serializeJson(json_document, output);
return output;
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
}
bool parse_json(const std::string &data, const json_parse_t &f) {
// Here we are allocating 1.5 times the data size,
// with the heap size minus 2kb to be safe if less than that
// as we can not have a true dynamic sized document.
// The excess memory is freed below with `shrinkToFit()`
auto free_heap = ALLOCATOR.get_max_free_block_size();
size_t request_size = std::min(free_heap, (size_t) (data.size() * 1.5));
while (true) {
DynamicJsonDocument json_document(request_size);
if (json_document.capacity() == 0) {
ESP_LOGE(TAG, "Could not allocate memory for document! Requested %zu bytes, free heap: %zu", request_size,
free_heap);
return false;
}
DeserializationError err = deserializeJson(json_document, data);
json_document.shrinkToFit();
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
auto doc_allocator = SpiRamAllocator();
JsonDocument json_document(&doc_allocator);
if (json_document.overflowed()) {
ESP_LOGE(TAG, "Could not allocate memory for JSON document!");
return false;
}
DeserializationError err = deserializeJson(json_document, data);
JsonObject root = json_document.as<JsonObject>();
JsonObject root = json_document.as<JsonObject>();
if (err == DeserializationError::Ok) {
return f(root);
} else if (err == DeserializationError::NoMemory) {
if (request_size * 2 >= free_heap) {
ESP_LOGE(TAG, "Can not allocate more memory for deserialization. Consider making source string smaller");
return false;
}
ESP_LOGV(TAG, "Increasing memory allocation.");
request_size *= 2;
continue;
} else {
ESP_LOGE(TAG, "Parse error: %s", err.c_str());
return false;
}
};
if (err == DeserializationError::Ok) {
return f(root);
} else if (err == DeserializationError::NoMemory) {
ESP_LOGE(TAG, "Can not allocate more memory for deserialization. Consider making source string smaller");
return false;
}
ESP_LOGE(TAG, "Parse error: %s", err.c_str());
return false;
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
}
} // namespace json

View File

@@ -178,13 +178,8 @@ static constexpr uint8_t NO_MAC[] = {0x08, 0x05, 0x04, 0x03, 0x02, 0x01};
static inline int two_byte_to_int(char firstbyte, char secondbyte) { return (int16_t) (secondbyte << 8) + firstbyte; }
static bool validate_header_footer(const uint8_t *header_footer, const uint8_t *buffer) {
for (uint8_t i = 0; i < HEADER_FOOTER_SIZE; i++) {
if (header_footer[i] != buffer[i]) {
return false; // Mismatch in header/footer
}
}
return true; // Valid header/footer
static inline bool validate_header_footer(const uint8_t *header_footer, const uint8_t *buffer) {
return std::memcmp(header_footer, buffer, HEADER_FOOTER_SIZE) == 0;
}
void LD2410Component::dump_config() {
@@ -300,14 +295,12 @@ void LD2410Component::send_command_(uint8_t command, const uint8_t *command_valu
if (command_value != nullptr) {
len += command_value_len;
}
uint8_t len_cmd[] = {lowbyte(len), highbyte(len), command, 0x00};
// 2 length bytes (low, high) + 2 command bytes (low, high)
uint8_t len_cmd[] = {len, 0x00, command, 0x00};
this->write_array(len_cmd, sizeof(len_cmd));
// command value bytes
if (command_value != nullptr) {
for (uint8_t i = 0; i < command_value_len; i++) {
this->write_byte(command_value[i]);
}
this->write_array(command_value, command_value_len);
}
// frame footer bytes
this->write_array(CMD_FRAME_FOOTER, sizeof(CMD_FRAME_FOOTER));
@@ -401,7 +394,7 @@ void LD2410Component::handle_periodic_data_() {
/*
Moving distance range: 18th byte
Still distance range: 19th byte
Moving enery: 20~28th bytes
Moving energy: 20~28th bytes
*/
for (std::vector<sensor::Sensor *>::size_type i = 0; i != this->gate_move_sensors_.size(); i++) {
sensor::Sensor *s = this->gate_move_sensors_[i];
@@ -480,7 +473,7 @@ bool LD2410Component::handle_ack_data_() {
ESP_LOGE(TAG, "Invalid status");
return true;
}
if (ld2410::two_byte_to_int(this->buffer_data_[8], this->buffer_data_[9]) != 0x00) {
if (this->buffer_data_[8] || this->buffer_data_[9]) {
ESP_LOGW(TAG, "Invalid command: %02X, %02X", this->buffer_data_[8], this->buffer_data_[9]);
return true;
}
@@ -534,8 +527,8 @@ bool LD2410Component::handle_ack_data_() {
const auto *light_function_str = find_str(LIGHT_FUNCTIONS_BY_UINT, this->light_function_);
const auto *out_pin_level_str = find_str(OUT_PIN_LEVELS_BY_UINT, this->out_pin_level_);
ESP_LOGV(TAG,
"Light function is: %s\n"
"Light threshold is: %u\n"
"Light function: %s\n"
"Light threshold: %u\n"
"Out pin level: %s",
light_function_str, this->light_threshold_, out_pin_level_str);
#ifdef USE_SELECT
@@ -600,7 +593,7 @@ bool LD2410Component::handle_ack_data_() {
break;
case CMD_QUERY: { // Query parameters response
if (this->buffer_data_[10] != 0xAA)
if (this->buffer_data_[10] != HEADER)
return true; // value head=0xAA
#ifdef USE_NUMBER
/*
@@ -656,17 +649,11 @@ void LD2410Component::readline_(int readch) {
if (this->buffer_pos_ < 4) {
return; // Not enough data to process yet
}
if (this->buffer_data_[this->buffer_pos_ - 4] == DATA_FRAME_FOOTER[0] &&
this->buffer_data_[this->buffer_pos_ - 3] == DATA_FRAME_FOOTER[1] &&
this->buffer_data_[this->buffer_pos_ - 2] == DATA_FRAME_FOOTER[2] &&
this->buffer_data_[this->buffer_pos_ - 1] == DATA_FRAME_FOOTER[3]) {
if (ld2410::validate_header_footer(DATA_FRAME_FOOTER, &this->buffer_data_[this->buffer_pos_ - 4])) {
ESP_LOGV(TAG, "Handling Periodic Data: %s", format_hex_pretty(this->buffer_data_, this->buffer_pos_).c_str());
this->handle_periodic_data_();
this->buffer_pos_ = 0; // Reset position index for next message
} else if (this->buffer_data_[this->buffer_pos_ - 4] == CMD_FRAME_FOOTER[0] &&
this->buffer_data_[this->buffer_pos_ - 3] == CMD_FRAME_FOOTER[1] &&
this->buffer_data_[this->buffer_pos_ - 2] == CMD_FRAME_FOOTER[2] &&
this->buffer_data_[this->buffer_pos_ - 1] == CMD_FRAME_FOOTER[3]) {
} else if (ld2410::validate_header_footer(CMD_FRAME_FOOTER, &this->buffer_data_[this->buffer_pos_ - 4])) {
ESP_LOGV(TAG, "Handling Ack Data: %s", format_hex_pretty(this->buffer_data_, this->buffer_pos_).c_str());
if (this->handle_ack_data_()) {
this->buffer_pos_ = 0; // Reset position index for next message
@@ -772,7 +759,6 @@ void LD2410Component::set_max_distances_timeout() {
0x00};
this->set_config_mode_(true);
this->send_command_(CMD_MAXDIST_DURATION, value, sizeof(value));
delay(50); // NOLINT
this->query_parameters_();
this->set_timeout(200, [this]() { this->restart_and_read_all_info(); });
this->set_config_mode_(false);
@@ -802,7 +788,6 @@ void LD2410Component::set_gate_threshold(uint8_t gate) {
0x01, 0x00, lowbyte(motion), highbyte(motion), 0x00, 0x00,
0x02, 0x00, lowbyte(still), highbyte(still), 0x00, 0x00};
this->send_command_(CMD_GATE_SENS, value, sizeof(value));
delay(50); // NOLINT
this->query_parameters_();
this->set_config_mode_(false);
}
@@ -833,7 +818,6 @@ void LD2410Component::set_light_out_control() {
this->set_config_mode_(true);
uint8_t value[4] = {this->light_function_, this->light_threshold_, this->out_pin_level_, 0x00};
this->send_command_(CMD_SET_LIGHT_CONTROL, value, sizeof(value));
delay(50); // NOLINT
this->query_light_control_();
this->set_timeout(200, [this]() { this->restart_and_read_all_info(); });
this->set_config_mode_(false);

View File

@@ -5,10 +5,10 @@
namespace esphome {
namespace ld2420 {
static const char *const TAG = "LD2420.binary_sensor";
static const char *const TAG = "ld2420.binary_sensor";
void LD2420BinarySensor::dump_config() {
ESP_LOGCONFIG(TAG, "LD2420 BinarySensor:");
ESP_LOGCONFIG(TAG, "Binary Sensor:");
LOG_BINARY_SENSOR(" ", "Presence", this->presence_bsensor_);
}

View File

@@ -2,7 +2,7 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
static const char *const TAG = "LD2420.button";
static const char *const TAG = "ld2420.button";
namespace esphome {
namespace ld2420 {

View File

@@ -137,7 +137,7 @@ static const std::string OP_SIMPLE_MODE_STRING = "Simple";
// Memory-efficient lookup tables
struct StringToUint8 {
const char *str;
uint8_t value;
const uint8_t value;
};
static constexpr StringToUint8 OP_MODE_BY_STR[] = {
@@ -155,8 +155,9 @@ static constexpr const char *ERR_MESSAGE[] = {
// Helper function for lookups
template<size_t N> uint8_t find_uint8(const StringToUint8 (&arr)[N], const std::string &str) {
for (const auto &entry : arr) {
if (str == entry.str)
if (str == entry.str) {
return entry.value;
}
}
return 0xFF; // Not found
}
@@ -326,15 +327,8 @@ void LD2420Component::revert_config_action() {
void LD2420Component::loop() {
// If there is a active send command do not process it here, the send command call will handle it.
if (!this->get_cmd_active_()) {
if (!this->available())
return;
static uint8_t buffer[2048];
static uint8_t rx_data;
while (this->available()) {
rx_data = this->read();
this->readline_(rx_data, buffer, sizeof(buffer));
}
while (!this->cmd_active_ && this->available()) {
this->readline_(this->read(), this->buffer_data_, MAX_LINE_LENGTH);
}
}
@@ -365,8 +359,9 @@ void LD2420Component::auto_calibrate_sensitivity() {
// Store average and peak values
this->gate_avg[gate] = sum / CALIBRATE_SAMPLES;
if (this->gate_peak[gate] < peak)
if (this->gate_peak[gate] < peak) {
this->gate_peak[gate] = peak;
}
uint32_t calculated_value =
(static_cast<uint32_t>(this->gate_peak[gate]) + (move_factor * static_cast<uint32_t>(this->gate_peak[gate])));
@@ -403,8 +398,9 @@ void LD2420Component::set_operating_mode(const std::string &state) {
}
} else {
// Set the current data back so we don't have new data that can be applied in error.
if (this->get_calibration_())
if (this->get_calibration_()) {
memcpy(&this->new_config, &this->current_config, sizeof(this->current_config));
}
this->set_calibration_(false);
}
} else {
@@ -414,30 +410,32 @@ void LD2420Component::set_operating_mode(const std::string &state) {
}
void LD2420Component::readline_(int rx_data, uint8_t *buffer, int len) {
static int pos = 0;
if (rx_data >= 0) {
if (pos < len - 1) {
buffer[pos++] = rx_data;
buffer[pos] = 0;
} else {
pos = 0;
}
if (pos >= 4) {
if (memcmp(&buffer[pos - 4], &CMD_FRAME_FOOTER, sizeof(CMD_FRAME_FOOTER)) == 0) {
this->set_cmd_active_(false); // Set command state to inactive after responce.
this->handle_ack_data_(buffer, pos);
pos = 0;
} else if ((buffer[pos - 2] == 0x0D && buffer[pos - 1] == 0x0A) &&
(this->get_mode_() == CMD_SYSTEM_MODE_SIMPLE)) {
this->handle_simple_mode_(buffer, pos);
pos = 0;
} else if ((memcmp(&buffer[pos - 4], &ENERGY_FRAME_FOOTER, sizeof(ENERGY_FRAME_FOOTER)) == 0) &&
(this->get_mode_() == CMD_SYSTEM_MODE_ENERGY)) {
this->handle_energy_mode_(buffer, pos);
pos = 0;
}
}
if (rx_data < 0) {
return; // No data available
}
if (this->buffer_pos_ < len - 1) {
buffer[this->buffer_pos_++] = rx_data;
buffer[this->buffer_pos_] = 0;
} else {
// We should never get here, but just in case...
ESP_LOGW(TAG, "Max command length exceeded; ignoring");
this->buffer_pos_ = 0;
}
if (this->buffer_pos_ < 4) {
return; // Not enough data to process yet
}
if (memcmp(&buffer[this->buffer_pos_ - 4], &CMD_FRAME_FOOTER, sizeof(CMD_FRAME_FOOTER)) == 0) {
this->cmd_active_ = false; // Set command state to inactive after response
this->handle_ack_data_(buffer, this->buffer_pos_);
this->buffer_pos_ = 0;
} else if ((buffer[this->buffer_pos_ - 2] == 0x0D && buffer[this->buffer_pos_ - 1] == 0x0A) &&
(this->get_mode_() == CMD_SYSTEM_MODE_SIMPLE)) {
this->handle_simple_mode_(buffer, this->buffer_pos_);
this->buffer_pos_ = 0;
} else if ((memcmp(&buffer[this->buffer_pos_ - 4], &ENERGY_FRAME_FOOTER, sizeof(ENERGY_FRAME_FOOTER)) == 0) &&
(this->get_mode_() == CMD_SYSTEM_MODE_ENERGY)) {
this->handle_energy_mode_(buffer, this->buffer_pos_);
this->buffer_pos_ = 0;
}
}
@@ -462,8 +460,9 @@ void LD2420Component::handle_energy_mode_(uint8_t *buffer, int len) {
// Resonable refresh rate for home assistant database size health
const int32_t current_millis = App.get_loop_component_start_time();
if (current_millis - this->last_periodic_millis < REFRESH_RATE_MS)
if (current_millis - this->last_periodic_millis < REFRESH_RATE_MS) {
return;
}
this->last_periodic_millis = current_millis;
for (auto &listener : this->listeners_) {
listener->on_distance(this->get_distance_());
@@ -506,14 +505,16 @@ void LD2420Component::handle_simple_mode_(const uint8_t *inbuf, int len) {
}
}
outbuf[index] = '\0';
if (index > 1)
if (index > 1) {
this->set_distance_(strtol(outbuf, &endptr, 10));
}
if (this->get_mode_() == CMD_SYSTEM_MODE_SIMPLE) {
// Resonable refresh rate for home assistant database size health
const int32_t current_millis = App.get_loop_component_start_time();
if (current_millis - this->last_normal_periodic_millis < REFRESH_RATE_MS)
if (current_millis - this->last_normal_periodic_millis < REFRESH_RATE_MS) {
return;
}
this->last_normal_periodic_millis = current_millis;
for (auto &listener : this->listeners_)
listener->on_distance(this->get_distance_());
@@ -593,11 +594,12 @@ void LD2420Component::handle_ack_data_(uint8_t *buffer, int len) {
int LD2420Component::send_cmd_from_array(CmdFrameT frame) {
uint32_t start_millis = millis();
uint8_t error = 0;
uint8_t ack_buffer[64];
uint8_t cmd_buffer[64];
uint8_t ack_buffer[MAX_LINE_LENGTH];
uint8_t cmd_buffer[MAX_LINE_LENGTH];
this->cmd_reply_.ack = false;
if (frame.command != CMD_RESTART)
this->set_cmd_active_(true); // Restart does not reply, thus no ack state required.
if (frame.command != CMD_RESTART) {
this->cmd_active_ = true;
} // Restart does not reply, thus no ack state required
uint8_t retry = 3;
while (retry) {
frame.length = 0;
@@ -619,9 +621,7 @@ int LD2420Component::send_cmd_from_array(CmdFrameT frame) {
memcpy(cmd_buffer + frame.length, &frame.footer, sizeof(frame.footer));
frame.length += sizeof(frame.footer);
for (uint16_t index = 0; index < frame.length; index++) {
this->write_byte(cmd_buffer[index]);
}
this->write_array(cmd_buffer, frame.length);
error = 0;
if (frame.command == CMD_RESTART) {
@@ -630,7 +630,7 @@ int LD2420Component::send_cmd_from_array(CmdFrameT frame) {
while (!this->cmd_reply_.ack) {
while (this->available()) {
this->readline_(read(), ack_buffer, sizeof(ack_buffer));
this->readline_(this->read(), ack_buffer, sizeof(ack_buffer));
}
delay_microseconds_safe(1450);
// Wait on an Rx from the LD2420 for up to 3 1 second loops, otherwise it could trigger a WDT.
@@ -641,10 +641,12 @@ int LD2420Component::send_cmd_from_array(CmdFrameT frame) {
break;
}
}
if (this->cmd_reply_.ack)
if (this->cmd_reply_.ack) {
retry = 0;
if (this->cmd_reply_.error > 0)
}
if (this->cmd_reply_.error > 0) {
this->handle_cmd_error(error);
}
}
return error;
}
@@ -764,8 +766,9 @@ void LD2420Component::set_system_mode(uint16_t mode) {
cmd_frame.data_length += sizeof(unknown_parm);
cmd_frame.footer = CMD_FRAME_FOOTER;
ESP_LOGV(TAG, "Sending write system mode command: %2X", cmd_frame.command);
if (this->send_cmd_from_array(cmd_frame) == 0)
if (this->send_cmd_from_array(cmd_frame) == 0) {
this->set_mode_(mode);
}
}
void LD2420Component::get_firmware_version_() {
@@ -840,18 +843,24 @@ void LD2420Component::set_gate_threshold(uint8_t gate) {
#ifdef USE_NUMBER
void LD2420Component::init_gate_config_numbers() {
if (this->gate_timeout_number_ != nullptr)
if (this->gate_timeout_number_ != nullptr) {
this->gate_timeout_number_->publish_state(static_cast<uint16_t>(this->current_config.timeout));
if (this->gate_select_number_ != nullptr)
}
if (this->gate_select_number_ != nullptr) {
this->gate_select_number_->publish_state(0);
if (this->min_gate_distance_number_ != nullptr)
}
if (this->min_gate_distance_number_ != nullptr) {
this->min_gate_distance_number_->publish_state(static_cast<uint16_t>(this->current_config.min_gate));
if (this->max_gate_distance_number_ != nullptr)
}
if (this->max_gate_distance_number_ != nullptr) {
this->max_gate_distance_number_->publish_state(static_cast<uint16_t>(this->current_config.max_gate));
if (this->gate_move_sensitivity_factor_number_ != nullptr)
}
if (this->gate_move_sensitivity_factor_number_ != nullptr) {
this->gate_move_sensitivity_factor_number_->publish_state(this->gate_move_sensitivity_factor);
if (this->gate_still_sensitivity_factor_number_ != nullptr)
}
if (this->gate_still_sensitivity_factor_number_ != nullptr) {
this->gate_still_sensitivity_factor_number_->publish_state(this->gate_still_sensitivity_factor);
}
for (uint8_t gate = 0; gate < TOTAL_GATES; gate++) {
if (this->gate_still_threshold_numbers_[gate] != nullptr) {
this->gate_still_threshold_numbers_[gate]->publish_state(

View File

@@ -20,8 +20,9 @@
namespace esphome {
namespace ld2420 {
static const uint8_t TOTAL_GATES = 16;
static const uint8_t CALIBRATE_SAMPLES = 64;
static const uint8_t MAX_LINE_LENGTH = 46; // Max characters for serial buffer
static const uint8_t TOTAL_GATES = 16;
enum OpMode : uint8_t {
OP_NORMAL_MODE = 1,
@@ -118,10 +119,10 @@ class LD2420Component : public Component, public uart::UARTDevice {
float gate_move_sensitivity_factor{0.5};
float gate_still_sensitivity_factor{0.5};
int32_t last_periodic_millis = millis();
int32_t report_periodic_millis = millis();
int32_t monitor_periodic_millis = millis();
int32_t last_normal_periodic_millis = millis();
int32_t last_periodic_millis{0};
int32_t report_periodic_millis{0};
int32_t monitor_periodic_millis{0};
int32_t last_normal_periodic_millis{0};
uint16_t radar_data[TOTAL_GATES][CALIBRATE_SAMPLES];
uint16_t gate_avg[TOTAL_GATES];
uint16_t gate_peak[TOTAL_GATES];
@@ -161,8 +162,6 @@ class LD2420Component : public Component, public uart::UARTDevice {
void set_presence_(bool presence) { this->presence_ = presence; };
uint16_t get_distance_() { return this->distance_; };
void set_distance_(uint16_t distance) { this->distance_ = distance; };
bool get_cmd_active_() { return this->cmd_active_; };
void set_cmd_active_(bool active) { this->cmd_active_ = active; };
void handle_simple_mode_(const uint8_t *inbuf, int len);
void handle_energy_mode_(uint8_t *buffer, int len);
void handle_ack_data_(uint8_t *buffer, int len);
@@ -181,12 +180,11 @@ class LD2420Component : public Component, public uart::UARTDevice {
std::vector<number::Number *> gate_move_threshold_numbers_ = std::vector<number::Number *>(16);
#endif
uint32_t max_distance_gate_;
uint32_t min_distance_gate_;
uint16_t distance_{0};
uint16_t system_mode_;
uint16_t gate_energy_[TOTAL_GATES];
uint16_t distance_{0};
uint8_t config_checksum_{0};
uint8_t buffer_pos_{0}; // where to resume processing/populating buffer
uint8_t buffer_data_[MAX_LINE_LENGTH];
char firmware_ver_[8]{"v0.0.0"};
bool cmd_active_{false};
bool presence_{false};

View File

@@ -2,7 +2,7 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
static const char *const TAG = "LD2420.number";
static const char *const TAG = "ld2420.number";
namespace esphome {
namespace ld2420 {

View File

@@ -5,7 +5,7 @@
namespace esphome {
namespace ld2420 {
static const char *const TAG = "LD2420.select";
static const char *const TAG = "ld2420.select";
void LD2420Select::control(const std::string &value) {
this->publish_state(value);

View File

@@ -5,10 +5,10 @@
namespace esphome {
namespace ld2420 {
static const char *const TAG = "LD2420.sensor";
static const char *const TAG = "ld2420.sensor";
void LD2420Sensor::dump_config() {
ESP_LOGCONFIG(TAG, "LD2420 Sensor:");
ESP_LOGCONFIG(TAG, "Sensor:");
LOG_SENSOR(" ", "Distance", this->distance_sensor_);
}

View File

@@ -5,10 +5,10 @@
namespace esphome {
namespace ld2420 {
static const char *const TAG = "LD2420.text_sensor";
static const char *const TAG = "ld2420.text_sensor";
void LD2420TextSensor::dump_config() {
ESP_LOGCONFIG(TAG, "LD2420 TextSensor:");
ESP_LOGCONFIG(TAG, "Text Sensor:");
LOG_TEXT_SENSOR(" ", "Firmware", this->fw_version_text_sensor_);
}

View File

@@ -268,7 +268,7 @@ async def component_to_code(config):
# disable library compatibility checks
cg.add_platformio_option("lib_ldf_mode", "off")
cg.add_platformio_option("lib_compat_mode", "strict")
cg.add_platformio_option("lib_compat_mode", "soft")
# include <Arduino.h> in every file
cg.add_platformio_option("build_src_flags", "-include Arduino.h")
# dummy version code

View File

@@ -9,6 +9,7 @@ namespace light {
// See https://www.home-assistant.io/integrations/light.mqtt/#json-schema for documentation on the schema
void LightJSONSchema::dump_json(LightState &state, JsonObject root) {
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
if (state.supports_effects())
root["effect"] = state.get_effect_name();
@@ -52,7 +53,7 @@ void LightJSONSchema::dump_json(LightState &state, JsonObject root) {
if (values.get_color_mode() & ColorCapability::BRIGHTNESS)
root["brightness"] = uint8_t(values.get_brightness() * 255);
JsonObject color = root.createNestedObject("color");
JsonObject color = root["color"].to<JsonObject>();
if (values.get_color_mode() & ColorCapability::RGB) {
color["r"] = uint8_t(values.get_color_brightness() * values.get_red() * 255);
color["g"] = uint8_t(values.get_color_brightness() * values.get_green() * 255);
@@ -73,7 +74,7 @@ void LightJSONSchema::dump_json(LightState &state, JsonObject root) {
}
void LightJSONSchema::parse_color_json(LightState &state, LightCall &call, JsonObject root) {
if (root.containsKey("state")) {
if (root["state"].is<const char *>()) {
auto val = parse_on_off(root["state"]);
switch (val) {
case PARSE_ON:
@@ -90,40 +91,40 @@ void LightJSONSchema::parse_color_json(LightState &state, LightCall &call, JsonO
}
}
if (root.containsKey("brightness")) {
if (root["brightness"].is<uint8_t>()) {
call.set_brightness(float(root["brightness"]) / 255.0f);
}
if (root.containsKey("color")) {
if (root["color"].is<JsonObject>()) {
JsonObject color = root["color"];
// HA also encodes brightness information in the r, g, b values, so extract that and set it as color brightness.
float max_rgb = 0.0f;
if (color.containsKey("r")) {
if (color["r"].is<uint8_t>()) {
float r = float(color["r"]) / 255.0f;
max_rgb = fmaxf(max_rgb, r);
call.set_red(r);
}
if (color.containsKey("g")) {
if (color["g"].is<uint8_t>()) {
float g = float(color["g"]) / 255.0f;
max_rgb = fmaxf(max_rgb, g);
call.set_green(g);
}
if (color.containsKey("b")) {
if (color["b"].is<uint8_t>()) {
float b = float(color["b"]) / 255.0f;
max_rgb = fmaxf(max_rgb, b);
call.set_blue(b);
}
if (color.containsKey("r") || color.containsKey("g") || color.containsKey("b")) {
if (color["r"].is<uint8_t>() || color["g"].is<uint8_t>() || color["b"].is<uint8_t>()) {
call.set_color_brightness(max_rgb);
}
if (color.containsKey("c")) {
if (color["c"].is<uint8_t>()) {
call.set_cold_white(float(color["c"]) / 255.0f);
}
if (color.containsKey("w")) {
if (color["w"].is<uint8_t>()) {
// the HA scheme is ambiguous here, the same key is used for white channel in RGBW and warm
// white channel in RGBWW.
if (color.containsKey("c")) {
if (color["c"].is<uint8_t>()) {
call.set_warm_white(float(color["w"]) / 255.0f);
} else {
call.set_white(float(color["w"]) / 255.0f);
@@ -131,11 +132,11 @@ void LightJSONSchema::parse_color_json(LightState &state, LightCall &call, JsonO
}
}
if (root.containsKey("white_value")) { // legacy API
if (root["white_value"].is<uint8_t>()) { // legacy API
call.set_white(float(root["white_value"]) / 255.0f);
}
if (root.containsKey("color_temp")) {
if (root["color_temp"].is<uint16_t>()) {
call.set_color_temperature(float(root["color_temp"]));
}
}
@@ -143,17 +144,17 @@ void LightJSONSchema::parse_color_json(LightState &state, LightCall &call, JsonO
void LightJSONSchema::parse_json(LightState &state, LightCall &call, JsonObject root) {
LightJSONSchema::parse_color_json(state, call, root);
if (root.containsKey("flash")) {
if (root["flash"].is<uint32_t>()) {
auto length = uint32_t(float(root["flash"]) * 1000);
call.set_flash_length(length);
}
if (root.containsKey("transition")) {
if (root["transition"].is<uint16_t>()) {
auto length = uint32_t(float(root["transition"]) * 1000);
call.set_transition_length(length);
}
if (root.containsKey("effect")) {
if (root["effect"].is<const char *>()) {
const char *effect = root["effect"];
call.set_effect(effect);
}

View File

@@ -21,6 +21,11 @@ from esphome.components.libretiny.const import (
COMPONENT_LN882X,
COMPONENT_RTL87XX,
)
from esphome.components.zephyr import (
zephyr_add_cdc_acm,
zephyr_add_overlay,
zephyr_add_prj_conf,
)
from esphome.config_helpers import filter_source_files_from_platform
import esphome.config_validation as cv
from esphome.const import (
@@ -41,6 +46,7 @@ from esphome.const import (
PLATFORM_ESP32,
PLATFORM_ESP8266,
PLATFORM_LN882X,
PLATFORM_NRF52,
PLATFORM_RP2040,
PLATFORM_RTL87XX,
PlatformFramework,
@@ -115,6 +121,8 @@ ESP_ARDUINO_UNSUPPORTED_USB_UARTS = [USB_SERIAL_JTAG]
UART_SELECTION_RP2040 = [USB_CDC, UART0, UART1]
UART_SELECTION_NRF52 = [USB_CDC, UART0]
HARDWARE_UART_TO_UART_SELECTION = {
UART0: logger_ns.UART_SELECTION_UART0,
UART0_SWAP: logger_ns.UART_SELECTION_UART0_SWAP,
@@ -167,6 +175,8 @@ def uart_selection(value):
return cv.one_of(*UART_SELECTION_LIBRETINY[component], upper=True)(value)
if CORE.is_host:
raise cv.Invalid("Uart selection not valid for host platform")
if CORE.is_nrf52:
return cv.one_of(*UART_SELECTION_NRF52, upper=True)(value)
raise NotImplementedError
@@ -186,6 +196,7 @@ LoggerMessageTrigger = logger_ns.class_(
automation.Trigger.template(cg.int_, cg.const_char_ptr, cg.const_char_ptr),
)
CONF_ESP8266_STORE_LOG_STRINGS_IN_FLASH = "esp8266_store_log_strings_in_flash"
CONFIG_SCHEMA = cv.All(
cv.Schema(
@@ -227,6 +238,7 @@ CONFIG_SCHEMA = cv.All(
bk72xx=DEFAULT,
ln882x=DEFAULT,
rtl87xx=DEFAULT,
nrf52=USB_CDC,
): cv.All(
cv.only_on(
[
@@ -236,6 +248,7 @@ CONFIG_SCHEMA = cv.All(
PLATFORM_BK72XX,
PLATFORM_LN882X,
PLATFORM_RTL87XX,
PLATFORM_NRF52,
]
),
uart_selection,
@@ -358,6 +371,15 @@ async def to_code(config):
except cv.Invalid:
pass
if CORE.using_zephyr:
if config[CONF_HARDWARE_UART] == UART0:
zephyr_add_overlay("""&uart0 { status = "okay";};""")
if config[CONF_HARDWARE_UART] == UART1:
zephyr_add_overlay("""&uart1 { status = "okay";};""")
if config[CONF_HARDWARE_UART] == USB_CDC:
zephyr_add_prj_conf("UART_LINE_CTRL", True)
zephyr_add_cdc_acm(config, 0)
# Register at end for safe mode
await cg.register_component(log, config)
@@ -462,6 +484,7 @@ FILTER_SOURCE_FILES = filter_source_files_from_platform(
PlatformFramework.RTL87XX_ARDUINO,
PlatformFramework.LN882X_ARDUINO,
},
"logger_zephyr.cpp": {PlatformFramework.NRF52_ZEPHYR},
"task_log_buffer.cpp": {
PlatformFramework.ESP32_ARDUINO,
PlatformFramework.ESP32_IDF,

View File

@@ -4,9 +4,9 @@
#include <memory> // For unique_ptr
#endif
#include "esphome/core/application.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
#include "esphome/core/application.h"
namespace esphome {
namespace logger {
@@ -160,6 +160,8 @@ Logger::Logger(uint32_t baud_rate, size_t tx_buffer_size) : baud_rate_(baud_rate
this->tx_buffer_ = new char[this->tx_buffer_size_ + 1]; // NOLINT
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
this->main_task_ = xTaskGetCurrentTaskHandle();
#elif defined(USE_ZEPHYR)
this->main_task_ = k_current_get();
#endif
}
#ifdef USE_ESPHOME_TASK_LOG_BUFFER
@@ -172,6 +174,7 @@ void Logger::init_log_buffer(size_t total_buffer_size) {
}
#endif
#ifndef USE_ZEPHYR
#if defined(USE_LOGGER_USB_CDC) || defined(USE_ESP32)
void Logger::loop() {
#if defined(USE_LOGGER_USB_CDC) && defined(USE_ARDUINO)
@@ -185,8 +188,13 @@ void Logger::loop() {
}
opened = !opened;
}
#endif
this->process_messages_();
}
#endif
#endif
void Logger::process_messages_() {
#ifdef USE_ESPHOME_TASK_LOG_BUFFER
// Process any buffered messages when available
if (this->log_buffer_->has_messages()) {
@@ -227,12 +235,11 @@ void Logger::loop() {
}
#endif
}
#endif
void Logger::set_baud_rate(uint32_t baud_rate) { this->baud_rate_ = baud_rate; }
void Logger::set_log_level(const std::string &tag, uint8_t log_level) { this->log_levels_[tag] = log_level; }
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY)
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR)
UARTSelection Logger::get_uart() const { return this->uart_; }
#endif

View File

@@ -29,6 +29,11 @@
#include <driver/uart.h>
#endif // USE_ESP_IDF
#ifdef USE_ZEPHYR
#include <zephyr/kernel.h>
struct device;
#endif
namespace esphome {
namespace logger {
@@ -56,7 +61,7 @@ static const char *const LOG_LEVEL_LETTERS[] = {
"VV", // VERY_VERBOSE
};
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY)
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR)
/** Enum for logging UART selection
*
* Advanced configuration (pin selection, etc) is not supported.
@@ -82,7 +87,7 @@ enum UARTSelection : uint8_t {
UART_SELECTION_UART0_SWAP,
#endif // USE_ESP8266
};
#endif // USE_ESP32 || USE_ESP8266 || USE_RP2040 || USE_LIBRETINY
#endif // USE_ESP32 || USE_ESP8266 || USE_RP2040 || USE_LIBRETINY || USE_ZEPHYR
/**
* @brief Logger component for all ESPHome logging.
@@ -107,7 +112,7 @@ class Logger : public Component {
#ifdef USE_ESPHOME_TASK_LOG_BUFFER
void init_log_buffer(size_t total_buffer_size);
#endif
#if defined(USE_LOGGER_USB_CDC) || defined(USE_ESP32)
#if defined(USE_LOGGER_USB_CDC) || defined(USE_ESP32) || defined(USE_ZEPHYR)
void loop() override;
#endif
/// Manually set the baud rate for serial, set to 0 to disable.
@@ -122,7 +127,7 @@ class Logger : public Component {
#ifdef USE_ESP32
void create_pthread_key() { pthread_key_create(&log_recursion_key_, nullptr); }
#endif
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY)
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR)
void set_uart_selection(UARTSelection uart_selection) { uart_ = uart_selection; }
/// Get the UART used by the logger.
UARTSelection get_uart() const;
@@ -157,6 +162,7 @@ class Logger : public Component {
#endif
protected:
void process_messages_();
void write_msg_(const char *msg);
// Format a log message with printf-style arguments and write it to a buffer with header, footer, and null terminator
@@ -164,7 +170,7 @@ class Logger : public Component {
inline void HOT format_log_to_buffer_with_terminator_(uint8_t level, const char *tag, int line, const char *format,
va_list args, char *buffer, uint16_t *buffer_at,
uint16_t buffer_size) {
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
#if defined(USE_ESP32) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR)
this->write_header_to_buffer_(level, tag, line, this->get_thread_name_(), buffer, buffer_at, buffer_size);
#else
this->write_header_to_buffer_(level, tag, line, nullptr, buffer, buffer_at, buffer_size);
@@ -231,7 +237,10 @@ class Logger : public Component {
#ifdef USE_ARDUINO
Stream *hw_serial_{nullptr};
#endif
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
#if defined(USE_ZEPHYR)
const device *uart_dev_{nullptr};
#endif
#if defined(USE_ESP32) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR)
void *main_task_ = nullptr; // Only used for thread name identification
#endif
#ifdef USE_ESP32
@@ -256,7 +265,7 @@ class Logger : public Component {
uint16_t tx_buffer_at_{0};
uint16_t tx_buffer_size_{0};
uint8_t current_level_{ESPHOME_LOG_LEVEL_VERY_VERBOSE};
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040)
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_ZEPHYR)
UARTSelection uart_{UART_SELECTION_UART0};
#endif
#ifdef USE_LIBRETINY
@@ -268,9 +277,13 @@ class Logger : public Component {
bool global_recursion_guard_{false}; // Simple global recursion guard for single-task platforms
#endif
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
#if defined(USE_ESP32) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR)
const char *HOT get_thread_name_() {
#ifdef USE_ZEPHYR
k_tid_t current_task = k_current_get();
#else
TaskHandle_t current_task = xTaskGetCurrentTaskHandle();
#endif
if (current_task == main_task_) {
return nullptr; // Main task
} else {
@@ -278,6 +291,8 @@ class Logger : public Component {
return pcTaskGetName(current_task);
#elif defined(USE_LIBRETINY)
return pcTaskGetTaskName(current_task);
#elif defined(USE_ZEPHYR)
return k_thread_name_get(current_task);
#endif
}
}
@@ -319,7 +334,7 @@ class Logger : public Component {
const char *color = esphome::logger::LOG_LEVEL_COLORS[level];
const char *letter = esphome::logger::LOG_LEVEL_LETTERS[level];
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
#if defined(USE_ESP32) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR)
if (thread_name != nullptr) {
// Non-main task with thread name
this->printf_to_buffer_(buffer, buffer_at, buffer_size, "%s[%s][%s:%03u]%s[%s]%s: ", color, letter, tag, line,

View File

@@ -0,0 +1,88 @@
#ifdef USE_ZEPHYR
#include "esphome/core/application.h"
#include "esphome/core/log.h"
#include "logger.h"
#include <zephyr/device.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/usb/usb_device.h>
namespace esphome {
namespace logger {
static const char *const TAG = "logger";
void Logger::loop() {
#ifdef USE_LOGGER_USB_CDC
if (this->uart_ != UART_SELECTION_USB_CDC || nullptr == this->uart_dev_) {
return;
}
static bool opened = false;
uint32_t dtr = 0;
uart_line_ctrl_get(this->uart_dev_, UART_LINE_CTRL_DTR, &dtr);
/* Poll if the DTR flag was set, optional */
if (opened == dtr) {
return;
}
if (!opened) {
App.schedule_dump_config();
}
opened = !opened;
#endif
this->process_messages_();
}
void Logger::pre_setup() {
if (this->baud_rate_ > 0) {
static const struct device *uart_dev = nullptr;
switch (this->uart_) {
case UART_SELECTION_UART0:
uart_dev = DEVICE_DT_GET_OR_NULL(DT_NODELABEL(uart0));
break;
case UART_SELECTION_UART1:
uart_dev = DEVICE_DT_GET_OR_NULL(DT_NODELABEL(uart1));
break;
#ifdef USE_LOGGER_USB_CDC
case UART_SELECTION_USB_CDC:
uart_dev = DEVICE_DT_GET_OR_NULL(DT_NODELABEL(cdc_acm_uart0));
if (device_is_ready(uart_dev)) {
usb_enable(nullptr);
}
break;
#endif
}
if (!device_is_ready(uart_dev)) {
ESP_LOGE(TAG, "%s is not ready.", get_uart_selection_());
} else {
this->uart_dev_ = uart_dev;
}
}
global_logger = this;
ESP_LOGI(TAG, "Log initialized");
}
void HOT Logger::write_msg_(const char *msg) {
#ifdef CONFIG_PRINTK
printk("%s\n", msg);
#endif
if (nullptr == this->uart_dev_) {
return;
}
while (*msg) {
uart_poll_out(this->uart_dev_, *msg);
++msg;
}
uart_poll_out(this->uart_dev_, '\n');
}
const char *const UART_SELECTIONS[] = {"UART0", "UART1", "USB_CDC"};
const char *Logger::get_uart_selection_() { return UART_SELECTIONS[this->uart_]; }
} // namespace logger
} // namespace esphome
#endif

View File

@@ -1,16 +1,16 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor
import esphome.config_validation as cv
from esphome.const import (
CONF_ID,
CONF_TEMPERATURE,
CONF_PRESSURE,
CONF_TEMPERATURE,
DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_TEMPERATURE,
ICON_THERMOMETER,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
UNIT_HECTOPASCAL,
ICON_THERMOMETER,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_PRESSURE,
)
CODEOWNERS = ["@nagisa"]

View File

@@ -76,6 +76,7 @@ async def theme_to_code(config):
for w_name, style in theme.items():
# Work around Python 3.10 bug with nested async comprehensions
# With Python 3.11 this could be simplified
# TODO: Now that we require Python 3.11+, this can be updated to use nested comprehensions
styles = {}
for part, states in collect_parts(style).items():
styles[part] = {

View File

@@ -29,9 +29,9 @@ from ..defines import (
)
from ..helpers import add_lv_use, lvgl_components_required
from ..lv_validation import (
angle,
get_end_value,
get_start_value,
lv_angle,
lv_bool,
lv_color,
lv_float,
@@ -162,7 +162,7 @@ SCALE_SCHEMA = cv.Schema(
cv.Optional(CONF_RANGE_FROM, default=0.0): cv.float_,
cv.Optional(CONF_RANGE_TO, default=100.0): cv.float_,
cv.Optional(CONF_ANGLE_RANGE, default=270): cv.int_range(0, 360),
cv.Optional(CONF_ROTATION): angle,
cv.Optional(CONF_ROTATION): lv_angle,
cv.Optional(CONF_INDICATORS): cv.ensure_list(INDICATOR_SCHEMA),
}
)
@@ -187,7 +187,7 @@ class MeterType(WidgetType):
for scale_conf in config.get(CONF_SCALES, ()):
rotation = 90 + (360 - scale_conf[CONF_ANGLE_RANGE]) / 2
if CONF_ROTATION in scale_conf:
rotation = scale_conf[CONF_ROTATION] // 10
rotation = await lv_angle.process(scale_conf[CONF_ROTATION])
with LocalVariable(
"meter_var", "lv_meter_scale_t", lv_expr.meter_add_scale(var)
) as meter_var:
@@ -205,21 +205,20 @@ class MeterType(WidgetType):
var,
meter_var,
ticks[CONF_COUNT],
ticks[CONF_WIDTH],
ticks[CONF_LENGTH],
await size.process(ticks[CONF_WIDTH]),
await size.process(ticks[CONF_LENGTH]),
color,
)
if CONF_MAJOR in ticks:
major = ticks[CONF_MAJOR]
color = await lv_color.process(major[CONF_COLOR])
lv.meter_set_scale_major_ticks(
var,
meter_var,
major[CONF_STRIDE],
major[CONF_WIDTH],
major[CONF_LENGTH],
color,
major[CONF_LABEL_GAP],
await size.process(major[CONF_WIDTH]),
await size.process(major[CONF_LENGTH]),
await lv_color.process(major[CONF_COLOR]),
await size.process(major[CONF_LABEL_GAP]),
)
for indicator in scale_conf.get(CONF_INDICATORS, ()):
(t, v) = next(iter(indicator.items()))
@@ -233,7 +232,11 @@ class MeterType(WidgetType):
lv_assign(
ivar,
lv_expr.meter_add_needle_line(
var, meter_var, v[CONF_WIDTH], color, v[CONF_R_MOD]
var,
meter_var,
await size.process(v[CONF_WIDTH]),
color,
await size.process(v[CONF_R_MOD]),
),
)
if t == CONF_ARC:
@@ -241,7 +244,11 @@ class MeterType(WidgetType):
lv_assign(
ivar,
lv_expr.meter_add_arc(
var, meter_var, v[CONF_WIDTH], color, v[CONF_R_MOD]
var,
meter_var,
await size.process(v[CONF_WIDTH]),
color,
await size.process(v[CONF_R_MOD]),
),
)
if t == CONF_TICK_STYLE:
@@ -257,7 +264,7 @@ class MeterType(WidgetType):
color_start,
color_end,
v[CONF_LOCAL],
v[CONF_WIDTH],
size.process(v[CONF_WIDTH]),
),
)
if t == CONF_IMAGE:

View File

@@ -2,10 +2,8 @@ CODEOWNERS = ["@clydebarrow"]
DOMAIN = "mipi_spi"
CONF_DRAW_FROM_ORIGIN = "draw_from_origin"
CONF_SPI_16 = "spi_16"
CONF_PIXEL_MODE = "pixel_mode"
CONF_COLOR_DEPTH = "color_depth"
CONF_BUS_MODE = "bus_mode"
CONF_USE_AXIS_FLIPS = "use_axis_flips"
CONF_NATIVE_WIDTH = "native_width"

View File

@@ -3,11 +3,18 @@ import logging
from esphome import pins
import esphome.codegen as cg
from esphome.components import display, spi
from esphome.components.const import (
CONF_BYTE_ORDER,
CONF_COLOR_DEPTH,
CONF_DRAW_ROUNDING,
)
from esphome.components.display import CONF_SHOW_TEST_CARD, DISPLAY_ROTATIONS
from esphome.components.spi import TYPE_OCTAL, TYPE_QUAD, TYPE_SINGLE
import esphome.config_validation as cv
from esphome.config_validation import ALLOW_EXTRA
from esphome.const import (
CONF_BRIGHTNESS,
CONF_BUFFER_SIZE,
CONF_COLOR_ORDER,
CONF_CS_PIN,
CONF_DATA_RATE,
@@ -24,19 +31,19 @@ from esphome.const import (
CONF_MODEL,
CONF_OFFSET_HEIGHT,
CONF_OFFSET_WIDTH,
CONF_PAGES,
CONF_RESET_PIN,
CONF_ROTATION,
CONF_SWAP_XY,
CONF_TRANSFORM,
CONF_WIDTH,
)
from esphome.core import TimePeriod
from esphome.core import CORE, TimePeriod
from esphome.cpp_generator import TemplateArguments
from esphome.final_validate import full_config
from ..const import CONF_DRAW_ROUNDING
from ..lvgl.defines import CONF_COLOR_DEPTH
from . import (
CONF_BUS_MODE,
CONF_DRAW_FROM_ORIGIN,
CONF_NATIVE_HEIGHT,
CONF_NATIVE_WIDTH,
CONF_PIXEL_MODE,
@@ -55,6 +62,7 @@ from .models import (
MADCTL_XFLIP,
MADCTL_YFLIP,
DriverChip,
adafruit,
amoled,
cyd,
ili,
@@ -69,43 +77,112 @@ DEPENDENCIES = ["spi"]
LOGGER = logging.getLogger(DOMAIN)
mipi_spi_ns = cg.esphome_ns.namespace("mipi_spi")
MipiSpi = mipi_spi_ns.class_(
"MipiSpi", display.Display, display.DisplayBuffer, cg.Component, spi.SPIDevice
MipiSpi = mipi_spi_ns.class_("MipiSpi", display.Display, cg.Component, spi.SPIDevice)
MipiSpiBuffer = mipi_spi_ns.class_(
"MipiSpiBuffer", MipiSpi, display.Display, cg.Component, spi.SPIDevice
)
ColorOrder = display.display_ns.enum("ColorMode")
ColorBitness = display.display_ns.enum("ColorBitness")
Model = mipi_spi_ns.enum("Model")
PixelMode = mipi_spi_ns.enum("PixelMode")
BusType = mipi_spi_ns.enum("BusType")
COLOR_ORDERS = {
MODE_RGB: ColorOrder.COLOR_ORDER_RGB,
MODE_BGR: ColorOrder.COLOR_ORDER_BGR,
}
COLOR_DEPTHS = {
8: ColorBitness.COLOR_BITNESS_332,
16: ColorBitness.COLOR_BITNESS_565,
8: PixelMode.PIXEL_MODE_8,
16: PixelMode.PIXEL_MODE_16,
18: PixelMode.PIXEL_MODE_18,
}
DATA_PIN_SCHEMA = pins.internal_gpio_output_pin_schema
BusTypes = {
TYPE_SINGLE: BusType.BUS_TYPE_SINGLE,
TYPE_QUAD: BusType.BUS_TYPE_QUAD,
TYPE_OCTAL: BusType.BUS_TYPE_OCTAL,
}
DriverChip("CUSTOM", initsequence={})
DriverChip("CUSTOM")
MODELS = DriverChip.models
# These statements are noops, but serve to suppress linting of side-effect-only imports
for _ in (ili, jc, amoled, lilygo, lanbon, cyd, waveshare):
# This loop is a noop, but suppresses linting of side-effect-only imports
for _ in (ili, jc, amoled, lilygo, lanbon, cyd, waveshare, adafruit):
pass
PixelMode = mipi_spi_ns.enum("PixelMode")
PIXEL_MODE_18BIT = "18bit"
PIXEL_MODE_16BIT = "16bit"
DISPLAY_18BIT = "18bit"
DISPLAY_16BIT = "16bit"
PIXEL_MODES = {
PIXEL_MODE_16BIT: 0x55,
PIXEL_MODE_18BIT: 0x66,
DISPLAY_PIXEL_MODES = {
DISPLAY_16BIT: (0x55, PixelMode.PIXEL_MODE_16),
DISPLAY_18BIT: (0x66, PixelMode.PIXEL_MODE_18),
}
def get_dimensions(config):
if CONF_DIMENSIONS in config:
# Explicit dimensions, just use as is
dimensions = config[CONF_DIMENSIONS]
if isinstance(dimensions, dict):
width = dimensions[CONF_WIDTH]
height = dimensions[CONF_HEIGHT]
offset_width = dimensions[CONF_OFFSET_WIDTH]
offset_height = dimensions[CONF_OFFSET_HEIGHT]
return width, height, offset_width, offset_height
(width, height) = dimensions
return width, height, 0, 0
# Default dimensions, use model defaults
transform = get_transform(config)
model = MODELS[config[CONF_MODEL]]
width = model.get_default(CONF_WIDTH)
height = model.get_default(CONF_HEIGHT)
offset_width = model.get_default(CONF_OFFSET_WIDTH, 0)
offset_height = model.get_default(CONF_OFFSET_HEIGHT, 0)
# if mirroring axes and there are offsets, also mirror the offsets to cater for situations where
# the offset is asymmetric
if transform[CONF_MIRROR_X]:
native_width = model.get_default(CONF_NATIVE_WIDTH, width + offset_width * 2)
offset_width = native_width - width - offset_width
if transform[CONF_MIRROR_Y]:
native_height = model.get_default(
CONF_NATIVE_HEIGHT, height + offset_height * 2
)
offset_height = native_height - height - offset_height
# Swap default dimensions if swap_xy is set
if transform[CONF_SWAP_XY] is True:
width, height = height, width
offset_height, offset_width = offset_width, offset_height
return width, height, offset_width, offset_height
def denominator(config):
"""
Calculate the best denominator for a buffer size fraction.
The denominator must be a number between 2 and 16 that divides the display height evenly,
and the fraction represented by the denominator must be less than or equal to the given fraction.
:config: The configuration dictionary containing the buffer size fraction and display dimensions
:return: The denominator to use for the buffer size fraction
"""
frac = config.get(CONF_BUFFER_SIZE)
if frac is None or frac > 0.75:
return 1
height, _width, _offset_width, _offset_height = get_dimensions(config)
try:
return next(x for x in range(2, 17) if frac >= 1 / x and height % x == 0)
except StopIteration:
raise cv.Invalid(
f"Buffer size fraction {frac} is not compatible with display height {height}"
) from StopIteration
def validate_dimension(rounding):
def validator(value):
value = cv.positive_int(value)
@@ -158,41 +235,50 @@ def dimension_schema(rounding):
)
def model_schema(bus_mode, model: DriverChip, swapsies: bool):
def swap_xy_schema(model):
uses_swap = model.get_default(CONF_SWAP_XY, None) != cv.UNDEFINED
def validator(value):
if value:
raise cv.Invalid("Axis swapping not supported by this model")
return cv.boolean(value)
if uses_swap:
return {cv.Required(CONF_SWAP_XY): cv.boolean}
return {cv.Optional(CONF_SWAP_XY, default=False): validator}
def model_schema(config):
model = MODELS[config[CONF_MODEL]]
bus_mode = config.get(CONF_BUS_MODE, model.modes[0])
transform = cv.Schema(
{
cv.Required(CONF_MIRROR_X): cv.boolean,
cv.Required(CONF_MIRROR_Y): cv.boolean,
**swap_xy_schema(model),
}
)
if model.get_default(CONF_SWAP_XY, False) == cv.UNDEFINED:
transform = transform.extend(
{
cv.Optional(CONF_SWAP_XY): cv.invalid(
"Axis swapping not supported by this model"
)
}
)
else:
transform = transform.extend(
{
cv.Required(CONF_SWAP_XY): cv.boolean,
}
)
# CUSTOM model will need to provide a custom init sequence
iseqconf = (
cv.Required(CONF_INIT_SEQUENCE)
if model.initsequence is None
else cv.Optional(CONF_INIT_SEQUENCE)
)
# Dimensions are optional if the model has a default width and the transform is not overridden
# Dimensions are optional if the model has a default width and the x-y transform is not overridden
is_swapped = config.get(CONF_TRANSFORM, {}).get(CONF_SWAP_XY) is True
cv_dimensions = (
cv.Optional if model.get_default(CONF_WIDTH) and not swapsies else cv.Required
cv.Optional if model.get_default(CONF_WIDTH) and not is_swapped else cv.Required
)
pixel_modes = PIXEL_MODES if bus_mode == TYPE_SINGLE else (PIXEL_MODE_16BIT,)
pixel_modes = DISPLAY_PIXEL_MODES if bus_mode == TYPE_SINGLE else (DISPLAY_16BIT,)
color_depth = (
("16", "8", "16bit", "8bit") if bus_mode == TYPE_SINGLE else ("16", "16bit")
)
other_options = [
CONF_INVERT_COLORS,
CONF_USE_AXIS_FLIPS,
]
if bus_mode == TYPE_SINGLE:
other_options.append(CONF_SPI_16)
schema = (
display.FULL_DISPLAY_SCHEMA.extend(
spi.spi_device_schema(
@@ -220,11 +306,13 @@ def model_schema(bus_mode, model: DriverChip, swapsies: bool):
model.option(CONF_COLOR_ORDER, MODE_BGR): cv.enum(
COLOR_ORDERS, upper=True
),
model.option(CONF_BYTE_ORDER, "big_endian"): cv.one_of(
"big_endian", "little_endian", lower=True
),
model.option(CONF_COLOR_DEPTH, 16): cv.one_of(*color_depth, lower=True),
model.option(CONF_DRAW_ROUNDING, 2): power_of_two,
model.option(CONF_PIXEL_MODE, PIXEL_MODE_16BIT): cv.Any(
cv.one_of(*pixel_modes, lower=True),
cv.int_range(0, 255, min_included=True, max_included=True),
model.option(CONF_PIXEL_MODE, DISPLAY_16BIT): cv.one_of(
*pixel_modes, lower=True
),
cv.Optional(CONF_TRANSFORM): transform,
cv.Optional(CONF_BUS_MODE, default=bus_mode): cv.one_of(
@@ -232,19 +320,12 @@ def model_schema(bus_mode, model: DriverChip, swapsies: bool):
),
cv.Required(CONF_MODEL): cv.one_of(model.name, upper=True),
iseqconf: cv.ensure_list(map_sequence),
cv.Optional(CONF_BUFFER_SIZE): cv.All(
cv.percentage, cv.Range(0.12, 1.0)
),
}
)
.extend(
{
model.option(x): cv.boolean
for x in [
CONF_DRAW_FROM_ORIGIN,
CONF_SPI_16,
CONF_INVERT_COLORS,
CONF_USE_AXIS_FLIPS,
]
}
)
.extend({model.option(x): cv.boolean for x in other_options})
)
if brightness := model.get_default(CONF_BRIGHTNESS):
schema = schema.extend(
@@ -259,18 +340,25 @@ def model_schema(bus_mode, model: DriverChip, swapsies: bool):
return schema
def rotation_as_transform(model, config):
def is_rotation_transformable(config):
"""
Check if a rotation can be implemented in hardware using the MADCTL register.
A rotation of 180 is always possible, 90 and 270 are possible if the model supports swapping X and Y.
"""
model = MODELS[config[CONF_MODEL]]
rotation = config.get(CONF_ROTATION, 0)
return rotation and (
model.get_default(CONF_SWAP_XY) != cv.UNDEFINED or rotation == 180
)
def config_schema(config):
def customise_schema(config):
"""
Create a customised config schema for a specific model and validate the configuration.
:param config: The configuration dictionary to validate
:return: The validated configuration dictionary
:raises cv.Invalid: If the configuration is invalid
"""
# First get the model and bus mode
config = cv.Schema(
{
@@ -288,29 +376,94 @@ def config_schema(config):
extra=ALLOW_EXTRA,
)(config)
bus_mode = config.get(CONF_BUS_MODE, model.modes[0])
swapsies = config.get(CONF_TRANSFORM, {}).get(CONF_SWAP_XY) is True
config = model_schema(bus_mode, model, swapsies)(config)
config = model_schema(config)(config)
# Check for invalid combinations of MADCTL config
if init_sequence := config.get(CONF_INIT_SEQUENCE):
if MADCTL in [x[0] for x in init_sequence] and CONF_TRANSFORM in config:
commands = [x[0] for x in init_sequence]
if MADCTL in commands and CONF_TRANSFORM in config:
raise cv.Invalid(
f"transform is not supported when MADCTL ({MADCTL:#X}) is in the init sequence"
)
if PIXFMT in commands:
raise cv.Invalid(
f"PIXFMT ({PIXFMT:#X}) should not be in the init sequence, it will be set automatically"
)
if bus_mode == TYPE_QUAD and CONF_DC_PIN in config:
raise cv.Invalid("DC pin is not supported in quad mode")
if config[CONF_PIXEL_MODE] == PIXEL_MODE_18BIT and bus_mode != TYPE_SINGLE:
raise cv.Invalid("18-bit pixel mode is not supported on a quad or octal bus")
if bus_mode != TYPE_QUAD and CONF_DC_PIN not in config:
raise cv.Invalid(f"DC pin is required in {bus_mode} mode")
denominator(config)
return config
CONFIG_SCHEMA = config_schema
CONFIG_SCHEMA = customise_schema
def get_transform(model, config):
can_transform = rotation_as_transform(model, config)
def requires_buffer(config):
"""
Check if the display configuration requires a buffer. It will do so if any drawing methods are configured.
:param config:
:return: True if a buffer is required, False otherwise
"""
return any(
config.get(key) for key in (CONF_LAMBDA, CONF_PAGES, CONF_SHOW_TEST_CARD)
)
def get_color_depth(config):
return int(config[CONF_COLOR_DEPTH].removesuffix("bit"))
def _final_validate(config):
global_config = full_config.get()
from esphome.components.lvgl import DOMAIN as LVGL_DOMAIN
if not requires_buffer(config) and LVGL_DOMAIN not in global_config:
# If no drawing methods are configured, and LVGL is not enabled, show a test card
config[CONF_SHOW_TEST_CARD] = True
if "psram" not in global_config and CONF_BUFFER_SIZE not in config:
if not requires_buffer(config):
return config # No buffer needed, so no need to set a buffer size
# If PSRAM is not enabled, choose a small buffer size by default
if not requires_buffer(config):
# not our problem.
return config
color_depth = get_color_depth(config)
frac = denominator(config)
height, width, _offset_width, _offset_height = get_dimensions(config)
buffer_size = color_depth // 8 * width * height // frac
# Target a buffer size of 20kB
fraction = 20000.0 / buffer_size
try:
config[CONF_BUFFER_SIZE] = 1.0 / next(
x for x in range(2, 17) if fraction >= 1 / x and height % x == 0
)
except StopIteration:
# Either the screen is too big, or the height is not divisible by any of the fractions, so use 1.0
# PSRAM will be needed.
if CORE.is_esp32:
raise cv.Invalid(
"PSRAM is required for this display"
) from StopIteration
return config
FINAL_VALIDATE_SCHEMA = _final_validate
def get_transform(config):
"""
Get the transformation configuration for the display.
:param config:
:return:
"""
model = MODELS[config[CONF_MODEL]]
can_transform = is_rotation_transformable(config)
transform = config.get(
CONF_TRANSFORM,
{
@@ -350,16 +503,13 @@ def get_sequence(model, config):
sequence = [x if isinstance(x, tuple) else (x,) for x in sequence]
commands = [x[0] for x in sequence]
# Set pixel format if not already in the custom sequence
if PIXFMT not in commands:
pixel_mode = config[CONF_PIXEL_MODE]
if not isinstance(pixel_mode, int):
pixel_mode = PIXEL_MODES[pixel_mode]
sequence.append((PIXFMT, pixel_mode))
pixel_mode = DISPLAY_PIXEL_MODES[config[CONF_PIXEL_MODE]]
sequence.append((PIXFMT, pixel_mode[0]))
# Does the chip use the flipping bits for mirroring rather than the reverse order bits?
use_flip = config[CONF_USE_AXIS_FLIPS]
if MADCTL not in commands:
madctl = 0
transform = get_transform(model, config)
transform = get_transform(config)
if transform.get(CONF_TRANSFORM):
LOGGER.info("Using hardware transform to implement rotation")
if transform.get(CONF_MIRROR_X):
@@ -396,63 +546,62 @@ def get_sequence(model, config):
)
def get_instance(config):
"""
Get the type of MipiSpi instance to create based on the configuration,
and the template arguments.
:param config:
:return: type, template arguments
"""
width, height, offset_width, offset_height = get_dimensions(config)
color_depth = int(config[CONF_COLOR_DEPTH].removesuffix("bit"))
bufferpixels = COLOR_DEPTHS[color_depth]
display_pixel_mode = DISPLAY_PIXEL_MODES[config[CONF_PIXEL_MODE]][1]
bus_type = config[CONF_BUS_MODE]
if bus_type == TYPE_SINGLE and config.get(CONF_SPI_16, False):
# If the bus mode is single and spi_16 is set, use single 16-bit mode
bus_type = BusType.BUS_TYPE_SINGLE_16
else:
bus_type = BusTypes[bus_type]
buffer_type = cg.uint8 if color_depth == 8 else cg.uint16
frac = denominator(config)
rotation = DISPLAY_ROTATIONS[
0 if is_rotation_transformable(config) else config.get(CONF_ROTATION, 0)
]
templateargs = [
buffer_type,
bufferpixels,
config[CONF_BYTE_ORDER] == "big_endian",
display_pixel_mode,
bus_type,
width,
height,
offset_width,
offset_height,
]
# If a buffer is required, use MipiSpiBuffer, otherwise use MipiSpi
if requires_buffer(config):
templateargs.append(rotation)
templateargs.append(frac)
return MipiSpiBuffer, templateargs
return MipiSpi, templateargs
async def to_code(config):
model = MODELS[config[CONF_MODEL]]
transform = get_transform(model, config)
if CONF_DIMENSIONS in config:
# Explicit dimensions, just use as is
dimensions = config[CONF_DIMENSIONS]
if isinstance(dimensions, dict):
width = dimensions[CONF_WIDTH]
height = dimensions[CONF_HEIGHT]
offset_width = dimensions[CONF_OFFSET_WIDTH]
offset_height = dimensions[CONF_OFFSET_HEIGHT]
else:
(width, height) = dimensions
offset_width = 0
offset_height = 0
else:
# Default dimensions, use model defaults and transform if needed
width = model.get_default(CONF_WIDTH)
height = model.get_default(CONF_HEIGHT)
offset_width = model.get_default(CONF_OFFSET_WIDTH, 0)
offset_height = model.get_default(CONF_OFFSET_HEIGHT, 0)
# if mirroring axes and there are offsets, also mirror the offsets to cater for situations where
# the offset is asymmetric
if transform[CONF_MIRROR_X]:
native_width = model.get_default(
CONF_NATIVE_WIDTH, width + offset_width * 2
)
offset_width = native_width - width - offset_width
if transform[CONF_MIRROR_Y]:
native_height = model.get_default(
CONF_NATIVE_HEIGHT, height + offset_height * 2
)
offset_height = native_height - height - offset_height
# Swap default dimensions if swap_xy is set
if transform[CONF_SWAP_XY] is True:
width, height = height, width
offset_height, offset_width = offset_width, offset_height
color_depth = config[CONF_COLOR_DEPTH]
if color_depth.endswith("bit"):
color_depth = color_depth[:-3]
color_depth = COLOR_DEPTHS[int(color_depth)]
var = cg.new_Pvariable(
config[CONF_ID], width, height, offset_width, offset_height, color_depth
)
var_id = config[CONF_ID]
var_id.type, templateargs = get_instance(config)
var = cg.new_Pvariable(var_id, TemplateArguments(*templateargs))
cg.add(var.set_init_sequence(get_sequence(model, config)))
if rotation_as_transform(model, config):
if is_rotation_transformable(config):
if CONF_TRANSFORM in config:
LOGGER.warning("Use of 'transform' with 'rotation' is not recommended")
else:
config[CONF_ROTATION] = 0
cg.add(var.set_model(config[CONF_MODEL]))
cg.add(var.set_draw_from_origin(config[CONF_DRAW_FROM_ORIGIN]))
cg.add(var.set_draw_rounding(config[CONF_DRAW_ROUNDING]))
cg.add(var.set_spi_16(config[CONF_SPI_16]))
if enable_pin := config.get(CONF_ENABLE_PIN):
enable = [await cg.gpio_pin_expression(pin) for pin in enable_pin]
cg.add(var.set_enable_pins(enable))
@@ -472,4 +621,5 @@ async def to_code(config):
cg.add(var.set_writer(lambda_))
await display.register_display(var, config)
await spi.register_spi_device(var, config)
# Displays are write-only, set the SPI device to write-only as well
cg.add(var.set_write_only(True))

View File

@@ -2,489 +2,5 @@
#include "esphome/core/log.h"
namespace esphome {
namespace mipi_spi {
void MipiSpi::setup() {
ESP_LOGCONFIG(TAG, "Running setup");
this->spi_setup();
if (this->dc_pin_ != nullptr) {
this->dc_pin_->setup();
this->dc_pin_->digital_write(false);
}
for (auto *pin : this->enable_pins_) {
pin->setup();
pin->digital_write(true);
}
if (this->reset_pin_ != nullptr) {
this->reset_pin_->setup();
this->reset_pin_->digital_write(true);
delay(5);
this->reset_pin_->digital_write(false);
delay(5);
this->reset_pin_->digital_write(true);
}
this->bus_width_ = this->parent_->get_bus_width();
// need to know when the display is ready for SLPOUT command - will be 120ms after reset
auto when = millis() + 120;
delay(10);
size_t index = 0;
auto &vec = this->init_sequence_;
while (index != vec.size()) {
if (vec.size() - index < 2) {
ESP_LOGE(TAG, "Malformed init sequence");
this->mark_failed();
return;
}
uint8_t cmd = vec[index++];
uint8_t x = vec[index++];
if (x == DELAY_FLAG) {
ESP_LOGD(TAG, "Delay %dms", cmd);
delay(cmd);
} else {
uint8_t num_args = x & 0x7F;
if (vec.size() - index < num_args) {
ESP_LOGE(TAG, "Malformed init sequence");
this->mark_failed();
return;
}
auto arg_byte = vec[index];
switch (cmd) {
case SLEEP_OUT: {
// are we ready, boots?
int duration = when - millis();
if (duration > 0) {
ESP_LOGD(TAG, "Sleep %dms", duration);
delay(duration);
}
} break;
case INVERT_ON:
this->invert_colors_ = true;
break;
case MADCTL_CMD:
this->madctl_ = arg_byte;
break;
case PIXFMT:
this->pixel_mode_ = arg_byte & 0x11 ? PIXEL_MODE_16 : PIXEL_MODE_18;
break;
case BRIGHTNESS:
this->brightness_ = arg_byte;
break;
default:
break;
}
const auto *ptr = vec.data() + index;
ESP_LOGD(TAG, "Command %02X, length %d, byte %02X", cmd, num_args, arg_byte);
this->write_command_(cmd, ptr, num_args);
index += num_args;
if (cmd == SLEEP_OUT)
delay(10);
}
}
this->setup_complete_ = true;
if (this->draw_from_origin_)
check_buffer_();
ESP_LOGCONFIG(TAG, "MIPI SPI setup complete");
}
void MipiSpi::update() {
if (!this->setup_complete_ || this->is_failed()) {
return;
}
this->do_update_();
if (this->buffer_ == nullptr || this->x_low_ > this->x_high_ || this->y_low_ > this->y_high_)
return;
ESP_LOGV(TAG, "x_low %d, y_low %d, x_high %d, y_high %d", this->x_low_, this->y_low_, this->x_high_, this->y_high_);
// Some chips require that the drawing window be aligned on certain boundaries
auto dr = this->draw_rounding_;
this->x_low_ = this->x_low_ / dr * dr;
this->y_low_ = this->y_low_ / dr * dr;
this->x_high_ = (this->x_high_ + dr) / dr * dr - 1;
this->y_high_ = (this->y_high_ + dr) / dr * dr - 1;
if (this->draw_from_origin_) {
this->x_low_ = 0;
this->y_low_ = 0;
this->x_high_ = this->width_ - 1;
}
int w = this->x_high_ - this->x_low_ + 1;
int h = this->y_high_ - this->y_low_ + 1;
this->write_to_display_(this->x_low_, this->y_low_, w, h, this->buffer_, this->x_low_, this->y_low_,
this->width_ - w - this->x_low_);
// invalidate watermarks
this->x_low_ = this->width_;
this->y_low_ = this->height_;
this->x_high_ = 0;
this->y_high_ = 0;
}
void MipiSpi::fill(Color color) {
if (!this->check_buffer_())
return;
this->x_low_ = 0;
this->y_low_ = 0;
this->x_high_ = this->get_width_internal() - 1;
this->y_high_ = this->get_height_internal() - 1;
switch (this->color_depth_) {
case display::COLOR_BITNESS_332: {
auto new_color = display::ColorUtil::color_to_332(color, display::ColorOrder::COLOR_ORDER_RGB);
memset(this->buffer_, (uint8_t) new_color, this->buffer_bytes_);
break;
}
default: {
auto new_color = display::ColorUtil::color_to_565(color);
if (((uint8_t) (new_color >> 8)) == ((uint8_t) new_color)) {
// Upper and lower is equal can use quicker memset operation. Takes ~20ms.
memset(this->buffer_, (uint8_t) new_color, this->buffer_bytes_);
} else {
auto *ptr_16 = reinterpret_cast<uint16_t *>(this->buffer_);
auto len = this->buffer_bytes_ / 2;
while (len--) {
*ptr_16++ = new_color;
}
}
}
}
}
void MipiSpi::draw_absolute_pixel_internal(int x, int y, Color color) {
if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) {
return;
}
if (!this->check_buffer_())
return;
size_t pos = (y * this->width_) + x;
switch (this->color_depth_) {
case display::COLOR_BITNESS_332: {
uint8_t new_color = display::ColorUtil::color_to_332(color);
if (this->buffer_[pos] == new_color)
return;
this->buffer_[pos] = new_color;
break;
}
case display::COLOR_BITNESS_565: {
auto *ptr_16 = reinterpret_cast<uint16_t *>(this->buffer_);
uint8_t hi_byte = static_cast<uint8_t>(color.r & 0xF8) | (color.g >> 5);
uint8_t lo_byte = static_cast<uint8_t>((color.g & 0x1C) << 3) | (color.b >> 3);
uint16_t new_color = hi_byte | (lo_byte << 8); // big endian
if (ptr_16[pos] == new_color)
return;
ptr_16[pos] = new_color;
break;
}
default:
return;
}
// low and high watermark may speed up drawing from buffer
if (x < this->x_low_)
this->x_low_ = x;
if (y < this->y_low_)
this->y_low_ = y;
if (x > this->x_high_)
this->x_high_ = x;
if (y > this->y_high_)
this->y_high_ = y;
}
void MipiSpi::reset_params_() {
if (!this->is_ready())
return;
this->write_command_(this->invert_colors_ ? INVERT_ON : INVERT_OFF);
if (this->brightness_.has_value())
this->write_command_(BRIGHTNESS, this->brightness_.value());
}
void MipiSpi::write_init_sequence_() {
size_t index = 0;
auto &vec = this->init_sequence_;
while (index != vec.size()) {
if (vec.size() - index < 2) {
ESP_LOGE(TAG, "Malformed init sequence");
this->mark_failed();
return;
}
uint8_t cmd = vec[index++];
uint8_t x = vec[index++];
if (x == DELAY_FLAG) {
ESP_LOGV(TAG, "Delay %dms", cmd);
delay(cmd);
} else {
uint8_t num_args = x & 0x7F;
if (vec.size() - index < num_args) {
ESP_LOGE(TAG, "Malformed init sequence");
this->mark_failed();
return;
}
const auto *ptr = vec.data() + index;
this->write_command_(cmd, ptr, num_args);
index += num_args;
}
}
this->setup_complete_ = true;
ESP_LOGCONFIG(TAG, "MIPI SPI setup complete");
}
void MipiSpi::set_addr_window_(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) {
ESP_LOGVV(TAG, "Set addr %d/%d, %d/%d", x1, y1, x2, y2);
uint8_t buf[4];
x1 += this->offset_width_;
x2 += this->offset_width_;
y1 += this->offset_height_;
y2 += this->offset_height_;
put16_be(buf, y1);
put16_be(buf + 2, y2);
this->write_command_(RASET, buf, sizeof buf);
put16_be(buf, x1);
put16_be(buf + 2, x2);
this->write_command_(CASET, buf, sizeof buf);
}
void MipiSpi::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order,
display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) {
if (!this->setup_complete_ || this->is_failed())
return;
if (w <= 0 || h <= 0)
return;
if (bitness != this->color_depth_ || big_endian != (this->bit_order_ == spi::BIT_ORDER_MSB_FIRST)) {
Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, x_pad);
return;
}
if (this->draw_from_origin_) {
auto stride = x_offset + w + x_pad;
for (int y = 0; y != h; y++) {
memcpy(this->buffer_ + ((y + y_start) * this->width_ + x_start) * 2,
ptr + ((y + y_offset) * stride + x_offset) * 2, w * 2);
}
ptr = this->buffer_;
w = this->width_;
h += y_start;
x_start = 0;
y_start = 0;
x_offset = 0;
y_offset = 0;
}
this->write_to_display_(x_start, y_start, w, h, ptr, x_offset, y_offset, x_pad);
}
void MipiSpi::write_18_from_16_bit_(const uint16_t *ptr, size_t w, size_t h, size_t stride) {
stride -= w;
uint8_t transfer_buffer[6 * 256];
size_t idx = 0; // index into transfer_buffer
while (h-- != 0) {
for (auto x = w; x-- != 0;) {
auto color_val = *ptr++;
// deal with byte swapping
transfer_buffer[idx++] = (color_val & 0xF8); // Blue
transfer_buffer[idx++] = ((color_val & 0x7) << 5) | ((color_val & 0xE000) >> 11); // Green
transfer_buffer[idx++] = (color_val >> 5) & 0xF8; // Red
if (idx == sizeof(transfer_buffer)) {
this->write_array(transfer_buffer, idx);
idx = 0;
}
}
ptr += stride;
}
if (idx != 0)
this->write_array(transfer_buffer, idx);
}
void MipiSpi::write_18_from_8_bit_(const uint8_t *ptr, size_t w, size_t h, size_t stride) {
stride -= w;
uint8_t transfer_buffer[6 * 256];
size_t idx = 0; // index into transfer_buffer
while (h-- != 0) {
for (auto x = w; x-- != 0;) {
auto color_val = *ptr++;
transfer_buffer[idx++] = color_val & 0xE0; // Red
transfer_buffer[idx++] = (color_val << 3) & 0xE0; // Green
transfer_buffer[idx++] = color_val << 6; // Blue
if (idx == sizeof(transfer_buffer)) {
this->write_array(transfer_buffer, idx);
idx = 0;
}
}
ptr += stride;
}
if (idx != 0)
this->write_array(transfer_buffer, idx);
}
void MipiSpi::write_16_from_8_bit_(const uint8_t *ptr, size_t w, size_t h, size_t stride) {
stride -= w;
uint8_t transfer_buffer[6 * 256];
size_t idx = 0; // index into transfer_buffer
while (h-- != 0) {
for (auto x = w; x-- != 0;) {
auto color_val = *ptr++;
transfer_buffer[idx++] = (color_val & 0xE0) | ((color_val & 0x1C) >> 2);
transfer_buffer[idx++] = (color_val & 0x3) << 3;
if (idx == sizeof(transfer_buffer)) {
this->write_array(transfer_buffer, idx);
idx = 0;
}
}
ptr += stride;
}
if (idx != 0)
this->write_array(transfer_buffer, idx);
}
void MipiSpi::write_to_display_(int x_start, int y_start, int w, int h, const uint8_t *ptr, int x_offset, int y_offset,
int x_pad) {
this->set_addr_window_(x_start, y_start, x_start + w - 1, y_start + h - 1);
auto stride = x_offset + w + x_pad;
const auto *offset_ptr = ptr;
if (this->color_depth_ == display::COLOR_BITNESS_332) {
offset_ptr += y_offset * stride + x_offset;
} else {
stride *= 2;
offset_ptr += y_offset * stride + x_offset * 2;
}
switch (this->bus_width_) {
case 4:
this->enable();
if (x_offset == 0 && x_pad == 0 && y_offset == 0) {
// we could deal here with a non-zero y_offset, but if x_offset is zero, y_offset probably will be so don't
// bother
this->write_cmd_addr_data(8, 0x32, 24, WDATA << 8, ptr, w * h * 2, 4);
} else {
this->write_cmd_addr_data(8, 0x32, 24, WDATA << 8, nullptr, 0, 4);
for (int y = 0; y != h; y++) {
this->write_cmd_addr_data(0, 0, 0, 0, offset_ptr, w * 2, 4);
offset_ptr += stride;
}
}
break;
case 8:
this->write_command_(WDATA);
this->enable();
if (x_offset == 0 && x_pad == 0 && y_offset == 0) {
this->write_cmd_addr_data(0, 0, 0, 0, ptr, w * h * 2, 8);
} else {
for (int y = 0; y != h; y++) {
this->write_cmd_addr_data(0, 0, 0, 0, offset_ptr, w * 2, 8);
offset_ptr += stride;
}
}
break;
default:
this->write_command_(WDATA);
this->enable();
if (this->color_depth_ == display::COLOR_BITNESS_565) {
// Source buffer is 16-bit RGB565
if (this->pixel_mode_ == PIXEL_MODE_18) {
// Convert RGB565 to RGB666
this->write_18_from_16_bit_(reinterpret_cast<const uint16_t *>(offset_ptr), w, h, stride / 2);
} else {
// Direct RGB565 output
if (x_offset == 0 && x_pad == 0 && y_offset == 0) {
this->write_array(ptr, w * h * 2);
} else {
for (int y = 0; y != h; y++) {
this->write_array(offset_ptr, w * 2);
offset_ptr += stride;
}
}
}
} else {
// Source buffer is 8-bit RGB332
if (this->pixel_mode_ == PIXEL_MODE_18) {
// Convert RGB332 to RGB666
this->write_18_from_8_bit_(offset_ptr, w, h, stride);
} else {
this->write_16_from_8_bit_(offset_ptr, w, h, stride);
}
break;
}
}
this->disable();
}
void MipiSpi::write_command_(uint8_t cmd, const uint8_t *bytes, size_t len) {
ESP_LOGV(TAG, "Command %02X, length %d, bytes %s", cmd, len, format_hex_pretty(bytes, len).c_str());
if (this->bus_width_ == 4) {
this->enable();
this->write_cmd_addr_data(8, 0x02, 24, cmd << 8, bytes, len);
this->disable();
} else if (this->bus_width_ == 8) {
this->dc_pin_->digital_write(false);
this->enable();
this->write_cmd_addr_data(0, 0, 0, 0, &cmd, 1, 8);
this->disable();
this->dc_pin_->digital_write(true);
if (len != 0) {
this->enable();
this->write_cmd_addr_data(0, 0, 0, 0, bytes, len, 8);
this->disable();
}
} else {
this->dc_pin_->digital_write(false);
this->enable();
this->write_byte(cmd);
this->disable();
this->dc_pin_->digital_write(true);
if (len != 0) {
if (this->spi_16_) {
for (size_t i = 0; i != len; i++) {
this->enable();
this->write_byte(0);
this->write_byte(bytes[i]);
this->disable();
}
} else {
this->enable();
this->write_array(bytes, len);
this->disable();
}
}
}
}
void MipiSpi::dump_config() {
ESP_LOGCONFIG(TAG,
"MIPI_SPI Display\n"
" Model: %s\n"
" Width: %u\n"
" Height: %u",
this->model_, this->width_, this->height_);
if (this->offset_width_ != 0)
ESP_LOGCONFIG(TAG, " Offset width: %u", this->offset_width_);
if (this->offset_height_ != 0)
ESP_LOGCONFIG(TAG, " Offset height: %u", this->offset_height_);
ESP_LOGCONFIG(TAG,
" Swap X/Y: %s\n"
" Mirror X: %s\n"
" Mirror Y: %s\n"
" Color depth: %d bits\n"
" Invert colors: %s\n"
" Color order: %s\n"
" Pixel mode: %s",
YESNO(this->madctl_ & MADCTL_MV), YESNO(this->madctl_ & (MADCTL_MX | MADCTL_XFLIP)),
YESNO(this->madctl_ & (MADCTL_MY | MADCTL_YFLIP)),
this->color_depth_ == display::COLOR_BITNESS_565 ? 16 : 8, YESNO(this->invert_colors_),
this->madctl_ & MADCTL_BGR ? "BGR" : "RGB", this->pixel_mode_ == PIXEL_MODE_18 ? "18bit" : "16bit");
if (this->brightness_.has_value())
ESP_LOGCONFIG(TAG, " Brightness: %u", this->brightness_.value());
if (this->spi_16_)
ESP_LOGCONFIG(TAG, " SPI 16bit: YES");
ESP_LOGCONFIG(TAG, " Draw rounding: %u", this->draw_rounding_);
if (this->draw_from_origin_)
ESP_LOGCONFIG(TAG, " Draw from origin: YES");
LOG_PIN(" CS Pin: ", this->cs_);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
LOG_PIN(" DC Pin: ", this->dc_pin_);
ESP_LOGCONFIG(TAG,
" SPI Mode: %d\n"
" SPI Data rate: %dMHz\n"
" SPI Bus width: %d",
this->mode_, static_cast<unsigned>(this->data_rate_ / 1000000), this->bus_width_);
}
} // namespace mipi_spi
namespace mipi_spi {} // namespace mipi_spi
} // namespace esphome

View File

@@ -4,40 +4,39 @@
#include "esphome/components/spi/spi.h"
#include "esphome/components/display/display.h"
#include "esphome/components/display/display_buffer.h"
#include "esphome/components/display/display_color_utils.h"
namespace esphome {
namespace mipi_spi {
constexpr static const char *const TAG = "display.mipi_spi";
static const uint8_t SW_RESET_CMD = 0x01;
static const uint8_t SLEEP_OUT = 0x11;
static const uint8_t NORON = 0x13;
static const uint8_t INVERT_OFF = 0x20;
static const uint8_t INVERT_ON = 0x21;
static const uint8_t ALL_ON = 0x23;
static const uint8_t WRAM = 0x24;
static const uint8_t MIPI = 0x26;
static const uint8_t DISPLAY_ON = 0x29;
static const uint8_t RASET = 0x2B;
static const uint8_t CASET = 0x2A;
static const uint8_t WDATA = 0x2C;
static const uint8_t TEON = 0x35;
static const uint8_t MADCTL_CMD = 0x36;
static const uint8_t PIXFMT = 0x3A;
static const uint8_t BRIGHTNESS = 0x51;
static const uint8_t SWIRE1 = 0x5A;
static const uint8_t SWIRE2 = 0x5B;
static const uint8_t PAGESEL = 0xFE;
static constexpr uint8_t SW_RESET_CMD = 0x01;
static constexpr uint8_t SLEEP_OUT = 0x11;
static constexpr uint8_t NORON = 0x13;
static constexpr uint8_t INVERT_OFF = 0x20;
static constexpr uint8_t INVERT_ON = 0x21;
static constexpr uint8_t ALL_ON = 0x23;
static constexpr uint8_t WRAM = 0x24;
static constexpr uint8_t MIPI = 0x26;
static constexpr uint8_t DISPLAY_ON = 0x29;
static constexpr uint8_t RASET = 0x2B;
static constexpr uint8_t CASET = 0x2A;
static constexpr uint8_t WDATA = 0x2C;
static constexpr uint8_t TEON = 0x35;
static constexpr uint8_t MADCTL_CMD = 0x36;
static constexpr uint8_t PIXFMT = 0x3A;
static constexpr uint8_t BRIGHTNESS = 0x51;
static constexpr uint8_t SWIRE1 = 0x5A;
static constexpr uint8_t SWIRE2 = 0x5B;
static constexpr uint8_t PAGESEL = 0xFE;
static const uint8_t MADCTL_MY = 0x80; // Bit 7 Bottom to top
static const uint8_t MADCTL_MX = 0x40; // Bit 6 Right to left
static const uint8_t MADCTL_MV = 0x20; // Bit 5 Swap axes
static const uint8_t MADCTL_RGB = 0x00; // Bit 3 Red-Green-Blue pixel order
static const uint8_t MADCTL_BGR = 0x08; // Bit 3 Blue-Green-Red pixel order
static const uint8_t MADCTL_XFLIP = 0x02; // Mirror the display horizontally
static const uint8_t MADCTL_YFLIP = 0x01; // Mirror the display vertically
static constexpr uint8_t MADCTL_MY = 0x80; // Bit 7 Bottom to top
static constexpr uint8_t MADCTL_MX = 0x40; // Bit 6 Right to left
static constexpr uint8_t MADCTL_MV = 0x20; // Bit 5 Swap axes
static constexpr uint8_t MADCTL_RGB = 0x00; // Bit 3 Red-Green-Blue pixel order
static constexpr uint8_t MADCTL_BGR = 0x08; // Bit 3 Blue-Green-Red pixel order
static constexpr uint8_t MADCTL_XFLIP = 0x02; // Mirror the display horizontally
static constexpr uint8_t MADCTL_YFLIP = 0x01; // Mirror the display vertically
static const uint8_t DELAY_FLAG = 0xFF;
// store a 16 bit value in a buffer, big endian.
@@ -46,28 +45,44 @@ static inline void put16_be(uint8_t *buf, uint16_t value) {
buf[1] = value;
}
// Buffer mode, conveniently also the number of bytes in a pixel
enum PixelMode {
PIXEL_MODE_16,
PIXEL_MODE_18,
PIXEL_MODE_8 = 1,
PIXEL_MODE_16 = 2,
PIXEL_MODE_18 = 3,
};
class MipiSpi : public display::DisplayBuffer,
enum BusType {
BUS_TYPE_SINGLE = 1,
BUS_TYPE_QUAD = 4,
BUS_TYPE_OCTAL = 8,
BUS_TYPE_SINGLE_16 = 16, // Single bit bus, but 16 bits per transfer
};
/**
* Base class for MIPI SPI displays.
* All the methods are defined here in the header file, as it is not possible to define templated methods in a cpp file.
*
* @tparam BUFFERTYPE The type of the buffer pixels, e.g. uint8_t or uint16_t
* @tparam BUFFERPIXEL Color depth of the buffer
* @tparam DISPLAYPIXEL Color depth of the display
* @tparam BUS_TYPE The type of the interface bus (single, quad, octal)
* @tparam WIDTH Width of the display in pixels
* @tparam HEIGHT Height of the display in pixels
* @tparam OFFSET_WIDTH The x-offset of the display in pixels
* @tparam OFFSET_HEIGHT The y-offset of the display in pixels
* buffer
*/
template<typename BUFFERTYPE, PixelMode BUFFERPIXEL, bool IS_BIG_ENDIAN, PixelMode DISPLAYPIXEL, BusType BUS_TYPE,
int WIDTH, int HEIGHT, int OFFSET_WIDTH, int OFFSET_HEIGHT>
class MipiSpi : public display::Display,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING,
spi::DATA_RATE_1MHZ> {
public:
MipiSpi(size_t width, size_t height, int16_t offset_width, int16_t offset_height, display::ColorBitness color_depth)
: width_(width),
height_(height),
offset_width_(offset_width),
offset_height_(offset_height),
color_depth_(color_depth) {}
MipiSpi() {}
void update() override { this->stop_poller(); }
void draw_pixel_at(int x, int y, Color color) override {}
void set_model(const char *model) { this->model_ = model; }
void update() override;
void setup() override;
display::ColorOrder get_color_mode() {
return this->madctl_ & MADCTL_BGR ? display::COLOR_ORDER_BGR : display::COLOR_ORDER_RGB;
}
void set_reset_pin(GPIOPin *reset_pin) { this->reset_pin_ = reset_pin; }
void set_enable_pins(std::vector<GPIOPin *> enable_pins) { this->enable_pins_ = std::move(enable_pins); }
void set_dc_pin(GPIOPin *dc_pin) { this->dc_pin_ = dc_pin; }
@@ -79,93 +94,524 @@ class MipiSpi : public display::DisplayBuffer,
this->brightness_ = brightness;
this->reset_params_();
}
void set_draw_from_origin(bool draw_from_origin) { this->draw_from_origin_ = draw_from_origin; }
display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; }
void dump_config() override;
int get_width_internal() override { return this->width_; }
int get_height_internal() override { return this->height_; }
bool can_proceed() override { return this->setup_complete_; }
int get_width_internal() override { return WIDTH; }
int get_height_internal() override { return HEIGHT; }
void set_init_sequence(const std::vector<uint8_t> &sequence) { this->init_sequence_ = sequence; }
void set_draw_rounding(unsigned rounding) { this->draw_rounding_ = rounding; }
void set_spi_16(bool spi_16) { this->spi_16_ = spi_16; }
// reset the display, and write the init sequence
void setup() override {
this->spi_setup();
if (this->dc_pin_ != nullptr) {
this->dc_pin_->setup();
this->dc_pin_->digital_write(false);
}
for (auto *pin : this->enable_pins_) {
pin->setup();
pin->digital_write(true);
}
if (this->reset_pin_ != nullptr) {
this->reset_pin_->setup();
this->reset_pin_->digital_write(true);
delay(5);
this->reset_pin_->digital_write(false);
delay(5);
this->reset_pin_->digital_write(true);
}
// need to know when the display is ready for SLPOUT command - will be 120ms after reset
auto when = millis() + 120;
delay(10);
size_t index = 0;
auto &vec = this->init_sequence_;
while (index != vec.size()) {
if (vec.size() - index < 2) {
esph_log_e(TAG, "Malformed init sequence");
this->mark_failed();
return;
}
uint8_t cmd = vec[index++];
uint8_t x = vec[index++];
if (x == DELAY_FLAG) {
esph_log_d(TAG, "Delay %dms", cmd);
delay(cmd);
} else {
uint8_t num_args = x & 0x7F;
if (vec.size() - index < num_args) {
esph_log_e(TAG, "Malformed init sequence");
this->mark_failed();
return;
}
auto arg_byte = vec[index];
switch (cmd) {
case SLEEP_OUT: {
// are we ready, boots?
int duration = when - millis();
if (duration > 0) {
esph_log_d(TAG, "Sleep %dms", duration);
delay(duration);
}
} break;
case INVERT_ON:
this->invert_colors_ = true;
break;
case MADCTL_CMD:
this->madctl_ = arg_byte;
break;
case BRIGHTNESS:
this->brightness_ = arg_byte;
break;
default:
break;
}
const auto *ptr = vec.data() + index;
esph_log_d(TAG, "Command %02X, length %d, byte %02X", cmd, num_args, arg_byte);
this->write_command_(cmd, ptr, num_args);
index += num_args;
if (cmd == SLEEP_OUT)
delay(10);
}
}
// init sequence no longer needed
this->init_sequence_.clear();
}
// Drawing operations
void draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order,
display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) override {
if (this->is_failed())
return;
if (w <= 0 || h <= 0)
return;
if (get_pixel_mode(bitness) != BUFFERPIXEL || big_endian != IS_BIG_ENDIAN) {
// note that the usual logging macros are banned in header files, so use their replacement
esph_log_e(TAG, "Unsupported color depth or bit order");
return;
}
this->write_to_display_(x_start, y_start, w, h, reinterpret_cast<const BUFFERTYPE *>(ptr), x_offset, y_offset,
x_pad);
}
void dump_config() override {
esph_log_config(TAG,
"MIPI_SPI Display\n"
" Model: %s\n"
" Width: %u\n"
" Height: %u",
this->model_, WIDTH, HEIGHT);
if constexpr (OFFSET_WIDTH != 0)
esph_log_config(TAG, " Offset width: %u", OFFSET_WIDTH);
if constexpr (OFFSET_HEIGHT != 0)
esph_log_config(TAG, " Offset height: %u", OFFSET_HEIGHT);
esph_log_config(TAG,
" Swap X/Y: %s\n"
" Mirror X: %s\n"
" Mirror Y: %s\n"
" Invert colors: %s\n"
" Color order: %s\n"
" Display pixels: %d bits\n"
" Endianness: %s\n",
YESNO(this->madctl_ & MADCTL_MV), YESNO(this->madctl_ & (MADCTL_MX | MADCTL_XFLIP)),
YESNO(this->madctl_ & (MADCTL_MY | MADCTL_YFLIP)), YESNO(this->invert_colors_),
this->madctl_ & MADCTL_BGR ? "BGR" : "RGB", DISPLAYPIXEL * 8, IS_BIG_ENDIAN ? "Big" : "Little");
if (this->brightness_.has_value())
esph_log_config(TAG, " Brightness: %u", this->brightness_.value());
if (this->cs_ != nullptr)
esph_log_config(TAG, " CS Pin: %s", this->cs_->dump_summary().c_str());
if (this->reset_pin_ != nullptr)
esph_log_config(TAG, " Reset Pin: %s", this->reset_pin_->dump_summary().c_str());
if (this->dc_pin_ != nullptr)
esph_log_config(TAG, " DC Pin: %s", this->dc_pin_->dump_summary().c_str());
esph_log_config(TAG,
" SPI Mode: %d\n"
" SPI Data rate: %dMHz\n"
" SPI Bus width: %d",
this->mode_, static_cast<unsigned>(this->data_rate_ / 1000000), BUS_TYPE);
}
protected:
bool check_buffer_() {
if (this->is_failed())
return false;
if (this->buffer_ != nullptr)
return true;
auto bytes_per_pixel = this->color_depth_ == display::COLOR_BITNESS_565 ? 2 : 1;
this->init_internal_(this->width_ * this->height_ * bytes_per_pixel);
if (this->buffer_ == nullptr) {
this->mark_failed();
return false;
}
this->buffer_bytes_ = this->width_ * this->height_ * bytes_per_pixel;
return true;
}
void fill(Color color) override;
void draw_absolute_pixel_internal(int x, int y, Color color) override;
void draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order,
display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) override;
void write_18_from_16_bit_(const uint16_t *ptr, size_t w, size_t h, size_t stride);
void write_18_from_8_bit_(const uint8_t *ptr, size_t w, size_t h, size_t stride);
void write_16_from_8_bit_(const uint8_t *ptr, size_t w, size_t h, size_t stride);
void write_to_display_(int x_start, int y_start, int w, int h, const uint8_t *ptr, int x_offset, int y_offset,
int x_pad);
/**
* the RM67162 in quad SPI mode seems to work like this (not in the datasheet, this is deduced from the
* sample code.)
*
* Immediately after enabling /CS send 4 bytes in single-dataline SPI mode:
* 0: either 0x2 or 0x32. The first indicates that any subsequent data bytes after the initial 4 will be
* sent in 1-dataline SPI. The second indicates quad mode.
* 1: 0x00
* 2: The command (register address) byte.
* 3: 0x00
*
* This is followed by zero or more data bytes in either 1-wire or 4-wire mode, depending on the first byte.
* At the conclusion of the write, de-assert /CS.
*
* @param cmd
* @param bytes
* @param len
*/
void write_command_(uint8_t cmd, const uint8_t *bytes, size_t len);
/* METHODS */
// convenience functions to write commands with or without data
void write_command_(uint8_t cmd, uint8_t data) { this->write_command_(cmd, &data, 1); }
void write_command_(uint8_t cmd) { this->write_command_(cmd, &cmd, 0); }
void reset_params_();
void write_init_sequence_();
void set_addr_window_(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
// Writes a command to the display, with the given bytes.
void write_command_(uint8_t cmd, const uint8_t *bytes, size_t len) {
esph_log_v(TAG, "Command %02X, length %d, bytes %s", cmd, len, format_hex_pretty(bytes, len).c_str());
if constexpr (BUS_TYPE == BUS_TYPE_QUAD) {
this->enable();
this->write_cmd_addr_data(8, 0x02, 24, cmd << 8, bytes, len);
this->disable();
} else if constexpr (BUS_TYPE == BUS_TYPE_OCTAL) {
this->dc_pin_->digital_write(false);
this->enable();
this->write_cmd_addr_data(0, 0, 0, 0, &cmd, 1, 8);
this->disable();
this->dc_pin_->digital_write(true);
if (len != 0) {
this->enable();
this->write_cmd_addr_data(0, 0, 0, 0, bytes, len, 8);
this->disable();
}
} else if constexpr (BUS_TYPE == BUS_TYPE_SINGLE) {
this->dc_pin_->digital_write(false);
this->enable();
this->write_byte(cmd);
this->disable();
this->dc_pin_->digital_write(true);
if (len != 0) {
this->enable();
this->write_array(bytes, len);
this->disable();
}
} else if constexpr (BUS_TYPE == BUS_TYPE_SINGLE_16) {
this->dc_pin_->digital_write(false);
this->enable();
this->write_byte(cmd);
this->disable();
this->dc_pin_->digital_write(true);
for (size_t i = 0; i != len; i++) {
this->enable();
this->write_byte(0);
this->write_byte(bytes[i]);
this->disable();
}
}
}
// write changed parameters to the display
void reset_params_() {
if (!this->is_ready())
return;
this->write_command_(this->invert_colors_ ? INVERT_ON : INVERT_OFF);
if (this->brightness_.has_value())
this->write_command_(BRIGHTNESS, this->brightness_.value());
}
// set the address window for the next data write
void set_addr_window_(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) {
esph_log_v(TAG, "Set addr %d/%d, %d/%d", x1, y1, x2, y2);
uint8_t buf[4];
x1 += OFFSET_WIDTH;
x2 += OFFSET_WIDTH;
y1 += OFFSET_HEIGHT;
y2 += OFFSET_HEIGHT;
put16_be(buf, y1);
put16_be(buf + 2, y2);
this->write_command_(RASET, buf, sizeof buf);
put16_be(buf, x1);
put16_be(buf + 2, x2);
this->write_command_(CASET, buf, sizeof buf);
if constexpr (BUS_TYPE != BUS_TYPE_QUAD) {
this->write_command_(WDATA);
}
}
// map the display color bitness to the pixel mode
static PixelMode get_pixel_mode(display::ColorBitness bitness) {
switch (bitness) {
case display::COLOR_BITNESS_888:
return PIXEL_MODE_18; // 18 bits per pixel
case display::COLOR_BITNESS_565:
return PIXEL_MODE_16; // 16 bits per pixel
default:
return PIXEL_MODE_8; // Default to 8 bits per pixel
}
}
/**
* Writes a buffer to the display.
* @param w Width of each line in bytes
* @param h Height of the buffer in rows
* @param pad Padding in bytes after each line
*/
void write_display_data_(const uint8_t *ptr, size_t w, size_t h, size_t pad) {
if (pad == 0) {
if constexpr (BUS_TYPE == BUS_TYPE_SINGLE || BUS_TYPE == BUS_TYPE_SINGLE_16) {
this->write_array(ptr, w * h);
} else if constexpr (BUS_TYPE == BUS_TYPE_QUAD) {
this->write_cmd_addr_data(8, 0x32, 24, WDATA << 8, ptr, w * h, 4);
} else if constexpr (BUS_TYPE == BUS_TYPE_OCTAL) {
this->write_cmd_addr_data(0, 0, 0, 0, ptr, w * h, 8);
}
} else {
for (size_t y = 0; y != h; y++) {
if constexpr (BUS_TYPE == BUS_TYPE_SINGLE || BUS_TYPE == BUS_TYPE_SINGLE_16) {
this->write_array(ptr, w);
} else if constexpr (BUS_TYPE == BUS_TYPE_QUAD) {
this->write_cmd_addr_data(8, 0x32, 24, WDATA << 8, ptr, w, 4);
} else if constexpr (BUS_TYPE == BUS_TYPE_OCTAL) {
this->write_cmd_addr_data(0, 0, 0, 0, ptr, w, 8);
}
ptr += w + pad;
}
}
}
/**
* Writes a buffer to the display.
*
* The ptr is a pointer to the pixel data
* The other parameters are all in pixel units.
*/
void write_to_display_(int x_start, int y_start, int w, int h, const BUFFERTYPE *ptr, int x_offset, int y_offset,
int x_pad) {
this->set_addr_window_(x_start, y_start, x_start + w - 1, y_start + h - 1);
this->enable();
ptr += y_offset * (x_offset + w + x_pad) + x_offset;
if constexpr (BUFFERPIXEL == DISPLAYPIXEL) {
this->write_display_data_(reinterpret_cast<const uint8_t *>(ptr), w * sizeof(BUFFERTYPE), h,
x_pad * sizeof(BUFFERTYPE));
} else {
// type conversion required, do it in chunks
uint8_t dbuffer[DISPLAYPIXEL * 48];
uint8_t *dptr = dbuffer;
auto stride = x_offset + w + x_pad; // stride in pixels
for (size_t y = 0; y != h; y++) {
for (size_t x = 0; x != w; x++) {
auto color_val = ptr[y * stride + x];
if constexpr (DISPLAYPIXEL == PIXEL_MODE_18 && BUFFERPIXEL == PIXEL_MODE_16) {
// 16 to 18 bit conversion
if constexpr (IS_BIG_ENDIAN) {
*dptr++ = color_val & 0xF8;
*dptr++ = ((color_val & 0x7) << 5) | (color_val & 0xE000) >> 11;
*dptr++ = (color_val >> 5) & 0xF8;
} else {
*dptr++ = (color_val >> 8) & 0xF8; // Blue
*dptr++ = (color_val & 0x7E0) >> 3;
*dptr++ = color_val << 3;
}
} else if constexpr (DISPLAYPIXEL == PIXEL_MODE_18 && BUFFERPIXEL == PIXEL_MODE_8) {
// 8 bit to 18 bit conversion
*dptr++ = color_val << 6; // Blue
*dptr++ = (color_val & 0x1C) << 3; // Green
*dptr++ = (color_val & 0xE0); // Red
} else if constexpr (DISPLAYPIXEL == PIXEL_MODE_16 && BUFFERPIXEL == PIXEL_MODE_8) {
if constexpr (IS_BIG_ENDIAN) {
*dptr++ = (color_val & 0xE0) | ((color_val & 0x1C) >> 2);
*dptr++ = (color_val & 3) << 3;
} else {
*dptr++ = (color_val & 3) << 3;
*dptr++ = (color_val & 0xE0) | ((color_val & 0x1C) >> 2);
}
}
// buffer full? Flush.
if (dptr == dbuffer + sizeof(dbuffer)) {
this->write_display_data_(dbuffer, sizeof(dbuffer), 1, 0);
dptr = dbuffer;
}
}
}
// flush any remaining data
if (dptr != dbuffer) {
this->write_display_data_(dbuffer, dptr - dbuffer, 1, 0);
}
}
this->disable();
}
/* PROPERTIES */
// GPIO pins
GPIOPin *reset_pin_{nullptr};
std::vector<GPIOPin *> enable_pins_{};
GPIOPin *dc_pin_{nullptr};
uint16_t x_low_{1};
uint16_t y_low_{1};
uint16_t x_high_{0};
uint16_t y_high_{0};
bool setup_complete_{};
// other properties set by configuration
bool invert_colors_{};
size_t width_;
size_t height_;
int16_t offset_width_;
int16_t offset_height_;
size_t buffer_bytes_{0};
display::ColorBitness color_depth_;
PixelMode pixel_mode_{PIXEL_MODE_16};
uint8_t bus_width_{};
bool spi_16_{};
uint8_t madctl_{};
bool draw_from_origin_{false};
unsigned draw_rounding_{2};
optional<uint8_t> brightness_{};
const char *model_{"Unknown"};
std::vector<uint8_t> init_sequence_{};
uint8_t madctl_{};
};
/**
* Class for MIPI SPI displays with a buffer.
*
* @tparam BUFFERTYPE The type of the buffer pixels, e.g. uint8_t or uint16_t
* @tparam BUFFERPIXEL Color depth of the buffer
* @tparam DISPLAYPIXEL Color depth of the display
* @tparam BUS_TYPE The type of the interface bus (single, quad, octal)
* @tparam ROTATION The rotation of the display
* @tparam WIDTH Width of the display in pixels
* @tparam HEIGHT Height of the display in pixels
* @tparam OFFSET_WIDTH The x-offset of the display in pixels
* @tparam OFFSET_HEIGHT The y-offset of the display in pixels
* @tparam FRACTION The fraction of the display size to use for the buffer (e.g. 4 means a 1/4 buffer).
*/
template<typename BUFFERTYPE, PixelMode BUFFERPIXEL, bool IS_BIG_ENDIAN, PixelMode DISPLAYPIXEL, BusType BUS_TYPE,
int WIDTH, int HEIGHT, int OFFSET_WIDTH, int OFFSET_HEIGHT, display::DisplayRotation ROTATION, int FRACTION>
class MipiSpiBuffer : public MipiSpi<BUFFERTYPE, BUFFERPIXEL, IS_BIG_ENDIAN, DISPLAYPIXEL, BUS_TYPE, WIDTH, HEIGHT,
OFFSET_WIDTH, OFFSET_HEIGHT> {
public:
MipiSpiBuffer() { this->rotation_ = ROTATION; }
void dump_config() override {
MipiSpi<BUFFERTYPE, BUFFERPIXEL, IS_BIG_ENDIAN, DISPLAYPIXEL, BUS_TYPE, WIDTH, HEIGHT, OFFSET_WIDTH,
OFFSET_HEIGHT>::dump_config();
esph_log_config(TAG,
" Rotation: %d°\n"
" Buffer pixels: %d bits\n"
" Buffer fraction: 1/%d\n"
" Buffer bytes: %zu\n"
" Draw rounding: %u",
this->rotation_, BUFFERPIXEL * 8, FRACTION, sizeof(BUFFERTYPE) * WIDTH * HEIGHT / FRACTION,
this->draw_rounding_);
}
void setup() override {
MipiSpi<BUFFERTYPE, BUFFERPIXEL, IS_BIG_ENDIAN, DISPLAYPIXEL, BUS_TYPE, WIDTH, HEIGHT, OFFSET_WIDTH,
OFFSET_HEIGHT>::setup();
RAMAllocator<BUFFERTYPE> allocator{};
this->buffer_ = allocator.allocate(WIDTH * HEIGHT / FRACTION);
if (this->buffer_ == nullptr) {
this->mark_failed("Buffer allocation failed");
}
}
void update() override {
#if ESPHOME_LOG_LEVEL == ESPHOME_LOG_LEVEL_VERBOSE
auto now = millis();
#endif
if (this->is_failed()) {
return;
}
// for updates with a small buffer, we repeatedly call the writer_ function, clipping the height to a fraction of
// the display height,
for (this->start_line_ = 0; this->start_line_ < HEIGHT; this->start_line_ += HEIGHT / FRACTION) {
#if ESPHOME_LOG_LEVEL == ESPHOME_LOG_LEVEL_VERBOSE
auto lap = millis();
#endif
this->end_line_ = this->start_line_ + HEIGHT / FRACTION;
if (this->auto_clear_enabled_) {
this->clear();
}
if (this->page_ != nullptr) {
this->page_->get_writer()(*this);
} else if (this->writer_.has_value()) {
(*this->writer_)(*this);
} else {
this->test_card();
}
#if ESPHOME_LOG_LEVEL == ESPHOME_LOG_LEVEL_VERBOSE
esph_log_v(TAG, "Drawing from line %d took %dms", this->start_line_, millis() - lap);
lap = millis();
#endif
if (this->x_low_ > this->x_high_ || this->y_low_ > this->y_high_)
return;
esph_log_v(TAG, "x_low %d, y_low %d, x_high %d, y_high %d", this->x_low_, this->y_low_, this->x_high_,
this->y_high_);
// Some chips require that the drawing window be aligned on certain boundaries
auto dr = this->draw_rounding_;
this->x_low_ = this->x_low_ / dr * dr;
this->y_low_ = this->y_low_ / dr * dr;
this->x_high_ = (this->x_high_ + dr) / dr * dr - 1;
this->y_high_ = (this->y_high_ + dr) / dr * dr - 1;
int w = this->x_high_ - this->x_low_ + 1;
int h = this->y_high_ - this->y_low_ + 1;
this->write_to_display_(this->x_low_, this->y_low_, w, h, this->buffer_, this->x_low_,
this->y_low_ - this->start_line_, WIDTH - w);
// invalidate watermarks
this->x_low_ = WIDTH;
this->y_low_ = HEIGHT;
this->x_high_ = 0;
this->y_high_ = 0;
#if ESPHOME_LOG_LEVEL == ESPHOME_LOG_LEVEL_VERBOSE
esph_log_v(TAG, "Write to display took %dms", millis() - lap);
lap = millis();
#endif
}
#if ESPHOME_LOG_LEVEL == ESPHOME_LOG_LEVEL_VERBOSE
esph_log_v(TAG, "Total update took %dms", millis() - now);
#endif
}
// Draw a pixel at the given coordinates.
void draw_pixel_at(int x, int y, Color color) override {
rotate_coordinates_(x, y);
if (x < 0 || x >= WIDTH || y < this->start_line_ || y >= this->end_line_)
return;
this->buffer_[(y - this->start_line_) * WIDTH + x] = convert_color_(color);
if (x < this->x_low_) {
this->x_low_ = x;
}
if (x > this->x_high_) {
this->x_high_ = x;
}
if (y < this->y_low_) {
this->y_low_ = y;
}
if (y > this->y_high_) {
this->y_high_ = y;
}
}
// Fills the display with a color.
void fill(Color color) override {
this->x_low_ = 0;
this->y_low_ = this->start_line_;
this->x_high_ = WIDTH - 1;
this->y_high_ = this->end_line_ - 1;
std::fill_n(this->buffer_, HEIGHT * WIDTH / FRACTION, convert_color_(color));
}
int get_width() override {
if constexpr (ROTATION == display::DISPLAY_ROTATION_90_DEGREES || ROTATION == display::DISPLAY_ROTATION_270_DEGREES)
return HEIGHT;
return WIDTH;
}
int get_height() override {
if constexpr (ROTATION == display::DISPLAY_ROTATION_90_DEGREES || ROTATION == display::DISPLAY_ROTATION_270_DEGREES)
return WIDTH;
return HEIGHT;
}
protected:
// Rotate the coordinates to match the display orientation.
void rotate_coordinates_(int &x, int &y) const {
if constexpr (ROTATION == display::DISPLAY_ROTATION_180_DEGREES) {
x = WIDTH - x - 1;
y = HEIGHT - y - 1;
} else if constexpr (ROTATION == display::DISPLAY_ROTATION_90_DEGREES) {
auto tmp = x;
x = WIDTH - y - 1;
y = tmp;
} else if constexpr (ROTATION == display::DISPLAY_ROTATION_270_DEGREES) {
auto tmp = y;
y = HEIGHT - x - 1;
x = tmp;
}
}
// Convert a color to the buffer pixel format.
BUFFERTYPE convert_color_(Color &color) const {
if constexpr (BUFFERPIXEL == PIXEL_MODE_8) {
return (color.red & 0xE0) | (color.g & 0xE0) >> 3 | color.b >> 6;
} else if constexpr (BUFFERPIXEL == PIXEL_MODE_16) {
if constexpr (IS_BIG_ENDIAN) {
return (color.r & 0xF8) | color.g >> 5 | (color.g & 0x1C) << 11 | (color.b & 0xF8) << 5;
} else {
return (color.r & 0xF8) << 8 | (color.g & 0xFC) << 3 | color.b >> 3;
}
}
return static_cast<BUFFERTYPE>(0);
}
BUFFERTYPE *buffer_{};
uint16_t x_low_{WIDTH};
uint16_t y_low_{HEIGHT};
uint16_t x_high_{0};
uint16_t y_high_{0};
uint16_t start_line_{0};
uint16_t end_line_{1};
};
} // namespace mipi_spi
} // namespace esphome

View File

@@ -0,0 +1,30 @@
from .ili import ST7789V
ST7789V.extend(
"ADAFRUIT-FUNHOUSE",
height=240,
width=240,
offset_height=0,
offset_width=0,
cs_pin=40,
dc_pin=39,
reset_pin=41,
invert_colors=True,
mirror_x=True,
mirror_y=True,
data_rate="80MHz",
)
ST7789V.extend(
"ADAFRUIT-S2-TFT-FEATHER",
height=240,
width=135,
offset_height=52,
offset_width=40,
cs_pin=7,
dc_pin=39,
reset_pin=40,
invert_colors=True,
)
models = {}

View File

@@ -67,6 +67,14 @@ RM690B0 = DriverChip(
),
)
T4_S3_AMOLED = RM690B0.extend("T4-S3", width=450, offset_width=16, bus_mode=TYPE_QUAD)
T4_S3_AMOLED = RM690B0.extend(
"T4-S3",
width=450,
offset_width=16,
cs_pin=11,
reset_pin=13,
enable_pin=9,
bus_mode=TYPE_QUAD,
)
models = {}

View File

@@ -1,3 +1,5 @@
import esphome.config_validation as cv
from . import DriverChip
from .ili import ILI9488_A
@@ -128,6 +130,7 @@ DriverChip(
ILI9488_A.extend(
"PICO-RESTOUCH-LCD-3.5",
swap_xy=cv.UNDEFINED,
spi_16=True,
pixel_mode="16bit",
mirror_x=True,

View File

@@ -55,7 +55,8 @@ void MQTTAlarmControlPanelComponent::dump_config() {
}
void MQTTAlarmControlPanelComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
JsonArray supported_features = root.createNestedArray(MQTT_SUPPORTED_FEATURES);
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
JsonArray supported_features = root[MQTT_SUPPORTED_FEATURES].to<JsonArray>();
const uint32_t acp_supported_features = this->alarm_control_panel_->get_supported_features();
if (acp_supported_features & ACP_FEAT_ARM_AWAY) {
supported_features.add("arm_away");

View File

@@ -153,11 +153,15 @@ void MQTTBackendESP32::mqtt_event_handler_(const Event &event) {
case MQTT_EVENT_DATA: {
static std::string topic;
if (!event.topic.empty()) {
// When a single message arrives as multiple chunks, the topic will be empty
// on any but the first message, leading to event.topic being an empty string.
// To ensure handlers get the correct topic, cache the last seen topic to
// simulate always receiving the topic from underlying library
topic = event.topic;
}
ESP_LOGV(TAG, "MQTT_EVENT_DATA %s", topic.c_str());
this->on_message_.call(!event.topic.empty() ? topic.c_str() : nullptr, event.data.data(), event.data.size(),
event.current_data_offset, event.total_data_len);
this->on_message_.call(topic.c_str(), event.data.data(), event.data.size(), event.current_data_offset,
event.total_data_len);
} break;
case MQTT_EVENT_ERROR:
ESP_LOGE(TAG, "MQTT_EVENT_ERROR");

View File

@@ -30,6 +30,7 @@ MQTTBinarySensorComponent::MQTTBinarySensorComponent(binary_sensor::BinarySensor
}
void MQTTBinarySensorComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
if (!this->binary_sensor_->get_device_class().empty())
root[MQTT_DEVICE_CLASS] = this->binary_sensor_->get_device_class();
if (this->binary_sensor_->is_status_binary_sensor())

View File

@@ -31,9 +31,12 @@ void MQTTButtonComponent::dump_config() {
}
void MQTTButtonComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
config.state_topic = false;
if (!this->button_->get_device_class().empty())
if (!this->button_->get_device_class().empty()) {
root[MQTT_DEVICE_CLASS] = this->button_->get_device_class();
}
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
}
std::string MQTTButtonComponent::component_type() const { return "button"; }

View File

@@ -92,6 +92,7 @@ void MQTTClientComponent::send_device_info_() {
std::string topic = "esphome/discover/";
topic.append(App.get_name());
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
this->publish_json(
topic,
[](JsonObject root) {
@@ -147,6 +148,7 @@ void MQTTClientComponent::send_device_info_() {
#endif
},
2, this->discovery_info_.retain);
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
}
void MQTTClientComponent::dump_config() {

View File

@@ -14,6 +14,7 @@ static const char *const TAG = "mqtt.climate";
using namespace esphome::climate;
void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
auto traits = this->device_->get_traits();
// current_temperature_topic
if (traits.get_supports_current_temperature()) {
@@ -28,7 +29,7 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo
// mode_state_topic
root[MQTT_MODE_STATE_TOPIC] = this->get_mode_state_topic();
// modes
JsonArray modes = root.createNestedArray(MQTT_MODES);
JsonArray modes = root[MQTT_MODES].to<JsonArray>();
// sort array for nice UI in HA
if (traits.supports_mode(CLIMATE_MODE_AUTO))
modes.add("auto");
@@ -89,7 +90,7 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo
// preset_mode_state_topic
root[MQTT_PRESET_MODE_STATE_TOPIC] = this->get_preset_state_topic();
// presets
JsonArray presets = root.createNestedArray("preset_modes");
JsonArray presets = root["preset_modes"].to<JsonArray>();
if (traits.supports_preset(CLIMATE_PRESET_HOME))
presets.add("home");
if (traits.supports_preset(CLIMATE_PRESET_AWAY))
@@ -119,7 +120,7 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo
// fan_mode_state_topic
root[MQTT_FAN_MODE_STATE_TOPIC] = this->get_fan_mode_state_topic();
// fan_modes
JsonArray fan_modes = root.createNestedArray("fan_modes");
JsonArray fan_modes = root["fan_modes"].to<JsonArray>();
if (traits.supports_fan_mode(CLIMATE_FAN_ON))
fan_modes.add("on");
if (traits.supports_fan_mode(CLIMATE_FAN_OFF))
@@ -150,7 +151,7 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo
// swing_mode_state_topic
root[MQTT_SWING_MODE_STATE_TOPIC] = this->get_swing_mode_state_topic();
// swing_modes
JsonArray swing_modes = root.createNestedArray("swing_modes");
JsonArray swing_modes = root["swing_modes"].to<JsonArray>();
if (traits.supports_swing_mode(CLIMATE_SWING_OFF))
swing_modes.add("off");
if (traits.supports_swing_mode(CLIMATE_SWING_BOTH))
@@ -163,6 +164,7 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo
config.state_topic = false;
config.command_topic = false;
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
}
void MQTTClimateComponent::setup() {
auto traits = this->device_->get_traits();

View File

@@ -70,6 +70,7 @@ bool MQTTComponent::send_discovery_() {
ESP_LOGV(TAG, "'%s': Sending discovery", this->friendly_name().c_str());
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
return global_mqtt_client->publish_json(
this->get_discovery_topic_(discovery_info),
[this](JsonObject root) {
@@ -128,21 +129,16 @@ bool MQTTComponent::send_discovery_() {
root[MQTT_PAYLOAD_NOT_AVAILABLE] = this->availability_->payload_not_available;
}
std::string unique_id = this->unique_id();
const MQTTDiscoveryInfo &discovery_info = global_mqtt_client->get_discovery_info();
if (!unique_id.empty()) {
root[MQTT_UNIQUE_ID] = unique_id;
if (discovery_info.unique_id_generator == MQTT_MAC_ADDRESS_UNIQUE_ID_GENERATOR) {
char friendly_name_hash[9];
sprintf(friendly_name_hash, "%08" PRIx32, fnv1_hash(this->friendly_name()));
friendly_name_hash[8] = 0; // ensure the hash-string ends with null
root[MQTT_UNIQUE_ID] = get_mac_address() + "-" + this->component_type() + "-" + friendly_name_hash;
} else {
if (discovery_info.unique_id_generator == MQTT_MAC_ADDRESS_UNIQUE_ID_GENERATOR) {
char friendly_name_hash[9];
sprintf(friendly_name_hash, "%08" PRIx32, fnv1_hash(this->friendly_name()));
friendly_name_hash[8] = 0; // ensure the hash-string ends with null
root[MQTT_UNIQUE_ID] = get_mac_address() + "-" + this->component_type() + "-" + friendly_name_hash;
} else {
// default to almost-unique ID. It's a hack but the only way to get that
// gorgeous device registry view.
root[MQTT_UNIQUE_ID] = "ESP" + this->component_type() + this->get_default_object_id_();
}
// default to almost-unique ID. It's a hack but the only way to get that
// gorgeous device registry view.
root[MQTT_UNIQUE_ID] = "ESP" + this->component_type() + this->get_default_object_id_();
}
const std::string &node_name = App.get_name();
@@ -155,7 +151,7 @@ bool MQTTComponent::send_discovery_() {
}
std::string node_area = App.get_area();
JsonObject device_info = root.createNestedObject(MQTT_DEVICE);
JsonObject device_info = root[MQTT_DEVICE].to<JsonObject>();
const auto mac = get_mac_address();
device_info[MQTT_DEVICE_IDENTIFIERS] = mac;
device_info[MQTT_DEVICE_NAME] = node_friendly_name;
@@ -192,6 +188,7 @@ bool MQTTComponent::send_discovery_() {
device_info[MQTT_DEVICE_CONNECTIONS][0][1] = mac;
},
this->qos_, discovery_info.retain);
// NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
}
uint8_t MQTTComponent::get_qos() const { return this->qos_; }
@@ -284,7 +281,6 @@ void MQTTComponent::call_dump_config() {
this->dump_config();
}
void MQTTComponent::schedule_resend_state() { this->resend_state_ = true; }
std::string MQTTComponent::unique_id() { return ""; }
bool MQTTComponent::is_connected_() const { return global_mqtt_client->is_connected(); }
// Pull these properties from EntityBase if not overridden

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