Compare commits

..

767 Commits

Author SHA1 Message Date
J. Nick Koston
0ef86a9e5e Optimize Application class memory layout and reduce loop_interval size 2025-06-26 02:43:00 +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
17497eec43 Reduce memory required for sensor entities (#9201)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-25 18:15:59 -05:00
Clyde Stubbs
6d0c6329ad [lvgl] Allow linear positioning of grid cells (#9196) 2025-06-26 10:45:14 +12: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
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
9074ef792f Reduce component_iterator memory usage 2025-06-25 19:35:40 +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
Clyde Stubbs
f35be6b5cc [binary_sensor] Add timeout filter (#9198) 2025-06-25 14:09:43 +02:00
DanielV
b18ff48b4a [API] Sub devices and areas (#8544)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick+github@koston.org>
2025-06-25 12:03:41 +00:00
J. Nick Koston
0dedbcdd71 Merge branch 'multi_device' into integration 2025-06-25 13:44:20 +02:00
Artem Draft
7c28134214 Rename kVARh/VARh to kvarh/varh (#9191) 2025-06-25 22:36:24 +12:00
Rodrigo Martín
16860e8a30 fix(MQTT): Call disconnect callback on DNS error (#9016)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-25 22:20:29 +12:00
Jonathan Swoboda
5362d1a89f [esp32_hall] Add dummy component (#9125)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-06-25 21:49:31 +12:00
Keith Burzinski
5531296ee0 [ld2410] Use `App.get_loop_component_start_time()`, shorten log messages (#9194)
Co-authored-by: J. Nick Koston <nick+github@koston.org>
2025-06-25 21:48:32 +12:00
Keith Burzinski
47db5e26f3 [ld2420] Shorten log messages + other clean-up (#9200) 2025-06-25 03:16:05 -05:00
Keith Burzinski
cf5197b68a [ld2450] Use `App.get_loop_component_start_time()`, shorten log messages (#9192) 2025-06-25 03:15:50 -05:00
Keith Burzinski
9f831e91b3 [helpers] Add `format_mac_address_pretty` function, migrate components (#9193) 2025-06-25 12:36:33 +12:00
Javier Peletier
2df0ebd895 [modbus_controller] Fix modbus read_lambda precision for non-floats or large integers (#9159) 2025-06-25 11:31:23 +12: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
Jesse Hills
7ad6dab383 [mqtt] Don't wait for connection unless configured to (#8933) 2025-06-24 13:31:38 +12:00
Clyde Stubbs
612c8d5841 [lvgl] Fix dangling pointer issue with qrcode (#9190) 2025-06-24 09:43:40 +10:00
J. Nick Koston
5cd498fbe9 Merge branch 'multi_device' into integration 2025-06-23 22:56:28 +02:00
Cody Cutrer
a35e476be5 [opt3001] New component (#6625)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-06-23 14:31:20 -05:00
Jesse Hills
87a7157fc4 Merge branch 'release' into dev 2025-06-24 07:28:40 +12:00
Jesse Hills
fa34adbf6c Merge pull request #9185 from esphome/bump-2025.6.1
2025.6.1
2025-06-24 07:27:59 +12:00
dependabot[bot]
ac942e0670 Bump aioesphomeapi from 33.1.0 to 33.1.1 (#9187) 2025-06-23 19:58:32 +02:00
Jesse Hills
22e360d479 Bump version to 2025.6.1 2025-06-23 23:32:22 +12:00
myhomeiot
649936200e Restore access to BLEScanResult as get_scan_result (#9148) 2025-06-23 23:32:22 +12:00
rwrozelle
5d6e690c12 Fixes for setup of OpenThread either using TLV or entering Credentials directly (#9157)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-06-23 23:32:22 +12:00
Jesse Hills
2f2ecadae7 [config validation] Add more ip address / network validators (#9181) 2025-06-23 23:32:22 +12:00
J. Nick Koston
6dfb9eba61 Fix missing BLE GAP events causing RSSI sensor and beacon failures (#9138) 2025-06-23 23:32:22 +12:00
Edward Firmo
24587fe875 [nextion] Fix command spacing double timing and response blocking issues (#9134) 2025-06-23 23:32:22 +12:00
J. Nick Koston
a1aebe6a2c Eliminate memory fragmentation with BLE event pool (#9101) 2025-06-23 23:32:22 +12:00
Gustavo Ambrozio
2ad266582f [online_image] Allow suppressing update on url change (#8885) 2025-06-23 20:40:07 +10: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
JonasB2497
1a47164876 Feature fontmetrics (#8978) 2025-06-23 14:47:47 +10:00
myhomeiot
cd22723623 Restore access to BLEScanResult as get_scan_result (#9148) 2025-06-23 15:42:20 +12:00
rwrozelle
aecaffa2f5 Fixes for setup of OpenThread either using TLV or entering Credentials directly (#9157)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-06-23 15:41:29 +12:00
Jesse Hills
87df3596a2 [config validation] Add more ip address / network validators (#9181) 2025-06-23 15:41:06 +12:00
Clyde Stubbs
41c7852128 [lvgl] Use styles instead of object properties for themes (#9116) 2025-06-23 14:25:26 +12:00
Clyde Stubbs
78ec9856fb [lvgl] Add start_value to bar; make values templatable and updateable (#9056) 2025-06-23 14:23:41 +12:00
Jesse Hills
7aea82a273 Move define 2025-06-23 14:15:10 +12:00
J. Nick Koston
2a45467bf6 Pre-reserve looping components vector to reduce memory allocations (#9177) 2025-06-23 14:10:09 +12:00
J. Nick Koston
7fc5bfd787 Reduce RAM usage for scheduled tasks (#9180) 2025-06-23 14:09:34 +12:00
J. Nick Koston
04f592ba6d Fix slow noise handshake by reading multiple messages per loop (#9130) 2025-06-23 14:07:53 +12:00
J. Nick Koston
59889a6286 Reduce Logger memory usage by optimizing variable sizes (#9161) 2025-06-23 14:06:02 +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
Jesse Hills
dc5cbd4df8 [const] Move `CONF_DEVICES to const.py` (#9179) 2025-06-23 09:54:49 +12: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
Edward Firmo
7ab9083d77 [nextion] Revert to millis() on recv_ret_string_ (#9168) 2025-06-22 20:56:50 +00: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
dependabot[bot]
788803d588 Bump flake8 from 7.2.0 to 7.3.0 (#9172)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-06-22 19:05:54 +00: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
dependabot[bot]
cbfd904b9f Bump aioesphomeapi from 32.2.4 to 33.1.0 (#9173)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-22 11:00:42 +00: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
Jimmy Hedman
c81dbf9d59 Improve on C++17 (#9170) 2025-06-22 12:09:38 +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
dependabot[bot]
ac9c608542 Bump esptool from 4.8.1 to 4.9.0 (#9158) 2025-06-21 18:13:07 +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
Edward Firmo
a6c20853ca [nextion] Extract common upload_end_ function to shared file (#9155) 2025-06-21 11:26:14 +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
Jesse Hills
4ef0264ed3 Clean up RAMAllocators in light related code (#9142) 2025-06-21 17:32:24 +10:00
J. Nick Koston
fdde9c4681 Reduce Logger memory usage by optimizing variable sizes 2025-06-21 00:27:05 +02:00
Clyde Stubbs
169db9cc0a [spi] Enable >6 devices with ESP-IDF (#9128) 2025-06-21 07:55:08 +10: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
RoganDawes
b693b8ccb1 [usb-host] Add support for USB Hubs (#9154) 2025-06-20 22:03:15 +10:00
Keith Burzinski
3e98cceb00 [bh1750] Remove redundant platform name from logging (#9153) 2025-06-20 12:33:46 +02:00
Keith Burzinski
46d962dcf1 [wifi, wifi_info] Tidy up/shorten more log messages (#9151) 2025-06-20 22:02:36 +12:00
Edward Firmo
7dbad42470 [nextion] Cached timing optimization (#9150)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-06-20 07:46:12 +00:00
Edward Firmo
eb97781f68 [nextion] Add command queuing to prevent command loss when spacing is active (#9139)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-06-20 01:38:40 -05:00
Jesse Hills
4d0f8528d2 [esp32_camera] Allow sharing i2c bus (#9137)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-06-19 01:31:19 -05:00
Jesse Hills
2c17b2bacc [i2c] Make `get_port()` public (#9146) 2025-06-19 05:44:33 +00:00
Jesse Hills
30bea20f7a Clean up RAMAllocators in display related code (#9141) 2025-06-19 05:17:08 +00:00
Jesse Hills
d4cb4ef994 Clean up RAMAllocators in http_request code (#9143) 2025-06-19 03:11:18 +00:00
J. Nick Koston
9c90ca297a Fix missing BLE GAP events causing RSSI sensor and beacon failures (#9138) 2025-06-19 03:03:09 +00:00
Jesse Hills
a9e1a4cef3 Clean up RAMAllocators in audio related code (#9140) 2025-06-19 02:53:54 +00:00
J. Nick Koston
0ce3621ac0 Disable Ethernet loop polling when connected and stable (#9102) 2025-06-19 14:49:31 +12:00
J. Nick Koston
bcf961c0b0 Merge branch 'dev' into integration 2025-06-19 04:05:25 +02:00
Jesse Hills
d527398dae [i2c] Expose internal i2c bus port number (#9136) 2025-06-18 20:50:47 -05: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
Edward Firmo
2e9ac8945d [nextion] Fix command spacing double timing and response blocking issues (#9134) 2025-06-19 13:41:20 +12:00
J. Nick Koston
de0cd0ec67 Merge branch 'dev' into disable_ethernet_loop 2025-06-19 03:39:15 +02:00
J. Nick Koston
40a5638005 Optimize OTA loop to avoid unnecessary stack allocations (#9129) 2025-06-19 13:33:00 +12:00
J. Nick Koston
8ba22183b9 Add enable_loop_soon_any_context() for thread and ISR-safe loop enabling (#9127) 2025-06-19 13:30:41 +12:00
J. Nick Koston
2e11e66db4 Optimize bluetooth_proxy memory usage on ESP32 (#9114)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-19 13:11:13 +12:00
J. Nick Koston
eeb0710ad4 Optimize API component memory usage by reordering class members to reduce padding (#9111) 2025-06-19 13:08:25 +12:00
J. Nick Koston
43c677ef37 Optimize API server performance by using cached loop time (#9104) 2025-06-19 12:12:14 +12:00
J. Nick Koston
95544e489d Use smaller atomic types for ESP32 BLE Tracker ring buffer indices (#9106) 2025-06-19 12:10:50 +12:00
J. Nick Koston
a08d021f77 Reduce code duplication in auto-generated API protocol code (#9097) 2025-06-19 12:10:01 +12:00
J. Nick Koston
b7b1d17ecb Remove empty generated protobuf methods (#9098) 2025-06-19 12:06:39 +12: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
Jonathan Swoboda
aa180b9581 Bump ESP32 Arduino version to 3.1.3 (#8604)
Co-authored-by: Kuba Szczodrzyński <kuba@szczodrzynski.pl>
2025-06-19 08:16:25 +12:00
dependabot[bot]
57388254c4 Bump pytest from 8.4.0 to 8.4.1 (#9131)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-18 19:56:26 +00:00
dependabot[bot]
f16f4e2c4c Bump aioesphomeapi from 32.2.3 to 32.2.4 (#9132)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-18 19:55:59 +00:00
J. Nick Koston
deabac18b2 Merge branch 'disable_ethernet_loop' into integration 2025-06-18 21:39:35 +02:00
dependabot[bot]
89b70e4352 Bump docker/setup-buildx-action from 3.11.0 to 3.11.1 in the docker-actions group (#9133)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-18 21:37:21 +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
6667336bd8 Eliminate memory fragmentation with BLE event pool (#9101) 2025-06-18 21:57:49 +12:00
Kuba Szczodrzyński
669ef7a0b1 [web_server] Upgrade ESPAsync libraries (#8867) 2025-06-18 21:51:00 +12:00
Severin von Wnuck-Lipinski
c612985930 Add support for Xiaomi XMWSDJ04MMC (#8591) 2025-06-18 21:49:39 +12:00
J. Nick Koston
2e534ce41e Reduce CPU overhead by allowing components to disable their loop() (#9089) 2025-06-18 21:49:25 +12:00
Jesse Hills
fedb54bb38 Merge branch 'release' into dev 2025-06-18 21:41:59 +12:00
Jesse Hills
68f5144084 Merge pull request #9126 from esphome/bump-2025.6.0
2025.6.0
2025-06-18 21:41:00 +12:00
Jonathan Swoboda
fd3c22945b [i2s_audio] Bump esphome/ESP32-audioI2S to 2.3.0 (#9124) 2025-06-18 04:18:23 +00:00
Jonathan Swoboda
53496a1ecd [heatpumpir] Bump HeatpumpIR to 1.0.35 (#9123) 2025-06-18 04:15:26 +00:00
Michael Hansen
da5cf99549 Add intent progress event to voice assistant enum (#9103) 2025-06-18 15:15:37 +12:00
Jesse Hills
849c858495 Bump version to 2025.6.0 2025-06-18 14:16:24 +12:00
Jesse Hills
808f964841 Merge branch 'beta' into dev 2025-06-18 12:37:57 +12:00
Jesse Hills
16a0f9db97 Merge pull request #9122 from esphome/bump-2025.6.0b3
2025.6.0b3
2025-06-18 12:37:25 +12:00
J. Nick Koston
3bc5db4fd7 Bump ruff in pre-commit to 0.12.0 (#9121) 2025-06-18 10:54:45 +12:00
Jesse Hills
5269523ca1 Bump version to 2025.6.0b3 2025-06-18 10:17:56 +12: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
89267b9e06 Reduce Switch component memory usage by 8 bytes per instance (#9112) 2025-06-18 10:09:11 +12:00
J. Nick Koston
4bc9646e8f Optimize LightState memory layout (#9113) 2025-06-18 10:09:11 +12:00
Clyde Stubbs
fd83628c49 [spi] Cater for non-word-aligned buffers on esp8266 (#9108) 2025-06-18 10:09:11 +12:00
Kevin Ahrendt
62abfbec9e [i2s_audio] Bugfix: crashes when unlocking i2s bus multiple times (#9100) 2025-06-18 10:09:11 +12:00
Keith Burzinski
7cc0008837 [i2s_audio] Add `dump_config` methods, shorten log messages (#9099) 2025-06-18 10:09:11 +12:00
dependabot[bot]
0bf613bd34 Bump ruff from 0.11.13 to 0.12.0 (#9120)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-18 00:08:22 +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
Jonathan Swoboda
43ab63455b Pin libretiny to 1.9.1 (#9118) 2025-06-17 22:42:36 +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
47e7988c8e Reduce Switch component memory usage by 8 bytes per instance (#9112) 2025-06-17 13:14:03 -05:00
J. Nick Koston
7ed095e635 Optimize LightState memory layout (#9113) 2025-06-17 13:07:45 -05:00
Michael Hansen
cb8b0ec62e Add intent progress event to voice assistant enum (#9103) 2025-06-17 13:05:06 -05:00
J. Nick Koston
bf161f1eaa Resolve esphome::optional vs std::optional ambiguity in code generation (#9119) 2025-06-17 13:04:45 -05: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
Jonathan Swoboda
78c8447d1e [esp32_hall] Remove esp32_hall (#9117) 2025-06-17 15:47:42 +00: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
dependabot[bot]
5ffe50381a Bump docker/setup-buildx-action from 3.10.0 to 3.11.0 in the docker-actions group (#9105)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-17 10:41:54 +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
Jonathan Swoboda
b08bd0c24a Bump LibreTiny recommended version to 1.9.1 (#9110) 2025-06-17 04:41:18 +02:00
J. Nick Koston
daa1fb9a7a Merge remote-tracking branch 'swoboda1337/bump_libretiny' into integration 2025-06-17 04:33:57 +02:00
Clyde Stubbs
738ad8e9d3 [spi] Cater for non-word-aligned buffers on esp8266 (#9108) 2025-06-17 02:30:09 +00:00
Jonathan Swoboda
b7d543290b Bump LibreTiny 2025-06-16 21:40:06 -04:00
Kevin Ahrendt
fa7c42511a [i2s_audio] Bugfix: crashes when unlocking i2s bus multiple times (#9100) 2025-06-17 12:59:07 +12: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
Keith Burzinski
68ef9cb3dc [i2s_audio] Add `dump_config` methods, shorten log messages (#9099) 2025-06-16 07:36:49 +00:00
Jesse Hills
8e176b9c61 Merge branch 'beta' into dev 2025-06-16 17:07:31 +12:00
Jesse Hills
426be153db Merge pull request #9094 from esphome/bump-2025.6.0b2
2025.6.0b2
2025-06-16 17:06:59 +12: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
Jesse Hills
c4f7c2d259 [ruff] Apply various ruff suggestions (#8947) 2025-06-15 22:13:14 -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
Jesse Hills
2a81efda0b Remove `std::` prefix as not all platforms have access yet. (#9095) 2025-06-16 12:55:51 +12:00
Jesse Hills
882bfc79c7 Remove `std::` prefix as not all platforms have access yet. (#9095) 2025-06-16 12:55:23 +12: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
dependabot[bot]
6bad276589 Bump aioesphomeapi from 32.2.1 to 32.2.3 (#9091)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-16 11:45:58 +12: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
Jesse Hills
47d8048a62 Bump version to 2025.6.0b2 2025-06-16 10:07:07 +12:00
J. Nick Koston
20d7ba5d7c Reduce Component blocking threshold memory usage by 2 bytes per component (#9081) 2025-06-16 10:07:07 +12:00
J. Nick Koston
e435e72654 Add common base classes for entity protobuf messages to reduce duplicate code (#9090) 2025-06-16 10:07:07 +12:00
J. Nick Koston
497d66f7ec Ensure we can send batches where the first message exceeds MAX_PACKET_SIZE (#9068) 2025-06-16 10:07:07 +12:00
Kevin Ahrendt
242b02a416 [i2s_audio] Check for a nullptr before disabling and deleting channel (#9062) 2025-06-16 10:07:07 +12:00
J. Nick Koston
9644a6bb9c Fix protobuf encoding size mismatch by passing force parameter in encode_string (#9074) 2025-06-16 10:07:07 +12:00
J. Nick Koston
70d66062d6 Make BLE queue lock free (#9088) 2025-06-16 10:07:07 +12:00
J. Nick Koston
39f6f9b0dc Implement a lock free ring buffer for BLEScanResult to avoid drops (#9087) 2025-06-16 10:07:07 +12:00
dhewg
0454dd4e07 [fan] fix initial FanCall to properly set speed (#8277) 2025-06-16 10:07:07 +12:00
J. Nick Koston
6f4e76c8f3 Fix unbound BLE event queue growth and reduce memory usage (#9052) 2025-06-16 10:07:07 +12:00
J. Nick Koston
5cdcf2415d Optimize Application area_ from std::string to const char* (#9085) 2025-06-16 10:07:07 +12:00
J. Nick Koston
1719a2e08b Fix API message encoding to return actual size instead of calculated size (#9073) 2025-06-16 10:07:07 +12:00
J. Nick Koston
5640a9fe73 Optimize memory usage by lazy-allocating raw callbacks in sensors (#9077) 2025-06-16 10:07:07 +12:00
J. Nick Koston
4787e22f61 Reduce entity memory usage by eliminating field shadowing and bit-packing (#9076) 2025-06-16 10:07:01 +12:00
J. Nick Koston
fb12e4e66a Small optimizations to api buffer helper (#9071) 2025-06-16 09:49:45 +12:00
J. Nick Koston
77740a1044 Optimize Component and Application state storage from uint32_t to uint8_t (#9082) 2025-06-16 09:49:45 +12:00
J. Nick Koston
1fdfe7578f Make ParseOnOffState enum uint8_t (#9083) 2025-06-16 09:49:45 +12:00
J. Nick Koston
ebecf7047e Fix captive_portal loading entire web_server (#9066) 2025-06-16 09:49:45 +12:00
Jesse Hills
00e8332bf5 [esp32] Dynamically set default framework based on variant (#9060) 2025-06-16 09:49:45 +12:00
Jesse Hills
5fc1f90822 [prometheus] Remove `cv.only_with_arduino` (#9061) 2025-06-16 09:49:45 +12:00
J. Nick Koston
0a1be3d19c Fix misleading comment in API (#9069) 2025-06-16 09:49:45 +12:00
Nate Clark
40db3146b9 Fix BYPASS_AUTO feature to work with or without an arming delay (#9051) 2025-06-16 09:49:45 +12:00
Edward Firmo
535c495b33 [nextion] Remove upload flags reset from success path to prevent TFT corruption (#9064) 2025-06-16 09:49:45 +12:00
J. Nick Koston
592446e430 Always perform select() when loop duration exceeds interval (#9058) 2025-06-16 09:49:45 +12:00
J. Nick Koston
7a5c9a821a Fix dashboard logging being escaped before parser (#9054) 2025-06-16 09:49:45 +12:00
J. Nick Koston
72ecf7a288 Merge remote-tracking branch 'upstream/dev' into integration 2025-06-15 16:48:20 -05:00
J. Nick Koston
c17a3b6fcc Reduce Component memory usage by 20 bytes per component (#9080)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-06-16 09:34:37 +12:00
J. Nick Koston
28d11553e0 Reduce Component blocking threshold memory usage by 2 bytes per component (#9081) 2025-06-16 09:33:38 +12:00
J. Nick Koston
1dbebe90ba Add common base classes for entity protobuf messages to reduce duplicate code (#9090) 2025-06-16 09:29:25 +12:00
J. Nick Koston
06810e8e6a Ensure we can send batches where the first message exceeds MAX_PACKET_SIZE (#9068) 2025-06-16 09:22:14 +12:00
Kevin Ahrendt
bd85ba9b6a [i2s_audio] Check for a nullptr before disabling and deleting channel (#9062) 2025-06-16 09:19:50 +12:00
J. Nick Koston
be58cdda3b Fix protobuf encoding size mismatch by passing force parameter in encode_string (#9074) 2025-06-16 09:19:04 +12:00
J. Nick Koston
fcce4a8be6 Make BLE queue lock free (#9088) 2025-06-16 09:16:46 +12:00
J. Nick Koston
61a558a062 Implement a lock free ring buffer for BLEScanResult to avoid drops (#9087) 2025-06-16 08:53:45 +12:00
J. Nick Koston
ef98c7502d Merge remote-tracking branch 'origin/dev' into integration 2025-06-15 13:42:11 -05:00
dhewg
59f69ac5ca [fan] fix initial FanCall to properly set speed (#8277) 2025-06-15 13:16:33 -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
dependabot[bot]
f82ac34784 Bump aioesphomeapi from 32.2.1 to 32.2.3 (#9091)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-15 10:42:01 -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
07cf6e723b Fix unbound BLE event queue growth and reduce memory usage (#9052) 2025-06-15 04:45:41 +00: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
78e3c6333f Optimize Application area_ from std::string to const char* (#9085) 2025-06-14 22:46:40 -05:00
J. Nick Koston
98e2684107 Fix API message encoding to return actual size instead of calculated size (#9073) 2025-06-15 15:46:02 +12: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
cb019fff9a Optimize memory usage by lazy-allocating raw callbacks in sensors (#9077) 2025-06-15 15:28:15 +12:00
J. Nick Koston
4305c44440 Reduce entity memory usage by eliminating field shadowing and bit-packing (#9076) 2025-06-15 15:21:55 +12: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
a1e4143600 Small optimizations to api buffer helper (#9071) 2025-06-15 14:55:03 +12:00
J. Nick Koston
374c33e8dc Optimize Component and Application state storage from uint32_t to uint8_t (#9082) 2025-06-15 14:48:53 +12:00
J. Nick Koston
dcfe7af9d3 Make ParseOnOffState enum uint8_t (#9083) 2025-06-15 14:44:45 +12: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
Keith Burzinski
049c7e00ca Move some consts to `const.py` (#9084) 2025-06-14 23:23:52 +00: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
Jimmy Hedman
ee37d2f9c8 Build with C++17 (#8603)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-06-14 08:21:39 -05:00
J. Nick Koston
92ea697119 Fix captive_portal loading entire web_server (#9066) 2025-06-14 08:19:41 -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
dependabot[bot]
1c488d375f Bump pytest-asyncio from 0.26.0 to 1.0.0 (#9067)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-13 18:40:18 -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
Jesse Hills
1a03b4949f [esp32] Dynamically set default framework based on variant (#9060) 2025-06-14 11:17:06 +12:00
Jesse Hills
731b7808cd [prometheus] Remove `cv.only_with_arduino` (#9061) 2025-06-14 11:08:07 +12: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
d9da4cf24d Fix misleading comment in API (#9069) 2025-06-14 09:10:33 +12: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
Nate Clark
666a3ee5e9 Fix BYPASS_AUTO feature to work with or without an arming delay (#9051) 2025-06-13 13:31:00 -05:00
Nico B
02469c2d4c ina219: powerdown the sensor on shutdown (#9053) 2025-06-13 18:17:38 +00: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
Edward Firmo
2a629cae93 [nextion] Remove upload flags reset from success path to prevent TFT corruption (#9064) 2025-06-13 13:39:32 +12:00
J. Nick Koston
0877b3e2af suppress unused events 2025-06-12 19:18:22 -05:00
dependabot[bot]
1f14c316a3 Bump pytest-cov from 6.1.1 to 6.2.1 (#9063)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-12 18:16:37 -05:00
J. Nick Koston
dac738a916 Always perform select() when loop duration exceeds interval (#9058) 2025-06-12 03:27:10 +00:00
J. Nick Koston
99a54369bf Merge remote-tracking branch 'upstream/dev' into loop_runtime_stats 2025-06-11 22:01:22 -05:00
Clyde Stubbs
261b561bb2 [binary_sensor] Add action to invalidate state and pass to HA (#8961)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-06-12 09:16:20 +10:00
J. Nick Koston
f7533dfc5c review 2025-06-11 16:25:31 -05:00
J. Nick Koston
0228379a2e Fix dashboard logging being escaped before parser (#9054) 2025-06-11 16:17:47 -05:00
Jesse Hills
da79215bc3 Merge branch 'beta' into dev 2025-06-12 07:56:24 +12:00
Jesse Hills
44323dc285 Merge pull request #9049 from esphome/bump-2025.6.0b1
2025.6.0b1
2025-06-12 07:55:49 +12: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
Thomas Rupprecht
a59e1c7011 [core/pins] improve pins types (#8848)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-06-11 18:06:41 +00: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
Jesse Hills
abb4d991ad Bump version to 2025.6.0b1 2025-06-11 23:16:56 +12:00
Jesse Hills
f467c79a20 Bump version to 2025.7.0-dev 2025-06-11 23:16:56 +12:00
Keith Burzinski
dcf41db878 [sgp4x] Shorten log messages, various clean-up (#9048) 2025-06-11 11:11:11 +00:00
Jesse Hills
c3c3a27af2 Openthread code updates (#9047) 2025-06-11 22:41:38 +12:00
Stanislav Meduna
052f558131 Add support for custom request headers in online_image component (#8985) 2025-06-11 22:14:02 +12:00
J. Nick Koston
e8aa7cff36 Improve shutdown reliability when tx buffer is full (#9043) 2025-06-11 22:08:23 +12:00
J. Nick Koston
3411e45a0a Reserve memory for component and platform vectors (#9042) 2025-06-11 22:05:42 +12:00
Clyde Stubbs
a488c8cd5c [spi] Restrict octal spi to S3/S2/P4 (#9041) 2025-06-11 19:45:26 +10:00
Keith Burzinski
9652b1a556 [application] Fix build error on some IDF versions (#9045) 2025-06-11 21:44:49 +12:00
Keith Burzinski
69f2c79ccb [shtcx] Shorten log messages (#9046) 2025-06-11 21:44:10 +12:00
Mathieu Rene
9d9d210176 Add OpenThread support on ESP-IDF (#7506)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-06-11 17:27:58 +12:00
Thomas Rupprecht
487e1f871f use `encode_uintXX` (#8847)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-11 17:06:45 +12:00
J. Nick Koston
0e27ac281f Ensure components only powerdown after teardown (#9044) 2025-06-11 04:21:22 +00:00
Jonathan Swoboda
ad37f103fa [esp32_rmt] Add variant validation for use_dma (#8897) 2025-06-11 14:00:59 +12:00
Jesse Hills
b579bbf03b [esp32] Use release zip from pioarduino/platform-espressif32 instead of git tag (#8975) 2025-06-11 13:53:55 +12:00
Citric Li
7f4d2534aa Fix: Seeed Studio MR60FDA2 threshold height could not be set (#9011) 2025-06-11 12:44:51 +12:00
Clyde Stubbs
9e26daeb94 [esp_ldo] Implement support for ESP32-P4 LDO (#9009)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-06-11 12:43:19 +12:00
Keith Burzinski
da6af184a6 Use a define for log message constants (#8952) 2025-06-11 12:32:51 +12:00
Jonathan Swoboda
4d347f1cc6 [core] Include esp_mac.h on Arduino too (#9040) 2025-06-11 00:08:51 +00:00
Jesse Hills
84e57b8136 [inkplate] Remove arduino dependency (#9031) 2025-06-11 12:01:11 +12:00
J. Nick Koston
63882c4a74 Reduce Bluetooth overhead by disabling unused logging categories (#8945) 2025-06-11 11:57:43 +12:00
J. Nick Koston
2ed5611a08 Replace API deferred queue with efficient message batching system (#9012) 2025-06-11 11:49:15 +12:00
Clyde Stubbs
1467b704b8 [lvgl] Fix templated argument to lvgl.is_idle (#9014) 2025-06-10 03:47:08 -05:00
Edward Firmo
94848e4811 [nextion] Add configurable limit for commands processed per loop (#8972)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-06-10 07:27:46 +00:00
Edward Firmo
3174f7ae86 [nextion] Optimize log messages to reduce memory usage (#9039) 2025-06-09 18:51:08 -05:00
dependabot[bot]
962a339a8a Bump ruamel-yaml from 0.18.13 to 0.18.14 (#9037)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-09 18:45:05 -05:00
dependabot[bot]
6a76e6ae4a Bump aioesphomeapi from 32.2.0 to 32.2.1 (#9038)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-09 18:44:53 -05:00
Edward Firmo
ce4371a80d [globals] Prevent redundant oversized string checks in loop (#9001)
Co-authored-by: J. Nick Koston <nick+github@koston.org>
2025-06-09 11:19:24 -05:00
J. Nick Koston
b7ca6e087a Add LWIP optimization options to reduce flash usage (#8946)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-06-09 06:08:10 +00:00
pseud0sphere
368a0eea8a Change RP2040 PIO SK6812 timings (#9020) 2025-06-09 17:46:27 +12:00
Keith Burzinski
99c368fe62 [tsl2561, tsl2591] Shorten log messages (#9034) 2025-06-09 17:45:17 +12:00
Keith Burzinski
ff406f8e11 [max7219digit, servo, tsl2591] ESP_LOGCONFIG call reduction (Extend #9026) (#9033) 2025-06-09 04:19:20 +00:00
Edward Firmo
b98165e077 [nextion] Use safe restart to properly handle globals and restart logging (#9010) 2025-06-08 21:10:36 -05:00
Clyde Stubbs
e2a9cced94 [psram] Add P4 support (#8545)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: clydeps <U5yx99dok9>
2025-06-09 01:07:54 +00:00
Jon Krause
cdae06e571 Force socket ready when high frequency looping (#9032) 2025-06-09 00:40:25 +00:00
J. Nick Koston
c0b05ada1a Reduce ESP_LOGCONFIG calls (#9026) 2025-06-09 00:02:30 +00:00
esphomebot
80dddb4cae Update webserver local assets to 20250608-225410 (#9030) 2025-06-08 23:41:35 +00:00
J. Nick Koston
245c89a6c1 Disable ruff rule UP038 (#9029) 2025-06-08 23:15:46 +00:00
Clyde Stubbs
4d044d4ac9 [config] Clean build on ESP-IDF when component/platform combos change (#9028) 2025-06-09 08:39:02 +10:00
J. Nick Koston
9cc2a04d54 Implement proper API connection teardown before deep sleep/reboot (#9008) 2025-06-09 10:29:26 +12:00
dependabot[bot]
50cdec19dd Bump aioesphomeapi from 32.1.0 to 32.2.0 (#9025)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-08 16:28:39 +00:00
dependabot[bot]
6d587278bd Bump aioesphomeapi from 32.0.0 to 32.1.0 (#9024)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-08 15:26:04 +00:00
Keith Burzinski
dde63e7459 [esp32] Add config vars for compiler (#9023) 2025-06-08 07:38:15 -05:00
Keith Burzinski
8894f5030a [qwiic_pir] Clean-up, shorten some log messages (#8951) 2025-06-07 22:44:35 -05:00
Clyde Stubbs
9e862b8b53 [list-components.py] Only add platforms that are actually platforms. (#9005) 2025-06-07 21:25:24 -05:00
dependabot[bot]
24d4ada841 Bump ruamel-yaml from 0.18.12 to 0.18.13 (#9018)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-07 13:48:41 -05:00
dependabot[bot]
b1a8887548 Bump ruff from 0.11.11 to 0.11.13 (#9017)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-07 13:47:51 -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
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
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
764 changed files with 15230 additions and 6631 deletions

View File

@@ -49,7 +49,7 @@ jobs:
with:
python-version: "3.10"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.10.0
uses: docker/setup-buildx-action@v3.11.1
- name: Set TAG
run: |

View File

@@ -99,7 +99,7 @@ jobs:
python-version: "3.10"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.10.0
uses: docker/setup-buildx-action@v3.11.1
- name: Log in to docker hub
uses: docker/login-action@v3.4.0
@@ -178,7 +178,7 @@ jobs:
merge-multiple: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.10.0
uses: docker/setup-buildx-action@v3.11.1
- name: Log in to docker hub
if: matrix.registry == 'dockerhub'

View File

@@ -4,7 +4,7 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.11.10
rev: v0.12.0
hooks:
# Run the linter.
- id: ruff
@@ -12,7 +12,7 @@ repos:
# Run the formatter.
- id: ruff-format
- repo: https://github.com/PyCQA/flake8
rev: 7.2.0
rev: 7.3.0
hooks:
- id: flake8
additional_dependencies:

View File

@@ -150,6 +150,7 @@ esphome/components/esp32_improv/* @jesserockz
esphome/components/esp32_rmt/* @jesserockz
esphome/components/esp32_rmt_led_strip/* @jesserockz
esphome/components/esp8266/* @esphome/core
esphome/components/esp_ldo/* @clydebarrow
esphome/components/ethernet_info/* @gtjadsonsantos
esphome/components/event/* @nohat
esphome/components/event_emitter/* @Rapsssito
@@ -321,6 +322,8 @@ esphome/components/number/* @esphome/core
esphome/components/one_wire/* @ssieb
esphome/components/online_image/* @clydebarrow @guillempages
esphome/components/opentherm/* @olegtarasov
esphome/components/openthread/* @mrene
esphome/components/opt3001/* @ccutrer
esphome/components/ota/* @esphome/core
esphome/components/output/* @esphome/core
esphome/components/packet_transport/* @clydebarrow
@@ -518,6 +521,7 @@ esphome/components/xiaomi_lywsd03mmc/* @ahpohl
esphome/components/xiaomi_mhoc303/* @drug123
esphome/components/xiaomi_mhoc401/* @vevsvevs
esphome/components/xiaomi_rtcgq02lm/* @jesserockz
esphome/components/xiaomi_xmwsdj04mmc/* @medusalix
esphome/components/xl9535/* @mreditor97
esphome/components/xpt2046/touchscreen/* @nielsnl68 @numo68
esphome/components/xxtea/* @clydebarrow

View File

@@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 2025.6.0-dev
PROJECT_NUMBER = 2025.7.0-dev
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a

View File

@@ -134,6 +134,7 @@ def get_port_type(port):
def run_miniterm(config, port, args):
from aioesphomeapi import LogParser
import serial
from esphome import platformio_api
@@ -158,6 +159,7 @@ def run_miniterm(config, port, args):
ser.dtr = False
ser.rts = False
parser = LogParser()
tries = 0
while tries < 5:
try:
@@ -174,8 +176,7 @@ def run_miniterm(config, port, args):
.decode("utf8", "backslashreplace")
)
time_str = datetime.now().time().strftime("[%H:%M:%S]")
message = time_str + line
safe_print(message)
safe_print(parser.parse_line(line, time_str))
backtrace_state = platformio_api.process_stacktrace(
config, line, backtrace_state=backtrace_state

View File

@@ -22,6 +22,7 @@ from esphome.cpp_generator import ( # noqa: F401
TemplateArguments,
add,
add_build_flag,
add_build_unflag,
add_define,
add_global,
add_library,
@@ -34,6 +35,7 @@ from esphome.cpp_generator import ( # noqa: F401
process_lambda,
progmem_array,
safe_exp,
set_cpp_standard,
statement,
static_const_array,
templatable,

View File

@@ -40,9 +40,11 @@ void AbsoluteHumidityComponent::dump_config() {
break;
}
ESP_LOGCONFIG(TAG, "Sources");
ESP_LOGCONFIG(TAG, " Temperature: '%s'", this->temperature_sensor_->get_name().c_str());
ESP_LOGCONFIG(TAG, " Relative Humidity: '%s'", this->humidity_sensor_->get_name().c_str());
ESP_LOGCONFIG(TAG,
"Sources\n"
" Temperature: '%s'\n"
" Relative Humidity: '%s'",
this->temperature_sensor_->get_name().c_str(), this->humidity_sensor_->get_name().c_str());
}
float AbsoluteHumidityComponent::get_setup_priority() const { return setup_priority::DATA; }

View File

@@ -193,14 +193,13 @@ void AcDimmer::setup() {
setTimer1Callback(&timer_interrupt);
#endif
#ifdef USE_ESP32
// 80 Divider -> 1 count=1µs
dimmer_timer = timerBegin(0, 80, true);
timerAttachInterrupt(dimmer_timer, &AcDimmerDataStore::s_timer_intr, true);
// timer frequency of 1mhz
dimmer_timer = timerBegin(1000000);
timerAttachInterrupt(dimmer_timer, &AcDimmerDataStore::s_timer_intr);
// For ESP32, we can't use dynamic interval calculation because the timerX functions
// are not callable from ISR (placed in flash storage).
// Here we just use an interrupt firing every 50 µs.
timerAlarmWrite(dimmer_timer, 50, true);
timerAlarmEnable(dimmer_timer);
timerAlarm(dimmer_timer, 50, true, 0);
#endif
}
void AcDimmer::write_state(float state) {
@@ -214,8 +213,10 @@ void AcDimmer::dump_config() {
ESP_LOGCONFIG(TAG, "AcDimmer:");
LOG_PIN(" Output Pin: ", this->gate_pin_);
LOG_PIN(" Zero-Cross Pin: ", this->zero_cross_pin_);
ESP_LOGCONFIG(TAG, " Min Power: %.1f%%", this->store_.min_power / 10.0f);
ESP_LOGCONFIG(TAG, " Init with half cycle: %s", YESNO(this->init_with_half_cycle_));
ESP_LOGCONFIG(TAG,
" Min Power: %.1f%%\n"
" Init with half cycle: %s",
this->store_.min_power / 10.0f, YESNO(this->init_with_half_cycle_));
if (method_ == DIM_METHOD_LEADING_PULSE) {
ESP_LOGCONFIG(TAG, " Method: leading pulse");
} else if (method_ == DIM_METHOD_LEADING) {

View File

@@ -77,8 +77,10 @@ void ADCSensor::dump_config() {
break;
}
}
ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_);
ESP_LOGCONFIG(TAG, " Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
ESP_LOGCONFIG(TAG,
" Samples: %i\n"
" Sampling mode: %s",
this->sample_count_, LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
LOG_UPDATE_INTERVAL(this);
}

View File

@@ -30,8 +30,10 @@ void ADCSensor::dump_config() {
#else
LOG_PIN(" Pin: ", this->pin_);
#endif // USE_ADC_SENSOR_VCC
ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_);
ESP_LOGCONFIG(TAG, " Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
ESP_LOGCONFIG(TAG,
" Samples: %i\n"
" Sampling mode: %s",
this->sample_count_, LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
LOG_UPDATE_INTERVAL(this);
}

View File

@@ -22,8 +22,10 @@ void ADCSensor::dump_config() {
#else // USE_ADC_SENSOR_VCC
LOG_PIN(" Pin: ", this->pin_);
#endif // USE_ADC_SENSOR_VCC
ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_);
ESP_LOGCONFIG(TAG, " Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
ESP_LOGCONFIG(TAG,
" Samples: %i\n"
" Sampling mode: %s",
this->sample_count_, LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
LOG_UPDATE_INTERVAL(this);
}

View File

@@ -33,8 +33,10 @@ void ADCSensor::dump_config() {
LOG_PIN(" Pin: ", this->pin_);
#endif // USE_ADC_SENSOR_VCC
}
ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_);
ESP_LOGCONFIG(TAG, " Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
ESP_LOGCONFIG(TAG,
" Samples: %i\n"
" Sampling mode: %s",
this->sample_count_, LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
LOG_UPDATE_INTERVAL(this);
}

View File

@@ -177,11 +177,14 @@ void ADE7880::dump_config() {
LOG_SENSOR(" ", "Power Factor", this->channel_a_->power_factor);
LOG_SENSOR(" ", "Forward Active Energy", this->channel_a_->forward_active_energy);
LOG_SENSOR(" ", "Reverse Active Energy", this->channel_a_->reverse_active_energy);
ESP_LOGCONFIG(TAG, " Calibration:");
ESP_LOGCONFIG(TAG, " Current: %" PRId32, this->channel_a_->current_gain_calibration);
ESP_LOGCONFIG(TAG, " Voltage: %" PRId32, this->channel_a_->voltage_gain_calibration);
ESP_LOGCONFIG(TAG, " Power: %" PRId32, this->channel_a_->power_gain_calibration);
ESP_LOGCONFIG(TAG, " Phase Angle: %u", this->channel_a_->phase_angle_calibration);
ESP_LOGCONFIG(TAG,
" Calibration:\n"
" Current: %" PRId32 "\n"
" Voltage: %" PRId32 "\n"
" Power: %" PRId32 "\n"
" Phase Angle: %u",
this->channel_a_->current_gain_calibration, this->channel_a_->voltage_gain_calibration,
this->channel_a_->power_gain_calibration, this->channel_a_->phase_angle_calibration);
}
if (this->channel_b_ != nullptr) {
@@ -193,11 +196,14 @@ void ADE7880::dump_config() {
LOG_SENSOR(" ", "Power Factor", this->channel_b_->power_factor);
LOG_SENSOR(" ", "Forward Active Energy", this->channel_b_->forward_active_energy);
LOG_SENSOR(" ", "Reverse Active Energy", this->channel_b_->reverse_active_energy);
ESP_LOGCONFIG(TAG, " Calibration:");
ESP_LOGCONFIG(TAG, " Current: %" PRId32, this->channel_b_->current_gain_calibration);
ESP_LOGCONFIG(TAG, " Voltage: %" PRId32, this->channel_b_->voltage_gain_calibration);
ESP_LOGCONFIG(TAG, " Power: %" PRId32, this->channel_b_->power_gain_calibration);
ESP_LOGCONFIG(TAG, " Phase Angle: %u", this->channel_b_->phase_angle_calibration);
ESP_LOGCONFIG(TAG,
" Calibration:\n"
" Current: %" PRId32 "\n"
" Voltage: %" PRId32 "\n"
" Power: %" PRId32 "\n"
" Phase Angle: %u",
this->channel_b_->current_gain_calibration, this->channel_b_->voltage_gain_calibration,
this->channel_b_->power_gain_calibration, this->channel_b_->phase_angle_calibration);
}
if (this->channel_c_ != nullptr) {
@@ -209,18 +215,23 @@ void ADE7880::dump_config() {
LOG_SENSOR(" ", "Power Factor", this->channel_c_->power_factor);
LOG_SENSOR(" ", "Forward Active Energy", this->channel_c_->forward_active_energy);
LOG_SENSOR(" ", "Reverse Active Energy", this->channel_c_->reverse_active_energy);
ESP_LOGCONFIG(TAG, " Calibration:");
ESP_LOGCONFIG(TAG, " Current: %" PRId32, this->channel_c_->current_gain_calibration);
ESP_LOGCONFIG(TAG, " Voltage: %" PRId32, this->channel_c_->voltage_gain_calibration);
ESP_LOGCONFIG(TAG, " Power: %" PRId32, this->channel_c_->power_gain_calibration);
ESP_LOGCONFIG(TAG, " Phase Angle: %u", this->channel_c_->phase_angle_calibration);
ESP_LOGCONFIG(TAG,
" Calibration:\n"
" Current: %" PRId32 "\n"
" Voltage: %" PRId32 "\n"
" Power: %" PRId32 "\n"
" Phase Angle: %u",
this->channel_c_->current_gain_calibration, this->channel_c_->voltage_gain_calibration,
this->channel_c_->power_gain_calibration, this->channel_c_->phase_angle_calibration);
}
if (this->channel_n_ != nullptr) {
ESP_LOGCONFIG(TAG, " Neutral:");
LOG_SENSOR(" ", "Current", this->channel_n_->current);
ESP_LOGCONFIG(TAG, " Calibration:");
ESP_LOGCONFIG(TAG, " Current: %" PRId32, this->channel_n_->current_gain_calibration);
ESP_LOGCONFIG(TAG,
" Calibration:\n"
" Current: %" PRId32,
this->channel_n_->current_gain_calibration);
}
LOG_I2C_DEVICE(this);

View File

@@ -58,15 +58,18 @@ void ADE7953::dump_config() {
LOG_SENSOR(" ", "Active Power B Sensor", this->active_power_b_sensor_);
LOG_SENSOR(" ", "Rective Power A Sensor", this->reactive_power_a_sensor_);
LOG_SENSOR(" ", "Reactive Power B Sensor", this->reactive_power_b_sensor_);
ESP_LOGCONFIG(TAG, " USE_ACC_ENERGY_REGS: %d", this->use_acc_energy_regs_);
ESP_LOGCONFIG(TAG, " PGA_V_8: 0x%X", pga_v_);
ESP_LOGCONFIG(TAG, " PGA_IA_8: 0x%X", pga_ia_);
ESP_LOGCONFIG(TAG, " PGA_IB_8: 0x%X", pga_ib_);
ESP_LOGCONFIG(TAG, " VGAIN_32: 0x%08jX", (uintmax_t) vgain_);
ESP_LOGCONFIG(TAG, " AIGAIN_32: 0x%08jX", (uintmax_t) aigain_);
ESP_LOGCONFIG(TAG, " BIGAIN_32: 0x%08jX", (uintmax_t) bigain_);
ESP_LOGCONFIG(TAG, " AWGAIN_32: 0x%08jX", (uintmax_t) awgain_);
ESP_LOGCONFIG(TAG, " BWGAIN_32: 0x%08jX", (uintmax_t) bwgain_);
ESP_LOGCONFIG(TAG,
" USE_ACC_ENERGY_REGS: %d\n"
" PGA_V_8: 0x%X\n"
" PGA_IA_8: 0x%X\n"
" PGA_IB_8: 0x%X\n"
" VGAIN_32: 0x%08jX\n"
" AIGAIN_32: 0x%08jX\n"
" BIGAIN_32: 0x%08jX\n"
" AWGAIN_32: 0x%08jX\n"
" BWGAIN_32: 0x%08jX",
this->use_acc_energy_regs_, pga_v_, pga_ia_, pga_ib_, (uintmax_t) vgain_, (uintmax_t) aigain_,
(uintmax_t) bigain_, (uintmax_t) awgain_, (uintmax_t) bwgain_);
}
#define ADE_PUBLISH_(name, val, factor) \

View File

@@ -1,6 +1,6 @@
#include "ade7953_i2c.h"
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace ade7953_i2c {

View File

@@ -1,6 +1,6 @@
#include "ade7953_spi.h"
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace ade7953_spi {

View File

@@ -1,4 +1,5 @@
#include "ads1118.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {

View File

@@ -1,4 +1,5 @@
#include "ags10.h"
#include "esphome/core/helpers.h"
#include <cinttypes>

View File

@@ -13,9 +13,9 @@
// results making successive requests; the current implementation makes 3 attempts with a delay of 30ms each time.
#include "aht10.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace aht10 {

View File

@@ -14,8 +14,8 @@ from esphome.const import (
CONF_WEB_SERVER,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@grahambrown11", "@hwstar"]
IS_PLATFORM_COMPONENT = True
@@ -149,6 +149,9 @@ _ALARM_CONTROL_PANEL_SCHEMA = (
)
_ALARM_CONTROL_PANEL_SCHEMA.add_extra(entity_duplicate_validator("alarm_control_panel"))
def alarm_control_panel_schema(
class_: MockObjClass,
*,
@@ -190,7 +193,7 @@ ALARM_CONTROL_PANEL_CONDITION_SCHEMA = maybe_simple_id(
async def setup_alarm_control_panel_core_(var, config):
await setup_entity(var, config)
await setup_entity(var, config, "alarm_control_panel")
for conf in config.get(CONF_ON_STATE, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf)
@@ -235,6 +238,7 @@ async def register_alarm_control_panel(var, config):
if not CORE.has_id(config[CONF_ID]):
var = cg.Pvariable(config[CONF_ID], var)
cg.add(cg.App.register_alarm_control_panel(var))
CORE.register_platform_component("alarm_control_panel", var)
await setup_alarm_control_panel_core_(var, config)

View File

@@ -1,7 +1,7 @@
#pragma once
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace am43 {

View File

@@ -12,8 +12,10 @@ using namespace esphome::cover;
void Am43Component::dump_config() {
LOG_COVER("", "AM43 Cover", this);
ESP_LOGCONFIG(TAG, " Device Pin: %d", this->pin_);
ESP_LOGCONFIG(TAG, " Invert Position: %d", (int) this->invert_position_);
ESP_LOGCONFIG(TAG,
" Device Pin: %d\n"
" Invert Position: %d",
this->pin_, (int) this->invert_position_);
}
void Am43Component::setup() {

View File

@@ -34,8 +34,10 @@ void AnalogThresholdBinarySensor::set_sensor(sensor::Sensor *analog_sensor) {
void AnalogThresholdBinarySensor::dump_config() {
LOG_BINARY_SENSOR("", "Analog Threshold Binary Sensor", this);
LOG_SENSOR(" ", "Sensor", this->sensor_);
ESP_LOGCONFIG(TAG, " Upper threshold: %.11f", this->upper_threshold_.value());
ESP_LOGCONFIG(TAG, " Lower threshold: %.11f", this->lower_threshold_.value());
ESP_LOGCONFIG(TAG,
" Upper threshold: %.11f\n"
" Lower threshold: %.11f",
this->upper_threshold_.value(), this->lower_threshold_.value());
}
} // namespace analog_threshold

View File

@@ -17,7 +17,11 @@ void Anova::setup() {
this->current_request_ = 0;
}
void Anova::loop() {}
void Anova::loop() {
// Parent BLEClientNode has a loop() method, but this component uses
// polling via update() and BLE callbacks so loop isn't needed
this->disable_loop();
}
void Anova::control(const ClimateCall &call) {
if (call.get_mode().has_value()) {

View File

@@ -108,9 +108,12 @@ void APDS9306::dump_config() {
}
}
ESP_LOGCONFIG(TAG, " Gain: %u", AMBIENT_LIGHT_GAIN_VALUES[this->gain_]);
ESP_LOGCONFIG(TAG, " Measurement rate: %u", MEASUREMENT_RATE_VALUES[this->measurement_rate_]);
ESP_LOGCONFIG(TAG, " Measurement Resolution/Bit width: %d", MEASUREMENT_BIT_WIDTH_VALUES[this->bit_width_]);
ESP_LOGCONFIG(TAG,
" Gain: %u\n"
" Measurement rate: %u\n"
" Measurement Resolution/Bit width: %d",
AMBIENT_LIGHT_GAIN_VALUES[this->gain_], MEASUREMENT_RATE_VALUES[this->measurement_rate_],
MEASUREMENT_BIT_WIDTH_VALUES[this->bit_width_]);
LOG_UPDATE_INTERVAL(this);
}

View File

@@ -188,6 +188,17 @@ message DeviceInfoRequest {
// Empty
}
message AreaInfo {
uint32 area_id = 1;
string name = 2;
}
message DeviceInfo {
uint32 device_id = 1;
string name = 2;
uint32 area_id = 3;
}
message DeviceInfoResponse {
option (id) = 10;
option (source) = SOURCE_SERVER;
@@ -236,6 +247,12 @@ message DeviceInfoResponse {
// Supports receiving and saving api encryption key
bool api_encryption_supported = 19;
repeated DeviceInfo devices = 20;
repeated AreaInfo areas = 21;
// Top-level area info to phase out suggested_area
AreaInfo area = 22;
}
message ListEntitiesRequest {
@@ -266,6 +283,7 @@ enum EntityCategory {
// ==================== BINARY SENSOR ====================
message ListEntitiesBinarySensorResponse {
option (id) = 12;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_BINARY_SENSOR";
@@ -279,9 +297,11 @@ message ListEntitiesBinarySensorResponse {
bool disabled_by_default = 7;
string icon = 8;
EntityCategory entity_category = 9;
uint32 device_id = 10;
}
message BinarySensorStateResponse {
option (id) = 21;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_BINARY_SENSOR";
option (no_delay) = true;
@@ -296,6 +316,7 @@ message BinarySensorStateResponse {
// ==================== COVER ====================
message ListEntitiesCoverResponse {
option (id) = 13;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_COVER";
@@ -312,6 +333,7 @@ message ListEntitiesCoverResponse {
string icon = 10;
EntityCategory entity_category = 11;
bool supports_stop = 12;
uint32 device_id = 13;
}
enum LegacyCoverState {
@@ -325,6 +347,7 @@ enum CoverOperation {
}
message CoverStateResponse {
option (id) = 22;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_COVER";
option (no_delay) = true;
@@ -367,6 +390,7 @@ message CoverCommandRequest {
// ==================== FAN ====================
message ListEntitiesFanResponse {
option (id) = 14;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_FAN";
@@ -383,6 +407,7 @@ message ListEntitiesFanResponse {
string icon = 10;
EntityCategory entity_category = 11;
repeated string supported_preset_modes = 12;
uint32 device_id = 13;
}
enum FanSpeed {
FAN_SPEED_LOW = 0;
@@ -395,6 +420,7 @@ enum FanDirection {
}
message FanStateResponse {
option (id) = 23;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_FAN";
option (no_delay) = true;
@@ -444,6 +470,7 @@ enum ColorMode {
}
message ListEntitiesLightResponse {
option (id) = 15;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_LIGHT";
@@ -464,9 +491,11 @@ message ListEntitiesLightResponse {
bool disabled_by_default = 13;
string icon = 14;
EntityCategory entity_category = 15;
uint32 device_id = 16;
}
message LightStateResponse {
option (id) = 24;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_LIGHT";
option (no_delay) = true;
@@ -536,6 +565,7 @@ enum SensorLastResetType {
message ListEntitiesSensorResponse {
option (id) = 16;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_SENSOR";
@@ -554,9 +584,11 @@ message ListEntitiesSensorResponse {
SensorLastResetType legacy_last_reset_type = 11;
bool disabled_by_default = 12;
EntityCategory entity_category = 13;
uint32 device_id = 14;
}
message SensorStateResponse {
option (id) = 25;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_SENSOR";
option (no_delay) = true;
@@ -571,6 +603,7 @@ message SensorStateResponse {
// ==================== SWITCH ====================
message ListEntitiesSwitchResponse {
option (id) = 17;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_SWITCH";
@@ -584,9 +617,11 @@ message ListEntitiesSwitchResponse {
bool disabled_by_default = 7;
EntityCategory entity_category = 8;
string device_class = 9;
uint32 device_id = 10;
}
message SwitchStateResponse {
option (id) = 26;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_SWITCH";
option (no_delay) = true;
@@ -607,6 +642,7 @@ message SwitchCommandRequest {
// ==================== TEXT SENSOR ====================
message ListEntitiesTextSensorResponse {
option (id) = 18;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_TEXT_SENSOR";
@@ -619,9 +655,11 @@ message ListEntitiesTextSensorResponse {
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
string device_class = 8;
uint32 device_id = 9;
}
message TextSensorStateResponse {
option (id) = 27;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_TEXT_SENSOR";
option (no_delay) = true;
@@ -789,6 +827,7 @@ message ExecuteServiceRequest {
// ==================== CAMERA ====================
message ListEntitiesCameraResponse {
option (id) = 43;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_ESP32_CAMERA";
@@ -799,6 +838,7 @@ message ListEntitiesCameraResponse {
bool disabled_by_default = 5;
string icon = 6;
EntityCategory entity_category = 7;
uint32 device_id = 8;
}
message CameraImageResponse {
@@ -869,6 +909,7 @@ enum ClimatePreset {
}
message ListEntitiesClimateResponse {
option (id) = 46;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_CLIMATE";
@@ -900,9 +941,11 @@ message ListEntitiesClimateResponse {
bool supports_target_humidity = 23;
float visual_min_humidity = 24;
float visual_max_humidity = 25;
uint32 device_id = 26;
}
message ClimateStateResponse {
option (id) = 47;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_CLIMATE";
option (no_delay) = true;
@@ -964,6 +1007,7 @@ enum NumberMode {
}
message ListEntitiesNumberResponse {
option (id) = 49;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_NUMBER";
@@ -981,9 +1025,11 @@ message ListEntitiesNumberResponse {
string unit_of_measurement = 11;
NumberMode mode = 12;
string device_class = 13;
uint32 device_id = 14;
}
message NumberStateResponse {
option (id) = 50;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_NUMBER";
option (no_delay) = true;
@@ -1007,6 +1053,7 @@ message NumberCommandRequest {
// ==================== SELECT ====================
message ListEntitiesSelectResponse {
option (id) = 52;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_SELECT";
@@ -1019,9 +1066,11 @@ message ListEntitiesSelectResponse {
repeated string options = 6;
bool disabled_by_default = 7;
EntityCategory entity_category = 8;
uint32 device_id = 9;
}
message SelectStateResponse {
option (id) = 53;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_SELECT";
option (no_delay) = true;
@@ -1045,6 +1094,7 @@ message SelectCommandRequest {
// ==================== SIREN ====================
message ListEntitiesSirenResponse {
option (id) = 55;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_SIREN";
@@ -1059,9 +1109,11 @@ message ListEntitiesSirenResponse {
bool supports_duration = 8;
bool supports_volume = 9;
EntityCategory entity_category = 10;
uint32 device_id = 11;
}
message SirenStateResponse {
option (id) = 56;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_SIREN";
option (no_delay) = true;
@@ -1102,6 +1154,7 @@ enum LockCommand {
}
message ListEntitiesLockResponse {
option (id) = 58;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_LOCK";
@@ -1120,9 +1173,11 @@ message ListEntitiesLockResponse {
// Not yet implemented:
string code_format = 11;
uint32 device_id = 12;
}
message LockStateResponse {
option (id) = 59;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_LOCK";
option (no_delay) = true;
@@ -1145,6 +1200,7 @@ message LockCommandRequest {
// ==================== BUTTON ====================
message ListEntitiesButtonResponse {
option (id) = 61;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_BUTTON";
@@ -1157,6 +1213,7 @@ message ListEntitiesButtonResponse {
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
string device_class = 8;
uint32 device_id = 9;
}
message ButtonCommandRequest {
option (id) = 62;
@@ -1196,6 +1253,7 @@ message MediaPlayerSupportedFormat {
}
message ListEntitiesMediaPlayerResponse {
option (id) = 63;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_MEDIA_PLAYER";
@@ -1211,9 +1269,12 @@ message ListEntitiesMediaPlayerResponse {
bool supports_pause = 8;
repeated MediaPlayerSupportedFormat supported_formats = 9;
uint32 device_id = 10;
}
message MediaPlayerStateResponse {
option (id) = 64;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_MEDIA_PLAYER";
option (no_delay) = true;
@@ -1615,6 +1676,7 @@ enum VoiceAssistantEvent {
VOICE_ASSISTANT_STT_VAD_END = 12;
VOICE_ASSISTANT_TTS_STREAM_START = 98;
VOICE_ASSISTANT_TTS_STREAM_END = 99;
VOICE_ASSISTANT_INTENT_PROGRESS = 100;
}
message VoiceAssistantEventData {
@@ -1735,6 +1797,7 @@ enum AlarmControlPanelStateCommand {
message ListEntitiesAlarmControlPanelResponse {
option (id) = 94;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_ALARM_CONTROL_PANEL";
@@ -1748,10 +1811,12 @@ message ListEntitiesAlarmControlPanelResponse {
uint32 supported_features = 8;
bool requires_code = 9;
bool requires_code_to_arm = 10;
uint32 device_id = 11;
}
message AlarmControlPanelStateResponse {
option (id) = 95;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_ALARM_CONTROL_PANEL";
option (no_delay) = true;
@@ -1776,6 +1841,7 @@ enum TextMode {
}
message ListEntitiesTextResponse {
option (id) = 97;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_TEXT";
@@ -1791,9 +1857,11 @@ message ListEntitiesTextResponse {
uint32 max_length = 9;
string pattern = 10;
TextMode mode = 11;
uint32 device_id = 12;
}
message TextStateResponse {
option (id) = 98;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_TEXT";
option (no_delay) = true;
@@ -1818,6 +1886,7 @@ message TextCommandRequest {
// ==================== DATETIME DATE ====================
message ListEntitiesDateResponse {
option (id) = 100;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_DATETIME_DATE";
@@ -1829,9 +1898,11 @@ message ListEntitiesDateResponse {
string icon = 5;
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
uint32 device_id = 8;
}
message DateStateResponse {
option (id) = 101;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_DATETIME_DATE";
option (no_delay) = true;
@@ -1859,6 +1930,7 @@ message DateCommandRequest {
// ==================== DATETIME TIME ====================
message ListEntitiesTimeResponse {
option (id) = 103;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_DATETIME_TIME";
@@ -1870,9 +1942,11 @@ message ListEntitiesTimeResponse {
string icon = 5;
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
uint32 device_id = 8;
}
message TimeStateResponse {
option (id) = 104;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_DATETIME_TIME";
option (no_delay) = true;
@@ -1900,6 +1974,7 @@ message TimeCommandRequest {
// ==================== EVENT ====================
message ListEntitiesEventResponse {
option (id) = 107;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_EVENT";
@@ -1914,9 +1989,11 @@ message ListEntitiesEventResponse {
string device_class = 8;
repeated string event_types = 9;
uint32 device_id = 10;
}
message EventResponse {
option (id) = 108;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_EVENT";
@@ -1927,6 +2004,7 @@ message EventResponse {
// ==================== VALVE ====================
message ListEntitiesValveResponse {
option (id) = 109;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_VALVE";
@@ -1943,6 +2021,7 @@ message ListEntitiesValveResponse {
bool assumed_state = 9;
bool supports_position = 10;
bool supports_stop = 11;
uint32 device_id = 12;
}
enum ValveOperation {
@@ -1952,6 +2031,7 @@ enum ValveOperation {
}
message ValveStateResponse {
option (id) = 110;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_VALVE";
option (no_delay) = true;
@@ -1976,6 +2056,7 @@ message ValveCommandRequest {
// ==================== DATETIME DATETIME ====================
message ListEntitiesDateTimeResponse {
option (id) = 112;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_DATETIME_DATETIME";
@@ -1987,9 +2068,11 @@ message ListEntitiesDateTimeResponse {
string icon = 5;
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
uint32 device_id = 8;
}
message DateTimeStateResponse {
option (id) = 113;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_DATETIME_DATETIME";
option (no_delay) = true;
@@ -2013,6 +2096,7 @@ message DateTimeCommandRequest {
// ==================== UPDATE ====================
message ListEntitiesUpdateResponse {
option (id) = 116;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_UPDATE";
@@ -2025,9 +2109,11 @@ message ListEntitiesUpdateResponse {
bool disabled_by_default = 6;
EntityCategory entity_category = 7;
string device_class = 8;
uint32 device_id = 9;
}
message UpdateStateResponse {
option (id) = 117;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_UPDATE";
option (no_delay) = true;

View File

@@ -28,8 +28,19 @@
namespace esphome {
namespace api {
// Read a maximum of 5 messages per loop iteration to prevent starving other components.
// This is a balance between API responsiveness and allowing other components to run.
// Since each message could contain multiple protobuf messages when using packet batching,
// this limits the number of messages processed, not the number of TCP packets.
static constexpr uint8_t MAX_MESSAGES_PER_LOOP = 5;
static constexpr uint8_t MAX_PING_RETRIES = 60;
static constexpr uint16_t PING_RETRY_INTERVAL = 1000;
static constexpr uint32_t KEEPALIVE_DISCONNECT_TIMEOUT = (KEEPALIVE_TIMEOUT_MS * 5) / 2;
static const char *const TAG = "api.connection";
#ifdef USE_ESP32_CAMERA
static const int ESP32_CAMERA_STOP_STREAM = 5000;
#endif
APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *parent)
: parent_(parent), initial_state_iterator_(this), list_entities_iterator_(this) {
@@ -54,15 +65,11 @@ uint32_t APIConnection::get_batch_delay_ms_() const { return this->parent_->get_
void APIConnection::start() {
this->last_traffic_ = App.get_loop_component_start_time();
// Set next_ping_retry_ to prevent immediate ping
// This ensures the first ping happens after the keepalive period
this->next_ping_retry_ = this->last_traffic_ + KEEPALIVE_TIMEOUT_MS;
APIError err = this->helper_->init();
if (err != APIError::OK) {
on_fatal_error();
ESP_LOGW(TAG, "%s: Helper init failed: %s errno=%d", this->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();
@@ -84,16 +91,6 @@ APIConnection::~APIConnection() {
}
void APIConnection::loop() {
if (this->remove_)
return;
if (!network::is_connected()) {
// when network is disconnected force disconnect immediately
// don't wait for timeout
this->on_fatal_error();
ESP_LOGW(TAG, "%s: Network unavailable; disconnecting", this->client_combined_info_.c_str());
return;
}
if (this->next_close_) {
// requested a disconnect
this->helper_->close();
@@ -104,77 +101,73 @@ 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->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;
}
const uint32_t now = App.get_loop_component_start_time();
// Check if socket has data ready before attempting to read
if (this->helper_->is_socket_ready()) {
ReadPacketBuffer buffer;
err = this->helper_->read_packet(&buffer);
if (err == APIError::WOULD_BLOCK) {
// pass
} else if (err != APIError::OK) {
on_fatal_error();
if (err == APIError::SOCKET_READ_FAILED && errno == ECONNRESET) {
ESP_LOGW(TAG, "%s: Connection reset", this->client_combined_info_.c_str());
} else if (err == APIError::CONNECTION_CLOSED) {
ESP_LOGW(TAG, "%s: Connection closed", this->client_combined_info_.c_str());
} else {
ESP_LOGW(TAG, "%s: Reading failed: %s errno=%d", this->client_combined_info_.c_str(), api_error_to_str(err),
errno);
}
return;
} else {
this->last_traffic_ = App.get_loop_component_start_time();
// read a packet
if (buffer.data_len > 0) {
this->read_message(buffer.data_len, buffer.type, &buffer.container[buffer.data_offset]);
} else {
this->read_message(0, buffer.type, nullptr);
}
if (this->remove_)
// Read up to MAX_MESSAGES_PER_LOOP messages per loop to improve throughput
for (uint8_t message_count = 0; message_count < MAX_MESSAGES_PER_LOOP; message_count++) {
ReadPacketBuffer buffer;
err = this->helper_->read_packet(&buffer);
if (err == APIError::WOULD_BLOCK) {
// No more data available
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);
}
return;
} else {
this->last_traffic_ = now;
// read a packet
if (buffer.data_len > 0) {
this->read_message(buffer.data_len, buffer.type, &buffer.container[buffer.data_offset]);
} else {
this->read_message(0, buffer.type, nullptr);
}
if (this->remove_)
return;
}
}
}
// Process deferred batch if scheduled
if (this->deferred_batch_.batch_scheduled &&
App.get_loop_component_start_time() - this->deferred_batch_.batch_start_time >= this->get_batch_delay_ms_()) {
now - this->deferred_batch_.batch_start_time >= this->get_batch_delay_ms_()) {
this->process_batch_();
}
if (!this->list_entities_iterator_.completed())
if (!this->list_entities_iterator_.completed()) {
this->list_entities_iterator_.advance();
if (!this->initial_state_iterator_.completed() && this->list_entities_iterator_.completed())
} else if (!this->initial_state_iterator_.completed()) {
this->initial_state_iterator_.advance();
}
static uint8_t max_ping_retries = 60;
static uint16_t ping_retry_interval = 1000;
const uint32_t now = App.get_loop_component_start_time();
if (this->sent_ping_) {
// Disconnect if not responded within 2.5*keepalive
if (now - this->last_traffic_ > (KEEPALIVE_TIMEOUT_MS * 5) / 2) {
if (now - this->last_traffic_ > KEEPALIVE_DISCONNECT_TIMEOUT) {
on_fatal_error();
ESP_LOGW(TAG, "%s is unresponsive; disconnecting", this->client_combined_info_.c_str());
ESP_LOGW(TAG, "%s is unresponsive; disconnecting", this->get_client_combined_info().c_str());
}
} else if (now - this->last_traffic_ > KEEPALIVE_TIMEOUT_MS && now > this->next_ping_retry_) {
} else if (now - this->last_traffic_ > KEEPALIVE_TIMEOUT_MS) {
ESP_LOGVV(TAG, "Sending keepalive PING");
this->sent_ping_ = this->send_message(PingRequest());
if (!this->sent_ping_) {
this->next_ping_retry_ = now + ping_retry_interval;
this->ping_retries_++;
std::string warn_str = str_sprintf("%s: Sending keepalive failed %u time(s);",
this->client_combined_info_.c_str(), this->ping_retries_);
if (this->ping_retries_ >= max_ping_retries) {
on_fatal_error();
ESP_LOGE(TAG, "%s disconnecting", warn_str.c_str());
} else if (this->ping_retries_ >= 10) {
ESP_LOGW(TAG, "%s retrying in %u ms", warn_str.c_str(), ping_retry_interval);
} else {
ESP_LOGD(TAG, "%s retrying in %u ms", warn_str.c_str(), ping_retry_interval);
}
// If we can't send the ping request directly (tx_buffer full),
// schedule it at the front of the batch so it will be sent with priority
ESP_LOGVV(TAG, "Failed to send ping directly, scheduling at front of batch");
this->schedule_message_front_(nullptr, &APIConnection::try_send_ping_request, PingRequest::MESSAGE_TYPE);
this->sent_ping_ = true; // Mark as sent to avoid scheduling multiple pings
}
}
@@ -197,22 +190,20 @@ void APIConnection::loop() {
// bool done = 3;
buffer.encode_bool(3, done);
bool success = this->send_buffer(buffer, 44);
bool success = this->send_buffer(buffer, CameraImageResponse::MESSAGE_TYPE);
if (success) {
this->image_reader_.consume_data(to_send);
}
if (success && done) {
this->image_reader_.return_image();
if (done) {
this->image_reader_.return_image();
}
}
}
#endif
if (state_subs_at_ != -1) {
if (state_subs_at_ >= 0) {
const auto &subs = this->parent_->get_state_subs();
if (state_subs_at_ >= (int) subs.size()) {
state_subs_at_ = -1;
} else {
if (state_subs_at_ < static_cast<int>(subs.size())) {
auto &it = subs[state_subs_at_];
SubscribeHomeAssistantStateResponse resp;
resp.entity_id = it.entity_id;
@@ -221,6 +212,8 @@ void APIConnection::loop() {
if (this->send_message(resp)) {
state_subs_at_++;
}
} else {
state_subs_at_ = -1;
}
}
}
@@ -233,13 +226,14 @@ DisconnectResponse APIConnection::disconnect(const DisconnectRequest &msg) {
// remote initiated disconnect_client
// don't close yet, we still need to send the disconnect response
// close will happen on next loop
ESP_LOGD(TAG, "%s disconnected", this->client_combined_info_.c_str());
ESP_LOGD(TAG, "%s disconnected", this->get_client_combined_info().c_str());
this->next_close_ = true;
DisconnectResponse resp;
return resp;
}
void APIConnection::on_disconnect_response(const DisconnectResponse &value) {
// pass
this->helper_->close();
this->remove_ = true;
}
// Encodes a message to the buffer and returns the total number of bytes used,
@@ -247,32 +241,44 @@ void APIConnection::on_disconnect_response(const DisconnectResponse &value) {
uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint16_t message_type, APIConnection *conn,
uint32_t remaining_size, bool is_single) {
// Calculate size
uint32_t size = 0;
msg.calculate_size(size);
uint32_t calculated_size = 0;
msg.calculate_size(calculated_size);
// Cache frame sizes to avoid repeated virtual calls
const uint8_t header_padding = conn->helper_->frame_header_padding();
const uint8_t footer_size = conn->helper_->frame_footer_size();
// Calculate total size with padding for buffer allocation
uint16_t total_size =
static_cast<uint16_t>(size) + conn->helper_->frame_header_padding() + conn->helper_->frame_footer_size();
size_t total_calculated_size = calculated_size + header_padding + footer_size;
// Check if it fits
if (total_size > remaining_size) {
if (total_calculated_size > remaining_size) {
return 0; // Doesn't fit
}
// Allocate exact buffer space needed (just the payload, not the overhead)
ProtoWriteBuffer buffer =
is_single ? conn->allocate_single_message_buffer(size) : conn->allocate_batch_message_buffer(size);
// Allocate buffer space - pass payload size, allocation functions add header/footer space
ProtoWriteBuffer buffer = is_single ? conn->allocate_single_message_buffer(calculated_size)
: conn->allocate_batch_message_buffer(calculated_size);
// Get buffer size after allocation (which includes header padding)
std::vector<uint8_t> &shared_buf = conn->parent_->get_shared_buffer_ref();
size_t size_before_encode = shared_buf.size();
// Encode directly into buffer
msg.encode(buffer);
return total_size;
// Calculate actual encoded size (not including header that was already added)
size_t actual_payload_size = shared_buf.size() - size_before_encode;
// Return actual total size (header + actual payload + footer)
size_t actual_total_size = header_padding + actual_payload_size + footer_size;
// Verify that calculate_size() returned the correct value
assert(calculated_size == actual_payload_size);
return static_cast<uint16_t>(actual_total_size);
}
#ifdef USE_BINARY_SENSOR
bool APIConnection::send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor, bool state) {
return this->schedule_message_(binary_sensor, MessageCreator(state, BinarySensorStateResponse::MESSAGE_TYPE),
BinarySensorStateResponse::MESSAGE_TYPE);
}
bool APIConnection::send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor) {
return this->schedule_message_(binary_sensor, &APIConnection::try_send_binary_sensor_state,
BinarySensorStateResponse::MESSAGE_TYPE);
@@ -281,20 +287,15 @@ void APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_
this->schedule_message_(binary_sensor, &APIConnection::try_send_binary_sensor_info,
ListEntitiesBinarySensorResponse::MESSAGE_TYPE);
}
uint16_t APIConnection::try_send_binary_sensor_state_response(binary_sensor::BinarySensor *binary_sensor, bool state,
APIConnection *conn, uint32_t remaining_size,
bool is_single) {
BinarySensorStateResponse resp;
resp.state = state;
resp.missing_state = !binary_sensor->has_state();
resp.key = binary_sensor->get_object_id_hash();
return encode_message_to_buffer(resp, BinarySensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
uint16_t APIConnection::try_send_binary_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *binary_sensor = static_cast<binary_sensor::BinarySensor *>(entity);
return try_send_binary_sensor_state_response(binary_sensor, binary_sensor->state, conn, remaining_size, is_single);
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);
}
uint16_t APIConnection::try_send_binary_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -327,7 +328,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);
msg.key = cover->get_object_id_hash();
fill_entity_state_base(cover, msg);
return encode_message_to_buffer(msg, CoverStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
uint16_t APIConnection::try_send_cover_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -395,7 +396,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;
msg.key = fan->get_object_id_hash();
fill_entity_state_base(fan, msg);
return encode_message_to_buffer(msg, FanStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
uint16_t APIConnection::try_send_fan_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -462,7 +463,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();
resp.key = light->get_object_id_hash();
fill_entity_state_base(light, resp);
return encode_message_to_buffer(resp, LightStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
uint16_t APIConnection::try_send_light_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -531,29 +532,21 @@ void APIConnection::light_command(const LightCommandRequest &msg) {
#endif
#ifdef USE_SENSOR
bool APIConnection::send_sensor_state(sensor::Sensor *sensor, float state) {
return this->schedule_message_(sensor, MessageCreator(state, SensorStateResponse::MESSAGE_TYPE),
SensorStateResponse::MESSAGE_TYPE);
}
bool APIConnection::send_sensor_state(sensor::Sensor *sensor) {
return this->schedule_message_(sensor, &APIConnection::try_send_sensor_state, SensorStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_sensor_info(sensor::Sensor *sensor) {
this->schedule_message_(sensor, &APIConnection::try_send_sensor_info, ListEntitiesSensorResponse::MESSAGE_TYPE);
}
uint16_t APIConnection::try_send_sensor_state_response(sensor::Sensor *sensor, float state, APIConnection *conn,
uint32_t remaining_size, bool is_single) {
SensorStateResponse resp;
resp.state = state;
resp.missing_state = !sensor->has_state();
resp.key = sensor->get_object_id_hash();
return encode_message_to_buffer(resp, SensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
uint16_t APIConnection::try_send_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *sensor = static_cast<sensor::Sensor *>(entity);
return try_send_sensor_state_response(sensor, sensor->state, conn, remaining_size, is_single);
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);
}
uint16_t APIConnection::try_send_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -574,28 +567,20 @@ uint16_t APIConnection::try_send_sensor_info(EntityBase *entity, APIConnection *
#endif
#ifdef USE_SWITCH
bool APIConnection::send_switch_state(switch_::Switch *a_switch, bool state) {
return this->schedule_message_(a_switch, MessageCreator(state, SwitchStateResponse::MESSAGE_TYPE),
SwitchStateResponse::MESSAGE_TYPE);
}
bool APIConnection::send_switch_state(switch_::Switch *a_switch) {
return this->schedule_message_(a_switch, &APIConnection::try_send_switch_state, SwitchStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_switch_info(switch_::Switch *a_switch) {
this->schedule_message_(a_switch, &APIConnection::try_send_switch_info, ListEntitiesSwitchResponse::MESSAGE_TYPE);
}
uint16_t APIConnection::try_send_switch_state_response(switch_::Switch *a_switch, bool state, APIConnection *conn,
uint32_t remaining_size, bool is_single) {
SwitchStateResponse resp;
resp.state = state;
resp.key = a_switch->get_object_id_hash();
return encode_message_to_buffer(resp, SwitchStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
uint16_t APIConnection::try_send_switch_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *a_switch = static_cast<switch_::Switch *>(entity);
return try_send_switch_state_response(a_switch, a_switch->state, conn, remaining_size, is_single);
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);
}
uint16_t APIConnection::try_send_switch_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -622,10 +607,6 @@ void APIConnection::switch_command(const SwitchCommandRequest &msg) {
#endif
#ifdef USE_TEXT_SENSOR
bool APIConnection::send_text_sensor_state(text_sensor::TextSensor *text_sensor, const std::string &state) {
return this->schedule_message_(text_sensor, MessageCreator(state, TextSensorStateResponse::MESSAGE_TYPE),
TextSensorStateResponse::MESSAGE_TYPE);
}
bool APIConnection::send_text_sensor_state(text_sensor::TextSensor *text_sensor) {
return this->schedule_message_(text_sensor, &APIConnection::try_send_text_sensor_state,
TextSensorStateResponse::MESSAGE_TYPE);
@@ -634,20 +615,15 @@ void APIConnection::send_text_sensor_info(text_sensor::TextSensor *text_sensor)
this->schedule_message_(text_sensor, &APIConnection::try_send_text_sensor_info,
ListEntitiesTextSensorResponse::MESSAGE_TYPE);
}
uint16_t APIConnection::try_send_text_sensor_state_response(text_sensor::TextSensor *text_sensor,
const std::string &state, APIConnection *conn,
uint32_t remaining_size, bool is_single) {
TextSensorStateResponse resp;
resp.state = state;
resp.missing_state = !text_sensor->has_state();
resp.key = text_sensor->get_object_id_hash();
return encode_message_to_buffer(resp, TextSensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
uint16_t APIConnection::try_send_text_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *text_sensor = static_cast<text_sensor::TextSensor *>(entity);
return try_send_text_sensor_state_response(text_sensor, text_sensor->get_state(), conn, remaining_size, is_single);
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);
}
uint16_t APIConnection::try_send_text_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
@@ -670,7 +646,7 @@ uint16_t APIConnection::try_send_climate_state(EntityBase *entity, APIConnection
bool is_single) {
auto *climate = static_cast<climate::Climate *>(entity);
ClimateStateResponse resp;
resp.key = climate->get_object_id_hash();
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);
@@ -766,29 +742,21 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) {
#endif
#ifdef USE_NUMBER
bool APIConnection::send_number_state(number::Number *number, float state) {
return this->schedule_message_(number, MessageCreator(state, NumberStateResponse::MESSAGE_TYPE),
NumberStateResponse::MESSAGE_TYPE);
}
bool APIConnection::send_number_state(number::Number *number) {
return this->schedule_message_(number, &APIConnection::try_send_number_state, NumberStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_number_info(number::Number *number) {
this->schedule_message_(number, &APIConnection::try_send_number_info, ListEntitiesNumberResponse::MESSAGE_TYPE);
}
uint16_t APIConnection::try_send_number_state_response(number::Number *number, float state, APIConnection *conn,
uint32_t remaining_size, bool is_single) {
NumberStateResponse resp;
resp.state = state;
resp.missing_state = !number->has_state();
resp.key = number->get_object_id_hash();
return encode_message_to_buffer(resp, NumberStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
uint16_t APIConnection::try_send_number_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *number = static_cast<number::Number *>(entity);
return try_send_number_state_response(number, number->state, conn, remaining_size, is_single);
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);
}
uint16_t APIConnection::try_send_number_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -828,7 +796,7 @@ uint16_t APIConnection::try_send_date_state(EntityBase *entity, APIConnection *c
resp.year = date->year;
resp.month = date->month;
resp.day = date->day;
resp.key = date->get_object_id_hash();
fill_entity_state_base(date, resp);
return encode_message_to_buffer(resp, DateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::send_date_info(datetime::DateEntity *date) {
@@ -865,7 +833,7 @@ uint16_t APIConnection::try_send_time_state(EntityBase *entity, APIConnection *c
resp.hour = time->hour;
resp.minute = time->minute;
resp.second = time->second;
resp.key = time->get_object_id_hash();
fill_entity_state_base(time, resp);
return encode_message_to_buffer(resp, TimeStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::send_time_info(datetime::TimeEntity *time) {
@@ -904,7 +872,7 @@ uint16_t APIConnection::try_send_datetime_state(EntityBase *entity, APIConnectio
ESPTime state = datetime->state_as_esptime();
resp.epoch_seconds = state.timestamp;
}
resp.key = datetime->get_object_id_hash();
fill_entity_state_base(datetime, resp);
return encode_message_to_buffer(resp, DateTimeStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::send_datetime_info(datetime::DateTimeEntity *datetime) {
@@ -930,29 +898,21 @@ void APIConnection::datetime_command(const DateTimeCommandRequest &msg) {
#endif
#ifdef USE_TEXT
bool APIConnection::send_text_state(text::Text *text, const std::string &state) {
return this->schedule_message_(text, MessageCreator(state, TextStateResponse::MESSAGE_TYPE),
TextStateResponse::MESSAGE_TYPE);
}
bool APIConnection::send_text_state(text::Text *text) {
return this->schedule_message_(text, &APIConnection::try_send_text_state, TextStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_text_info(text::Text *text) {
this->schedule_message_(text, &APIConnection::try_send_text_info, ListEntitiesTextResponse::MESSAGE_TYPE);
}
uint16_t APIConnection::try_send_text_state_response(text::Text *text, const std::string &state, APIConnection *conn,
uint32_t remaining_size, bool is_single) {
TextStateResponse resp;
resp.state = state;
resp.missing_state = !text->has_state();
resp.key = text->get_object_id_hash();
return encode_message_to_buffer(resp, TextStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
uint16_t APIConnection::try_send_text_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *text = static_cast<text::Text *>(entity);
return try_send_text_state_response(text, text->state, conn, remaining_size, is_single);
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);
}
uint16_t APIConnection::try_send_text_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -979,29 +939,21 @@ void APIConnection::text_command(const TextCommandRequest &msg) {
#endif
#ifdef USE_SELECT
bool APIConnection::send_select_state(select::Select *select, const std::string &state) {
return this->schedule_message_(select, MessageCreator(state, SelectStateResponse::MESSAGE_TYPE),
SelectStateResponse::MESSAGE_TYPE);
}
bool APIConnection::send_select_state(select::Select *select) {
return this->schedule_message_(select, &APIConnection::try_send_select_state, SelectStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_select_info(select::Select *select) {
this->schedule_message_(select, &APIConnection::try_send_select_info, ListEntitiesSelectResponse::MESSAGE_TYPE);
}
uint16_t APIConnection::try_send_select_state_response(select::Select *select, const std::string &state,
APIConnection *conn, uint32_t remaining_size, bool is_single) {
SelectStateResponse resp;
resp.state = state;
resp.missing_state = !select->has_state();
resp.key = select->get_object_id_hash();
return encode_message_to_buffer(resp, SelectStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
uint16_t APIConnection::try_send_select_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *select = static_cast<select::Select *>(entity);
return try_send_select_state_response(select, select->state, conn, remaining_size, is_single);
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);
}
uint16_t APIConnection::try_send_select_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -1048,28 +1000,20 @@ void esphome::api::APIConnection::button_command(const ButtonCommandRequest &msg
#endif
#ifdef USE_LOCK
bool APIConnection::send_lock_state(lock::Lock *a_lock, lock::LockState state) {
return this->schedule_message_(a_lock, MessageCreator(state, LockStateResponse::MESSAGE_TYPE),
LockStateResponse::MESSAGE_TYPE);
}
bool APIConnection::send_lock_state(lock::Lock *a_lock) {
return this->schedule_message_(a_lock, &APIConnection::try_send_lock_state, LockStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_lock_info(lock::Lock *a_lock) {
this->schedule_message_(a_lock, &APIConnection::try_send_lock_info, ListEntitiesLockResponse::MESSAGE_TYPE);
}
uint16_t APIConnection::try_send_lock_state_response(lock::Lock *a_lock, lock::LockState state, APIConnection *conn,
uint32_t remaining_size, bool is_single) {
LockStateResponse resp;
resp.state = static_cast<enums::LockState>(state);
resp.key = a_lock->get_object_id_hash();
return encode_message_to_buffer(resp, LockStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
uint16_t APIConnection::try_send_lock_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *a_lock = static_cast<lock::Lock *>(entity);
return try_send_lock_state_response(a_lock, a_lock->state, conn, remaining_size, is_single);
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);
}
uint16_t APIConnection::try_send_lock_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -1112,7 +1056,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);
resp.key = valve->get_object_id_hash();
fill_entity_state_base(valve, resp);
return encode_message_to_buffer(resp, ValveStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::send_valve_info(valve::Valve *valve) {
@@ -1160,7 +1104,7 @@ 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();
resp.key = media_player->get_object_id_hash();
fill_entity_state_base(media_player, resp);
return encode_message_to_buffer(resp, MediaPlayerStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::send_media_player_info(media_player::MediaPlayer *media_player) {
@@ -1424,7 +1368,7 @@ 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());
resp.key = a_alarm_control_panel->get_object_id_hash();
fill_entity_state_base(a_alarm_control_panel, resp);
return encode_message_to_buffer(resp, AlarmControlPanelStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
@@ -1479,7 +1423,7 @@ void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRe
#ifdef USE_EVENT
void APIConnection::send_event(event::Event *event, const std::string &event_type) {
this->schedule_message_(event, MessageCreator(event_type, EventResponse::MESSAGE_TYPE), EventResponse::MESSAGE_TYPE);
this->schedule_message_(event, MessageCreator(event_type), EventResponse::MESSAGE_TYPE);
}
void APIConnection::send_event_info(event::Event *event) {
this->schedule_message_(event, &APIConnection::try_send_event_info, ListEntitiesEventResponse::MESSAGE_TYPE);
@@ -1488,7 +1432,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;
resp.key = event->get_object_id_hash();
fill_entity_state_base(event, resp);
return encode_message_to_buffer(resp, EventResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
@@ -1526,7 +1470,7 @@ 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;
}
resp.key = update->get_object_id_hash();
fill_entity_state_base(update, resp);
return encode_message_to_buffer(resp, UpdateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::send_update_info(update::UpdateEntity *update) {
@@ -1587,14 +1531,13 @@ bool APIConnection::try_send_log_message(int level, const char *tag, const char
buffer.encode_string(3, line, line_length); // string message = 3
// SubscribeLogsResponse - 29
return this->send_buffer(buffer, 29);
return this->send_buffer(buffer, SubscribeLogsResponse::MESSAGE_TYPE);
}
HelloResponse APIConnection::hello(const HelloRequest &msg) {
this->client_info_ = msg.client_info;
this->client_peername_ = this->helper_->getpeername();
this->client_combined_info_ = this->client_info_ + " (" + this->client_peername_ + ")";
this->helper_->set_log_info(this->client_combined_info_);
this->helper_->set_log_info(this->get_client_combined_info());
this->client_api_version_major_ = msg.api_version_major;
this->client_api_version_minor_ = msg.api_version_minor;
ESP_LOGV(TAG, "Hello from client: '%s' | %s | API Version %" PRIu32 ".%" PRIu32, this->client_info_.c_str(),
@@ -1616,7 +1559,7 @@ ConnectResponse APIConnection::connect(const ConnectRequest &msg) {
// bool invalid_password = 1;
resp.invalid_password = !correct;
if (correct) {
ESP_LOGD(TAG, "%s connected", this->client_combined_info_.c_str());
ESP_LOGD(TAG, "%s connected", this->get_client_combined_info().c_str());
this->connection_state_ = ConnectionState::AUTHENTICATED;
this->parent_->get_client_connected_trigger()->trigger(this->client_info_, this->client_peername_);
#ifdef USE_HOMEASSISTANT_TIME
@@ -1669,6 +1612,23 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
#endif
#ifdef USE_API_NOISE
resp.api_encryption_supported = true;
#endif
#ifdef USE_DEVICES
for (auto const &device : App.get_devices()) {
DeviceInfo device_info;
device_info.device_id = device->get_device_id();
device_info.name = device->get_name();
device_info.area_id = device->get_area_id();
resp.devices.push_back(device_info);
}
#endif
#ifdef USE_AREAS
for (auto const &area : App.get_areas()) {
AreaInfo area_info;
area_info.area_id = area->get_area_id();
area_info.name = area->get_name();
resp.areas.push_back(area_info);
}
#endif
return resp;
}
@@ -1722,7 +1682,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->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;
}
@@ -1734,7 +1694,7 @@ bool APIConnection::try_to_clear_buffer(bool log_out_of_space) {
return false;
}
bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint16_t message_type) {
if (!this->try_to_clear_buffer(message_type != 29)) { // SubscribeLogsResponse
if (!this->try_to_clear_buffer(message_type != SubscribeLogsResponse::MESSAGE_TYPE)) { // SubscribeLogsResponse
return false;
}
@@ -1744,10 +1704,10 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint16_t message_type)
if (err != APIError::OK) {
on_fatal_error();
if (err == APIError::SOCKET_WRITE_FAILED && errno == ECONNRESET) {
ESP_LOGW(TAG, "%s: Connection reset", this->client_combined_info_.c_str());
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->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;
}
@@ -1756,11 +1716,11 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint16_t message_type)
}
void APIConnection::on_unauthenticated_access() {
this->on_fatal_error();
ESP_LOGD(TAG, "%s requested access without authentication", this->client_combined_info_.c_str());
ESP_LOGD(TAG, "%s requested 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->client_combined_info_.c_str());
ESP_LOGD(TAG, "%s requested access without full connection", this->get_client_combined_info().c_str());
}
void APIConnection::on_fatal_error() {
this->helper_->close();
@@ -1783,6 +1743,11 @@ void APIConnection::DeferredBatch::add_item(EntityBase *entity, MessageCreator c
items.emplace_back(entity, std::move(creator), message_type);
}
void APIConnection::DeferredBatch::add_item_front(EntityBase *entity, MessageCreator creator, uint16_t message_type) {
// Insert at front for high priority messages (no deduplication check)
items.insert(items.begin(), BatchItem(entity, std::move(creator), message_type));
}
bool APIConnection::schedule_batch_() {
if (!this->deferred_batch_.batch_scheduled) {
this->deferred_batch_.batch_scheduled = true;
@@ -1818,7 +1783,8 @@ void APIConnection::process_batch_() {
const auto &item = this->deferred_batch_.items[0];
// Let the creator calculate size and encode if it fits
uint16_t payload_size = item.creator(item.entity, this, std::numeric_limits<uint16_t>::max(), true);
uint16_t payload_size =
item.creator(item.entity, this, std::numeric_limits<uint16_t>::max(), true, item.message_type);
if (payload_size > 0 &&
this->send_buffer(ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, item.message_type)) {
@@ -1856,7 +1822,7 @@ void APIConnection::process_batch_() {
this->batch_first_message_ = true;
size_t items_processed = 0;
uint32_t remaining_size = MAX_PACKET_SIZE;
uint16_t remaining_size = std::numeric_limits<uint16_t>::max();
// Track where each message's header padding begins in the buffer
// For plaintext: this is where the 6-byte header padding starts
@@ -1868,7 +1834,7 @@ void APIConnection::process_batch_() {
for (const auto &item : this->deferred_batch_.items) {
// Try to encode message
// The creator will calculate overhead to determine if the message fits
uint16_t payload_size = item.creator(item.entity, this, remaining_size, false);
uint16_t payload_size = item.creator(item.entity, this, remaining_size, false, item.message_type);
if (payload_size == 0) {
// Message won't fit, stop processing
@@ -1881,11 +1847,15 @@ void APIConnection::process_batch_() {
packet_info.emplace_back(item.message_type, current_offset, proto_payload_size);
// Update tracking variables
items_processed++;
// After first message, set remaining size to MAX_PACKET_SIZE to avoid fragmentation
if (items_processed == 1) {
remaining_size = MAX_PACKET_SIZE;
}
remaining_size -= payload_size;
// Calculate where the next message's header padding will start
// Current buffer size + footer space (that prepare_message_buffer will add for this message)
current_offset = this->parent_->get_shared_buffer_ref().size() + footer_size;
items_processed++;
}
if (items_processed == 0) {
@@ -1905,10 +1875,10 @@ void APIConnection::process_batch_() {
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->client_combined_info_.c_str());
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->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);
}
}
@@ -1927,78 +1897,23 @@ void APIConnection::process_batch_() {
}
uint16_t APIConnection::MessageCreator::operator()(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) const {
switch (message_type_) {
case 0: // Function pointer
return data_.ptr(entity, conn, remaining_size, is_single);
#ifdef USE_BINARY_SENSOR
case BinarySensorStateResponse::MESSAGE_TYPE: {
auto *bs = static_cast<binary_sensor::BinarySensor *>(entity);
return APIConnection::try_send_binary_sensor_state_response(bs, data_.bool_value, conn, remaining_size,
is_single);
}
#endif
#ifdef USE_SENSOR
case SensorStateResponse::MESSAGE_TYPE: {
auto *s = static_cast<sensor::Sensor *>(entity);
return APIConnection::try_send_sensor_state_response(s, data_.float_value, conn, remaining_size, is_single);
}
#endif
#ifdef USE_SWITCH
case SwitchStateResponse::MESSAGE_TYPE: {
auto *sw = static_cast<switch_::Switch *>(entity);
return APIConnection::try_send_switch_state_response(sw, data_.bool_value, conn, remaining_size, is_single);
}
#endif
#ifdef USE_TEXT_SENSOR
case TextSensorStateResponse::MESSAGE_TYPE: {
auto *ts = static_cast<text_sensor::TextSensor *>(entity);
return APIConnection::try_send_text_sensor_state_response(ts, *data_.string_ptr, conn, remaining_size, is_single);
}
#endif
#ifdef USE_SELECT
case SelectStateResponse::MESSAGE_TYPE: {
auto *s = static_cast<select::Select *>(entity);
return APIConnection::try_send_select_state_response(s, *data_.string_ptr, conn, remaining_size, is_single);
}
#endif
#ifdef USE_NUMBER
case NumberStateResponse::MESSAGE_TYPE: {
auto *n = static_cast<number::Number *>(entity);
return APIConnection::try_send_number_state_response(n, data_.float_value, conn, remaining_size, is_single);
}
#endif
#ifdef USE_TEXT
case TextStateResponse::MESSAGE_TYPE: {
auto *t = static_cast<text::Text *>(entity);
return APIConnection::try_send_text_state_response(t, *data_.string_ptr, conn, remaining_size, is_single);
}
#endif
#ifdef USE_LOCK
case LockStateResponse::MESSAGE_TYPE: {
auto *l = static_cast<lock::Lock *>(entity);
return APIConnection::try_send_lock_state_response(l, data_.lock_value, conn, remaining_size, is_single);
}
#endif
bool is_single, uint16_t message_type) const {
if (has_tagged_string_ptr_()) {
// Handle string-based messages
switch (message_type) {
#ifdef USE_EVENT
case EventResponse::MESSAGE_TYPE: {
auto *e = static_cast<event::Event *>(entity);
return APIConnection::try_send_event_response(e, *data_.string_ptr, conn, remaining_size, is_single);
}
case EventResponse::MESSAGE_TYPE: {
auto *e = static_cast<event::Event *>(entity);
return APIConnection::try_send_event_response(e, *get_string_ptr_(), conn, remaining_size, is_single);
}
#endif
default:
// Should not happen, return 0 to indicate no message
return 0;
default:
// Should not happen, return 0 to indicate no message
return 0;
}
} else {
// Function pointer case
return data_.ptr(entity, conn, remaining_size, is_single);
}
}
@@ -2008,6 +1923,18 @@ uint16_t APIConnection::try_send_list_info_done(EntityBase *entity, APIConnectio
return encode_message_to_buffer(resp, ListEntitiesDoneResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
uint16_t APIConnection::try_send_disconnect_request(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
DisconnectRequest req;
return encode_message_to_buffer(req, DisconnectRequest::MESSAGE_TYPE, conn, remaining_size, is_single);
}
uint16_t APIConnection::try_send_ping_request(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
PingRequest req;
return encode_message_to_buffer(req, PingRequest::MESSAGE_TYPE, conn, remaining_size, is_single);
}
uint16_t APIConnection::get_estimated_message_size(uint16_t message_type) {
// Use generated ESTIMATED_SIZE constants from each message type
switch (message_type) {
@@ -2143,6 +2070,8 @@ uint16_t APIConnection::get_estimated_message_size(uint16_t message_type) {
return ListEntitiesServicesResponse::ESTIMATED_SIZE;
case ListEntitiesDoneResponse::MESSAGE_TYPE:
return ListEntitiesDoneResponse::ESTIMATED_SIZE;
case DisconnectRequest::MESSAGE_TYPE:
return DisconnectRequest::ESTIMATED_SIZE;
default:
// Fallback for unknown message types
return 24;

View File

@@ -33,7 +33,6 @@ class APIConnection : public APIServerConnection {
ListEntitiesDoneResponse::MESSAGE_TYPE);
}
#ifdef USE_BINARY_SENSOR
bool send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor, bool state);
bool send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor);
void send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor);
#endif
@@ -53,18 +52,15 @@ class APIConnection : public APIServerConnection {
void light_command(const LightCommandRequest &msg) override;
#endif
#ifdef USE_SENSOR
bool send_sensor_state(sensor::Sensor *sensor, float state);
bool send_sensor_state(sensor::Sensor *sensor);
void send_sensor_info(sensor::Sensor *sensor);
#endif
#ifdef USE_SWITCH
bool send_switch_state(switch_::Switch *a_switch, bool state);
bool send_switch_state(switch_::Switch *a_switch);
void send_switch_info(switch_::Switch *a_switch);
void switch_command(const SwitchCommandRequest &msg) override;
#endif
#ifdef USE_TEXT_SENSOR
bool send_text_sensor_state(text_sensor::TextSensor *text_sensor, const std::string &state);
bool send_text_sensor_state(text_sensor::TextSensor *text_sensor);
void send_text_sensor_info(text_sensor::TextSensor *text_sensor);
#endif
@@ -79,7 +75,6 @@ class APIConnection : public APIServerConnection {
void climate_command(const ClimateCommandRequest &msg) override;
#endif
#ifdef USE_NUMBER
bool send_number_state(number::Number *number, float state);
bool send_number_state(number::Number *number);
void send_number_info(number::Number *number);
void number_command(const NumberCommandRequest &msg) override;
@@ -100,13 +95,11 @@ class APIConnection : public APIServerConnection {
void datetime_command(const DateTimeCommandRequest &msg) override;
#endif
#ifdef USE_TEXT
bool send_text_state(text::Text *text, const std::string &state);
bool send_text_state(text::Text *text);
void send_text_info(text::Text *text);
void text_command(const TextCommandRequest &msg) override;
#endif
#ifdef USE_SELECT
bool send_select_state(select::Select *select, const std::string &state);
bool send_select_state(select::Select *select);
void send_select_info(select::Select *select);
void select_command(const SelectCommandRequest &msg) override;
@@ -116,7 +109,6 @@ class APIConnection : public APIServerConnection {
void button_command(const ButtonCommandRequest &msg) override;
#endif
#ifdef USE_LOCK
bool send_lock_state(lock::Lock *a_lock, lock::LockState state);
bool send_lock_state(lock::Lock *a_lock);
void send_lock_info(lock::Lock *a_lock);
void lock_command(const LockCommandRequest &msg) override;
@@ -193,7 +185,6 @@ class APIConnection : public APIServerConnection {
void on_disconnect_response(const DisconnectResponse &value) override;
void on_ping_response(const PingResponse &value) override {
// we initiated ping
this->ping_retries_ = 0;
this->sent_ping_ = false;
}
void on_home_assistant_state_response(const HomeAssistantStateResponse &msg) override;
@@ -248,8 +239,8 @@ class APIConnection : public APIServerConnection {
// - Header padding: space for protocol headers (7 bytes for Noise, 6 for Plaintext)
// - Footer: space for MAC (16 bytes for Noise, 0 for Plaintext)
shared_buf.reserve(reserve_size + header_padding + this->helper_->frame_footer_size());
// Insert header padding bytes so message encoding starts at the correct position
shared_buf.insert(shared_buf.begin(), header_padding, 0);
// Resize to add header padding so message encoding starts at the correct position
shared_buf.resize(header_padding);
return {&shared_buf};
}
@@ -257,47 +248,47 @@ class APIConnection : public APIServerConnection {
ProtoWriteBuffer prepare_message_buffer(uint16_t message_size, bool is_first_message) {
// Get reference to shared buffer (it maintains state between batch messages)
std::vector<uint8_t> &shared_buf = this->parent_->get_shared_buffer_ref();
size_t current_size = shared_buf.size();
if (is_first_message) {
// For first message, initialize buffer with header padding
uint8_t header_padding = this->helper_->frame_header_padding();
shared_buf.clear();
shared_buf.reserve(message_size + header_padding);
shared_buf.resize(header_padding);
// Fill header padding with zeros
std::fill(shared_buf.begin(), shared_buf.end(), 0);
} else {
// For subsequent messages, add footer space for previous message and header for this message
uint8_t footer_size = this->helper_->frame_footer_size();
uint8_t header_padding = this->helper_->frame_header_padding();
// Reserve additional space for everything
shared_buf.reserve(current_size + footer_size + header_padding + message_size);
// Single resize to add both footer and header padding
size_t new_size = current_size + footer_size + header_padding;
shared_buf.resize(new_size);
// Fill the newly added bytes with zeros (footer + header padding)
std::fill(shared_buf.begin() + current_size, shared_buf.end(), 0);
}
size_t current_size = shared_buf.size();
// Calculate padding to add:
// - First message: just header padding
// - Subsequent messages: footer for previous message + header padding for this message
size_t padding_to_add = is_first_message
? this->helper_->frame_header_padding()
: this->helper_->frame_header_padding() + this->helper_->frame_footer_size();
// Reserve space for padding + message
shared_buf.reserve(current_size + padding_to_add + message_size);
// Resize to add the padding bytes
shared_buf.resize(current_size + padding_to_add);
return {&shared_buf};
}
bool try_to_clear_buffer(bool log_out_of_space);
bool send_buffer(ProtoWriteBuffer buffer, uint16_t message_type) override;
std::string get_client_combined_info() const { return this->client_combined_info_; }
std::string get_client_combined_info() const {
if (this->client_info_ == this->client_peername_) {
// Before Hello message, both are the same (just IP:port)
return this->client_info_;
}
return this->client_info_ + " (" + this->client_peername_ + ")";
}
// Buffer allocator methods for batch processing
ProtoWriteBuffer allocate_single_message_buffer(uint16_t size);
ProtoWriteBuffer allocate_batch_message_buffer(uint16_t size);
protected:
// Helper function to fill common entity fields
template<typename ResponseT> static void fill_entity_info_base(esphome::EntityBase *entity, ResponseT &response) {
// 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();
@@ -309,6 +300,14 @@ class APIConnection : public APIServerConnection {
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();
}
// Non-template helper to encode any ProtoMessage
@@ -316,9 +315,7 @@ class APIConnection : public APIServerConnection {
uint32_t remaining_size, bool is_single);
#ifdef USE_BINARY_SENSOR
static uint16_t try_send_binary_sensor_state_response(binary_sensor::BinarySensor *binary_sensor, bool state,
APIConnection *conn, uint32_t remaining_size, bool is_single);
static uint16_t try_send_binary_sensor_state(EntityBase *binary_sensor, APIConnection *conn, uint32_t remaining_size,
static uint16_t try_send_binary_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_binary_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
@@ -338,24 +335,18 @@ class APIConnection : public APIServerConnection {
static uint16_t try_send_light_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
#endif
#ifdef USE_SENSOR
static uint16_t try_send_sensor_state_response(sensor::Sensor *sensor, float state, APIConnection *conn,
uint32_t remaining_size, bool is_single);
static uint16_t try_send_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
#endif
#ifdef USE_SWITCH
static uint16_t try_send_switch_state_response(switch_::Switch *a_switch, bool state, APIConnection *conn,
uint32_t remaining_size, bool is_single);
static uint16_t try_send_switch_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_switch_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
#endif
#ifdef USE_TEXT_SENSOR
static uint16_t try_send_text_sensor_state_response(text_sensor::TextSensor *text_sensor, const std::string &state,
APIConnection *conn, uint32_t remaining_size, bool is_single);
static uint16_t try_send_text_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_text_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -368,8 +359,6 @@ class APIConnection : public APIServerConnection {
bool is_single);
#endif
#ifdef USE_NUMBER
static uint16_t try_send_number_state_response(number::Number *number, float state, APIConnection *conn,
uint32_t remaining_size, bool is_single);
static uint16_t try_send_number_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_number_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -390,14 +379,10 @@ class APIConnection : public APIServerConnection {
bool is_single);
#endif
#ifdef USE_TEXT
static uint16_t try_send_text_state_response(text::Text *text, const std::string &state, APIConnection *conn,
uint32_t remaining_size, bool is_single);
static uint16_t try_send_text_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
static uint16_t try_send_text_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
#endif
#ifdef USE_SELECT
static uint16_t try_send_select_state_response(select::Select *select, const std::string &state, APIConnection *conn,
uint32_t remaining_size, bool is_single);
static uint16_t try_send_select_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_select_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -408,8 +393,6 @@ class APIConnection : public APIServerConnection {
bool is_single);
#endif
#ifdef USE_LOCK
static uint16_t try_send_lock_state_response(lock::Lock *a_lock, lock::LockState state, APIConnection *conn,
uint32_t remaining_size, bool is_single);
static uint16_t try_send_lock_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
static uint16_t try_send_lock_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
#endif
@@ -450,104 +433,108 @@ class APIConnection : public APIServerConnection {
static uint16_t try_send_list_info_done(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
// Method for DisconnectRequest batching
static uint16_t try_send_disconnect_request(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
// Helper function to get estimated message size for buffer pre-allocation
static uint16_t get_estimated_message_size(uint16_t message_type);
enum class ConnectionState {
// Batch message method for ping requests
static uint16_t try_send_ping_request(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
// Pointers first (4 bytes each, naturally aligned)
std::unique_ptr<APIFrameHelper> helper_;
APIServer *parent_;
// 4-byte aligned types
uint32_t last_traffic_;
int state_subs_at_ = -1;
// Strings (12 bytes each on 32-bit)
std::string client_info_;
std::string client_peername_;
// 2-byte aligned types
uint16_t client_api_version_major_{0};
uint16_t client_api_version_minor_{0};
// Group all 1-byte types together to minimize padding
enum class ConnectionState : uint8_t {
WAITING_FOR_HELLO,
CONNECTED,
AUTHENTICATED,
} connection_state_{ConnectionState::WAITING_FOR_HELLO};
uint8_t log_subscription_{ESPHOME_LOG_LEVEL_NONE};
bool remove_{false};
bool state_subscription_{false};
bool sent_ping_{false};
bool service_call_subscription_{false};
bool next_close_ = false;
// 7 bytes used, 1 byte padding
std::unique_ptr<APIFrameHelper> helper_;
std::string client_info_;
std::string client_peername_;
std::string client_combined_info_;
uint32_t client_api_version_major_{0};
uint32_t client_api_version_minor_{0};
// Larger objects at the end
InitialStateIterator initial_state_iterator_;
ListEntitiesIterator list_entities_iterator_;
#ifdef USE_ESP32_CAMERA
esp32_camera::CameraImageReader image_reader_;
#endif
bool state_subscription_{false};
int log_subscription_{ESPHOME_LOG_LEVEL_NONE};
uint32_t last_traffic_;
uint32_t next_ping_retry_{0};
uint8_t ping_retries_{0};
bool sent_ping_{false};
bool service_call_subscription_{false};
bool next_close_ = false;
APIServer *parent_;
InitialStateIterator initial_state_iterator_;
ListEntitiesIterator list_entities_iterator_;
int state_subs_at_ = -1;
// Function pointer type for message encoding
using MessageCreatorPtr = uint16_t (*)(EntityBase *, APIConnection *, uint32_t remaining_size, bool is_single);
// Optimized MessageCreator class using union dispatch
// Optimized MessageCreator class using tagged pointer
class MessageCreator {
// Ensure pointer alignment allows LSB tagging
static_assert(alignof(std::string *) > 1, "String pointer alignment must be > 1 for LSB tagging");
public:
// Constructor for function pointer (message_type = 0)
MessageCreator(MessageCreatorPtr ptr) : message_type_(0) { data_.ptr = ptr; }
// Constructor for bool state capture
MessageCreator(bool value, uint16_t msg_type) : message_type_(msg_type) { data_.bool_value = value; }
// Constructor for float state capture
MessageCreator(float value, uint16_t msg_type) : message_type_(msg_type) { data_.float_value = value; }
// Constructor for string state capture
MessageCreator(const std::string &value, uint16_t msg_type) : message_type_(msg_type) {
data_.string_ptr = new std::string(value);
// Constructor for function pointer
MessageCreator(MessageCreatorPtr ptr) {
// Function pointers are always aligned, so LSB is 0
data_.ptr = ptr;
}
#ifdef USE_LOCK
// Constructor for lock state capture
MessageCreator(lock::LockState value, uint16_t msg_type) : message_type_(msg_type) { data_.lock_value = value; }
#endif
// Constructor for string state capture
explicit MessageCreator(const std::string &str_value) {
// Allocate string and tag the pointer
auto *str = new std::string(str_value);
// Set LSB to 1 to indicate string pointer
data_.tagged = reinterpret_cast<uintptr_t>(str) | 1;
}
// Destructor
~MessageCreator() {
// Clean up string data for string-based message types
if (uses_string_data_()) {
delete data_.string_ptr;
if (has_tagged_string_ptr_()) {
delete get_string_ptr_();
}
}
// Copy constructor
MessageCreator(const MessageCreator &other) : message_type_(other.message_type_) {
if (message_type_ == 0) {
data_.ptr = other.data_.ptr;
} else if (uses_string_data_()) {
data_.string_ptr = new std::string(*other.data_.string_ptr);
MessageCreator(const MessageCreator &other) {
if (other.has_tagged_string_ptr_()) {
auto *str = new std::string(*other.get_string_ptr_());
data_.tagged = reinterpret_cast<uintptr_t>(str) | 1;
} else {
data_ = other.data_; // For POD types
data_ = other.data_;
}
}
// Move constructor
MessageCreator(MessageCreator &&other) noexcept : data_(other.data_), message_type_(other.message_type_) {
other.message_type_ = 0; // Reset other to function pointer type
other.data_.ptr = nullptr;
}
MessageCreator(MessageCreator &&other) noexcept : data_(other.data_) { other.data_.ptr = nullptr; }
// Assignment operators (needed for batch deduplication)
MessageCreator &operator=(const MessageCreator &other) {
if (this != &other) {
// Clean up current string data if needed
if (uses_string_data_()) {
delete data_.string_ptr;
if (has_tagged_string_ptr_()) {
delete get_string_ptr_();
}
// Copy new data
message_type_ = other.message_type_;
if (other.message_type_ == 0) {
data_.ptr = other.data_.ptr;
} else if (other.uses_string_data_()) {
data_.string_ptr = new std::string(*other.data_.string_ptr);
if (other.has_tagged_string_ptr_()) {
auto *str = new std::string(*other.get_string_ptr_());
data_.tagged = reinterpret_cast<uintptr_t>(str) | 1;
} else {
data_ = other.data_;
}
@@ -558,39 +545,35 @@ class APIConnection : public APIServerConnection {
MessageCreator &operator=(MessageCreator &&other) noexcept {
if (this != &other) {
// Clean up current string data if needed
if (uses_string_data_()) {
delete data_.string_ptr;
if (has_tagged_string_ptr_()) {
delete get_string_ptr_();
}
// Move data
message_type_ = other.message_type_;
data_ = other.data_;
// Reset other to safe state
other.message_type_ = 0;
other.data_.ptr = nullptr;
}
return *this;
}
// Call operator
uint16_t operator()(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single) const;
// Call operator - now accepts message_type as parameter
uint16_t operator()(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single,
uint16_t message_type) const;
private:
// Helper to check if this message type uses heap-allocated strings
bool uses_string_data_() const {
return message_type_ == TextSensorStateResponse::MESSAGE_TYPE ||
message_type_ == SelectStateResponse::MESSAGE_TYPE || message_type_ == TextStateResponse::MESSAGE_TYPE ||
message_type_ == EventResponse::MESSAGE_TYPE;
// Check if this contains a string pointer
bool has_tagged_string_ptr_() const { return (data_.tagged & 1) != 0; }
// Get the actual string pointer (clears the tag bit)
std::string *get_string_ptr_() const {
// NOLINTNEXTLINE(performance-no-int-to-ptr)
return reinterpret_cast<std::string *>(data_.tagged & ~uintptr_t(1));
}
union CreatorData {
MessageCreatorPtr ptr; // 8 bytes
bool bool_value; // 1 byte
float float_value; // 4 bytes
std::string *string_ptr; // 8 bytes
#ifdef USE_LOCK
lock::LockState lock_value; // 4 bytes
#endif
} data_; // 8 bytes
uint16_t message_type_; // 2 bytes (0 = function ptr, >0 = state capture)
union {
MessageCreatorPtr ptr;
uintptr_t tagged;
} data_; // 4 bytes on 32-bit
};
// Generic batching mechanism for both state updates and entity info
@@ -616,6 +599,8 @@ class APIConnection : public APIServerConnection {
// Add item to the batch
void add_item(EntityBase *entity, MessageCreator creator, uint16_t message_type);
// Add item to the front of the batch (for high priority messages like ping)
void add_item_front(EntityBase *entity, MessageCreator creator, uint16_t message_type);
void clear() {
items.clear();
batch_scheduled = false;
@@ -655,6 +640,12 @@ class APIConnection : public APIServerConnection {
bool schedule_message_(EntityBase *entity, MessageCreatorPtr function_ptr, uint16_t message_type) {
return schedule_message_(entity, MessageCreator(function_ptr), message_type);
}
// Helper function to schedule a high priority message at the front of the batch
bool schedule_message_front_(EntityBase *entity, MessageCreatorPtr function_ptr, uint16_t message_type) {
this->deferred_batch_.add_item_front(entity, MessageCreator(function_ptr), message_type);
return this->schedule_batch_();
}
};
} // namespace api

View File

@@ -1,9 +1,9 @@
#include "api_frame_helper.h"
#ifdef USE_API
#include "esphome/core/log.h"
#include "esphome/core/application.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/application.h"
#include "esphome/core/log.h"
#include "proto.h"
#include "api_pb2_size.h"
#include <cstring>
@@ -66,6 +66,17 @@ const char *api_error_to_str(APIError err) {
return "UNKNOWN";
}
// Default implementation for loop - handles sending buffered data
APIError APIFrameHelper::loop() {
if (!this->tx_buf_.empty()) {
APIError err = try_send_tx_buf_();
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
return err;
}
}
return APIError::OK; // Convert WOULD_BLOCK to OK to avoid connection termination
}
// Helper method to buffer data from IOVs
void APIFrameHelper::buffer_data_from_iov_(const struct iovec *iov, int iovcnt, uint16_t total_write_len) {
SendBuffer buffer;
@@ -274,17 +285,21 @@ APIError APINoiseFrameHelper::init() {
}
/// Run through handshake messages (if in that phase)
APIError APINoiseFrameHelper::loop() {
APIError err = state_action_();
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
return err;
}
if (!this->tx_buf_.empty()) {
err = try_send_tx_buf_();
// During handshake phase, process as many actions as possible until we can't progress
// socket_->ready() stays true until next main loop, but state_action() will return
// WOULD_BLOCK when no more data is available to read
while (state_ != State::DATA && this->socket_->ready()) {
APIError err = state_action_();
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
return err;
}
if (err == APIError::WOULD_BLOCK) {
break;
}
}
return APIError::OK; // Convert WOULD_BLOCK to OK to avoid connection termination
// Use base class implementation for buffer sending
return APIFrameHelper::loop();
}
/** Read a packet into the rx_buf_. If successful, stores frame data in the frame parameter
@@ -330,17 +345,15 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
return APIError::WOULD_BLOCK;
}
if (rx_header_buf_[0] != 0x01) {
state_ = State::FAILED;
HELPER_LOG("Bad indicator byte %u", rx_header_buf_[0]);
return APIError::BAD_INDICATOR;
}
// header reading done
}
// read body
uint8_t indicator = rx_header_buf_[0];
if (indicator != 0x01) {
state_ = State::FAILED;
HELPER_LOG("Bad indicator byte %u", indicator);
return APIError::BAD_INDICATOR;
}
uint16_t msg_size = (((uint16_t) rx_header_buf_[1]) << 8) | rx_header_buf_[2];
if (state_ != State::DATA && msg_size > 128) {
@@ -586,10 +599,6 @@ APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) {
return APIError::BAD_DATA_PACKET;
}
// uint16_t type;
// uint16_t data_len;
// uint8_t *data;
// uint8_t *padding; zero or more bytes to fill up the rest of the packet
uint16_t type = (((uint16_t) msg_data[0]) << 8) | msg_data[1];
uint16_t data_len = (((uint16_t) msg_data[2]) << 8) | msg_data[3];
if (data_len > msg_size - 4) {
@@ -822,18 +831,12 @@ APIError APIPlaintextFrameHelper::init() {
state_ = State::DATA;
return APIError::OK;
}
/// Not used for plaintext
APIError APIPlaintextFrameHelper::loop() {
if (state_ != State::DATA) {
return APIError::BAD_STATE;
}
if (!this->tx_buf_.empty()) {
APIError err = try_send_tx_buf_();
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
return err;
}
}
return APIError::OK; // Convert WOULD_BLOCK to OK to avoid connection termination
// Use base class implementation for buffer sending
return APIFrameHelper::loop();
}
/** Read a packet into the rx_buf_. If successful, stores frame data in the frame parameter

View File

@@ -38,7 +38,7 @@ struct PacketInfo {
: message_type(type), offset(off), payload_size(size), padding(0) {}
};
enum class APIError : int {
enum class APIError : uint16_t {
OK = 0,
WOULD_BLOCK = 1001,
BAD_HANDSHAKE_PACKET_LEN = 1002,
@@ -74,7 +74,7 @@ class APIFrameHelper {
}
virtual ~APIFrameHelper() = default;
virtual APIError init() = 0;
virtual APIError loop() = 0;
virtual APIError loop();
virtual APIError read_packet(ReadPacketBuffer *buffer) = 0;
bool can_write_without_blocking() { return state_ == State::DATA && tx_buf_.empty(); }
std::string getpeername() { return socket_->getpeername(); }
@@ -125,38 +125,6 @@ class APIFrameHelper {
const uint8_t *current_data() const { return data.data() + offset; }
};
// Queue of data buffers to be sent
std::deque<SendBuffer> tx_buf_;
// Common state enum for all frame helpers
// Note: Not all states are used by all implementations
// - INITIALIZE: Used by both Noise and Plaintext
// - CLIENT_HELLO, SERVER_HELLO, HANDSHAKE: Only used by Noise protocol
// - DATA: Used by both Noise and Plaintext
// - CLOSED: Used by both Noise and Plaintext
// - FAILED: Used by both Noise and Plaintext
// - EXPLICIT_REJECT: Only used by Noise protocol
enum class State {
INITIALIZE = 1,
CLIENT_HELLO = 2, // Noise only
SERVER_HELLO = 3, // Noise only
HANDSHAKE = 4, // Noise only
DATA = 5,
CLOSED = 6,
FAILED = 7,
EXPLICIT_REJECT = 8, // Noise only
};
// Current state of the frame helper
State state_{State::INITIALIZE};
// Helper name for logging
std::string info_;
// Socket for communication
socket::Socket *socket_{nullptr};
std::unique_ptr<socket::Socket> socket_owned_;
// Common implementation for writing raw data to socket
APIError write_raw_(const struct iovec *iov, int iovcnt);
@@ -169,15 +137,41 @@ class APIFrameHelper {
APIError write_raw_(const struct iovec *iov, int iovcnt, socket::Socket *socket, std::vector<uint8_t> &tx_buf,
const std::string &info, StateEnum &state, StateEnum failed_state);
// Pointers first (4 bytes each)
socket::Socket *socket_{nullptr};
std::unique_ptr<socket::Socket> socket_owned_;
// Common state enum for all frame helpers
// Note: Not all states are used by all implementations
// - INITIALIZE: Used by both Noise and Plaintext
// - CLIENT_HELLO, SERVER_HELLO, HANDSHAKE: Only used by Noise protocol
// - DATA: Used by both Noise and Plaintext
// - CLOSED: Used by both Noise and Plaintext
// - FAILED: Used by both Noise and Plaintext
// - EXPLICIT_REJECT: Only used by Noise protocol
enum class State : uint8_t {
INITIALIZE = 1,
CLIENT_HELLO = 2, // Noise only
SERVER_HELLO = 3, // Noise only
HANDSHAKE = 4, // Noise only
DATA = 5,
CLOSED = 6,
FAILED = 7,
EXPLICIT_REJECT = 8, // Noise only
};
// Containers (size varies, but typically 12+ bytes on 32-bit)
std::deque<SendBuffer> tx_buf_;
std::string info_;
std::vector<struct iovec> reusable_iovs_;
std::vector<uint8_t> rx_buf_;
// Group smaller types together
uint16_t rx_buf_len_ = 0;
State state_{State::INITIALIZE};
uint8_t frame_header_padding_{0};
uint8_t frame_footer_size_{0};
// Reusable IOV array for write_protobuf_packets to avoid repeated allocations
std::vector<struct iovec> reusable_iovs_;
// Receive buffer for reading frame data
std::vector<uint8_t> rx_buf_;
uint16_t rx_buf_len_ = 0;
// 5 bytes total, 3 bytes padding
// Common initialization for both plaintext and noise protocols
APIError init_common_();
@@ -213,19 +207,28 @@ class APINoiseFrameHelper : public APIFrameHelper {
APIError init_handshake_();
APIError check_handshake_finished_();
void send_explicit_handshake_reject_(const std::string &reason);
// Pointers first (4 bytes each)
NoiseHandshakeState *handshake_{nullptr};
NoiseCipherState *send_cipher_{nullptr};
NoiseCipherState *recv_cipher_{nullptr};
// Shared pointer (8 bytes on 32-bit = 4 bytes control block pointer + 4 bytes object pointer)
std::shared_ptr<APINoiseContext> ctx_;
// Vector (12 bytes on 32-bit)
std::vector<uint8_t> prologue_;
// NoiseProtocolId (size depends on implementation)
NoiseProtocolId nid_;
// Group small types together
// Fixed-size header buffer for noise protocol:
// 1 byte for indicator + 2 bytes for message size (16-bit value, not varint)
// Note: Maximum message size is UINT16_MAX (65535), with a limit of 128 bytes during handshake phase
uint8_t rx_header_buf_[3];
uint8_t rx_header_buf_len_ = 0;
std::vector<uint8_t> prologue_;
std::shared_ptr<APINoiseContext> ctx_;
NoiseHandshakeState *handshake_{nullptr};
NoiseCipherState *send_cipher_{nullptr};
NoiseCipherState *recv_cipher_{nullptr};
NoiseProtocolId nid_;
// 4 bytes total, no padding
};
#endif // USE_API_NOISE
@@ -252,6 +255,12 @@ class APIPlaintextFrameHelper : public APIFrameHelper {
protected:
APIError try_read_frame_(ParsedFrame *frame);
// Group 2-byte aligned types
uint16_t rx_header_parsed_type_ = 0;
uint16_t rx_header_parsed_len_ = 0;
// Group 1-byte types together
// Fixed-size header buffer for plaintext protocol:
// We now store the indicator byte + the two varints.
// To match noise protocol's maximum message size (UINT16_MAX = 65535), we need:
@@ -263,8 +272,7 @@ class APIPlaintextFrameHelper : public APIFrameHelper {
uint8_t rx_header_buf_[6]; // 1 byte indicator + 5 bytes for varints (3 for size + 2 for type)
uint8_t rx_header_buf_pos_ = 0;
bool rx_header_parsed_ = false;
uint16_t rx_header_parsed_type_ = 0;
uint16_t rx_header_parsed_len_ = 0;
// 8 bytes total, no padding needed
};
#endif

View File

@@ -21,4 +21,5 @@ extend google.protobuf.MessageOptions {
optional string ifdef = 1038;
optional bool log = 1039 [default=true];
optional bool no_delay = 1040 [default=false];
optional string base_class = 1041;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -620,544 +620,300 @@ void APIServerConnection::on_ping_request(const PingRequest &msg) {
}
}
void APIServerConnection::on_device_info_request(const DeviceInfoRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
DeviceInfoResponse ret = this->device_info(msg);
if (!this->send_message(ret)) {
this->on_fatal_error();
if (this->check_connection_setup_()) {
DeviceInfoResponse ret = this->device_info(msg);
if (!this->send_message(ret)) {
this->on_fatal_error();
}
}
}
void APIServerConnection::on_list_entities_request(const ListEntitiesRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->list_entities(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->list_entities(msg);
}
void APIServerConnection::on_subscribe_states_request(const SubscribeStatesRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->subscribe_states(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->subscribe_states(msg);
}
void APIServerConnection::on_subscribe_logs_request(const SubscribeLogsRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->subscribe_logs(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->subscribe_logs(msg);
}
void APIServerConnection::on_subscribe_homeassistant_services_request(
const SubscribeHomeassistantServicesRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->subscribe_homeassistant_services(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->subscribe_homeassistant_services(msg);
}
void APIServerConnection::on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->subscribe_home_assistant_states(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->subscribe_home_assistant_states(msg);
}
void APIServerConnection::on_get_time_request(const GetTimeRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
GetTimeResponse ret = this->get_time(msg);
if (!this->send_message(ret)) {
this->on_fatal_error();
if (this->check_connection_setup_()) {
GetTimeResponse ret = this->get_time(msg);
if (!this->send_message(ret)) {
this->on_fatal_error();
}
}
}
void APIServerConnection::on_execute_service_request(const ExecuteServiceRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->execute_service(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->execute_service(msg);
}
#ifdef USE_API_NOISE
void APIServerConnection::on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
NoiseEncryptionSetKeyResponse ret = this->noise_encryption_set_key(msg);
if (!this->send_message(ret)) {
this->on_fatal_error();
if (this->check_authenticated_()) {
NoiseEncryptionSetKeyResponse ret = this->noise_encryption_set_key(msg);
if (!this->send_message(ret)) {
this->on_fatal_error();
}
}
}
#endif
#ifdef USE_BUTTON
void APIServerConnection::on_button_command_request(const ButtonCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->button_command(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->button_command(msg);
}
#endif
#ifdef USE_ESP32_CAMERA
void APIServerConnection::on_camera_image_request(const CameraImageRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->camera_image(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->camera_image(msg);
}
#endif
#ifdef USE_CLIMATE
void APIServerConnection::on_climate_command_request(const ClimateCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->climate_command(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->climate_command(msg);
}
#endif
#ifdef USE_COVER
void APIServerConnection::on_cover_command_request(const CoverCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->cover_command(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->cover_command(msg);
}
#endif
#ifdef USE_DATETIME_DATE
void APIServerConnection::on_date_command_request(const DateCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->date_command(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->date_command(msg);
}
#endif
#ifdef USE_DATETIME_DATETIME
void APIServerConnection::on_date_time_command_request(const DateTimeCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->datetime_command(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->datetime_command(msg);
}
#endif
#ifdef USE_FAN
void APIServerConnection::on_fan_command_request(const FanCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->fan_command(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->fan_command(msg);
}
#endif
#ifdef USE_LIGHT
void APIServerConnection::on_light_command_request(const LightCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->light_command(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->light_command(msg);
}
#endif
#ifdef USE_LOCK
void APIServerConnection::on_lock_command_request(const LockCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->lock_command(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->lock_command(msg);
}
#endif
#ifdef USE_MEDIA_PLAYER
void APIServerConnection::on_media_player_command_request(const MediaPlayerCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->media_player_command(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->media_player_command(msg);
}
#endif
#ifdef USE_NUMBER
void APIServerConnection::on_number_command_request(const NumberCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->number_command(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->number_command(msg);
}
#endif
#ifdef USE_SELECT
void APIServerConnection::on_select_command_request(const SelectCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->select_command(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->select_command(msg);
}
#endif
#ifdef USE_SIREN
void APIServerConnection::on_siren_command_request(const SirenCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->siren_command(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->siren_command(msg);
}
#endif
#ifdef USE_SWITCH
void APIServerConnection::on_switch_command_request(const SwitchCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->switch_command(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->switch_command(msg);
}
#endif
#ifdef USE_TEXT
void APIServerConnection::on_text_command_request(const TextCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->text_command(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->text_command(msg);
}
#endif
#ifdef USE_DATETIME_TIME
void APIServerConnection::on_time_command_request(const TimeCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->time_command(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->time_command(msg);
}
#endif
#ifdef USE_UPDATE
void APIServerConnection::on_update_command_request(const UpdateCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->update_command(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->update_command(msg);
}
#endif
#ifdef USE_VALVE
void APIServerConnection::on_valve_command_request(const ValveCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->valve_command(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->valve_command(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
const SubscribeBluetoothLEAdvertisementsRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->subscribe_bluetooth_le_advertisements(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->subscribe_bluetooth_le_advertisements(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_device_request(const BluetoothDeviceRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->bluetooth_device_request(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->bluetooth_device_request(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_gatt_get_services_request(const BluetoothGATTGetServicesRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->bluetooth_gatt_get_services(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->bluetooth_gatt_get_services(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_gatt_read_request(const BluetoothGATTReadRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->bluetooth_gatt_read(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->bluetooth_gatt_read(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_gatt_write_request(const BluetoothGATTWriteRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->bluetooth_gatt_write(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->bluetooth_gatt_write(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_gatt_read_descriptor_request(const BluetoothGATTReadDescriptorRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->bluetooth_gatt_read_descriptor(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->bluetooth_gatt_read_descriptor(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_gatt_write_descriptor_request(const BluetoothGATTWriteDescriptorRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->bluetooth_gatt_write_descriptor(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->bluetooth_gatt_write_descriptor(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->bluetooth_gatt_notify(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->bluetooth_gatt_notify(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_subscribe_bluetooth_connections_free_request(
const SubscribeBluetoothConnectionsFreeRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
BluetoothConnectionsFreeResponse ret = this->subscribe_bluetooth_connections_free(msg);
if (!this->send_message(ret)) {
this->on_fatal_error();
if (this->check_authenticated_()) {
BluetoothConnectionsFreeResponse ret = this->subscribe_bluetooth_connections_free(msg);
if (!this->send_message(ret)) {
this->on_fatal_error();
}
}
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_unsubscribe_bluetooth_le_advertisements_request(
const UnsubscribeBluetoothLEAdvertisementsRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->unsubscribe_bluetooth_le_advertisements(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->unsubscribe_bluetooth_le_advertisements(msg);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->bluetooth_scanner_set_mode(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->bluetooth_scanner_set_mode(msg);
}
#endif
#ifdef USE_VOICE_ASSISTANT
void APIServerConnection::on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->subscribe_voice_assistant(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->subscribe_voice_assistant(msg);
}
#endif
#ifdef USE_VOICE_ASSISTANT
void APIServerConnection::on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
VoiceAssistantConfigurationResponse ret = this->voice_assistant_get_configuration(msg);
if (!this->send_message(ret)) {
this->on_fatal_error();
if (this->check_authenticated_()) {
VoiceAssistantConfigurationResponse ret = this->voice_assistant_get_configuration(msg);
if (!this->send_message(ret)) {
this->on_fatal_error();
}
}
}
#endif
#ifdef USE_VOICE_ASSISTANT
void APIServerConnection::on_voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->voice_assistant_set_configuration(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->voice_assistant_set_configuration(msg);
}
#endif
#ifdef USE_ALARM_CONTROL_PANEL
void APIServerConnection::on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
if (this->check_authenticated_()) {
this->alarm_control_panel_command(msg);
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->alarm_control_panel_command(msg);
}
#endif

View File

@@ -47,6 +47,11 @@ void APIServer::setup() {
}
#endif
// Schedule reboot if no clients connect within timeout
if (this->reboot_timeout_ != 0) {
this->schedule_reboot_timeout_();
}
this->socket_ = socket::socket_ip_loop_monitored(SOCK_STREAM, 0); // monitored for incoming connections
if (this->socket_ == nullptr) {
ESP_LOGW(TAG, "Could not create socket");
@@ -92,6 +97,12 @@ void APIServer::setup() {
#ifdef USE_LOGGER
if (logger::global_logger != nullptr) {
logger::global_logger->add_on_log_callback([this](int level, const char *tag, const char *message) {
if (this->shutting_down_) {
// Don't try to send logs during shutdown
// as it could result in a recursion and
// we would be filling a buffer we are trying to clear
return;
}
for (auto &c : this->clients_) {
if (!c->remove_)
c->try_send_log_message(level, tag, message);
@@ -100,8 +111,6 @@ void APIServer::setup() {
}
#endif
this->last_connected_ = millis();
#ifdef USE_ESP32_CAMERA
if (esp32_camera::global_esp32_camera != nullptr && !esp32_camera::global_esp32_camera->is_internal()) {
esp32_camera::global_esp32_camera->add_image_callback(
@@ -115,66 +124,88 @@ void APIServer::setup() {
#endif
}
void APIServer::schedule_reboot_timeout_() {
this->status_set_warning();
this->set_timeout("api_reboot", this->reboot_timeout_, []() {
if (!global_api_server->is_connected()) {
ESP_LOGE(TAG, "No clients; rebooting");
App.reboot();
}
});
}
void APIServer::loop() {
// Accept new clients only if the socket has incoming connections
if (this->socket_->ready()) {
// Accept new clients only if the socket exists and has incoming connections
if (this->socket_ && this->socket_->ready()) {
while (true) {
struct sockaddr_storage source_addr;
socklen_t addr_len = sizeof(source_addr);
auto sock = this->socket_->accept_loop_monitored((struct sockaddr *) &source_addr, &addr_len);
if (!sock)
break;
ESP_LOGD(TAG, "Accepted %s", sock->getpeername().c_str());
ESP_LOGD(TAG, "Accept %s", sock->getpeername().c_str());
auto *conn = new APIConnection(std::move(sock), this);
this->clients_.emplace_back(conn);
conn->start();
// Clear warning status and cancel reboot when first client connects
if (this->clients_.size() == 1 && this->reboot_timeout_ != 0) {
this->status_clear_warning();
this->cancel_timeout("api_reboot");
}
}
}
if (this->clients_.empty()) {
return;
}
// Process clients and remove disconnected ones in a single pass
if (!this->clients_.empty()) {
size_t client_index = 0;
while (client_index < this->clients_.size()) {
auto &client = this->clients_[client_index];
if (client->remove_) {
// Handle disconnection
this->client_disconnected_trigger_->trigger(client->client_info_, client->client_peername_);
ESP_LOGV(TAG, "Removing connection to %s", client->client_info_.c_str());
// Swap with the last element and pop (avoids expensive vector shifts)
if (client_index < this->clients_.size() - 1) {
std::swap(this->clients_[client_index], this->clients_.back());
}
this->clients_.pop_back();
// Don't increment client_index since we need to process the swapped element
} else {
// Process active client
client->loop();
client_index++; // Move to next client
}
// Check network connectivity once for all clients
if (!network::is_connected()) {
// Network is down - disconnect all clients
for (auto &client : this->clients_) {
client->on_fatal_error();
ESP_LOGW(TAG, "%s: Network down; disconnect", client->get_client_combined_info().c_str());
}
// Continue to process and clean up the clients below
}
if (this->reboot_timeout_ != 0) {
const uint32_t now = millis();
if (!this->is_connected()) {
if (now - this->last_connected_ > this->reboot_timeout_) {
ESP_LOGE(TAG, "No client connected; rebooting");
App.reboot();
}
this->status_set_warning();
} else {
this->last_connected_ = now;
this->status_clear_warning();
size_t client_index = 0;
while (client_index < this->clients_.size()) {
auto &client = this->clients_[client_index];
if (!client->remove_) {
// Common case: process active client
client->loop();
client_index++;
continue;
}
// Rare case: handle disconnection
this->client_disconnected_trigger_->trigger(client->client_info_, client->client_peername_);
ESP_LOGV(TAG, "Remove connection %s", client->client_info_.c_str());
// Swap with the last element and pop (avoids expensive vector shifts)
if (client_index < this->clients_.size() - 1) {
std::swap(this->clients_[client_index], this->clients_.back());
}
this->clients_.pop_back();
// Schedule reboot when last client disconnects
if (this->clients_.empty() && this->reboot_timeout_ != 0) {
this->schedule_reboot_timeout_();
}
// Don't increment client_index since we need to process the swapped element
}
}
void APIServer::dump_config() {
ESP_LOGCONFIG(TAG, "API Server:");
ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->port_);
ESP_LOGCONFIG(TAG,
"API 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()));
if (!this->noise_ctx_->has_psk()) {
@@ -219,12 +250,11 @@ bool APIServer::check_password(const std::string &password) const {
void APIServer::handle_disconnect(APIConnection *conn) {}
#ifdef USE_BINARY_SENSOR
void APIServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) {
void APIServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj) {
if (obj->is_internal())
return;
bool use_current = (state == obj->state);
for (auto &c : this->clients_)
use_current ? c->send_binary_sensor_state(obj) : c->send_binary_sensor_state(obj, state);
c->send_binary_sensor_state(obj);
}
#endif
@@ -259,9 +289,8 @@ void APIServer::on_light_update(light::LightState *obj) {
void APIServer::on_sensor_update(sensor::Sensor *obj, float state) {
if (obj->is_internal())
return;
bool use_current = (state == obj->state);
for (auto &c : this->clients_)
use_current ? c->send_sensor_state(obj) : c->send_sensor_state(obj, state);
c->send_sensor_state(obj);
}
#endif
@@ -269,9 +298,8 @@ void APIServer::on_sensor_update(sensor::Sensor *obj, float state) {
void APIServer::on_switch_update(switch_::Switch *obj, bool state) {
if (obj->is_internal())
return;
bool use_current = (state == obj->state);
for (auto &c : this->clients_)
use_current ? c->send_switch_state(obj) : c->send_switch_state(obj, state);
c->send_switch_state(obj);
}
#endif
@@ -279,9 +307,8 @@ void APIServer::on_switch_update(switch_::Switch *obj, bool state) {
void APIServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) {
if (obj->is_internal())
return;
bool use_current = (state == obj->state);
for (auto &c : this->clients_)
use_current ? c->send_text_sensor_state(obj) : c->send_text_sensor_state(obj, state);
c->send_text_sensor_state(obj);
}
#endif
@@ -298,9 +325,8 @@ void APIServer::on_climate_update(climate::Climate *obj) {
void APIServer::on_number_update(number::Number *obj, float state) {
if (obj->is_internal())
return;
bool use_current = (state == obj->state);
for (auto &c : this->clients_)
use_current ? c->send_number_state(obj) : c->send_number_state(obj, state);
c->send_number_state(obj);
}
#endif
@@ -335,9 +361,8 @@ void APIServer::on_datetime_update(datetime::DateTimeEntity *obj) {
void APIServer::on_text_update(text::Text *obj, const std::string &state) {
if (obj->is_internal())
return;
bool use_current = (state == obj->state);
for (auto &c : this->clients_)
use_current ? c->send_text_state(obj) : c->send_text_state(obj, state);
c->send_text_state(obj);
}
#endif
@@ -345,9 +370,8 @@ void APIServer::on_text_update(text::Text *obj, const std::string &state) {
void APIServer::on_select_update(select::Select *obj, const std::string &state, size_t index) {
if (obj->is_internal())
return;
bool use_current = (state == obj->state);
for (auto &c : this->clients_)
use_current ? c->send_select_state(obj) : c->send_select_state(obj, state);
c->send_select_state(obj);
}
#endif
@@ -487,10 +511,36 @@ void APIServer::request_time() {
bool APIServer::is_connected() const { return !this->clients_.empty(); }
void APIServer::on_shutdown() {
for (auto &c : this->clients_) {
c->send_message(DisconnectRequest());
this->shutting_down_ = true;
// Close the listening socket to prevent new connections
if (this->socket_) {
this->socket_->close();
this->socket_ = nullptr;
}
delay(10);
// Change batch delay to 5ms for quick flushing during shutdown
this->batch_delay_ = 5;
// Send disconnect requests to all connected clients
for (auto &c : this->clients_) {
if (!c->send_message(DisconnectRequest())) {
// If we can't send the disconnect request directly (tx_buffer full),
// schedule it at the front of the batch so it will be sent with priority
c->schedule_message_front_(nullptr, &APIConnection::try_send_disconnect_request, DisconnectRequest::MESSAGE_TYPE);
}
}
}
bool APIServer::teardown() {
// If network is disconnected, no point trying to flush buffers
if (!network::is_connected()) {
return true;
}
this->loop();
// Return true only when all clients have been torn down
return this->clients_.empty();
}
} // namespace api

View File

@@ -34,6 +34,7 @@ class APIServer : public Component, public Controller {
void loop() override;
void dump_config() override;
void on_shutdown() override;
bool teardown() override;
bool check_password(const std::string &password) const;
bool uses_password() const;
void set_port(uint16_t port);
@@ -53,7 +54,7 @@ class APIServer : public Component, public Controller {
void handle_disconnect(APIConnection *conn);
#ifdef USE_BINARY_SENSOR
void on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) override;
void on_binary_sensor_update(binary_sensor::BinarySensor *obj) override;
#endif
#ifdef USE_COVER
void on_cover_update(cover::Cover *obj) override;
@@ -141,18 +142,27 @@ class APIServer : public Component, public Controller {
}
protected:
void schedule_reboot_timeout_();
// Pointers and pointer-like types first (4 bytes each)
std::unique_ptr<socket::Socket> socket_ = nullptr;
uint16_t port_{6053};
Trigger<std::string, std::string> *client_connected_trigger_ = new Trigger<std::string, std::string>();
Trigger<std::string, std::string> *client_disconnected_trigger_ = new Trigger<std::string, std::string>();
// 4-byte aligned types
uint32_t reboot_timeout_{300000};
uint32_t batch_delay_{100};
uint32_t last_connected_{0};
// Vectors and strings (12 bytes each on 32-bit)
std::vector<std::unique_ptr<APIConnection>> clients_;
std::string password_;
std::vector<uint8_t> shared_write_buffer_; // Shared proto write buffer for all connections
std::vector<HomeAssistantStateSubscription> state_subs_;
std::vector<UserServiceDescriptor *> user_services_;
Trigger<std::string, std::string> *client_connected_trigger_ = new Trigger<std::string, std::string>();
Trigger<std::string, std::string> *client_disconnected_trigger_ = new Trigger<std::string, std::string>();
// Group smaller types together
uint16_t port_{6053};
bool shutting_down_ = false;
// 3 bytes used, 1 byte padding
#ifdef USE_API_NOISE
std::shared_ptr<APINoiseContext> noise_ctx_ = std::make_shared<APINoiseContext>();

View File

@@ -5,7 +5,7 @@ from datetime import datetime
import logging
from typing import TYPE_CHECKING, Any
from aioesphomeapi import APIClient
from aioesphomeapi import APIClient, parse_log_message
from aioesphomeapi.log_runner import async_run
from esphome.const import CONF_KEY, CONF_PASSWORD, CONF_PORT, __version__
@@ -46,9 +46,10 @@ async def async_run_logs(config: dict[str, Any], address: str) -> None:
time_ = datetime.now()
message: bytes = msg.message
text = message.decode("utf8", "backslashreplace")
if dashboard:
text = text.replace("\033", "\\033")
print(f"[{time_.hour:02}:{time_.minute:02}:{time_.second:02}]{text}")
for parsed_msg in parse_log_message(
text, f"[{time_.hour:02}:{time_.minute:02}:{time_.second:02}]"
):
print(parsed_msg.replace("\033", "\\033") if dashboard else parsed_msg)
stop = await async_run(cli, on_log, name=name)
try:

View File

@@ -3,8 +3,8 @@
#include "api_server.h"
#ifdef USE_API
#include "api_pb2.h"
#include "esphome/core/helpers.h"
#include "esphome/core/automation.h"
#include "esphome/core/helpers.h"
#include <vector>
namespace esphome {

View File

@@ -1,5 +1,6 @@
#include "proto.h"
#include <cinttypes>
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {

View File

@@ -1,8 +1,8 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include <vector>
@@ -216,7 +216,7 @@ class ProtoWriteBuffer {
this->buffer_->insert(this->buffer_->end(), data, data + len);
}
void encode_string(uint32_t field_id, const std::string &value, bool force = false) {
this->encode_string(field_id, value.data(), value.size());
this->encode_string(field_id, value.data(), value.size(), force);
}
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);
@@ -327,9 +327,11 @@ class ProtoWriteBuffer {
class ProtoMessage {
public:
virtual ~ProtoMessage() = default;
virtual void encode(ProtoWriteBuffer buffer) const = 0;
// Default implementation for messages with no fields
virtual void encode(ProtoWriteBuffer buffer) const {}
void decode(const uint8_t *buffer, size_t length);
virtual void calculate_size(uint32_t &total_size) const = 0;
// 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;
@@ -377,6 +379,26 @@ class ProtoService {
// Send the buffer
return this->send_buffer(buffer, message_type);
}
// Authentication helper methods
bool check_connection_setup_() {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return false;
}
return true;
}
bool check_authenticated_() {
if (!this->check_connection_setup_()) {
return false;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return false;
}
return true;
}
};
} // namespace api

View File

@@ -95,11 +95,13 @@ void AS5600Component::dump_config() {
return;
}
ESP_LOGCONFIG(TAG, " Watchdog: %d", this->watchdog_);
ESP_LOGCONFIG(TAG, " Fast Filter: %d", this->fast_filter_);
ESP_LOGCONFIG(TAG, " Slow Filter: %d", this->slow_filter_);
ESP_LOGCONFIG(TAG, " Hysteresis: %d", this->hysteresis_);
ESP_LOGCONFIG(TAG, " Start Position: %d", this->start_position_);
ESP_LOGCONFIG(TAG,
" Watchdog: %d\n"
" Fast Filter: %d\n"
" Slow Filter: %d\n"
" Hysteresis: %d\n"
" Start Position: %d",
this->watchdog_, this->fast_filter_, this->slow_filter_, this->hysteresis_, this->start_position_);
if (this->end_mode_ == END_MODE_POSITION) {
ESP_LOGCONFIG(TAG, " End Position: %d", this->end_position_);
} else {

View File

@@ -41,9 +41,11 @@ void AS7341Component::dump_config() {
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
}
LOG_UPDATE_INTERVAL(this);
ESP_LOGCONFIG(TAG, " Gain: %u", get_gain());
ESP_LOGCONFIG(TAG, " ATIME: %u", get_atime());
ESP_LOGCONFIG(TAG, " ASTEP: %u", get_astep());
ESP_LOGCONFIG(TAG,
" Gain: %u\n"
" ATIME: %u\n"
" ASTEP: %u",
get_gain(), get_atime(), get_astep());
LOG_SENSOR(" ", "F1", this->f1_);
LOG_SENSOR(" ", "F2", this->f2_);

View File

@@ -21,8 +21,8 @@ CONFIG_SCHEMA = cv.All(
@coroutine_with_priority(200.0)
async def to_code(config):
if CORE.is_esp32 or CORE.is_libretiny:
# https://github.com/esphome/AsyncTCP/blob/master/library.json
cg.add_library("esphome/AsyncTCP-esphome", "2.1.4")
# https://github.com/ESP32Async/AsyncTCP
cg.add_library("ESP32Async/AsyncTCP", "3.4.4")
elif CORE.is_esp8266:
# https://github.com/esphome/ESPAsyncTCP
cg.add_library("esphome/ESPAsyncTCP-esphome", "2.0.0")
# https://github.com/ESP32Async/ESPAsyncTCP
cg.add_library("ESP32Async/ESPAsyncTCP", "2.0.0")

View File

@@ -75,15 +75,18 @@ void AT581XComponent::setup() { ESP_LOGCONFIG(TAG, "Running setup"); }
void AT581XComponent::dump_config() { LOG_I2C_DEVICE(this); }
#define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0]))
bool AT581XComponent::i2c_write_config() {
ESP_LOGCONFIG(TAG, "Writing new config for AT581X");
ESP_LOGCONFIG(TAG, "Frequency: %dMHz", this->freq_);
ESP_LOGCONFIG(TAG, "Sensing distance: %d", this->delta_);
ESP_LOGCONFIG(TAG, "Power: %dµA", this->power_);
ESP_LOGCONFIG(TAG, "Gain: %d", this->gain_);
ESP_LOGCONFIG(TAG, "Trigger base time: %dms", this->trigger_base_time_ms_);
ESP_LOGCONFIG(TAG, "Trigger keep time: %dms", this->trigger_keep_time_ms_);
ESP_LOGCONFIG(TAG, "Protect time: %dms", this->protect_time_ms_);
ESP_LOGCONFIG(TAG, "Self check time: %dms", this->self_check_time_ms_);
ESP_LOGCONFIG(TAG,
"Writing new config for AT581X\n"
"Frequency: %dMHz\n"
"Sensing distance: %d\n"
"Power: %dµA\n"
"Gain: %d\n"
"Trigger base time: %dms\n"
"Trigger keep time: %dms\n"
"Protect time: %dms\n"
"Self check time: %dms",
this->freq_, this->delta_, this->power_, this->gain_, this->trigger_base_time_ms_,
this->trigger_keep_time_ms_, this->protect_time_ms_, this->self_check_time_ms_);
// Set frequency point
if (!this->i2c_write_reg(FREQ_ADDR, GAIN61_VALUE)) {

View File

@@ -86,7 +86,7 @@ bool AudioTransferBuffer::reallocate(size_t new_buffer_size) {
bool AudioTransferBuffer::allocate_buffer_(size_t buffer_size) {
this->buffer_size_ = buffer_size;
RAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
RAMAllocator<uint8_t> allocator;
this->buffer_ = allocator.allocate(this->buffer_size_);
if (this->buffer_ == nullptr) {
@@ -101,7 +101,7 @@ bool AudioTransferBuffer::allocate_buffer_(size_t buffer_size) {
void AudioTransferBuffer::deallocate_buffer_() {
if (this->buffer_ != nullptr) {
RAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
RAMAllocator<uint8_t> allocator;
allocator.deallocate(this->buffer_, this->buffer_size_);
this->buffer_ = nullptr;
this->data_start_ = nullptr;

View File

@@ -60,8 +60,10 @@ void AXS15231Touchscreen::dump_config() {
LOG_I2C_DEVICE(this);
LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
ESP_LOGCONFIG(TAG, " Width: %d", this->x_raw_max_);
ESP_LOGCONFIG(TAG, " Height: %d", this->y_raw_max_);
ESP_LOGCONFIG(TAG,
" Width: %d\n"
" Height: %d",
this->x_raw_max_, this->y_raw_max_);
}
} // namespace axs15231

View File

@@ -194,11 +194,14 @@ Trigger<> *BangBangClimate::get_heat_trigger() const { return this->heat_trigger
void BangBangClimate::set_supports_heat(bool supports_heat) { this->supports_heat_ = supports_heat; }
void BangBangClimate::dump_config() {
LOG_CLIMATE("", "Bang Bang Climate", this);
ESP_LOGCONFIG(TAG, " Supports HEAT: %s", YESNO(this->supports_heat_));
ESP_LOGCONFIG(TAG, " Supports COOL: %s", YESNO(this->supports_cool_));
ESP_LOGCONFIG(TAG, " Supports AWAY mode: %s", YESNO(this->supports_away_));
ESP_LOGCONFIG(TAG, " Default Target Temperature Low: %.2f°C", this->normal_config_.default_temperature_low);
ESP_LOGCONFIG(TAG, " Default Target Temperature High: %.2f°C", this->normal_config_.default_temperature_high);
ESP_LOGCONFIG(TAG,
" Supports HEAT: %s\n"
" Supports COOL: %s\n"
" Supports AWAY mode: %s\n"
" Default Target Temperature Low: %.2f°C\n"
" Default Target Temperature High: %.2f°C",
YESNO(this->supports_heat_), YESNO(this->supports_cool_), YESNO(this->supports_away_),
this->normal_config_.default_temperature_low, this->normal_config_.default_temperature_high);
}
BangBangClimateTargetTempConfig::BangBangClimateTargetTempConfig() = default;

View File

@@ -480,13 +480,19 @@ void BedJetHub::set_clock(uint8_t hour, uint8_t minute) {
/* Internal */
void BedJetHub::loop() {}
void BedJetHub::loop() {
// Parent BLEClientNode has a loop() method, but this component uses
// polling via update() and BLE callbacks so loop isn't needed
this->disable_loop();
}
void BedJetHub::update() { this->dispatch_status_(); }
void BedJetHub::dump_config() {
ESP_LOGCONFIG(TAG, "BedJet Hub '%s'", this->get_name().c_str());
ESP_LOGCONFIG(TAG, " ble_client.app_id: %d", this->parent()->app_id);
ESP_LOGCONFIG(TAG, " ble_client.conn_id: %d", this->parent()->get_conn_id());
ESP_LOGCONFIG(TAG,
"BedJet Hub '%s'\n"
" ble_client.app_id: %d\n"
" ble_client.conn_id: %d",
this->get_name().c_str(), this->parent()->app_id, this->parent()->get_conn_id());
LOG_UPDATE_INTERVAL(this)
ESP_LOGCONFIG(TAG, " Child components (%d):", this->children_.size());
for (auto *child : this->children_) {

View File

@@ -83,7 +83,11 @@ void BedJetClimate::reset_state_() {
this->publish_state();
}
void BedJetClimate::loop() {}
void BedJetClimate::loop() {
// This component is controlled via the parent BedJetHub
// Empty loop not needed, disable to save CPU cycles
this->disable_loop();
}
void BedJetClimate::control(const ClimateCall &call) {
ESP_LOGD(TAG, "Received BedJetClimate::control");

View File

@@ -7,11 +7,13 @@
extern "C" {
#include "rtos_pub.h"
#include "spi.h"
// rtos_pub.h must be included before the rest of the includes
#include "arm_arch.h"
#include "general_dma_pub.h"
#include "gpio_pub.h"
#include "icu_pub.h"
#include "spi.h"
#undef SPI_DAT
#undef SPI_BASE
};
@@ -124,7 +126,7 @@ void BekenSPILEDStripLightOutput::setup() {
size_t buffer_size = this->get_buffer_size_();
size_t dma_buffer_size = (buffer_size * 8) + (2 * 64);
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
RAMAllocator<uint8_t> allocator;
this->buf_ = allocator.allocate(buffer_size);
if (this->buf_ == nullptr) {
ESP_LOGE(TAG, "Cannot allocate LED buffer!");
@@ -345,8 +347,10 @@ light::ESPColorView BekenSPILEDStripLightOutput::get_view_internal(int32_t index
}
void BekenSPILEDStripLightOutput::dump_config() {
ESP_LOGCONFIG(TAG, "Beken SPI LED Strip:");
ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_);
ESP_LOGCONFIG(TAG,
"Beken SPI LED Strip:\n"
" Pin: %u",
this->pin_);
const char *rgb_order;
switch (this->rgb_order_) {
case ORDER_RGB:
@@ -371,9 +375,11 @@ void BekenSPILEDStripLightOutput::dump_config() {
rgb_order = "UNKNOWN";
break;
}
ESP_LOGCONFIG(TAG, " RGB Order: %s", rgb_order);
ESP_LOGCONFIG(TAG, " Max refresh rate: %" PRIu32, *this->max_refresh_rate_);
ESP_LOGCONFIG(TAG, " Number of LEDs: %u", this->num_leds_);
ESP_LOGCONFIG(TAG,
" RGB Order: %s\n"
" Max refresh rate: %" PRIu32 "\n"
" Number of LEDs: %u",
rgb_order, *this->max_refresh_rate_, this->num_leds_);
}
float BekenSPILEDStripLightOutput::get_setup_priority() const { return setup_priority::HARDWARE; }

View File

@@ -50,7 +50,7 @@ void BH1750Sensor::read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function<
// turn on (after one-shot sensor automatically powers down)
uint8_t turn_on = BH1750_COMMAND_POWER_ON;
if (this->write(&turn_on, 1) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Turning on BH1750 failed");
ESP_LOGW(TAG, "Power on failed");
f(NAN);
return;
}
@@ -60,7 +60,7 @@ void BH1750Sensor::read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function<
uint8_t mtreg_hi = BH1750_COMMAND_MT_REG_HI | ((mtreg >> 5) & 0b111);
uint8_t mtreg_lo = BH1750_COMMAND_MT_REG_LO | ((mtreg >> 0) & 0b11111);
if (this->write(&mtreg_hi, 1) != i2c::ERROR_OK || this->write(&mtreg_lo, 1) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Setting measurement time for BH1750 failed");
ESP_LOGW(TAG, "Set measurement time failed");
active_mtreg_ = 0;
f(NAN);
return;
@@ -88,7 +88,7 @@ void BH1750Sensor::read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function<
return;
}
if (this->write(&cmd, 1) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Starting measurement for BH1750 failed");
ESP_LOGW(TAG, "Start measurement failed");
f(NAN);
return;
}
@@ -99,7 +99,7 @@ void BH1750Sensor::read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function<
this->set_timeout("read", meas_time, [this, mode, mtreg, f]() {
uint16_t raw_value;
if (this->read(reinterpret_cast<uint8_t *>(&raw_value), 2) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Reading BH1750 data failed");
ESP_LOGW(TAG, "Read data failed");
f(NAN);
return;
}
@@ -156,7 +156,7 @@ void BH1750Sensor::update() {
this->publish_state(NAN);
return;
}
ESP_LOGD(TAG, "'%s': Got illuminance=%.1flx", this->get_name().c_str(), val);
ESP_LOGD(TAG, "'%s': Illuminance=%.1flx", this->get_name().c_str(), val);
this->status_clear_warning();
this->publish_state(val);
});

View File

@@ -1,7 +1,10 @@
from logging import getLogger
from esphome import automation, core
from esphome.automation import Condition, maybe_simple_id
import esphome.codegen as cg
from esphome.components import mqtt, web_server
from esphome.components.const import CONF_ON_STATE_CHANGE
import esphome.config_validation as cv
from esphome.const import (
CONF_DELAY,
@@ -57,8 +60,8 @@ from esphome.const import (
DEVICE_CLASS_WINDOW,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
from esphome.util import Registry
CODEOWNERS = ["@esphome/core"]
@@ -98,6 +101,7 @@ IS_PLATFORM_COMPONENT = True
CONF_TIME_OFF = "time_off"
CONF_TIME_ON = "time_on"
CONF_TRIGGER_ON_INITIAL_STATE = "trigger_on_initial_state"
DEFAULT_DELAY = "1s"
DEFAULT_TIME_OFF = "100ms"
@@ -127,15 +131,24 @@ MultiClickTriggerEvent = binary_sensor_ns.struct("MultiClickTriggerEvent")
StateTrigger = binary_sensor_ns.class_(
"StateTrigger", automation.Trigger.template(bool)
)
StateChangeTrigger = binary_sensor_ns.class_(
"StateChangeTrigger",
automation.Trigger.template(cg.optional.template(bool), cg.optional.template(bool)),
)
BinarySensorPublishAction = binary_sensor_ns.class_(
"BinarySensorPublishAction", automation.Action
)
BinarySensorInvalidateAction = binary_sensor_ns.class_(
"BinarySensorInvalidateAction", automation.Action
)
# Condition
BinarySensorCondition = binary_sensor_ns.class_("BinarySensorCondition", Condition)
# Filters
Filter = binary_sensor_ns.class_("Filter")
TimeoutFilter = binary_sensor_ns.class_("TimeoutFilter", Filter, cg.Component)
DelayedOnOffFilter = binary_sensor_ns.class_("DelayedOnOffFilter", Filter, cg.Component)
DelayedOnFilter = binary_sensor_ns.class_("DelayedOnFilter", Filter, cg.Component)
DelayedOffFilter = binary_sensor_ns.class_("DelayedOffFilter", Filter, cg.Component)
@@ -144,6 +157,8 @@ AutorepeatFilter = binary_sensor_ns.class_("AutorepeatFilter", Filter, cg.Compon
LambdaFilter = binary_sensor_ns.class_("LambdaFilter", Filter)
SettleFilter = binary_sensor_ns.class_("SettleFilter", Filter, cg.Component)
_LOGGER = getLogger(__name__)
FILTER_REGISTRY = Registry()
validate_filters = cv.validate_registry("filter", FILTER_REGISTRY)
@@ -157,6 +172,19 @@ async def invert_filter_to_code(config, filter_id):
return cg.new_Pvariable(filter_id)
@register_filter(
"timeout",
TimeoutFilter,
cv.templatable(cv.positive_time_period_milliseconds),
)
async def timeout_filter_to_code(config, filter_id):
var = cg.new_Pvariable(filter_id)
await cg.register_component(var, {})
template_ = await cg.templatable(config, [], cg.uint32)
cg.add(var.set_timeout_value(template_))
return var
@register_filter(
"delayed_on_off",
DelayedOnOffFilter,
@@ -386,6 +414,14 @@ def validate_click_timing(value):
return value
def validate_publish_initial_state(value):
value = cv.boolean(value)
_LOGGER.warning(
"The 'publish_initial_state' option has been replaced by 'trigger_on_initial_state' and will be removed in a future release"
)
return value
_BINARY_SENSOR_SCHEMA = (
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
.extend(cv.MQTT_COMPONENT_SCHEMA)
@@ -395,7 +431,12 @@ _BINARY_SENSOR_SCHEMA = (
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
mqtt.MQTTBinarySensorComponent
),
cv.Optional(CONF_PUBLISH_INITIAL_STATE): cv.boolean,
cv.Exclusive(
CONF_PUBLISH_INITIAL_STATE, CONF_TRIGGER_ON_INITIAL_STATE
): validate_publish_initial_state,
cv.Exclusive(
CONF_TRIGGER_ON_INITIAL_STATE, CONF_TRIGGER_ON_INITIAL_STATE
): cv.boolean,
cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
cv.Optional(CONF_FILTERS): validate_filters,
cv.Optional(CONF_ON_PRESS): automation.validate_automation(
@@ -454,11 +495,19 @@ _BINARY_SENSOR_SCHEMA = (
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger),
}
),
cv.Optional(CONF_ON_STATE_CHANGE): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateChangeTrigger),
}
),
}
)
)
_BINARY_SENSOR_SCHEMA.add_extra(entity_duplicate_validator("binary_sensor"))
def binary_sensor_schema(
class_: MockObjClass = cv.UNDEFINED,
*,
@@ -489,12 +538,14 @@ BINARY_SENSOR_SCHEMA.add_extra(cv.deprecated_schema_constant("binary_sensor"))
async def setup_binary_sensor_core_(var, config):
await setup_entity(var, config)
await setup_entity(var, config, "binary_sensor")
if (device_class := config.get(CONF_DEVICE_CLASS)) is not None:
cg.add(var.set_device_class(device_class))
if publish_initial_state := config.get(CONF_PUBLISH_INITIAL_STATE):
cg.add(var.set_publish_initial_state(publish_initial_state))
trigger = config.get(CONF_TRIGGER_ON_INITIAL_STATE, False) or config.get(
CONF_PUBLISH_INITIAL_STATE, False
)
cg.add(var.set_trigger_on_initial_state(trigger))
if inverted := config.get(CONF_INVERTED):
cg.add(var.set_inverted(inverted))
if filters_config := config.get(CONF_FILTERS):
@@ -542,6 +593,17 @@ async def setup_binary_sensor_core_(var, config):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [(bool, "x")], conf)
for conf in config.get(CONF_ON_STATE_CHANGE, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(
trigger,
[
(cg.optional.template(bool), "x_previous"),
(cg.optional.template(bool), "x"),
],
conf,
)
if mqtt_id := config.get(CONF_MQTT_ID):
mqtt_ = cg.new_Pvariable(mqtt_id, var)
await mqtt.register_mqtt_component(mqtt_, config)
@@ -554,6 +616,7 @@ async def register_binary_sensor(var, config):
if not CORE.has_id(config[CONF_ID]):
var = cg.Pvariable(config[CONF_ID], var)
cg.add(cg.App.register_binary_sensor(var))
CORE.register_platform_component("binary_sensor", var)
await setup_binary_sensor_core_(var, config)
@@ -590,3 +653,18 @@ async def binary_sensor_is_off_to_code(config, condition_id, template_arg, args)
async def to_code(config):
cg.add_define("USE_BINARY_SENSOR")
cg.add_global(binary_sensor_ns.using)
@automation.register_action(
"binary_sensor.invalidate_state",
BinarySensorInvalidateAction,
cv.maybe_simple_value(
{
cv.Required(CONF_ID): cv.use_id(BinarySensor),
},
key=CONF_ID,
),
)
async def binary_sensor_invalidate_state_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, paren)

View File

@@ -96,7 +96,7 @@ class MultiClickTrigger : public Trigger<>, public Component {
: parent_(parent), timing_(std::move(timing)) {}
void setup() override {
this->last_state_ = this->parent_->state;
this->last_state_ = this->parent_->get_state_default(false);
auto f = std::bind(&MultiClickTrigger::on_state_, this, std::placeholders::_1);
this->parent_->add_on_state_callback(f);
}
@@ -130,6 +130,14 @@ class StateTrigger : public Trigger<bool> {
}
};
class StateChangeTrigger : public Trigger<optional<bool>, optional<bool> > {
public:
explicit StateChangeTrigger(BinarySensor *parent) {
parent->add_full_state_callback(
[this](optional<bool> old_state, optional<bool> state) { this->trigger(old_state, state); });
}
};
template<typename... Ts> class BinarySensorCondition : public Condition<Ts...> {
public:
BinarySensorCondition(BinarySensor *parent, bool state) : parent_(parent), state_(state) {}
@@ -154,5 +162,15 @@ template<typename... Ts> class BinarySensorPublishAction : public Action<Ts...>
BinarySensor *sensor_;
};
template<typename... Ts> class BinarySensorInvalidateAction : public Action<Ts...> {
public:
explicit BinarySensorInvalidateAction(BinarySensor *sensor) : sensor_(sensor) {}
void play(Ts... x) override { this->sensor_->invalidate_state(); }
protected:
BinarySensor *sensor_;
};
} // namespace binary_sensor
} // namespace esphome

View File

@@ -7,42 +7,25 @@ namespace binary_sensor {
static const char *const TAG = "binary_sensor";
void BinarySensor::add_on_state_callback(std::function<void(bool)> &&callback) {
this->state_callback_.add(std::move(callback));
}
void BinarySensor::publish_state(bool state) {
if (!this->publish_dedup_.next(state))
return;
void BinarySensor::publish_state(bool new_state) {
if (this->filter_list_ == nullptr) {
this->send_state_internal(state, false);
this->send_state_internal(new_state);
} else {
this->filter_list_->input(state, false);
this->filter_list_->input(new_state);
}
}
void BinarySensor::publish_initial_state(bool state) {
if (!this->publish_dedup_.next(state))
return;
if (this->filter_list_ == nullptr) {
this->send_state_internal(state, true);
} else {
this->filter_list_->input(state, true);
void BinarySensor::publish_initial_state(bool new_state) {
this->invalidate_state();
this->publish_state(new_state);
}
void BinarySensor::send_state_internal(bool new_state) {
// copy the new state to the visible property for backwards compatibility, before any callbacks
this->state = new_state;
// Note that set_state_ de-dups and will only trigger callbacks if the state has actually changed
if (this->set_state_(new_state)) {
ESP_LOGD(TAG, "'%s': New state is %s", this->get_name().c_str(), ONOFF(new_state));
}
}
void BinarySensor::send_state_internal(bool state, bool is_initial) {
if (is_initial) {
ESP_LOGD(TAG, "'%s': Sending initial state %s", this->get_name().c_str(), ONOFF(state));
} else {
ESP_LOGD(TAG, "'%s': Sending state %s", this->get_name().c_str(), ONOFF(state));
}
this->has_state_ = true;
this->state = state;
if (!is_initial || this->publish_initial_state_) {
this->state_callback_.call(state);
}
}
BinarySensor::BinarySensor() : state(false) {}
void BinarySensor::add_filter(Filter *filter) {
filter->parent_ = this;
@@ -60,7 +43,6 @@ void BinarySensor::add_filters(const std::vector<Filter *> &filters) {
this->add_filter(filter);
}
}
bool BinarySensor::has_state() const { return this->has_state_; }
bool BinarySensor::is_status_binary_sensor() const { return false; }
} // namespace binary_sensor

View File

@@ -1,6 +1,5 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/helpers.h"
#include "esphome/components/binary_sensor/filter.h"
@@ -34,52 +33,39 @@ namespace binary_sensor {
* The sub classes should notify the front-end of new states via the publish_state() method which
* handles inverted inputs for you.
*/
class BinarySensor : public EntityBase, public EntityBase_DeviceClass {
class BinarySensor : public StatefulEntityBase<bool>, public EntityBase_DeviceClass {
public:
explicit BinarySensor();
/** Add a callback to be notified of state changes.
*
* @param callback The void(bool) callback.
*/
void add_on_state_callback(std::function<void(bool)> &&callback);
explicit BinarySensor(){};
/** Publish a new state to the front-end.
*
* @param state The new state.
* @param new_state The new state.
*/
void publish_state(bool state);
void publish_state(bool new_state);
/** Publish the initial state, this will not make the callback manager send callbacks
* and is meant only for the initial state on boot.
*
* @param state The new state.
* @param new_state The new state.
*/
void publish_initial_state(bool state);
/// The current reported state of the binary sensor.
bool state{false};
void publish_initial_state(bool new_state);
void add_filter(Filter *filter);
void add_filters(const std::vector<Filter *> &filters);
void set_publish_initial_state(bool publish_initial_state) { this->publish_initial_state_ = publish_initial_state; }
// ========== INTERNAL METHODS ==========
// (In most use cases you won't need these)
void send_state_internal(bool state, bool is_initial);
void send_state_internal(bool new_state);
/// Return whether this binary sensor has outputted a state.
virtual bool has_state() const;
virtual bool is_status_binary_sensor() const;
// For backward compatibility, provide an accessible property
bool state{};
protected:
CallbackManager<void(bool)> state_callback_{};
Filter *filter_list_{nullptr};
bool has_state_{false};
bool publish_initial_state_{false};
Deduplicator<bool> publish_dedup_;
};
class BinarySensorInitiallyOff : public BinarySensor {

View File

@@ -9,37 +9,42 @@ namespace binary_sensor {
static const char *const TAG = "sensor.filter";
void Filter::output(bool value, bool is_initial) {
void Filter::output(bool value) {
if (this->next_ == nullptr) {
this->parent_->send_state_internal(value);
} else {
this->next_->input(value);
}
}
void Filter::input(bool value) {
if (!this->dedup_.next(value))
return;
if (this->next_ == nullptr) {
this->parent_->send_state_internal(value, is_initial);
} else {
this->next_->input(value, is_initial);
}
}
void Filter::input(bool value, bool is_initial) {
auto b = this->new_value(value, is_initial);
auto b = this->new_value(value);
if (b.has_value()) {
this->output(*b, is_initial);
this->output(*b);
}
}
optional<bool> DelayedOnOffFilter::new_value(bool value, bool is_initial) {
void TimeoutFilter::input(bool value) {
this->set_timeout("timeout", this->timeout_delay_.value(), [this]() { this->parent_->invalidate_state(); });
// we do not de-dup here otherwise changes from invalid to valid state will not be output
this->output(value);
}
optional<bool> DelayedOnOffFilter::new_value(bool value) {
if (value) {
this->set_timeout("ON_OFF", this->on_delay_.value(), [this, is_initial]() { this->output(true, is_initial); });
this->set_timeout("ON_OFF", this->on_delay_.value(), [this]() { this->output(true); });
} else {
this->set_timeout("ON_OFF", this->off_delay_.value(), [this, is_initial]() { this->output(false, is_initial); });
this->set_timeout("ON_OFF", this->off_delay_.value(), [this]() { this->output(false); });
}
return {};
}
float DelayedOnOffFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
optional<bool> DelayedOnFilter::new_value(bool value, bool is_initial) {
optional<bool> DelayedOnFilter::new_value(bool value) {
if (value) {
this->set_timeout("ON", this->delay_.value(), [this, is_initial]() { this->output(true, is_initial); });
this->set_timeout("ON", this->delay_.value(), [this]() { this->output(true); });
return {};
} else {
this->cancel_timeout("ON");
@@ -49,9 +54,9 @@ optional<bool> DelayedOnFilter::new_value(bool value, bool is_initial) {
float DelayedOnFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
optional<bool> DelayedOffFilter::new_value(bool value, bool is_initial) {
optional<bool> DelayedOffFilter::new_value(bool value) {
if (!value) {
this->set_timeout("OFF", this->delay_.value(), [this, is_initial]() { this->output(false, is_initial); });
this->set_timeout("OFF", this->delay_.value(), [this]() { this->output(false); });
return {};
} else {
this->cancel_timeout("OFF");
@@ -61,11 +66,11 @@ optional<bool> DelayedOffFilter::new_value(bool value, bool is_initial) {
float DelayedOffFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
optional<bool> InvertFilter::new_value(bool value, bool is_initial) { return !value; }
optional<bool> InvertFilter::new_value(bool value) { return !value; }
AutorepeatFilter::AutorepeatFilter(std::vector<AutorepeatFilterTiming> timings) : timings_(std::move(timings)) {}
optional<bool> AutorepeatFilter::new_value(bool value, bool is_initial) {
optional<bool> AutorepeatFilter::new_value(bool value) {
if (value) {
// Ignore if already running
if (this->active_timing_ != 0)
@@ -101,7 +106,7 @@ void AutorepeatFilter::next_timing_() {
void AutorepeatFilter::next_value_(bool val) {
const AutorepeatFilterTiming &timing = this->timings_[this->active_timing_ - 2];
this->output(val, false); // This is at least the second one so not initial
this->output(val); // This is at least the second one so not initial
this->set_timeout("ON_OFF", val ? timing.time_on : timing.time_off, [this, val]() { this->next_value_(!val); });
}
@@ -109,18 +114,18 @@ float AutorepeatFilter::get_setup_priority() const { return setup_priority::HARD
LambdaFilter::LambdaFilter(std::function<optional<bool>(bool)> f) : f_(std::move(f)) {}
optional<bool> LambdaFilter::new_value(bool value, bool is_initial) { return this->f_(value); }
optional<bool> LambdaFilter::new_value(bool value) { return this->f_(value); }
optional<bool> SettleFilter::new_value(bool value, bool is_initial) {
optional<bool> SettleFilter::new_value(bool value) {
if (!this->steady_) {
this->set_timeout("SETTLE", this->delay_.value(), [this, value, is_initial]() {
this->set_timeout("SETTLE", this->delay_.value(), [this, value]() {
this->steady_ = true;
this->output(value, is_initial);
this->output(value);
});
return {};
} else {
this->steady_ = false;
this->output(value, is_initial);
this->output(value);
this->set_timeout("SETTLE", this->delay_.value(), [this]() { this->steady_ = true; });
return value;
}

View File

@@ -14,11 +14,11 @@ class BinarySensor;
class Filter {
public:
virtual optional<bool> new_value(bool value, bool is_initial) = 0;
virtual optional<bool> new_value(bool value) = 0;
void input(bool value, bool is_initial);
virtual void input(bool value);
void output(bool value, bool is_initial);
void output(bool value);
protected:
friend BinarySensor;
@@ -28,9 +28,19 @@ class Filter {
Deduplicator<bool> dedup_;
};
class TimeoutFilter : public Filter, public Component {
public:
optional<bool> new_value(bool value) override { return value; }
void input(bool value) override;
template<typename T> void set_timeout_value(T timeout) { this->timeout_delay_ = timeout; }
protected:
TemplatableValue<uint32_t> timeout_delay_{};
};
class DelayedOnOffFilter : public Filter, public Component {
public:
optional<bool> new_value(bool value, bool is_initial) override;
optional<bool> new_value(bool value) override;
float get_setup_priority() const override;
@@ -44,7 +54,7 @@ class DelayedOnOffFilter : public Filter, public Component {
class DelayedOnFilter : public Filter, public Component {
public:
optional<bool> new_value(bool value, bool is_initial) override;
optional<bool> new_value(bool value) override;
float get_setup_priority() const override;
@@ -56,7 +66,7 @@ class DelayedOnFilter : public Filter, public Component {
class DelayedOffFilter : public Filter, public Component {
public:
optional<bool> new_value(bool value, bool is_initial) override;
optional<bool> new_value(bool value) override;
float get_setup_priority() const override;
@@ -68,7 +78,7 @@ class DelayedOffFilter : public Filter, public Component {
class InvertFilter : public Filter {
public:
optional<bool> new_value(bool value, bool is_initial) override;
optional<bool> new_value(bool value) override;
};
struct AutorepeatFilterTiming {
@@ -86,7 +96,7 @@ class AutorepeatFilter : public Filter, public Component {
public:
explicit AutorepeatFilter(std::vector<AutorepeatFilterTiming> timings);
optional<bool> new_value(bool value, bool is_initial) override;
optional<bool> new_value(bool value) override;
float get_setup_priority() const override;
@@ -102,7 +112,7 @@ class LambdaFilter : public Filter {
public:
explicit LambdaFilter(std::function<optional<bool>(bool)> f);
optional<bool> new_value(bool value, bool is_initial) override;
optional<bool> new_value(bool value) override;
protected:
std::function<optional<bool>(bool)> f_;
@@ -110,7 +120,7 @@ class LambdaFilter : public Filter {
class SettleFilter : public Filter, public Component {
public:
optional<bool> new_value(bool value, bool is_initial) override;
optional<bool> new_value(bool value) override;
float get_setup_priority() const override;

View File

@@ -196,14 +196,17 @@ void BL0942::received_package_(DataPacket *data) {
}
void BL0942::dump_config() { // NOLINT(readability-function-cognitive-complexity)
ESP_LOGCONFIG(TAG, "BL0942:");
ESP_LOGCONFIG(TAG, " Reset: %s", TRUEFALSE(this->reset_));
ESP_LOGCONFIG(TAG, " Address: %d", this->address_);
ESP_LOGCONFIG(TAG, " Nominal line frequency: %d Hz", this->line_freq_);
ESP_LOGCONFIG(TAG, " Current reference: %f", this->current_reference_);
ESP_LOGCONFIG(TAG, " Energy reference: %f", this->energy_reference_);
ESP_LOGCONFIG(TAG, " Power reference: %f", this->power_reference_);
ESP_LOGCONFIG(TAG, " Voltage reference: %f", this->voltage_reference_);
ESP_LOGCONFIG(TAG,
"BL0942:\n"
" Reset: %s\n"
" Address: %d\n"
" Nominal line frequency: %d Hz\n"
" Current reference: %f\n"
" Energy reference: %f\n"
" Power reference: %f\n"
" Voltage reference: %f",
TRUEFALSE(this->reset_), this->address_, this->line_freq_, this->current_reference_,
this->energy_reference_, this->power_reference_, this->voltage_reference_);
LOG_SENSOR("", "Voltage", this->voltage_sensor_);
LOG_SENSOR("", "Current", this->current_sensor_);
LOG_SENSOR("", "Power", this->power_sensor_);

View File

@@ -1,7 +1,8 @@
from esphome import automation
from esphome.automation import maybe_simple_id
import esphome.codegen as cg
from esphome.components import esp32_ble_client, esp32_ble_tracker
from esphome.components import esp32_ble, esp32_ble_client, esp32_ble_tracker
from esphome.components.esp32_ble import BTLoggers
import esphome.config_validation as cv
from esphome.const import (
CONF_CHARACTERISTIC_UUID,
@@ -287,6 +288,9 @@ async def remove_bond_to_code(config, action_id, template_arg, args):
async def to_code(config):
# Register the loggers this component needs
esp32_ble.register_bt_logger(BTLoggers.GATT, BTLoggers.SMP)
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await esp32_ble_tracker.register_client(var, config)

View File

@@ -10,9 +10,12 @@ static const char *const TAG = "ble_binary_output";
void BLEBinaryOutput::dump_config() {
ESP_LOGCONFIG(TAG, "BLE Binary Output:");
ESP_LOGCONFIG(TAG, " MAC address : %s", this->parent_->address_str().c_str());
ESP_LOGCONFIG(TAG, " Service UUID : %s", this->service_uuid_.to_string().c_str());
ESP_LOGCONFIG(TAG, " Characteristic UUID: %s", this->char_uuid_.to_string().c_str());
ESP_LOGCONFIG(TAG,
" MAC address : %s\n"
" Service UUID : %s\n"
" Characteristic UUID: %s",
this->parent_->address_str().c_str(), this->service_uuid_.to_string().c_str(),
this->char_uuid_.to_string().c_str());
LOG_BINARY_OUTPUT(this);
}

View File

@@ -11,7 +11,11 @@ namespace ble_client {
static const char *const TAG = "ble_rssi_sensor";
void BLEClientRSSISensor::loop() {}
void BLEClientRSSISensor::loop() {
// Parent BLEClientNode has a loop() method, but this component uses
// polling via update() and BLE GAP callbacks so loop isn't needed
this->disable_loop();
}
void BLEClientRSSISensor::dump_config() {
LOG_SENSOR("", "BLE Client RSSI Sensor", this);

View File

@@ -1,7 +1,7 @@
#include "ble_sensor.h"
#include "esphome/core/log.h"
#include "esphome/core/application.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#ifdef USE_ESP32
@@ -11,15 +11,22 @@ namespace ble_client {
static const char *const TAG = "ble_sensor";
void BLESensor::loop() {}
void BLESensor::loop() {
// Parent BLEClientNode has a loop() method, but this component uses
// polling via update() and BLE callbacks so loop isn't needed
this->disable_loop();
}
void BLESensor::dump_config() {
LOG_SENSOR("", "BLE Sensor", this);
ESP_LOGCONFIG(TAG, " MAC address : %s", this->parent()->address_str().c_str());
ESP_LOGCONFIG(TAG, " Service UUID : %s", this->service_uuid_.to_string().c_str());
ESP_LOGCONFIG(TAG, " Characteristic UUID: %s", this->char_uuid_.to_string().c_str());
ESP_LOGCONFIG(TAG, " Descriptor UUID : %s", this->descr_uuid_.to_string().c_str());
ESP_LOGCONFIG(TAG, " Notifications : %s", YESNO(this->notify_));
ESP_LOGCONFIG(TAG,
" MAC address : %s\n"
" Service UUID : %s\n"
" Characteristic UUID: %s\n"
" Descriptor UUID : %s\n"
" Notifications : %s",
this->parent()->address_str().c_str(), this->service_uuid_.to_string().c_str(),
this->char_uuid_.to_string().c_str(), this->descr_uuid_.to_string().c_str(), YESNO(this->notify_));
LOG_UPDATE_INTERVAL(this);
}

View File

@@ -14,15 +14,22 @@ static const char *const TAG = "ble_text_sensor";
static const std::string EMPTY = "";
void BLETextSensor::loop() {}
void BLETextSensor::loop() {
// Parent BLEClientNode has a loop() method, but this component uses
// polling via update() and BLE callbacks so loop isn't needed
this->disable_loop();
}
void BLETextSensor::dump_config() {
LOG_TEXT_SENSOR("", "BLE Text Sensor", this);
ESP_LOGCONFIG(TAG, " MAC address : %s", this->parent()->address_str().c_str());
ESP_LOGCONFIG(TAG, " Service UUID : %s", this->service_uuid_.to_string().c_str());
ESP_LOGCONFIG(TAG, " Characteristic UUID: %s", this->char_uuid_.to_string().c_str());
ESP_LOGCONFIG(TAG, " Descriptor UUID : %s", this->descr_uuid_.to_string().c_str());
ESP_LOGCONFIG(TAG, " Notifications : %s", YESNO(this->notify_));
ESP_LOGCONFIG(TAG,
" MAC address : %s\n"
" Service UUID : %s\n"
" Characteristic UUID: %s\n"
" Descriptor UUID : %s\n"
" Notifications : %s",
this->parent()->address_str().c_str(), this->service_uuid_.to_string().c_str(),
this->char_uuid_.to_string().c_str(), this->descr_uuid_.to_string().c_str(), YESNO(this->notify_));
LOG_UPDATE_INTERVAL(this);
}

View File

@@ -1,6 +1,7 @@
import esphome.codegen as cg
from esphome.components import esp32_ble_client, esp32_ble_tracker
from esphome.components import esp32_ble, esp32_ble_client, esp32_ble_tracker
from esphome.components.esp32 import add_idf_sdkconfig_option
from esphome.components.esp32_ble import BTLoggers
import esphome.config_validation as cv
from esphome.const import CONF_ACTIVE, CONF_ID
@@ -77,6 +78,9 @@ CONFIG_SCHEMA = cv.All(
async def to_code(config):
# Register the loggers this component needs
esp32_ble.register_bt_logger(BTLoggers.GATT, BTLoggers.L2CAP, BTLoggers.SMP)
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)

View File

@@ -26,10 +26,17 @@ class BluetoothConnection : public esp32_ble_client::BLEClientBase {
protected:
friend class BluetoothProxy;
bool seen_mtu_or_services_{false};
int16_t send_service_{-2};
// Memory optimized layout for 32-bit systems
// Group 1: Pointers (4 bytes each, naturally aligned)
BluetoothProxy *proxy_;
// Group 2: 2-byte types
int16_t send_service_{-2}; // Needs to handle negative values and service count
// Group 3: 1-byte types
bool seen_mtu_or_services_{false};
// 1 byte used, 1 byte padding
};
} // namespace bluetooth_proxy

View File

@@ -58,7 +58,7 @@ static std::vector<api::BluetoothLERawAdvertisement> &get_batch_buffer() {
return batch_buffer;
}
bool BluetoothProxy::parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) {
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_)
return false;
@@ -73,7 +73,7 @@ bool BluetoothProxy::parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_p
// Add new advertisements to the batch buffer
for (size_t i = 0; i < count; i++) {
auto &result = advertisements[i];
auto &result = scan_results[i];
uint8_t length = result.adv_data_len + result.scan_rsp_len;
batch_buffer.emplace_back();
@@ -146,9 +146,11 @@ void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &devi
void BluetoothProxy::dump_config() {
ESP_LOGCONFIG(TAG, "Bluetooth Proxy:");
ESP_LOGCONFIG(TAG, " Active: %s", YESNO(this->active_));
ESP_LOGCONFIG(TAG, " Connections: %d", this->connections_.size());
ESP_LOGCONFIG(TAG, " Raw advertisements: %s", YESNO(this->raw_advertisements_));
ESP_LOGCONFIG(TAG,
" Active: %s\n"
" Connections: %d\n"
" Raw advertisements: %s",
YESNO(this->active_), this->connections_.size(), YESNO(this->raw_advertisements_));
}
int BluetoothProxy::get_bluetooth_connections_free() {

View File

@@ -52,7 +52,7 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com
public:
BluetoothProxy();
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
bool parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) override;
bool parse_devices(const esp32_ble::BLEScanResult *scan_results, size_t count) override;
void dump_config() override;
void setup() override;
void loop() override;
@@ -134,11 +134,17 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com
BluetoothConnection *get_connection_(uint64_t address, bool reserve);
bool active_;
std::vector<BluetoothConnection *> connections_{};
// Memory optimized layout for 32-bit systems
// Group 1: Pointers (4 bytes each, naturally aligned)
api::APIConnection *api_connection_{nullptr};
// Group 2: Container types (typically 12 bytes on 32-bit)
std::vector<BluetoothConnection *> connections_{};
// Group 3: 1-byte types grouped together
bool active_;
bool raw_advertisements_{false};
// 2 bytes used, 2 bytes padding
};
extern BluetoothProxy *global_bluetooth_proxy; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)

View File

@@ -93,9 +93,8 @@ void BME280Component::setup() {
// Mark as not failed before initializing. Some devices will turn off sensors to save on batteries
// and when they come back on, the COMPONENT_STATE_FAILED bit must be unset on the component.
if ((this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED) {
this->component_state_ &= ~COMPONENT_STATE_MASK;
this->component_state_ |= COMPONENT_STATE_CONSTRUCTION;
if (this->is_failed()) {
this->reset_to_construction_state();
}
if (!this->read_byte(BME280_REGISTER_CHIPID, &chip_id)) {

View File

@@ -12,8 +12,8 @@ from esphome.const import (
CONF_OVERSAMPLING,
CONF_PRESSURE,
CONF_TEMPERATURE,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
ICON_GAS_CYLINDER,
STATE_CLASS_MEASUREMENT,

View File

@@ -1,6 +1,6 @@
#include "bme680_bsec.h"
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include <string>
namespace esphome {
@@ -159,11 +159,15 @@ void BME680BSECComponent::dump_config() {
this->bme680_status_);
}
ESP_LOGCONFIG(TAG, " Temperature Offset: %.2f", this->temperature_offset_);
ESP_LOGCONFIG(TAG, " IAQ Mode: %s", this->iaq_mode_ == IAQ_MODE_STATIC ? "Static" : "Mobile");
ESP_LOGCONFIG(TAG, " Supply Voltage: %sV", this->supply_voltage_ == SUPPLY_VOLTAGE_3V3 ? "3.3" : "1.8");
ESP_LOGCONFIG(TAG, " Sample Rate: %s", BME680_BSEC_SAMPLE_RATE_LOG(this->sample_rate_));
ESP_LOGCONFIG(TAG, " State Save Interval: %ims", this->state_save_interval_ms_);
ESP_LOGCONFIG(TAG,
" Temperature Offset: %.2f\n"
" IAQ Mode: %s\n"
" Supply Voltage: %sV\n"
" Sample Rate: %s\n"
" State Save Interval: %ims",
this->temperature_offset_, this->iaq_mode_ == IAQ_MODE_STATIC ? "Static" : "Mobile",
this->supply_voltage_ == SUPPLY_VOLTAGE_3V3 ? "3.3" : "1.8",
BME680_BSEC_SAMPLE_RATE_LOG(this->sample_rate_), this->state_save_interval_ms_);
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
ESP_LOGCONFIG(TAG, " Sample Rate: %s", BME680_BSEC_SAMPLE_RATE_LOG(this->temperature_sample_rate_));

View File

@@ -58,13 +58,13 @@ void BME68xBSEC2Component::setup() {
}
void BME68xBSEC2Component::dump_config() {
ESP_LOGCONFIG(TAG, "BME68X via BSEC2:");
ESP_LOGCONFIG(TAG, " BSEC2 version: %d.%d.%d.%d", this->version_.major, this->version_.minor,
this->version_.major_bugfix, this->version_.minor_bugfix);
ESP_LOGCONFIG(TAG, " BSEC2 configuration blob:");
ESP_LOGCONFIG(TAG, " Configured: %s", YESNO(this->bsec2_blob_configured_));
ESP_LOGCONFIG(TAG,
"BME68X via BSEC2:\n"
" BSEC2 version: %d.%d.%d.%d\n"
" BSEC2 configuration blob:\n"
" Configured: %s",
this->version_.major, this->version_.minor, this->version_.major_bugfix, this->version_.minor_bugfix,
YESNO(this->bsec2_blob_configured_));
if (this->bsec2_configuration_ != nullptr && this->bsec2_configuration_length_) {
ESP_LOGCONFIG(TAG, " Size: %" PRIu32, this->bsec2_configuration_length_);
}
@@ -77,11 +77,14 @@ void BME68xBSEC2Component::dump_config() {
if (this->algorithm_output_ != ALGORITHM_OUTPUT_IAQ) {
ESP_LOGCONFIG(TAG, " Algorithm output: %s", BME68X_BSEC2_ALGORITHM_OUTPUT_LOG(this->algorithm_output_));
}
ESP_LOGCONFIG(TAG, " Operating age: %s", BME68X_BSEC2_OPERATING_AGE_LOG(this->operating_age_));
ESP_LOGCONFIG(TAG, " Sample rate: %s", BME68X_BSEC2_SAMPLE_RATE_LOG(this->sample_rate_));
ESP_LOGCONFIG(TAG, " Voltage: %s", BME68X_BSEC2_VOLTAGE_LOG(this->voltage_));
ESP_LOGCONFIG(TAG, " State save interval: %ims", this->state_save_interval_ms_);
ESP_LOGCONFIG(TAG, " Temperature offset: %.2f", this->temperature_offset_);
ESP_LOGCONFIG(TAG,
" Operating age: %s\n"
" Sample rate: %s\n"
" Voltage: %s\n"
" State save interval: %ims\n"
" Temperature offset: %.2f",
BME68X_BSEC2_OPERATING_AGE_LOG(this->operating_age_), BME68X_BSEC2_SAMPLE_RATE_LOG(this->sample_rate_),
BME68X_BSEC2_VOLTAGE_LOG(this->voltage_), this->state_save_interval_ms_, this->temperature_offset_);
#ifdef USE_SENSOR
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);

View File

@@ -148,8 +148,10 @@ void BMP3XXComponent::setup() {
}
void BMP3XXComponent::dump_config() {
ESP_LOGCONFIG(TAG, "BMP3XX:");
ESP_LOGCONFIG(TAG, " Type: %s (0x%X)", LOG_STR_ARG(chip_type_to_str(this->chip_id_.reg)), this->chip_id_.reg);
ESP_LOGCONFIG(TAG,
"BMP3XX:\n"
" Type: %s (0x%X)",
LOG_STR_ARG(chip_type_to_str(this->chip_id_.reg)), this->chip_id_.reg);
switch (this->error_code_) {
case NONE:
break;

View File

@@ -98,14 +98,20 @@ void BMP581Component::dump_config() {
if (this->temperature_sensor_) {
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
ESP_LOGCONFIG(TAG, " IIR Filter: %s", LOG_STR_ARG(iir_filter_to_str(this->iir_temperature_level_)));
ESP_LOGCONFIG(TAG, " Oversampling: %s", LOG_STR_ARG(oversampling_to_str(this->temperature_oversampling_)));
ESP_LOGCONFIG(TAG,
" IIR Filter: %s\n"
" Oversampling: %s",
LOG_STR_ARG(iir_filter_to_str(this->iir_temperature_level_)),
LOG_STR_ARG(oversampling_to_str(this->temperature_oversampling_)));
}
if (this->pressure_sensor_) {
LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
ESP_LOGCONFIG(TAG, " IIR Filter: %s", LOG_STR_ARG(iir_filter_to_str(this->iir_pressure_level_)));
ESP_LOGCONFIG(TAG, " Oversampling: %s", LOG_STR_ARG(oversampling_to_str(this->pressure_oversampling_)));
ESP_LOGCONFIG(TAG,
" IIR Filter: %s\n"
" Oversampling: %s",
LOG_STR_ARG(iir_filter_to_str(this->iir_pressure_level_)),
LOG_STR_ARG(oversampling_to_str(this->pressure_oversampling_)));
}
}

View File

@@ -26,8 +26,10 @@ void BP1658CJ::dump_config() {
ESP_LOGCONFIG(TAG, "BP1658CJ:");
LOG_PIN(" Data Pin: ", this->data_pin_);
LOG_PIN(" Clock Pin: ", this->clock_pin_);
ESP_LOGCONFIG(TAG, " Color Channels Max Power: %u", this->max_power_color_channels_);
ESP_LOGCONFIG(TAG, " White Channels Max Power: %u", this->max_power_white_channels_);
ESP_LOGCONFIG(TAG,
" Color Channels Max Power: %u\n"
" White Channels Max Power: %u",
this->max_power_color_channels_, this->max_power_white_channels_);
}
void BP1658CJ::loop() {

View File

@@ -18,8 +18,8 @@ from esphome.const import (
DEVICE_CLASS_UPDATE,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@esphome/core"]
IS_PLATFORM_COMPONENT = True
@@ -61,6 +61,9 @@ _BUTTON_SCHEMA = (
)
_BUTTON_SCHEMA.add_extra(entity_duplicate_validator("button"))
def button_schema(
class_: MockObjClass,
*,
@@ -87,7 +90,7 @@ BUTTON_SCHEMA.add_extra(cv.deprecated_schema_constant("button"))
async def setup_button_core_(var, config):
await setup_entity(var, config)
await setup_entity(var, config, "button")
for conf in config.get(CONF_ON_PRESS, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
@@ -108,6 +111,7 @@ async def register_button(var, config):
if not CORE.has_id(config[CONF_ID]):
var = cg.Pvariable(config[CONF_ID], var)
cg.add(cg.App.register_button(var))
CORE.register_platform_component("button", var)
await setup_button_core_(var, config)

View File

@@ -1,4 +1,5 @@
import re
from esphome import automation
import esphome.codegen as cg
import esphome.config_validation as cv

View File

@@ -52,9 +52,11 @@ void CAP1188Component::dump_config() {
ESP_LOGCONFIG(TAG, "CAP1188:");
LOG_I2C_DEVICE(this);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
ESP_LOGCONFIG(TAG, " Product ID: 0x%x", this->cap1188_product_id_);
ESP_LOGCONFIG(TAG, " Manufacture ID: 0x%x", this->cap1188_manufacture_id_);
ESP_LOGCONFIG(TAG, " Revision ID: 0x%x", this->cap1188_revision_);
ESP_LOGCONFIG(TAG,
" Product ID: 0x%x\n"
" Manufacture ID: 0x%x\n"
" Revision ID: 0x%x",
this->cap1188_product_id_, this->cap1188_manufacture_id_, this->cap1188_revision_);
switch (this->error_code_) {
case COMMUNICATION_FAILED:

View File

@@ -41,6 +41,7 @@ async def to_code(config):
if CORE.using_arduino:
if CORE.is_esp32:
cg.add_library("ESP32 Async UDP", None)
cg.add_library("DNSServer", None)
cg.add_library("WiFi", None)
if CORE.is_esp8266:

View File

@@ -37,7 +37,12 @@ void CaptivePortal::handle_wifisave(AsyncWebServerRequest *request) {
request->redirect("/?save");
}
void CaptivePortal::setup() {}
void CaptivePortal::setup() {
#ifndef USE_ARDUINO
// No DNS server needed for non-Arduino frameworks
this->disable_loop();
#endif
}
void CaptivePortal::start() {
this->base_->init();
if (!this->initialized_) {
@@ -50,6 +55,8 @@ void CaptivePortal::start() {
this->dns_server_->setErrorReplyCode(DNSReplyCode::NoError);
network::IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip();
this->dns_server_->start(53, "*", ip);
// Re-enable loop() when DNS server is started
this->enable_loop();
#endif
this->base_->get_server()->onNotFound([this](AsyncWebServerRequest *req) {
@@ -68,7 +75,11 @@ void CaptivePortal::start() {
void CaptivePortal::handleRequest(AsyncWebServerRequest *req) {
if (req->url() == "/") {
#ifndef USE_ESP8266
auto *response = req->beginResponse(200, "text/html", INDEX_GZ, sizeof(INDEX_GZ));
#else
auto *response = req->beginResponse_P(200, "text/html", INDEX_GZ, sizeof(INDEX_GZ));
#endif
response->addHeader("Content-Encoding", "gzip");
req->send(response);
return;

View File

@@ -21,8 +21,11 @@ class CaptivePortal : public AsyncWebHandler, public Component {
void dump_config() override;
#ifdef USE_ARDUINO
void loop() override {
if (this->dns_server_ != nullptr)
if (this->dns_server_ != nullptr) {
this->dns_server_->processNextRequest();
} else {
this->disable_loop();
}
}
#endif
float get_setup_priority() const override;
@@ -37,7 +40,7 @@ class CaptivePortal : public AsyncWebHandler, public Component {
#endif
}
bool canHandle(AsyncWebServerRequest *request) override {
bool canHandle(AsyncWebServerRequest *request) const override {
if (!this->active_)
return false;

View File

@@ -1,6 +1,7 @@
#include "ccs811.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace ccs811 {

View File

@@ -38,9 +38,11 @@ void CHSC6XTouchscreen::dump_config() {
ESP_LOGCONFIG(TAG, "CHSC6X Touchscreen:");
LOG_I2C_DEVICE(this);
LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
ESP_LOGCONFIG(TAG, " Touch timeout: %d", this->touch_timeout_);
ESP_LOGCONFIG(TAG, " x_raw_max_: %d", this->x_raw_max_);
ESP_LOGCONFIG(TAG, " y_raw_max_: %d", this->y_raw_max_);
ESP_LOGCONFIG(TAG,
" Touch timeout: %d\n"
" x_raw_max_: %d\n"
" y_raw_max_: %d",
this->touch_timeout_, this->x_raw_max_, this->y_raw_max_);
}
} // namespace chsc6x

View File

@@ -48,8 +48,8 @@ from esphome.const import (
CONF_WEB_SERVER,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
IS_PLATFORM_COMPONENT = True
@@ -247,6 +247,9 @@ _CLIMATE_SCHEMA = (
)
_CLIMATE_SCHEMA.add_extra(entity_duplicate_validator("climate"))
def climate_schema(
class_: MockObjClass,
*,
@@ -273,7 +276,7 @@ CLIMATE_SCHEMA.add_extra(cv.deprecated_schema_constant("climate"))
async def setup_climate_core_(var, config):
await setup_entity(var, config)
await setup_entity(var, config, "climate")
visual = config[CONF_VISUAL]
if (min_temp := visual.get(CONF_MIN_TEMPERATURE)) is not None:
@@ -443,6 +446,7 @@ async def register_climate(var, config):
if not CORE.has_id(config[CONF_ID]):
var = cg.Pvariable(config[CONF_ID], var)
cg.add(cg.App.register_climate(var))
CORE.register_platform_component("climate", var)
await setup_climate_core_(var, config)

View File

@@ -569,17 +569,22 @@ bool Climate::set_custom_preset_(const std::string &preset) {
void Climate::dump_traits_(const char *tag) {
auto traits = this->get_traits();
ESP_LOGCONFIG(tag, "ClimateTraits:");
ESP_LOGCONFIG(tag, " [x] Visual settings:");
ESP_LOGCONFIG(tag, " - Min temperature: %.1f", traits.get_visual_min_temperature());
ESP_LOGCONFIG(tag, " - Max temperature: %.1f", traits.get_visual_max_temperature());
ESP_LOGCONFIG(tag, " - Temperature step:");
ESP_LOGCONFIG(tag, " Target: %.1f", traits.get_visual_target_temperature_step());
ESP_LOGCONFIG(tag,
" [x] Visual settings:\n"
" - Min temperature: %.1f\n"
" - Max temperature: %.1f\n"
" - Temperature step:\n"
" Target: %.1f",
traits.get_visual_min_temperature(), traits.get_visual_max_temperature(),
traits.get_visual_target_temperature_step());
if (traits.get_supports_current_temperature()) {
ESP_LOGCONFIG(tag, " Current: %.1f", traits.get_visual_current_temperature_step());
}
if (traits.get_supports_target_humidity() || traits.get_supports_current_humidity()) {
ESP_LOGCONFIG(tag, " - Min humidity: %.0f", traits.get_visual_min_humidity());
ESP_LOGCONFIG(tag, " - Max humidity: %.0f", traits.get_visual_max_humidity());
ESP_LOGCONFIG(tag,
" - Min humidity: %.0f\n"
" - Max humidity: %.0f",
traits.get_visual_min_humidity(), traits.get_visual_max_humidity());
}
if (traits.get_supports_two_point_target_temperature()) {
ESP_LOGCONFIG(tag, " [x] Supports two-point target temperature");

View File

@@ -3,8 +3,8 @@
#include "esphome/core/component.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/helpers.h"
#include "esphome/core/preferences.h"
#include "esphome/core/log.h"
#include "esphome/core/preferences.h"
#include "climate_mode.h"
#include "climate_traits.h"

View File

@@ -75,10 +75,13 @@ void ClimateIR::control(const climate::ClimateCall &call) {
}
void ClimateIR::dump_config() {
LOG_CLIMATE("", "IR Climate", this);
ESP_LOGCONFIG(TAG, " Min. Temperature: %.1f°C", this->minimum_temperature_);
ESP_LOGCONFIG(TAG, " Max. Temperature: %.1f°C", this->maximum_temperature_);
ESP_LOGCONFIG(TAG, " Supports HEAT: %s", YESNO(this->supports_heat_));
ESP_LOGCONFIG(TAG, " Supports COOL: %s", YESNO(this->supports_cool_));
ESP_LOGCONFIG(TAG,
" Min. Temperature: %.1f°C\n"
" Max. Temperature: %.1f°C\n"
" Supports HEAT: %s\n"
" Supports COOL: %s",
this->minimum_temperature_, this->maximum_temperature_, YESNO(this->supports_heat_),
YESNO(this->supports_cool_));
}
} // namespace climate_ir

View File

@@ -1,10 +1,10 @@
"""CM1106 Sensor component for ESPHome."""
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.automation import maybe_simple_id
import esphome.codegen as cg
from esphome.components import sensor, uart
import esphome.config_validation as cv
from esphome.const import (
CONF_CO2,
CONF_ID,

View File

@@ -3,4 +3,5 @@
CODEOWNERS = ["@esphome/core"]
CONF_DRAW_ROUNDING = "draw_rounding"
CONF_ON_STATE_CHANGE = "on_state_change"
CONF_REQUEST_HEADERS = "request_headers"

View File

@@ -33,8 +33,8 @@ from esphome.const import (
DEVICE_CLASS_WINDOW,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
IS_PLATFORM_COMPONENT = True
@@ -126,6 +126,9 @@ _COVER_SCHEMA = (
)
_COVER_SCHEMA.add_extra(entity_duplicate_validator("cover"))
def cover_schema(
class_: MockObjClass,
*,
@@ -154,7 +157,7 @@ COVER_SCHEMA.add_extra(cv.deprecated_schema_constant("cover"))
async def setup_cover_core_(var, config):
await setup_entity(var, config)
await setup_entity(var, config, "cover")
if (device_class := config.get(CONF_DEVICE_CLASS)) is not None:
cg.add(var.set_device_class(device_class))
@@ -189,6 +192,7 @@ async def register_cover(var, config):
if not CORE.has_id(config[CONF_ID]):
var = cg.Pvariable(config[CONF_ID], var)
cg.add(cg.App.register_cover(var))
CORE.register_platform_component("cover", var)
await setup_cover_core_(var, config)

View File

@@ -319,18 +319,23 @@ bool CS5460AComponent::check_status_() {
void CS5460AComponent::dump_config() {
uint32_t state = this->get_component_state();
ESP_LOGCONFIG(TAG, "CS5460A:");
ESP_LOGCONFIG(TAG, " Init status: %s",
ESP_LOGCONFIG(TAG,
"CS5460A:\n"
" Init status: %s",
state == COMPONENT_STATE_LOOP ? "OK" : (state == COMPONENT_STATE_FAILED ? "failed" : "other"));
LOG_PIN(" CS Pin: ", cs_);
ESP_LOGCONFIG(TAG, " Samples / cycle: %" PRIu32, samples_);
ESP_LOGCONFIG(TAG, " Phase offset: %i", phase_offset_);
ESP_LOGCONFIG(TAG, " PGA Gain: %s", pga_gain_ == CS5460A_PGA_GAIN_50X ? "50x" : "10x");
ESP_LOGCONFIG(TAG, " Current gain: %.5f", current_gain_);
ESP_LOGCONFIG(TAG, " Voltage gain: %.5f", voltage_gain_);
ESP_LOGCONFIG(TAG, " Current HPF: %s", current_hpf_ ? "enabled" : "disabled");
ESP_LOGCONFIG(TAG, " Voltage HPF: %s", voltage_hpf_ ? "enabled" : "disabled");
ESP_LOGCONFIG(TAG, " Pulse energy: %.2f Wh", pulse_energy_wh_);
ESP_LOGCONFIG(TAG,
" Samples / cycle: %" PRIu32 "\n"
" Phase offset: %i\n"
" PGA Gain: %s\n"
" Current gain: %.5f\n"
" Voltage gain: %.5f\n"
" Current HPF: %s\n"
" Voltage HPF: %s\n"
" Pulse energy: %.2f Wh",
samples_, phase_offset_, pga_gain_ == CS5460A_PGA_GAIN_50X ? "50x" : "10x", current_gain_,
voltage_gain_, current_hpf_ ? "enabled" : "disabled", voltage_hpf_ ? "enabled" : "disabled",
pulse_energy_wh_);
LOG_SENSOR(" ", "Voltage", voltage_sensor_);
LOG_SENSOR(" ", "Current", current_sensor_);
LOG_SENSOR(" ", "Power", power_sensor_);

View File

@@ -223,11 +223,6 @@ void CSE7766Component::parse_data_() {
#endif
}
uint32_t CSE7766Component::get_24_bit_uint_(uint8_t start_index) {
return (uint32_t(this->raw_data_[start_index]) << 16) | (uint32_t(this->raw_data_[start_index + 1]) << 8) |
uint32_t(this->raw_data_[start_index + 2]);
}
void CSE7766Component::dump_config() {
ESP_LOGCONFIG(TAG, "CSE7766:");
LOG_SENSOR(" ", "Voltage", this->voltage_sensor_);

View File

@@ -1,6 +1,7 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/uart/uart.h"
@@ -28,7 +29,10 @@ class CSE7766Component : public Component, public uart::UARTDevice {
protected:
bool check_byte_();
void parse_data_();
uint32_t get_24_bit_uint_(uint8_t start_index);
uint32_t get_24_bit_uint_(uint8_t start_index) const {
return encode_uint24(this->raw_data_[start_index], this->raw_data_[start_index + 1],
this->raw_data_[start_index + 2]);
}
uint8_t raw_data_[24];
uint8_t raw_data_index_{0};

View File

@@ -1,4 +1,5 @@
#include "cst816_touchscreen.h"
#include "esphome/core/helpers.h"
namespace esphome {
namespace cst816 {
@@ -74,8 +75,10 @@ void CST816Touchscreen::dump_config() {
LOG_I2C_DEVICE(this);
LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
ESP_LOGCONFIG(TAG, " X Raw Min: %d, X Raw Max: %d", this->x_raw_min_, this->x_raw_max_);
ESP_LOGCONFIG(TAG, " Y Raw Min: %d, Y Raw Max: %d", this->y_raw_min_, this->y_raw_max_);
ESP_LOGCONFIG(TAG,
" X Raw Min: %d, X Raw Max: %d\n"
" Y Raw Min: %d, Y Raw Max: %d",
this->x_raw_min_, this->x_raw_max_, this->y_raw_min_, this->y_raw_max_);
const char *name;
switch (this->chip_id_) {
case CST820_CHIP_ID:

View File

@@ -151,8 +151,10 @@ void CurrentBasedCover::dump_config() {
if (this->max_duration_ != UINT32_MAX) {
ESP_LOGCONFIG(TAG, "Maximum duration: %.1fs", this->max_duration_ / 1e3f);
}
ESP_LOGCONFIG(TAG, "Start sensing delay: %.1fs", this->start_sensing_delay_ / 1e3f);
ESP_LOGCONFIG(TAG, "Malfunction detection: %s", YESNO(this->malfunction_detection_));
ESP_LOGCONFIG(TAG,
"Start sensing delay: %.1fs\n"
"Malfunction detection: %s",
this->start_sensing_delay_ / 1e3f, YESNO(this->malfunction_detection_));
}
float CurrentBasedCover::get_setup_priority() const { return setup_priority::DATA; }

View File

@@ -1,7 +1,7 @@
#include "dac7678_output.h"
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace dac7678 {

View File

@@ -1,7 +1,8 @@
#include "daly_bms.h"
#include <vector>
#include "esphome/core/log.h"
#include "esphome/core/application.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace daly_bms {

View File

@@ -22,8 +22,8 @@ from esphome.const import (
CONF_YEAR,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@rfdarter", "@jesserockz"]
@@ -84,6 +84,8 @@ _DATETIME_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
).add_extra(_validate_time_present)
_DATETIME_SCHEMA.add_extra(entity_duplicate_validator("datetime"))
def date_schema(class_: MockObjClass) -> cv.Schema:
schema = cv.Schema(
@@ -133,7 +135,7 @@ def datetime_schema(class_: MockObjClass) -> cv.Schema:
async def setup_datetime_core_(var, config):
await setup_entity(var, config)
await setup_entity(var, config, "datetime")
if (mqtt_id := config.get(CONF_MQTT_ID)) is not None:
mqtt_ = cg.new_Pvariable(mqtt_id, var)
@@ -158,7 +160,9 @@ async def setup_datetime_core_(var, config):
async def register_datetime(var, config):
if not CORE.has_id(config[CONF_ID]):
var = cg.Pvariable(config[CONF_ID], var)
cg.add(getattr(cg.App, f"register_{config[CONF_TYPE].lower()}")(var))
entity_type = config[CONF_TYPE].lower()
cg.add(getattr(cg.App, f"register_{entity_type}")(var))
CORE.register_platform_component(entity_type, var)
await setup_datetime_core_(var, config)
cg.add_define(f"USE_DATETIME_{config[CONF_TYPE]}")

View File

@@ -11,25 +11,25 @@ static const char *const TAG = "datetime.date_entity";
void DateEntity::publish_state() {
if (this->year_ == 0 || this->month_ == 0 || this->day_ == 0) {
this->has_state_ = false;
this->set_has_state(false);
return;
}
if (this->year_ < 1970 || this->year_ > 3000) {
this->has_state_ = false;
this->set_has_state(false);
ESP_LOGE(TAG, "Year must be between 1970 and 3000");
return;
}
if (this->month_ < 1 || this->month_ > 12) {
this->has_state_ = false;
this->set_has_state(false);
ESP_LOGE(TAG, "Month must be between 1 and 12");
return;
}
if (this->day_ > days_in_month(this->month_, this->year_)) {
this->has_state_ = false;
this->set_has_state(false);
ESP_LOGE(TAG, "Day must be between 1 and %d for month %d", days_in_month(this->month_, this->year_), this->month_);
return;
}
this->has_state_ = true;
this->set_has_state(true);
ESP_LOGD(TAG, "'%s': Sending date %d-%d-%d", this->get_name().c_str(), this->year_, this->month_, this->day_);
this->state_callback_.call();
}

View File

@@ -13,9 +13,6 @@ namespace datetime {
class DateTimeBase : public EntityBase {
public:
/// Return whether this Datetime has gotten a full state yet.
bool has_state() const { return this->has_state_; }
virtual ESPTime state_as_esptime() const = 0;
void add_on_state_callback(std::function<void()> &&callback) { this->state_callback_.add(std::move(callback)); }
@@ -31,8 +28,6 @@ class DateTimeBase : public EntityBase {
#ifdef USE_TIME
time::RealTimeClock *rtc_;
#endif
bool has_state_{false};
};
#ifdef USE_TIME

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