Compare commits

...

563 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
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
J. Nick Koston
0dedbcdd71 Merge branch 'multi_device' into integration 2025-06-25 13:44:20 +02:00
J. Nick Koston
4bdd08887e use a common that does not have dupes on dev 2025-06-25 00:50:18 +02:00
J. Nick Koston
1fd8ebf386 update tests now that duplicate names are validated 2025-06-25 00:35:38 +02:00
J. Nick Koston
d2fc3e749c update tests now that duplicate names are validated 2025-06-25 00:34:50 +02:00
J. Nick Koston
71fbcbceaf update tests now that duplicate names are validated 2025-06-25 00:34:27 +02:00
J. Nick Koston
27347b2088 update tests now that duplicate names are validated 2025-06-25 00:34:04 +02:00
J. Nick Koston
599993d1a5 update tests now that duplicate names are validated 2025-06-25 00:22:51 +02:00
J. Nick Koston
bf359cb8e3 update tests now that duplicate names are validated 2025-06-25 00:20:51 +02:00
J. Nick Koston
509a704410 update tests now that duplicate names are validated 2025-06-25 00:19:32 +02:00
J. Nick Koston
1f48e2b01f update tests now that duplicate names are validated 2025-06-25 00:18:40 +02:00
J. Nick Koston
8b25b1eee6 update tests now that duplicate names are validated 2025-06-25 00:18:28 +02:00
J. Nick Koston
3bbf30ff5f Merge branch 'multi_device' into integration 2025-06-25 00:04:33 +02:00
J. Nick Koston
83613726d1 fix 2025-06-25 00:04:07 +02:00
J. Nick Koston
254b6a17f3 Merge remote-tracking branch 'dala318/multi_device' into integration 2025-06-24 23:54:40 +02:00
J. Nick Koston
796e12bd70 Merge branch 'dev' into multi_device 2025-06-24 23:54:24 +02:00
J. Nick Koston
ddbe17d3f6 fixes 2025-06-24 23:40:16 +02:00
J. Nick Koston
591ec36f4a fixes 2025-06-24 23:37:58 +02:00
J. Nick Koston
41eceb72ef preen 2025-06-24 23:28:06 +02:00
J. Nick Koston
0a5f094025 cleanup 2025-06-24 23:25:46 +02:00
J. Nick Koston
ca0f3ba262 cleanup 2025-06-24 23:23:59 +02:00
J. Nick Koston
30f4e782db cleanup 2025-06-24 23:23:35 +02:00
J. Nick Koston
192158ef1a cleanup 2025-06-24 23:22:18 +02:00
J. Nick Koston
602456db40 cleanup 2025-06-24 23:13:45 +02:00
J. Nick Koston
536e45668f migrate 2025-06-24 23:09:08 +02:00
J. Nick Koston
10bf05ab0d migrate 2025-06-24 22:59:46 +02:00
J. Nick Koston
5ad1af69e4 migrate 2025-06-24 22:57:10 +02:00
J. Nick Koston
48f2911434 raise 2025-06-24 22:18:29 +02:00
J. Nick Koston
dbb0d6349a Merge branch 'multi_device' into integration 2025-06-24 18:08:14 +02:00
J. Nick Koston
ac3598f12a cleanup 2025-06-24 18:07:58 +02:00
J. Nick Koston
66201be5ca preen 2025-06-24 18:00:10 +02:00
J. Nick Koston
ac0b0b652e cleanup 2025-06-24 17:55:58 +02:00
J. Nick Koston
d89ee2df42 Update esphome/core/application.h 2025-06-24 17:52:13 +02:00
J. Nick Koston
418e248e5e cleanup 2025-06-24 17:51:05 +02:00
J. Nick Koston
8c2b141049 cleanup 2025-06-24 17:41:40 +02:00
J. Nick Koston
2f8e07302b Update esphome/core/entity_base.cpp 2025-06-24 17:10:06 +02:00
J. Nick Koston
c3776240b6 fixes 2025-06-24 17:03:23 +02:00
J. Nick Koston
e370872ec1 fix conflicts 2025-06-24 16:13:34 +02:00
Jesse Hills
d4e978369a Store reference to device on EntityBase
This is so we can get the name of the device to use as part of the object id and to internally set the name for logging.
2025-06-24 19:56:30 +12:00
Jesse Hills
8d5d7f5237 Merge branch 'dev' into multi_device 2025-06-24 16:02:03 +12:00
J. Nick Koston
5cd498fbe9 Merge branch 'multi_device' into integration 2025-06-23 22:56:28 +02:00
J. Nick Koston
250f515f08 Merge branch 'api_opt' into integration 2025-06-23 12:20:40 +02:00
J. Nick Koston
0ec0a9e313 missing ifdef 2025-06-23 12:19:21 +02:00
J. Nick Koston
184f42ef03 Merge branch 'api_opt' into integration 2025-06-23 12:10:26 +02:00
J. Nick Koston
499517418d clang-tidy 2025-06-23 12:10:15 +02:00
J. Nick Koston
606b9c1a6d Merge branch 'api_opt' into integration 2025-06-23 12:00:34 +02:00
J. Nick Koston
971e954a54 follow logging guidelines 2025-06-23 11:59:07 +02:00
J. Nick Koston
e3aaf3219d speed up test 2025-06-23 11:58:16 +02:00
J. Nick Koston
0eea1c0e40 preen 2025-06-23 11:56:09 +02:00
J. Nick Koston
0773819778 cleanup 2025-06-23 11:45:58 +02:00
J. Nick Koston
170869b7db preen 2025-06-23 11:39:25 +02:00
J. Nick Koston
5dc54782e5 preen 2025-06-23 11:38:30 +02:00
J. Nick Koston
97b26fbefe preen 2025-06-23 11:38:10 +02:00
J. Nick Koston
686cc58d6c preen 2025-06-23 11:37:59 +02:00
J. Nick Koston
76a59759b2 preen 2025-06-23 11:37:27 +02:00
J. Nick Koston
93245a24b5 preen 2025-06-23 11:36:54 +02:00
J. Nick Koston
6a22ea1c7d preen 2025-06-23 11:35:41 +02:00
J. Nick Koston
56a02409c8 preen 2025-06-23 11:34:11 +02:00
J. Nick Koston
edeafd5a53 preen 2025-06-23 11:31:38 +02:00
J. Nick Koston
f67490b69b preen 2025-06-23 11:29:04 +02:00
J. Nick Koston
b76e34fb7b preen 2025-06-23 11:25:52 +02:00
J. Nick Koston
ddbda5032b preen 2025-06-23 11:25:24 +02:00
J. Nick Koston
5898d34b0a preen 2025-06-23 11:22:45 +02:00
J. Nick Koston
b0c02341ff preen 2025-06-23 11:22:08 +02:00
J. Nick Koston
19cbc8c33b preen 2025-06-23 11:21:37 +02:00
J. Nick Koston
02e61ef5d3 preen 2025-06-23 11:20:06 +02:00
J. Nick Koston
8d5d18064d preen 2025-06-23 11:19:56 +02:00
J. Nick Koston
c5ef7ebd27 preen 2025-06-23 11:19:07 +02:00
J. Nick Koston
047a3e0e8c preen 2025-06-23 11:18:47 +02:00
J. Nick Koston
13b23f840b preen 2025-06-23 11:17:17 +02:00
J. Nick Koston
147f6012b2 preen 2025-06-23 11:16:34 +02:00
J. Nick Koston
2c315595f0 preen 2025-06-23 11:12:04 +02:00
J. Nick Koston
20405c84ac preen 2025-06-23 11:10:07 +02:00
J. Nick Koston
0bc59b97de more api loop reductions 2025-06-23 11:06:51 +02:00
J. Nick Koston
a3a3bdc7eb more api loop reductions 2025-06-23 11:02:27 +02:00
J. Nick Koston
e767f30886 more api loop reductions 2025-06-23 10:59:49 +02:00
J. Nick Koston
e8c250a03c more api loop reductions 2025-06-23 10:59:00 +02:00
J. Nick Koston
d6725fc1ca more api loop reductions 2025-06-23 10:54:50 +02:00
J. Nick Koston
8ec998ff30 more api loop reductions 2025-06-23 10:52:34 +02:00
J. Nick Koston
23cc0c7f39 Merge remote-tracking branch 'upstream/dev' into api_reboot 2025-06-23 10:48:26 +02:00
J. Nick Koston
19b8bd6aa8 Merge remote-tracking branch 'upstream/logger_disable_loop' into integration 2025-06-23 09:03:16 +02:00
J. Nick Koston
ed57e7c6b0 Update esphome/components/logger/logger.cpp 2025-06-23 09:02:22 +02:00
J. Nick Koston
9f489c9f27 Update esphome/components/logger/logger.h 2025-06-23 09:01:21 +02:00
J. Nick Koston
f036989361 Update esphome/components/logger/logger.h 2025-06-23 09:01:01 +02:00
J. Nick Koston
6afa8141c0 Update esphome/components/logger/logger.cpp 2025-06-23 09:00:46 +02:00
J. Nick Koston
587964c6f1 Merge branch 'dev' into logger_disable_loop 2025-06-23 09:00:22 +02:00
Jesse Hills
7aea82a273 Move define 2025-06-23 14:15:10 +12:00
J. Nick Koston
20f946ccaf Merge branch 'dev' into multi_device 2025-06-23 00:32:09 +02:00
Jesse Hills
e5e972231c Update testing 2025-06-23 10:26:31 +12:00
J. Nick Koston
bfa80157f2 Merge branch 'scheduler_memory_opt' into integration 2025-06-23 00:07:43 +02:00
J. Nick Koston
99b1b079d0 Reduce RAM usage for scheduled tasks 2025-06-23 00:03:01 +02:00
J. Nick Koston
5697d549a8 Use scheduler for api reboot 2025-06-22 23:44:08 +02:00
Jesse Hills
754d2874e7 `this->` 2025-06-23 09:21:29 +12:00
Jesse Hills
06de58ff8b Dont need to warning about simple string area
A single device in a single area can have a simple string as the area
2025-06-23 09:20:53 +12:00
J. Nick Koston
a0b3527710 Merge branch 'logger_memory' into integration 2025-06-22 22:59:51 +02:00
J. Nick Koston
df24f48fa1 Merge branch 'pre_preserve_looping_components' into integration 2025-06-22 22:57:38 +02:00
J. Nick Koston
13d53590b2 Pre-reserve looping components vector to reduce memory allocations 2025-06-22 22:56:31 +02:00
J. Nick Koston
5857f7b9a7 Merge remote-tracking branch 'dala318/multi_device' into multi_device 2025-06-22 21:55:46 +02:00
J. Nick Koston
a5ea0cd41f remove unreachable code 2025-06-22 21:55:23 +02:00
J. Nick Koston
d677934417 Merge branch 'dev' into multi_device 2025-06-22 21:45:28 +02:00
J. Nick Koston
ba87a0b63c cleanups 2025-06-22 21:32:20 +02:00
J. Nick Koston
b725bb3dd1 lint 2025-06-22 21:28:16 +02:00
J. Nick Koston
c34ba3deb5 lint 2025-06-22 21:25:55 +02:00
J. Nick Koston
68b13340fb lint 2025-06-22 21:24:17 +02:00
J. Nick Koston
8831999ea6 lint 2025-06-22 21:23:41 +02:00
J. Nick Koston
c1853f8b84 document design decisions 2025-06-22 21:21:29 +02:00
J. Nick Koston
2b9b7e2853 validation should happen sooner 2025-06-22 21:18:04 +02:00
J. Nick Koston
d3b18debf9 validate sooner 2025-06-22 21:06:33 +02:00
J. Nick Koston
b01eb28d42 validate sooner 2025-06-22 21:05:15 +02:00
J. Nick Koston
02019dd16c validate sooner 2025-06-22 21:04:42 +02:00
J. Nick Koston
7be12f5ff6 validate sooner 2025-06-22 20:59:54 +02:00
J. Nick Koston
a90d59b6ba validate sooner 2025-06-22 20:59:07 +02:00
J. Nick Koston
e7fa156254 Merge remote-tracking branch 'upstream/dev' into integration 2025-06-22 20:15:02 +02:00
J. Nick Koston
a8ab6b1c43 Merge branch 'dev' into logger_disable_loop 2025-06-22 20:12:17 +02:00
J. Nick Koston
25ed7c890b cleanups 2025-06-22 20:03:02 +02:00
J. Nick Koston
85e3b63f05 adjust 2025-06-22 19:49:12 +02:00
J. Nick Koston
a37bac1956 add files 2025-06-22 19:47:19 +02:00
J. Nick Koston
818a978dfc units 2025-06-22 19:40:53 +02:00
J. Nick Koston
180aeb7d8e simplify 2025-06-22 13:50:29 +02:00
J. Nick Koston
0764fa7292 simplify 2025-06-22 13:48:27 +02:00
J. Nick Koston
17bf533ed7 simplify 2025-06-22 13:44:05 +02:00
J. Nick Koston
d7eae1c1a0 simplify 2025-06-22 13:43:52 +02:00
J. Nick Koston
7f2d979255 preen 2025-06-22 13:39:12 +02:00
J. Nick Koston
46b419ea8b preen 2025-06-22 13:38:14 +02:00
J. Nick Koston
b30b527ff9 one more place to check 2025-06-22 13:37:30 +02:00
J. Nick Koston
41b1bfc504 legacy test 2025-06-22 13:37:01 +02:00
J. Nick Koston
f4f14a7507 fixes 2025-06-22 13:29:49 +02:00
J. Nick Koston
61c29213a7 fixes 2025-06-22 13:29:41 +02:00
J. Nick Koston
e6d7639209 Merge branch 'dev' into multi_device 2025-06-22 13:03:16 +02:00
J. Nick Koston
3c07a186b2 Merge remote-tracking branch 'dala318/multi_device' into multi_device 2025-06-22 13:02:48 +02:00
J. Nick Koston
8a725250a9 Merge branch 'dev' into multi_device 2025-06-22 12:32:44 +02:00
J. Nick Koston
502b8a6073 fixes 2025-06-22 12:32:25 +02:00
J. Nick Koston
6212c6f80f Merge branch 'dev' into logger_disable_loop 2025-06-22 12:10:11 +02:00
J. Nick Koston
b03e3b8d4a fixes 2025-06-22 10:07:05 +02:00
J. Nick Koston
a98e34d190 handle collisions 2025-06-22 10:02:59 +02:00
J. Nick Koston
bf8d8b6e63 handle collisions 2025-06-22 10:01:53 +02:00
J. Nick Koston
57599f7a98 handle collisions 2025-06-22 10:00:31 +02:00
J. Nick Koston
ffccce7ffc handle collisions 2025-06-22 09:58:12 +02:00
J. Nick Koston
bbd5d050a9 Merge branch 'dev' into logger_disable_loop 2025-06-21 18:36:59 +02:00
J. Nick Koston
71a96fdcbf Merge branch 'dev' into logger_disable_loop 2025-06-21 18:11:19 +02:00
J. Nick Koston
221e3c6c9c preen 2025-06-21 18:09:16 +02:00
J. Nick Koston
fb1679d572 preen 2025-06-21 18:07:45 +02:00
J. Nick Koston
c19065f112 preen 2025-06-21 18:02:32 +02:00
J. Nick Koston
f2b04a077e preen 2025-06-21 18:01:12 +02:00
J. Nick Koston
8e7841c880 preen 2025-06-21 18:00:17 +02:00
J. Nick Koston
1873490b24 preen 2025-06-21 17:57:36 +02:00
J. Nick Koston
4d231953f4 preen 2025-06-21 17:57:10 +02:00
J. Nick Koston
aa4c399657 reverse space in vectors 2025-06-21 17:36:25 +02:00
J. Nick Koston
1f99d18982 reverse space in vectors 2025-06-21 17:34:08 +02:00
J. Nick Koston
be37178ef8 make areas and devices consistant 2025-06-21 17:32:11 +02:00
J. Nick Koston
fad86c655e make areas and devices consistant 2025-06-21 17:30:17 +02:00
J. Nick Koston
4a7958586e make areas and devices consistant 2025-06-21 17:19:16 +02:00
J. Nick Koston
f44ecd0891 make areas and devices consistant 2025-06-21 17:18:23 +02:00
J. Nick Koston
3d0392d668 make areas and devices consistant 2025-06-21 17:17:29 +02:00
J. Nick Koston
d300d2605b make areas and devices consistant 2025-06-21 17:13:04 +02:00
J. Nick Koston
66cce6a2f2 make areas and devices consistant 2025-06-21 17:12:25 +02:00
J. Nick Koston
65e3c6bfbb make areas and devices consistant 2025-06-21 17:12:00 +02:00
J. Nick Koston
2a39060912 Merge remote-tracking branch 'upstream/dev' into multi_device 2025-06-21 17:06:11 +02:00
J. Nick Koston
8714e80978 make areas and devices consistant 2025-06-21 17:05:46 +02:00
J. Nick Koston
98de53f60b migrate to using same area info for top level and sub devices 2025-06-21 16:47:03 +02:00
J. Nick Koston
41e11e9a0e migrate to using same area info for top level and sub devices 2025-06-21 16:43:48 +02:00
J. Nick Koston
e7a4eac8bd migrate to using same area info for top level and sub devices 2025-06-21 16:42:05 +02:00
J. Nick Koston
1589a131db migrate to using same area info for top level and sub devices 2025-06-21 16:39:07 +02:00
J. Nick Koston
7d84f0e650 migrate to using same area info for top level and sub devices 2025-06-21 16:37:21 +02:00
J. Nick Koston
86fb0e317f fixes 2025-06-21 15:22:35 +02:00
J. Nick Koston
32088d5ef7 revert 2025-06-21 13:35:32 +02:00
J. Nick Koston
63de88dd57 fixes 2025-06-21 13:33:29 +02:00
J. Nick Koston
153a6440dc cleanups to address review comments 2025-06-21 13:20:59 +02:00
J. Nick Koston
8937ed2269 cleanups to address review comments 2025-06-21 13:18:25 +02:00
J. Nick Koston
02e922b56f cleanups to address review comments 2025-06-21 13:16:42 +02:00
J. Nick Koston
bf9e901ab9 cleanups to address review comments 2025-06-21 13:13:44 +02:00
J. Nick Koston
1234ef8de2 Merge remote-tracking branch 'upstream/dev' into multi_device 2025-06-21 12:13:54 +02:00
J. Nick Koston
41697a7b1b Merge remote-tracking branch 'upstream/logger_disable_loop' into integration 2025-06-21 11:19:12 +02:00
J. Nick Koston
912e265bc0 Merge branch 'dev' into logger_disable_loop 2025-06-21 11:18:59 +02:00
J. Nick Koston
96ee6fb064 Merge branch 'logger_disable_loop' into integration 2025-06-21 11:17:13 +02:00
J. Nick Koston
788dba8ef3 define 2025-06-21 11:16:14 +02:00
J. Nick Koston
fdde9c4681 Reduce Logger memory usage by optimizing variable sizes 2025-06-21 00:27:05 +02:00
J. Nick Koston
f195e73d38 Merge branch 'logger_disable_loop' into integration 2025-06-20 22:54:40 +02:00
J. Nick Koston
b0d9ffc6a1 Reduce logger CPU usage by disabling loop when buffer is empty 2025-06-20 22:53:12 +02:00
J. Nick Koston
e17619841d fix last component being charged for stats 2025-06-20 22:03:53 +02:00
J. Nick Koston
eb6a7cf3b9 fix last component being charged for stats 2025-06-20 22:02:19 +02:00
J. Nick Koston
9901e2d72e Merge branch 'dev' into integration 2025-06-20 21:36:36 +02:00
J. Nick Koston
bcf961c0b0 Merge branch 'dev' into integration 2025-06-19 04:05:25 +02:00
J. Nick Koston
f84a4c9753 Merge remote-tracking branch 'origin/disable_ethernet_loop' into integration 2025-06-19 03:42:53 +02:00
J. Nick Koston
df56ca0236 remove redundant enable_loop, it must already be enabled to get here 2025-06-19 03:41:25 +02:00
J. Nick Koston
de0cd0ec67 Merge branch 'dev' into disable_ethernet_loop 2025-06-19 03:39:15 +02:00
J. Nick Koston
67c30245c4 make copilot happy 2025-06-19 02:01:55 +02:00
J. Nick Koston
1f72757591 tidy 2025-06-19 01:35:45 +02:00
J. Nick Koston
35c2fdf6af dry 2025-06-19 01:31:11 +02:00
J. Nick Koston
d1ecd841be avoid auto 2025-06-19 01:28:17 +02:00
J. Nick Koston
828a49697c Merge branch 'gap_events' into integration 2025-06-19 01:18:36 +02:00
J. Nick Koston
0551495501 try another way 2025-06-19 01:18:26 +02:00
J. Nick Koston
2bbffe4a68 try another way 2025-06-19 01:18:11 +02:00
J. Nick Koston
281ad90e39 fixes 2025-06-19 01:16:46 +02:00
J. Nick Koston
ed50976a07 fixes 2025-06-19 01:16:22 +02:00
J. Nick Koston
a3400037d9 fixes 2025-06-19 01:14:15 +02:00
J. Nick Koston
f0d82f75bc fixes 2025-06-19 01:14:05 +02:00
J. Nick Koston
349cb80e90 Merge remote-tracking branch 'origin/integration' into integration 2025-06-19 01:12:20 +02:00
J. Nick Koston
c263ee39af Merge branch 'gap_events' into integration 2025-06-19 01:12:07 +02:00
J. Nick Koston
e99bc52756 Fix missing BLE GAP events causing RSSI sensor and beacon failures 2025-06-19 01:09:13 +02:00
J. Nick Koston
7944b2b8e9 Merge branch 'ota_perf' into integration 2025-06-19 00:40:07 +02:00
J. Nick Koston
ca6ae746c1 be explict 2025-06-19 00:39:19 +02:00
J. Nick Koston
deabac18b2 Merge branch 'disable_ethernet_loop' into integration 2025-06-18 21:39:35 +02:00
J. Nick Koston
5cf8681c61 Merge branch 'ota_perf' into integration 2025-06-18 21:35:14 +02:00
J. Nick Koston
ca7ede8f96 more cleanups 2025-06-18 21:35:04 +02:00
J. Nick Koston
4969682d52 Merge branch 'ota_perf' into integration 2025-06-18 21:27:51 +02:00
J. Nick Koston
8002fe0dd5 remove safety check 2025-06-18 21:27:30 +02:00
J. Nick Koston
7dfdf965b7 remove safety check 2025-06-18 21:26:32 +02:00
J. Nick Koston
b408795dd6 Merge branch 'api_reads' into integration 2025-06-18 19:24:32 +02:00
J. Nick Koston
a5a099336b one more 2025-06-18 19:22:23 +02:00
J. Nick Koston
4ae56fc004 Merge branch 'api_reads' into integration 2025-06-18 18:40:35 +02:00
J. Nick Koston
3f71c09b7b Fix slow noise handshake by reading multiple messages per loop 2025-06-18 18:36:55 +02:00
J. Nick Koston
bd50a7f1ab cleanup 2025-06-18 14:33:58 +02:00
J. Nick Koston
51e4c45e5c Merge branch 'loop_done_enable_isr' into disable_ethernet_loop 2025-06-18 14:27:18 +02:00
J. Nick Koston
e3fae49add Merge branch 'binary_sensor_gpio_polling' into integration 2025-06-18 14:24:42 +02:00
J. Nick Koston
610215ab60 updates 2025-06-18 14:24:31 +02:00
J. Nick Koston
74acbda435 Merge branch 'loop_done_enable_isr' into binary_sensor_gpio_polling 2025-06-18 14:19:03 +02:00
J. Nick Koston
25c4af777c Merge branch 'loop_done_enable_isr' into integration 2025-06-18 14:18:35 +02:00
J. Nick Koston
ec186e6324 rename 2025-06-18 14:17:45 +02:00
J. Nick Koston
150b7a98f3 Merge branch 'dev' into ota_perf 2025-06-18 13:57:20 +02:00
J. Nick Koston
8ae7c1cff0 Merge branch 'ota_perf' into integration 2025-06-18 13:46:36 +02:00
J. Nick Koston
7f1d0eef98 Optimize OTA loop to avoid unnecessary stack allocations 2025-06-18 13:44:07 +02:00
J. Nick Koston
1179ab33f2 tweaks 2025-06-18 12:52:18 +02:00
J. Nick Koston
a09faa1c10 Merge branch 'dev' into disable_ethernet_loop 2025-06-18 12:36:22 +02:00
J. Nick Koston
c0319d9b2f Merge branch 'binary_sensor_gpio_polling' into integration 2025-06-18 12:28:59 +02:00
J. Nick Koston
4870cd2921 use enable_loop_soon_from_isr 2025-06-18 12:28:49 +02:00
J. Nick Koston
d4280ec68b Merge branch 'loop_done_enable_isr' into binary_sensor_gpio_polling 2025-06-18 12:23:55 +02:00
J. Nick Koston
52cdc11927 Merge remote-tracking branch 'origin/proxy_memory' into integration 2025-06-18 12:21:31 +02:00
J. Nick Koston
8345b8c9ce Update esphome/components/esp32_ble_client/ble_client_base.h
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-18 12:21:10 +02:00
J. Nick Koston
c56f0677c3 Merge remote-tracking branch 'upstream/proxy_memory' into integration 2025-06-18 12:16:23 +02:00
J. Nick Koston
00e9e1421e Merge branch 'dev' into proxy_memory 2025-06-18 12:16:12 +02:00
J. Nick Koston
93c72c6e6c Merge branch 'loop_done_enable_isr' into integration 2025-06-18 12:15:24 +02:00
J. Nick Koston
9cea930dbd Merge remote-tracking branch 'upstream/dev' into integration 2025-06-18 12:15:18 +02:00
J. Nick Koston
7b9bd70729 Add enable_loop_soon_from_isr 2025-06-18 12:09:12 +02:00
J. Nick Koston
5115c7a100 Merge branch 'bump_ruff_precommit' into integration 2025-06-18 00:15:23 +02:00
J. Nick Koston
5634494e64 Bump ruff in pre-commit to 0.12.0
matches https://github.com/esphome/esphome/pull/9120
2025-06-18 00:11:40 +02:00
J. Nick Koston
aa8bd4abf1 Bump ruff in pre-commit to 0.12.0
matches https://github.com/esphome/esphome/pull/9120
2025-06-18 00:10:30 +02:00
J. Nick Koston
17fd69dd7f Bump ruff in pre-commit to 0.12.0
matches https://github.com/esphome/esphome/pull/9120
2025-06-18 00:09:18 +02:00
J. Nick Koston
1d9dae374b Merge branch 'loop_done' into integration 2025-06-17 23:45:20 +02:00
J. Nick Koston
cb2241ad91 make sure components that disable in setup are disabled at start 2025-06-17 23:45:16 +02:00
J. Nick Koston
d8a7e9abc8 make sure components that disable in setup are disabled at start 2025-06-17 23:44:32 +02:00
J. Nick Koston
969abc3f29 make sure components that disable in setup are disabled at start 2025-06-17 23:40:46 +02:00
J. Nick Koston
766fdc8a1f make sure components that disable in setup are disabled at start 2025-06-17 23:40:31 +02:00
J. Nick Koston
4c37c20d76 cleaner fix 2025-06-17 22:30:35 +02:00
J. Nick Koston
7d314398e1 cleaner fix 2025-06-17 22:30:31 +02:00
J. Nick Koston
b69191e3a8 cleaner fix 2025-06-17 22:29:21 +02:00
J. Nick Koston
b27c6b3596 cleaner fix 2025-06-17 22:27:24 +02:00
J. Nick Koston
5453835963 make ble client disable/enable smarter 2025-06-17 18:13:09 +02:00
J. Nick Koston
4d55ba057c make ble client disable/enable smarter 2025-06-17 18:09:53 +02:00
J. Nick Koston
325c01242c tweak 2025-06-17 16:16:20 +02:00
J. Nick Koston
45b32bca89 tweak 2025-06-17 16:08:28 +02:00
J. Nick Koston
7620049214 tweak 2025-06-17 16:05:48 +02:00
J. Nick Koston
3553495a60 Merge remote-tracking branch 'origin/integration' into integration 2025-06-17 15:55:51 +02:00
J. Nick Koston
3ce6db61d5 Merge branch 'binary_sensor_gpio_polling' into integration 2025-06-17 15:55:28 +02:00
J. Nick Koston
798ff32c40 cleanup 2025-06-17 15:55:10 +02:00
J. Nick Koston
430cee8bda Merge branch 'integration' of https://github.com/esphome/esphome into integration 2025-06-17 15:05:27 +02:00
J. Nick Koston
1fe3fb25a6 Merge branch 'binary_sensor_gpio_polling' into integration 2025-06-17 14:38:08 +02:00
J. Nick Koston
685ed87581 preen 2025-06-17 14:38:00 +02:00
J. Nick Koston
ea3ea1eee7 tweak 2025-06-17 14:17:35 +02:00
J. Nick Koston
c9edcb909b Merge branch 'binary_sensor_gpio_polling' into integration 2025-06-17 13:42:02 +02:00
J. Nick Koston
35bfc9f069 tweak 2025-06-17 13:41:57 +02:00
J. Nick Koston
c4aec194b9 Merge branch 'binary_sensor_gpio_polling' into integration 2025-06-17 13:31:44 +02:00
J. Nick Koston
e8547b16f6 Avoid polling for GPIO binary sensors when possible 2025-06-17 13:20:41 +02:00
J. Nick Koston
2bbe08cee0 Avoid polling for GPIO binary sensors when possible 2025-06-17 13:18:45 +02:00
J. Nick Koston
0a0c369b88 Avoid polling for GPIO binary sensors when possible 2025-06-17 13:17:35 +02:00
J. Nick Koston
5d2f454a94 Avoid polling for GPIO binary sensors when possible 2025-06-17 13:13:58 +02:00
J. Nick Koston
04bcc5c879 Avoid polling for GPIO binary sensors when possible 2025-06-17 13:02:00 +02:00
J. Nick Koston
d4db16665f Avoid polling for GPIO binary sensors when possible 2025-06-17 12:41:17 +02:00
J. Nick Koston
20b7a494f6 Merge remote-tracking branch 'origin/proxy_memory' into integration 2025-06-17 12:05:43 +02:00
J. Nick Koston
fbdce3ad89 Optimize bluetooth_proxy memory usage on ESP32 2025-06-17 12:04:49 +02:00
J. Nick Koston
4fc8807f02 Merge branch 'light_memory' into integration 2025-06-17 11:49:58 +02:00
J. Nick Koston
83075bfb5c Optimize LightState memory layout 2025-06-17 11:49:15 +02:00
J. Nick Koston
4074ec0425 Merge branch 'switch_memory' into integration 2025-06-17 11:27:45 +02:00
J. Nick Koston
8e1694dd0f Reduce Switch component memory usage by 8 bytes per instance 2025-06-17 11:27:11 +02:00
J. Nick Koston
911df18855 Merge branch 'api_memory' into integration 2025-06-17 11:10:17 +02:00
J. Nick Koston
6b049e93f8 Optimize API component memory usage by reordering class members to reduce padding 2025-06-17 11:09:22 +02:00
J. Nick Koston
a335dcc379 Merge remote-tracking branch 'upstream/dev' into integration 2025-06-17 10:40:41 +02:00
J. Nick Koston
c6478c8a79 Merge branch 'reduce_duplicate_gen_code_api' into integration 2025-06-17 10:40:23 +02:00
J. Nick Koston
cc9d40cb60 tweaks 2025-06-17 10:40:12 +02:00
J. Nick Koston
0a6b7f9a1b Update script/api_protobuf/api_protobuf.py 2025-06-17 10:39:49 +02:00
J. Nick Koston
daa1fb9a7a Merge remote-tracking branch 'swoboda1337/bump_libretiny' into integration 2025-06-17 04:33:57 +02:00
Jonathan Swoboda
b7d543290b Bump LibreTiny 2025-06-16 21:40:06 -04:00
J. Nick Koston
ea852b60ac Merge branch 'esp32_ble_tracker_reduce_memory' into integration 2025-06-16 22:07:13 +02:00
J. Nick Koston
ed341988ea Use smaller atomic types for ESP32 BLE Tracker ring buffer indices 2025-06-16 22:06:04 +02:00
J. Nick Koston
057b6c8e30 Merge branch 'api_reduce_millis' into integration 2025-06-16 19:34:07 +02:00
J. Nick Koston
44444fe071 Optimize API server performance by using cached loop time 2025-06-16 19:33:29 +02:00
J. Nick Koston
797330d6ab Disable Ethernet loop polling when connected and stable 2025-06-16 17:28:04 +02:00
J. Nick Koston
a630d5b5f5 Merge branch 'ble_pool' into integration 2025-06-16 15:45:50 +02:00
J. Nick Koston
eb3dc82b5d naming 2025-06-16 15:45:38 +02:00
J. Nick Koston
34ed18d562 Merge branch 'ble_pool' into integration 2025-06-16 15:43:59 +02:00
J. Nick Koston
1ce02ee313 naming 2025-06-16 15:43:43 +02:00
J. Nick Koston
2a26a0188c ble pool 2025-06-16 15:29:37 +02:00
J. Nick Koston
50cb05d1b1 ble pool 2025-06-16 15:28:03 +02:00
J. Nick Koston
6e739ac453 ble pool 2025-06-16 15:23:04 +02:00
J. Nick Koston
7aa2fd9f0e ble pool 2025-06-16 15:19:10 +02:00
J. Nick Koston
8e254e1b03 ble pool 2025-06-16 15:18:19 +02:00
J. Nick Koston
1ad9d717ff ble pool 2025-06-16 15:17:57 +02:00
J. Nick Koston
104658e43a ble pool 2025-06-16 15:16:15 +02:00
J. Nick Koston
e7e4b995bf ble pool 2025-06-16 15:15:26 +02:00
J. Nick Koston
b35b54f2c2 ble pool 2025-06-16 15:11:42 +02:00
J. Nick Koston
f80aeb1d1d cleanup 2025-06-16 15:10:27 +02:00
J. Nick Koston
6a756ab3b6 cleanup 2025-06-16 15:09:49 +02:00
J. Nick Koston
58a697bed1 cleanup 2025-06-16 15:07:23 +02:00
J. Nick Koston
280960ac18 cleanup 2025-06-16 15:06:02 +02:00
J. Nick Koston
0640ff13aa ble pool 2025-06-16 15:04:40 +02:00
J. Nick Koston
545505691f ble pool 2025-06-16 15:02:10 +02:00
J. Nick Koston
11fcf81321 ble pool 2025-06-16 15:00:58 +02:00
J. Nick Koston
c565b37dc8 ble pool 2025-06-16 15:00:07 +02:00
J. Nick Koston
3d18495270 ble pool 2025-06-16 14:55:15 +02:00
J. Nick Koston
419e4e63e9 ble pool 2025-06-16 14:53:50 +02:00
J. Nick Koston
724aa2bf65 ble pool 2025-06-16 14:52:38 +02:00
J. Nick Koston
573fa8aeb3 ble pool 2025-06-16 14:52:28 +02:00
J. Nick Koston
8a672e34c5 ble pool 2025-06-16 14:47:05 +02:00
J. Nick Koston
bc49211dab ble pool 2025-06-16 14:43:29 +02:00
J. Nick Koston
4ef9c3667e Merge branch 'reduce_duplicate_gen_code_api' into integration 2025-06-16 06:06:19 -05:00
J. Nick Koston
6babe516ac move to proto.h to have less generated code 2025-06-16 06:05:19 -05:00
J. Nick Koston
e0b258ef7e Merge branch 'empty_methods' into integration 2025-06-15 22:30:02 -05:00
J. Nick Koston
ff0c3a89b1 Remove empty generated protobuf methods 2025-06-15 22:25:21 -05:00
J. Nick Koston
2511b81048 Merge branch 'reduce_duplicate_gen_code_api' into integration 2025-06-15 22:09:15 -05:00
J. Nick Koston
6ffcd94edc early return was worse for simple functions 2025-06-15 22:00:40 -05:00
J. Nick Koston
2fcf73c812 Reduce code duplication in auto-generated API protocol code 2025-06-15 21:53:33 -05:00
J. Nick Koston
dee0608af9 adjust 2025-06-15 20:47:53 -05:00
J. Nick Koston
d11860a383 Merge remote-tracking branch 'origin/loop_done' into integration 2025-06-15 20:44:24 -05:00
J. Nick Koston
1c05115bf5 Merge branch 'dev' into loop_done 2025-06-15 20:44:09 -05:00
J. Nick Koston
d7e7382d0b tests, address review comments 2025-06-15 20:43:30 -05:00
J. Nick Koston
872388f6e3 tests, address review comments 2025-06-15 20:43:01 -05:00
J. Nick Koston
1215ef920b Merge branch 'loop_done' into integration 2025-06-15 20:36:08 -05:00
J. Nick Koston
d19d5a23ea speed up test a bit 2025-06-15 20:36:00 -05:00
J. Nick Koston
f49a779f1d speed up test a bit 2025-06-15 20:35:52 -05:00
J. Nick Koston
d8bf5b80e1 Merge branch 'loop_done' into integration 2025-06-15 20:34:22 -05:00
J. Nick Koston
69483b9353 speed up test a bit 2025-06-15 20:34:13 -05:00
J. Nick Koston
14e8548989 speed up test a bit 2025-06-15 20:33:52 -05:00
J. Nick Koston
4abd93b661 tests, address review comments 2025-06-15 20:32:36 -05:00
J. Nick Koston
5d925af76f tests, address review comments 2025-06-15 20:31:25 -05:00
J. Nick Koston
b999c6064a tests, address review comments 2025-06-15 20:30:54 -05:00
J. Nick Koston
94e3576978 tests, address review comments 2025-06-15 20:30:43 -05:00
J. Nick Koston
7a22406a2d Merge remote-tracking branch 'origin/integration' into integration 2025-06-15 20:29:36 -05:00
J. Nick Koston
e60684494f Merge branch 'loop_done' into integration 2025-06-15 20:29:25 -05:00
J. Nick Koston
9db28ed779 cover 2025-06-15 20:29:12 -05:00
J. Nick Koston
6fd8c5cee7 tests, address review comments 2025-06-15 20:22:49 -05:00
J. Nick Koston
787ec43266 tests, address review comments 2025-06-15 20:22:29 -05:00
J. Nick Koston
a4efc63bf2 test 2025-06-15 19:57:20 -05:00
J. Nick Koston
80a8f1437e tests 2025-06-15 19:38:13 -05:00
J. Nick Koston
fcca94169d Merge remote-tracking branch 'origin/integration' into integration 2025-06-15 19:12:32 -05:00
J. Nick Koston
d1924088e3 Merge remote-tracking branch 'origin/loop_done' into integration 2025-06-15 19:11:57 -05:00
J. Nick Koston
fd31afe09c tidy 2025-06-15 18:58:42 -05:00
J. Nick Koston
7a763712c5 tidy 2025-06-15 18:58:32 -05:00
J. Nick Koston
7216be5da7 Merge branch 'loop_done' into integration 2025-06-15 18:44:36 -05:00
J. Nick Koston
711b0a291b comments 2025-06-15 18:44:28 -05:00
J. Nick Koston
dfc96496c8 comments 2025-06-15 18:44:15 -05:00
J. Nick Koston
2a1c5ef333 Merge branch 'loop_done' into integration 2025-06-15 18:42:49 -05:00
J. Nick Koston
9755209499 comments 2025-06-15 18:42:40 -05:00
J. Nick Koston
0b26e537d4 Merge branch 'loop_done' into integration 2025-06-15 18:40:46 -05:00
J. Nick Koston
98c6233ec3 Merge branch 'dev' into loop_done 2025-06-15 18:40:33 -05:00
J. Nick Koston
f711706b1a Fix ESP32 Improv component to re-enable loop when service starts again 2025-06-15 18:40:08 -05:00
J. Nick Koston
cee7789ab6 tweak 2025-06-15 18:37:05 -05:00
J. Nick Koston
8a06c4380d partition 2025-06-15 18:32:36 -05:00
J. Nick Koston
72ecf7a288 Merge remote-tracking branch 'upstream/dev' into integration 2025-06-15 16:48:20 -05:00
J. Nick Koston
ef98c7502d Merge remote-tracking branch 'origin/dev' into integration 2025-06-15 13:42:11 -05:00
J. Nick Koston
03d0e74b65 Merge remote-tracking branch 'upstream/less_templates' into integration 2025-06-15 10:48:53 -05:00
J. Nick Koston
5b8fdc0364 Merge branch 'dev' into less_templates 2025-06-15 10:42:41 -05:00
J. Nick Koston
593b4bd137 Update script/api_protobuf/api_protobuf.py 2025-06-15 10:42:28 -05:00
J. Nick Koston
267e12d058 lint 2025-06-15 10:09:54 -05:00
J. Nick Koston
4a5e39b651 Add common base classes for entity protobuf messages to reduce duplicate code 2025-06-15 09:40:45 -05:00
J. Nick Koston
ea24fa5b78 Merge branch 'loop_done' into integration 2025-06-15 01:52:27 -05:00
J. Nick Koston
bb2bb128f7 remove trailing . 2025-06-15 01:52:17 -05:00
J. Nick Koston
94e8a856d7 Merge branch 'loop_done' into integration 2025-06-15 01:47:22 -05:00
J. Nick Koston
4c19fbf98e lint 2025-06-15 01:47:10 -05:00
J. Nick Koston
60f8938bfa Merge branch 'loop_done' into integration 2025-06-15 01:34:12 -05:00
J. Nick Koston
55679662b5 ordering 2025-06-15 01:34:03 -05:00
J. Nick Koston
53df959e49 Merge branch 'loop_done' into integration 2025-06-15 01:26:56 -05:00
J. Nick Koston
8e6ef9966f Merge remote-tracking branch 'upstream/loop_done' into loop_done 2025-06-15 01:26:45 -05:00
J. Nick Koston
1d52fceafa rename, cleanup 2025-06-15 01:26:25 -05:00
J. Nick Koston
99186ed864 rename, cleanup 2025-06-15 01:25:59 -05:00
J. Nick Koston
383931d484 Merge branch 'ble_events_ring_buffer' into integration 2025-06-15 00:31:34 -05:00
J. Nick Koston
0b49a54cb3 comments 2025-06-15 00:31:25 -05:00
J. Nick Koston
705c0f1891 Merge branch 'ble_events_ring_buffer' into integration 2025-06-15 00:27:13 -05:00
J. Nick Koston
544c3ffc95 comments 2025-06-15 00:26:06 -05:00
J. Nick Koston
33f252a45d Implement a lock free ring buffer for BLEEvents to avoid drops 2025-06-15 00:22:24 -05:00
J. Nick Koston
f55d82a015 Merge branch 'ble_queue_lock_free' into integration 2025-06-15 00:16:02 -05:00
J. Nick Koston
8cf33fdef0 preen 2025-06-15 00:15:48 -05:00
J. Nick Koston
f858d98811 Merge branch 'ble_queue_lock_free' into integration 2025-06-15 00:12:47 -05:00
J. Nick Koston
2a6165d440 simplify 2025-06-15 00:12:34 -05:00
J. Nick Koston
4586528c40 merge 2025-06-15 00:01:15 -05:00
J. Nick Koston
23a07baa19 Merge branch 'ble_queue_lock_free' into integration 2025-06-14 23:58:53 -05:00
J. Nick Koston
f9040ca932 cleanup 2025-06-14 23:54:42 -05:00
J. Nick Koston
4cea7f0237 Update esphome/components/esp32_ble/ble.cpp 2025-06-14 23:49:38 -05:00
J. Nick Koston
b1847d5e98 Make ble events queue lock free 2025-06-14 23:48:26 -05:00
J. Nick Koston
9ce4d2e952 Merge remote-tracking branch 'upstream/dev' into integration 2025-06-14 23:21:51 -05:00
J. Nick Koston
247078e06d Merge remote-tracking branch 'origin/loop_done' into integration 2025-06-14 23:20:20 -05:00
J. Nick Koston
a0cd72de28 revert 2025-06-14 23:19:43 -05:00
J. Nick Koston
e467f569f0 Merge branch 'loop_done' into integration 2025-06-14 23:15:49 -05:00
J. Nick Koston
e31c7b7dfc one more 2025-06-14 23:15:06 -05:00
J. Nick Koston
dc2e0c832b Merge branch 'loop_done' into integration 2025-06-14 22:37:11 -05:00
J. Nick Koston
7ddf51bb51 fix 2025-06-14 22:36:29 -05:00
J. Nick Koston
8fb3856665 small fix 2025-06-14 22:17:27 -05:00
J. Nick Koston
183dd74f3e one more 2025-06-14 22:17:27 -05:00
J. Nick Koston
4f29039b41 mark_loop_done 2025-06-14 22:17:24 -05:00
J. Nick Koston
102fcbec20 small fix 2025-06-14 22:09:19 -05:00
J. Nick Koston
d00e5212c7 one more 2025-06-14 22:04:33 -05:00
J. Nick Koston
0e6bfb62cd mark_loop_done 2025-06-14 21:58:18 -05:00
J. Nick Koston
aa930fb6b6 Merge branch 'ble_queue_lock_free' into integration 2025-06-14 20:09:56 -05:00
J. Nick Koston
f327ed87e9 Make ble events queue lock free 2025-06-14 20:08:43 -05:00
J. Nick Koston
2de9be0589 Merge branch 'loop_runtime_stats' into integration 2025-06-14 19:43:45 -05:00
J. Nick Koston
345cde8645 Merge branch 'ble_events_ring_buffer' into integration 2025-06-14 19:25:57 -05:00
J. Nick Koston
cf152af9ae Implement a lock free ring buffer for BLEEvents to avoid drops 2025-06-14 19:24:57 -05:00
J. Nick Koston
d6333dcfd9 Revert "Reorder Application to reduce padding"
This reverts commit 82c39580df.
2025-06-14 18:18:45 -05:00
J. Nick Koston
0121f799f0 Merge branch 'reorder_app_reduce_padding' into integration 2025-06-14 18:16:39 -05:00
J. Nick Koston
82c39580df Reorder Application to reduce padding 2025-06-14 18:15:40 -05:00
J. Nick Koston
53a578a46f Merge branch 'area_str' into integration 2025-06-14 18:01:44 -05:00
J. Nick Koston
62612ef80b Optimize Application area_ from std::string to const char* 2025-06-14 18:00:32 -05:00
J. Nick Koston
61ac874c4c Merge branch 'parse_on_off_uint8t' into integration 2025-06-14 17:46:25 -05:00
J. Nick Koston
976b200ff6 Make ParseOnOffState enum uint8_t 2025-06-14 17:44:22 -05:00
J. Nick Koston
852343b6d8 Merge remote-tracking branch 'upstream/has_state_' into integration 2025-06-14 17:32:40 -05:00
J. Nick Koston
c56af9d52b Merge branch 'component_state_oversized' into integration 2025-06-14 17:03:31 -05:00
J. Nick Koston
05f18e2828 Optimize Component and Application state storage from uint32_t to uint8_t 2025-06-14 17:01:57 -05:00
J. Nick Koston
72804caab2 Merge branch 'warn_if_blocking_over_' into integration 2025-06-14 16:43:26 -05:00
J. Nick Koston
80cbe5c7c9 Reduce Component blocking threshold memory usage by 2 bytes per component 2025-06-14 16:42:08 -05:00
J. Nick Koston
21892d1236 Merge branch 'error_message_memory' into integration 2025-06-14 16:29:43 -05:00
J. Nick Koston
13824624f8 Reduce Component memory usage by 20 bytes per component 2025-06-14 16:27:45 -05:00
J. Nick Koston
0fd72ecbab Merge remote-tracking branch 'upstream/batch_exceeds_max_packet_size' into integration 2025-06-14 15:46:22 -05:00
J. Nick Koston
f848cb1546 Merge remote-tracking branch 'upstream/footer_not_reserved' into integration 2025-06-14 15:46:13 -05:00
J. Nick Koston
633854081a Merge remote-tracking branch 'upstream/guard_wrong_total_size' into integration 2025-06-14 15:45:35 -05:00
J. Nick Koston
4fed9a581b Merge remote-tracking branch 'upstream/missing_force' into integration 2025-06-14 15:45:28 -05:00
J. Nick Koston
e9c1202aaa Merge remote-tracking branch 'upstream/ble_events' into integration 2025-06-14 15:45:18 -05:00
J. Nick Koston
0a7ae279d0 preen 2025-06-14 11:40:50 -05:00
J. Nick Koston
0de2696543 Optimize memory usage by lazy-allocating raw callbacks in sensors 2025-06-14 11:37:11 -05:00
J. Nick Koston
a7dc239b71 cleanup 2025-06-14 10:49:23 -05:00
J. Nick Koston
fe0e6990f5 cover 2025-06-14 10:14:41 -05:00
J. Nick Koston
5ba65e92d9 cover 2025-06-14 10:12:12 -05:00
J. Nick Koston
a1452b52c9 Reduce entity memory usage by eliminating field shadowing and bit-packing 2025-06-14 10:00:49 -05:00
J. Nick Koston
dd2aa23a5f cover 2025-06-13 19:28:59 -05:00
J. Nick Koston
0e0359ba7d Fix protobuf encoding size mismatch by passing force parameter in encode_string 2025-06-13 19:20:08 -05:00
J. Nick Koston
93b1b7aded assert 2025-06-13 18:32:21 -05:00
J. Nick Koston
9472dc6a53 Fix API message encoding to return actual size instead of calculated size 2025-06-13 18:24:51 -05:00
J. Nick Koston
67b681854e Fix API message encoding to return actual size instead of calculated size 2025-06-13 18:20:01 -05:00
J. Nick Koston
7b5990833e Merge branch 'dev' into batch_exceeds_max_packet_size 2025-06-13 17:01:10 -05:00
J. Nick Koston
b6d5d04589 More coverage 2025-06-13 16:59:36 -05:00
J. Nick Koston
fdfbb3e944 Fix footer space not being reserved for batched messages
This only affects noise protocol, and its not a correctness issue, its only
fixing an inefficent reserve
2025-06-13 15:54:31 -05:00
J. Nick Koston
faa7a3e37f tweak 2025-06-13 15:14:14 -05:00
J. Nick Koston
23748b82bb Ensure api can send batches where the first message exceeds MAX_PACKET_SIZE 2025-06-13 14:58:09 -05:00
J. Nick Koston
bccb6f578a Ensure we can send batches where the first message exceeds MAX_PACKET_SIZE 2025-06-13 14:55:17 -05:00
J. Nick Koston
de8a5d6e9e Merge branch 'dev' into ble_events 2025-06-13 10:46:45 -05:00
J. Nick Koston
a8eb3f7961 lint 2025-06-13 10:46:09 -05:00
J. Nick Koston
0877b3e2af suppress unused events 2025-06-12 19:18:22 -05:00
J. Nick Koston
99a54369bf Merge remote-tracking branch 'upstream/dev' into loop_runtime_stats 2025-06-11 22:01:22 -05:00
J. Nick Koston
f7533dfc5c review 2025-06-11 16:25:31 -05:00
J. Nick Koston
ee7d95272d lets be sure 2025-06-11 13:32:55 -05:00
J. Nick Koston
2b9b1d12e6 lets be sure 2025-06-11 13:32:47 -05:00
J. Nick Koston
2cbb5c7d8e fix error 2025-06-11 13:16:44 -05:00
J. Nick Koston
9686c7babe Merge branch 'dev' into ble_events 2025-06-11 13:09:32 -05:00
J. Nick Koston
66bd4c96c4 safety 2025-06-11 13:05:30 -05:00
J. Nick Koston
dc47faa4b6 safety 2025-06-11 13:05:01 -05:00
J. Nick Koston
55ee0b116d lint 2025-06-11 13:03:50 -05:00
J. Nick Koston
c6957c08bc lint 2025-06-11 13:02:08 -05:00
J. Nick Koston
8fe6a323d8 remove workaround 2025-06-11 13:00:55 -05:00
J. Nick Koston
8e51590c32 remove workaround 2025-06-11 12:59:57 -05:00
J. Nick Koston
ae066d5627 cleanup 2025-06-11 11:55:28 -05:00
J. Nick Koston
6760279916 cleanup compacted code 2025-06-11 11:51:43 -05:00
J. Nick Koston
3c208050b0 comments 2025-06-11 11:47:34 -05:00
J. Nick Koston
bbc7c9fb37 dry 2025-06-11 11:46:17 -05:00
J. Nick Koston
e1c3862586 preen 2025-06-11 11:36:50 -05:00
J. Nick Koston
c24b7cb7bd v->d 2025-06-11 11:34:30 -05:00
J. Nick Koston
c91e16549d lint 2025-06-11 11:27:13 -05:00
J. Nick Koston
6e70aca458 wip 2025-06-11 11:23:13 -05:00
J. Nick Koston
d9ffd0ac8e wip 2025-06-11 11:22:01 -05:00
J. Nick Koston
4641f73d19 comments 2025-06-11 11:19:36 -05:00
J. Nick Koston
9f0051c21f cleanup 2025-06-11 11:17:10 -05:00
J. Nick Koston
0331cb09e8 reduce 2025-06-11 11:17:01 -05:00
J. Nick Koston
2f8946f86c cleanup 2025-06-11 11:14:10 -05:00
J. Nick Koston
88a3df4008 cleanup 2025-06-11 11:13:34 -05:00
J. Nick Koston
0adf514bd6 preen 2025-06-11 11:09:19 -05:00
J. Nick Koston
a1b5a2abcb tweak 2025-06-11 10:58:56 -05:00
J. Nick Koston
068c62c6fe adjust 2025-06-11 10:43:48 -05:00
J. Nick Koston
0e9f14f969 wip 2025-06-11 10:20:18 -05:00
J. Nick Koston
78315fd388 preen 2025-06-11 10:08:30 -05:00
J. Nick Koston
0ab69002df preen 2025-06-11 10:05:15 -05:00
J. Nick Koston
1eec1239ec wip 2025-06-11 09:56:02 -05:00
Daniel Vikstrom
57f4067fbf Move fnv1a_32bit_hash to helpers 2025-06-02 14:42:39 +02:00
Daniel Vikstrom
f4a9221232 Change hash method 2025-06-02 08:31:06 +02:00
J. Nick Koston
3d4a75148d Merge branch 'dev' into multi_device 2025-05-31 10:27:31 -05:00
J. Nick Koston
c2c5bd844d Merge branch 'dev' into multi_device 2025-05-29 13:43:21 -05:00
J. Nick Koston
98a2f23024 Merge remote-tracking branch 'upstream/dev' into loop_runtime_stats 2025-05-29 11:04:14 -05:00
J. Nick Koston
c955897d1b Merge remote-tracking branch 'upstream/dev' into loop_runtime_stats 2025-05-27 11:39:45 -05:00
Daniel Vikstrom
9624efa21e Fix proto generation and clang 2025-05-22 14:18:46 +02:00
DanielV
831638210d Merge branch 'dev' into multi_device 2025-05-22 08:41:54 +02:00
J. Nick Koston
cfdb0925ce Merge branch 'dev' into loop_runtime_stats 2025-05-13 23:42:19 -05:00
J. Nick Koston
83db3eddd9 revert ota 2025-05-13 01:07:43 -05:00
J. Nick Koston
cc2c5a544e revert ota 2025-05-13 01:07:38 -05:00
J. Nick Koston
8fba8c2800 revert ota 2025-05-13 01:05:37 -05:00
J. Nick Koston
51d1da8460 revert ota 2025-05-13 01:04:09 -05:00
J. Nick Koston
2f1257056d revert 2025-05-13 01:02:00 -05:00
J. Nick Koston
2f8f6967bf fix ota 2025-05-13 00:55:19 -05:00
J. Nick Koston
246527e618 runtime stats 2025-05-13 00:54:05 -05:00
J. Nick Koston
3857cc9c83 runtime stats 2025-05-13 00:51:14 -05:00
Daniel Vikström
a59a8c563e Attempt fixing circular import by lazy import 2025-05-06 12:30:04 +02:00
Daniel Vikström
856829bcbb More namespace and import fixes 2025-05-06 12:05:45 +02:00
Daniel Vikström
dd2b931f61 Fix namespace error 2025-05-06 11:46:23 +02:00
Daniel Vikström
39beccbbb0 remove from CODEOWNERS 2025-05-06 10:50:09 +02:00
Daniel Vikström
ff626b428f Attempt moving it to esphome config section 2025-05-06 10:48:26 +02:00
Daniel Vikström
3915e1f012 Revert "Improve stability for unrelated test"
This reverts commit 3922950951.
2025-05-06 03:36:03 +02:00
Daniel Vikström
7b460b6224 Restore ci-api-proto.yml 2025-05-06 03:34:33 +02:00
Daniel Vikström
8fb8e79730 Fix clang 2025-05-06 03:20:22 +02:00
Daniel Vikström
79bbc475f4 Fix generated files and revert entity config to device_id 2025-05-06 03:05:00 +02:00
Daniel Vikström
cef023283b Fix generated files 2025-05-06 02:55:44 +02:00
Daniel Vikström
d4fda79ada Attempt to replace device_id:str with device_uid:uint32 2025-05-06 02:07:59 +02:00
DanielV
ff0bdcf4cd Merge branch 'dev' into multi_device 2025-05-06 00:48:23 +02:00
DanielV
bfbc313144 Merge branch 'dev' into multi_device 2025-04-22 14:28:51 +02:00
Daniel Vikström
31f2376f15 Rename ref in codegen 2025-04-22 14:03:07 +02:00
DanielV
f76ecb6604 Merge pull request #10 from dala318/multi_device_2
Sub Devices (all entities)
2025-04-22 08:49:28 +02:00
Daniel Vikström
298cc58433 Activate the rest of entities 2025-04-19 23:18:26 +02:00
Daniel Vikström
825c0593e1 Fix generated code after merge 2025-04-19 19:07:50 +02:00
DanielV
87ed1dc3e3 Merge branch 'dev' into multi_device 2025-04-19 18:58:09 +02:00
DanielV
67e9db021c Merge branch 'dev' into multi_device 2025-04-14 22:21:50 +02:00
Daniel Vikström
3922950951 Improve stability for unrelated test 2025-04-14 21:37:27 +02:00
DanielV
9c4aa0ba53 Merge branch 'dev' into multi_device 2025-04-11 13:19:52 +02:00
Daniel Vikström
f5f1651b31 Fix clang 2025-04-10 09:35:08 +02:00
Daniel Vikström
32f4e4ca13 Cleaning up 2025-04-09 19:20:28 +02:00
Daniel Vikström
962e0c4c33 Make it a Class but only use the id in entities 2025-04-09 19:09:31 +02:00
Daniel Vikström
2c01bc5795 Fix clang-tidy 2025-04-09 15:22:40 +02:00
Daniel Vikström
0651f7cb3c Work on sub-device creation 2025-04-09 01:39:24 +02:00
Daniel Vikström
01ac59ce2a Store proto with all additions but commented out 2025-04-09 01:18:42 +02:00
Daniel Vikström
c1fd597757 Add CODEOWNER 2025-04-09 01:12:14 +02:00
Daniel Vikström
e79e244eee Fix generated proto-files 2025-04-09 01:09:45 +02:00
Daniel Vikström
68ecc08111 Register device_id to entity and separate struct for all device info 2025-04-09 00:11:05 +02:00
Daniel Vikström
3b5fbc359f Formating updates 2025-04-08 22:21:11 +02:00
Daniel Vikström
583e5ea47f Add code-owner tag 2025-04-08 22:21:08 +02:00
Daniel Vikström
7b647c3fae Add a single test 2025-04-08 22:21:07 +02:00
Daniel Vikström
a8b76c617c Some basic chain working 2025-04-08 22:07:09 +02:00
Daniel Vikström
1bd8985dff Add a device component 2025-04-08 22:00:09 +02:00
Daniel Vikström
25b5a6c4ae Add device_id to entity_base 2025-04-08 22:00:06 +02:00
24 changed files with 772 additions and 200 deletions

View File

@@ -33,9 +33,14 @@ namespace api {
// 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) {
@@ -60,10 +65,6 @@ 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();
@@ -90,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->get_client_combined_info().c_str());
return;
}
if (this->next_close_) {
// requested a disconnect
this->helper_->close();
@@ -152,39 +143,31 @@ void APIConnection::loop() {
// 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;
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->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->get_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
}
}
@@ -207,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;
@@ -231,6 +212,8 @@ void APIConnection::loop() {
if (this->send_message(resp)) {
state_subs_at_++;
}
} else {
state_subs_at_ = -1;
}
}
}
@@ -1440,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);
@@ -1760,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;
@@ -1795,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)) {
@@ -1845,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
@@ -1908,21 +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);
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);
}
}
@@ -1938,6 +1929,12 @@ uint16_t APIConnection::try_send_disconnect_request(EntityBase *entity, APIConne
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) {

View File

@@ -185,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;
@@ -441,13 +440,16 @@ class APIConnection : public APIServerConnection {
// Helper function to get estimated message size for buffer pre-allocation
static uint16_t get_estimated_message_size(uint16_t message_type);
// 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_;
uint32_t next_ping_retry_{0};
int state_subs_at_ = -1;
// Strings (12 bytes each on 32-bit)
@@ -470,8 +472,7 @@ class APIConnection : public APIServerConnection {
bool sent_ping_{false};
bool service_call_subscription_{false};
bool next_close_ = false;
uint8_t ping_retries_{0};
// 8 bytes used, no padding needed
// 7 bytes used, 1 byte padding
// Larger objects at the end
InitialStateIterator initial_state_iterator_;
@@ -483,55 +484,57 @@ class APIConnection : public APIServerConnection {
// 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 function pointer
MessageCreator(MessageCreatorPtr ptr) {
// Function pointers are always aligned, so LSB is 0
data_.ptr = ptr;
}
// 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);
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_;
}
@@ -542,30 +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_ == EventResponse::MESSAGE_TYPE; }
union CreatorData {
MessageCreatorPtr ptr; // 8 bytes
std::string *string_ptr; // 8 bytes
} data_; // 8 bytes
uint16_t message_type_; // 2 bytes (0 = function ptr, >0 = state capture)
// 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 {
MessageCreatorPtr ptr;
uintptr_t tagged;
} data_; // 4 bytes on 32-bit
};
// Generic batching mechanism for both state updates and entity info
@@ -591,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;
@@ -630,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

@@ -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;
@@ -287,13 +298,8 @@ APIError APINoiseFrameHelper::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
// 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
@@ -339,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) {
@@ -595,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) {
@@ -831,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(); }

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");
@@ -106,8 +111,6 @@ void APIServer::setup() {
}
#endif
this->last_connected_ = App.get_loop_component_start_time();
#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(
@@ -121,6 +124,16 @@ 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 exists and has incoming connections
if (this->socket_ && this->socket_->ready()) {
@@ -130,51 +143,61 @@ void APIServer::loop() {
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 = App.get_loop_component_start_time();
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
}
}
@@ -503,8 +526,8 @@ void APIServer::on_shutdown() {
for (auto &c : this->clients_) {
if (!c->send_message(DisconnectRequest())) {
// If we can't send the disconnect request directly (tx_buffer full),
// schedule it in the batch so it will be sent with the 5ms timer
c->schedule_message_(nullptr, &APIConnection::try_send_disconnect_request, DisconnectRequest::MESSAGE_TYPE);
// 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);
}
}
}

View File

@@ -142,6 +142,7 @@ 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;
Trigger<std::string, std::string> *client_connected_trigger_ = new Trigger<std::string, std::string>();
@@ -150,7 +151,6 @@ class APIServer : public Component, public Controller {
// 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_;

View File

@@ -10,11 +10,24 @@ GPIOBinarySensor = gpio_ns.class_(
"GPIOBinarySensor", binary_sensor.BinarySensor, cg.Component
)
CONF_USE_INTERRUPT = "use_interrupt"
CONF_INTERRUPT_TYPE = "interrupt_type"
INTERRUPT_TYPES = {
"RISING": gpio_ns.INTERRUPT_RISING_EDGE,
"FALLING": gpio_ns.INTERRUPT_FALLING_EDGE,
"ANY": gpio_ns.INTERRUPT_ANY_EDGE,
}
CONFIG_SCHEMA = (
binary_sensor.binary_sensor_schema(GPIOBinarySensor)
.extend(
{
cv.Required(CONF_PIN): pins.gpio_input_pin_schema,
cv.Optional(CONF_USE_INTERRUPT, default=True): cv.boolean,
cv.Optional(CONF_INTERRUPT_TYPE, default="ANY"): cv.enum(
INTERRUPT_TYPES, upper=True
),
}
)
.extend(cv.COMPONENT_SCHEMA)
@@ -27,3 +40,7 @@ async def to_code(config):
pin = await cg.gpio_pin_expression(config[CONF_PIN])
cg.add(var.set_pin(pin))
cg.add(var.set_use_interrupt(config[CONF_USE_INTERRUPT]))
if config[CONF_USE_INTERRUPT]:
cg.add(var.set_interrupt_type(INTERRUPT_TYPES[config[CONF_INTERRUPT_TYPE]]))

View File

@@ -6,17 +6,91 @@ namespace gpio {
static const char *const TAG = "gpio.binary_sensor";
void IRAM_ATTR GPIOBinarySensorStore::gpio_intr(GPIOBinarySensorStore *arg) {
bool new_state = arg->isr_pin_.digital_read();
if (new_state != arg->last_state_) {
arg->state_ = new_state;
arg->last_state_ = new_state;
arg->changed_ = true;
// Wake up the component from its disabled loop state
if (arg->component_ != nullptr) {
arg->component_->enable_loop_soon_any_context();
}
}
}
void GPIOBinarySensorStore::setup(InternalGPIOPin *pin, gpio::InterruptType type, Component *component) {
pin->setup();
this->isr_pin_ = pin->to_isr();
this->component_ = component;
// Read initial state
this->last_state_ = pin->digital_read();
this->state_ = this->last_state_;
// Attach interrupt - from this point on, any changes will be caught by the interrupt
pin->attach_interrupt(&GPIOBinarySensorStore::gpio_intr, this, type);
}
void GPIOBinarySensor::setup() {
this->pin_->setup();
this->publish_initial_state(this->pin_->digital_read());
if (this->use_interrupt_ && !this->pin_->is_internal()) {
ESP_LOGD(TAG, "GPIO is not internal, falling back to polling mode");
this->use_interrupt_ = false;
}
if (this->use_interrupt_) {
auto *internal_pin = static_cast<InternalGPIOPin *>(this->pin_);
this->store_.setup(internal_pin, this->interrupt_type_, this);
this->publish_initial_state(this->store_.get_state());
} else {
this->pin_->setup();
this->publish_initial_state(this->pin_->digital_read());
}
}
void GPIOBinarySensor::dump_config() {
LOG_BINARY_SENSOR("", "GPIO Binary Sensor", this);
LOG_PIN(" Pin: ", this->pin_);
const char *mode = this->use_interrupt_ ? "interrupt" : "polling";
ESP_LOGCONFIG(TAG, " Mode: %s", mode);
if (this->use_interrupt_) {
const char *interrupt_type;
switch (this->interrupt_type_) {
case gpio::INTERRUPT_RISING_EDGE:
interrupt_type = "RISING_EDGE";
break;
case gpio::INTERRUPT_FALLING_EDGE:
interrupt_type = "FALLING_EDGE";
break;
case gpio::INTERRUPT_ANY_EDGE:
interrupt_type = "ANY_EDGE";
break;
default:
interrupt_type = "UNKNOWN";
break;
}
ESP_LOGCONFIG(TAG, " Interrupt Type: %s", interrupt_type);
}
}
void GPIOBinarySensor::loop() { this->publish_state(this->pin_->digital_read()); }
void GPIOBinarySensor::loop() {
if (this->use_interrupt_) {
if (this->store_.is_changed()) {
// Clear the flag immediately to minimize the window where we might miss changes
this->store_.clear_changed();
// Read the state and publish it
// Note: If the ISR fires between clear_changed() and get_state(), that's fine -
// we'll process the new change on the next loop iteration
bool state = this->store_.get_state();
this->publish_state(state);
} else {
// No changes, disable the loop until the next interrupt
this->disable_loop();
}
} else {
this->publish_state(this->pin_->digital_read());
}
}
float GPIOBinarySensor::get_setup_priority() const { return setup_priority::HARDWARE; }

View File

@@ -2,14 +2,51 @@
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/components/binary_sensor/binary_sensor.h"
namespace esphome {
namespace gpio {
// Store class for ISR data (no vtables, ISR-safe)
class GPIOBinarySensorStore {
public:
void setup(InternalGPIOPin *pin, gpio::InterruptType type, Component *component);
static void gpio_intr(GPIOBinarySensorStore *arg);
bool get_state() const {
// No lock needed: state_ is atomically updated by ISR
// Volatile ensures we read the latest value
return this->state_;
}
bool is_changed() const {
// Simple read of volatile bool - no clearing here
return this->changed_;
}
void clear_changed() {
// Separate method to clear the flag
this->changed_ = false;
}
protected:
ISRInternalGPIOPin isr_pin_;
volatile bool state_{false};
volatile bool last_state_{false};
volatile bool changed_{false};
Component *component_{nullptr}; // Pointer to the component for enable_loop_soon_any_context()
};
class GPIOBinarySensor : public binary_sensor::BinarySensor, public Component {
public:
// No destructor needed: ESPHome components are created at boot and live forever.
// Interrupts are only detached on reboot when memory is cleared anyway.
void set_pin(GPIOPin *pin) { pin_ = pin; }
void set_use_interrupt(bool use_interrupt) { use_interrupt_ = use_interrupt; }
void set_interrupt_type(gpio::InterruptType type) { interrupt_type_ = type; }
// ========== INTERNAL METHODS ==========
// (In most use cases you won't need these)
/// Setup pin
@@ -22,6 +59,9 @@ class GPIOBinarySensor : public binary_sensor::BinarySensor, public Component {
protected:
GPIOPin *pin_;
bool use_interrupt_{true};
gpio::InterruptType interrupt_type_{gpio::INTERRUPT_ANY_EDGE};
GPIOBinarySensorStore store_;
};
} // namespace gpio

View File

@@ -48,6 +48,11 @@ void HOT Logger::log_vprintf_(uint8_t level, const char *tag, int line, const ch
// For non-main tasks, queue the message for callbacks - but only if we have any callbacks registered
message_sent =
this->log_buffer_->send_message_thread_safe(level, tag, static_cast<uint16_t>(line), current_task, format, args);
if (message_sent) {
// Enable logger loop to process the buffered message
// This is safe to call from any context including ISRs
this->enable_loop_soon_any_context();
}
#endif // USE_ESPHOME_TASK_LOG_BUFFER
// Emergency console logging for non-main tasks when ring buffer is full or disabled
@@ -139,6 +144,10 @@ Logger::Logger(uint32_t baud_rate, size_t tx_buffer_size) : baud_rate_(baud_rate
#ifdef USE_ESPHOME_TASK_LOG_BUFFER
void Logger::init_log_buffer(size_t total_buffer_size) {
this->log_buffer_ = esphome::make_unique<logger::TaskLogBuffer>(total_buffer_size);
// Start with loop disabled when using task buffer (unless using USB CDC)
// The loop will be enabled automatically when messages arrive
this->disable_loop_when_buffer_empty_();
}
#endif
@@ -189,6 +198,10 @@ void Logger::loop() {
this->write_msg_(this->tx_buffer_);
}
}
} else {
// No messages to process, disable loop if appropriate
// This reduces overhead when there's no async logging activity
this->disable_loop_when_buffer_empty_();
}
#endif
}

View File

@@ -358,6 +358,26 @@ class Logger : public Component {
static const uint16_t RESET_COLOR_LEN = strlen(ESPHOME_LOG_RESET_COLOR);
this->write_body_to_buffer_(ESPHOME_LOG_RESET_COLOR, RESET_COLOR_LEN, buffer, buffer_at, buffer_size);
}
#ifdef USE_ESP32
// Disable loop when task buffer is empty (with USB CDC check)
inline void disable_loop_when_buffer_empty_() {
// Thread safety note: This is safe even if another task calls enable_loop_soon_any_context()
// concurrently. If that happens between our check and disable_loop(), the enable request
// will be processed on the next main loop iteration since:
// - disable_loop() takes effect immediately
// - enable_loop_soon_any_context() sets a pending flag that's checked at loop start
#if defined(USE_LOGGER_USB_CDC) && defined(USE_ARDUINO)
// Only disable if not using USB CDC (which needs loop for connection detection)
if (this->uart_ != UART_SELECTION_USB_CDC) {
this->disable_loop();
}
#else
// No USB CDC support, always safe to disable
this->disable_loop();
#endif
}
#endif
};
extern Logger *global_logger; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)

View File

@@ -0,0 +1,26 @@
"""
Runtime statistics component for ESPHome.
"""
import esphome.codegen as cg
import esphome.config_validation as cv
DEPENDENCIES = []
CONF_ENABLED = "enabled"
CONF_LOG_INTERVAL = "log_interval"
CONFIG_SCHEMA = cv.Schema(
{
cv.Optional(CONF_ENABLED, default=True): cv.boolean,
cv.Optional(
CONF_LOG_INTERVAL, default=60000
): cv.positive_time_period_milliseconds,
}
)
async def to_code(config):
"""Generate code for the runtime statistics component."""
cg.add(cg.App.set_runtime_stats_enabled(config[CONF_ENABLED]))
cg.add(cg.App.set_runtime_stats_log_interval(config[CONF_LOG_INTERVAL]))

View File

@@ -136,6 +136,10 @@ void Application::loop() {
this->in_loop_ = false;
this->app_state_ = new_app_state;
// Process any pending runtime stats printing after all components have run
// This ensures stats printing doesn't affect component timing measurements
runtime_stats.process_pending_stats(last_op_end_time);
// Use the last component's end time instead of calling millis() again
auto elapsed = last_op_end_time - this->last_loop_;
if (elapsed >= this->loop_interval_ || HighFrequencyLoopRequester::is_high_frequency()) {

View File

@@ -1,5 +1,7 @@
#pragma once
#include <algorithm>
#include <limits>
#include <string>
#include <vector>
#include "esphome/core/component.h"
@@ -7,6 +9,7 @@
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/preferences.h"
#include "esphome/core/runtime_stats.h"
#include "esphome/core/scheduler.h"
#ifdef USE_DEVICES
@@ -337,9 +340,23 @@ class Application {
*
* @param loop_interval The interval in milliseconds to run the core loop at. Defaults to 16 milliseconds.
*/
void set_loop_interval(uint32_t loop_interval) { this->loop_interval_ = loop_interval; }
void set_loop_interval(uint32_t loop_interval) {
this->loop_interval_ = std::min(loop_interval, static_cast<uint32_t>(std::numeric_limits<uint16_t>::max()));
}
uint32_t get_loop_interval() const { return this->loop_interval_; }
uint32_t get_loop_interval() const { return static_cast<uint32_t>(this->loop_interval_); }
/** Enable or disable runtime statistics collection.
*
* @param enable Whether to enable runtime statistics collection.
*/
void set_runtime_stats_enabled(bool enable) { runtime_stats.set_enabled(enable); }
/** Set the interval at which runtime statistics are logged.
*
* @param interval The interval in milliseconds between logging of runtime statistics.
*/
void set_runtime_stats_log_interval(uint32_t interval) { runtime_stats.set_log_interval(interval); }
void schedule_dump_config() { this->dump_config_at_ = 0; }
@@ -618,6 +635,17 @@ class Application {
/// Perform a delay while also monitoring socket file descriptors for readiness
void yield_with_select_(uint32_t delay_ms);
// === Member variables ordered by size to minimize padding ===
// Pointer-sized members first
Component *current_component_{nullptr};
const char *comment_{nullptr};
const char *compilation_time_{nullptr};
// size_t members
size_t dump_config_at_{SIZE_MAX};
// Vectors (largest members)
std::vector<Component *> components_{};
// Partitioned vector design for looping components
@@ -637,11 +665,6 @@ class Application {
// and active_end_ is incremented
// - This eliminates branch mispredictions from flag checking in the hot loop
std::vector<Component *> looping_components_{};
uint16_t looping_components_active_end_{0};
// For safe reentrant modifications during iteration
uint16_t current_loop_index_{0};
bool in_loop_{false};
#ifdef USE_DEVICES
std::vector<Device *> devices_{};
@@ -713,26 +736,39 @@ class Application {
std::vector<update::UpdateEntity *> updates_{};
#endif
#ifdef USE_SOCKET_SELECT_SUPPORT
std::vector<int> socket_fds_; // Vector of all monitored socket file descriptors
#endif
// String members
std::string name_;
std::string friendly_name_;
const char *comment_{nullptr};
const char *compilation_time_{nullptr};
bool name_add_mac_suffix_;
// 4-byte members
uint32_t last_loop_{0};
uint32_t loop_interval_{16};
size_t dump_config_at_{SIZE_MAX};
uint8_t app_state_{0};
volatile bool has_pending_enable_loop_requests_{false};
Component *current_component_{nullptr};
uint32_t loop_component_start_time_{0};
#ifdef USE_SOCKET_SELECT_SUPPORT
// Socket select management
std::vector<int> socket_fds_; // Vector of all monitored socket file descriptors
int max_fd_{-1}; // Highest file descriptor number for select()
#endif
// 2-byte members (grouped together for alignment)
uint16_t loop_interval_{16}; // Loop interval in ms (max 65535ms = 65.5 seconds)
uint16_t looping_components_active_end_{0};
uint16_t current_loop_index_{0}; // For safe reentrant modifications during iteration
// 1-byte members (grouped together to minimize padding)
uint8_t app_state_{0};
bool name_add_mac_suffix_;
bool in_loop_{false};
volatile bool has_pending_enable_loop_requests_{false};
#ifdef USE_SOCKET_SELECT_SUPPORT
bool socket_fds_changed_{false}; // Flag to rebuild base_read_fds_ when socket_fds_ changes
int max_fd_{-1}; // Highest file descriptor number for select()
fd_set base_read_fds_{}; // Cached fd_set rebuilt only when socket_fds_ changes
fd_set read_fds_{}; // Working fd_set for select(), copied from base_read_fds_
// Variable-sized members at end
fd_set base_read_fds_{}; // Cached fd_set rebuilt only when socket_fds_ changes
fd_set read_fds_{}; // Working fd_set for select(), copied from base_read_fds_
#endif
};

View File

@@ -27,20 +27,67 @@ template<typename T, typename... X> class TemplatableValue {
public:
TemplatableValue() : type_(NONE) {}
template<typename F, enable_if_t<!is_invocable<F, X...>::value, int> = 0>
TemplatableValue(F value) : type_(VALUE), value_(std::move(value)) {}
template<typename F, enable_if_t<!is_invocable<F, X...>::value, int> = 0> TemplatableValue(F value) : type_(VALUE) {
new (&this->value_) T(std::move(value));
}
template<typename F, enable_if_t<is_invocable<F, X...>::value, int> = 0>
TemplatableValue(F f) : type_(LAMBDA), f_(f) {}
template<typename F, enable_if_t<is_invocable<F, X...>::value, int> = 0> TemplatableValue(F f) : type_(LAMBDA) {
this->f_ = new std::function<T(X...)>(std::move(f));
}
// Copy constructor
TemplatableValue(const TemplatableValue &other) : type_(other.type_) {
if (type_ == VALUE) {
new (&this->value_) T(other.value_);
} else if (type_ == LAMBDA) {
this->f_ = new std::function<T(X...)>(*other.f_);
}
}
// Move constructor
TemplatableValue(TemplatableValue &&other) noexcept : type_(other.type_) {
if (type_ == VALUE) {
new (&this->value_) T(std::move(other.value_));
} else if (type_ == LAMBDA) {
this->f_ = other.f_;
other.f_ = nullptr;
}
other.type_ = NONE;
}
// Assignment operators
TemplatableValue &operator=(const TemplatableValue &other) {
if (this != &other) {
this->~TemplatableValue();
new (this) TemplatableValue(other);
}
return *this;
}
TemplatableValue &operator=(TemplatableValue &&other) noexcept {
if (this != &other) {
this->~TemplatableValue();
new (this) TemplatableValue(std::move(other));
}
return *this;
}
~TemplatableValue() {
if (type_ == VALUE) {
this->value_.~T();
} else if (type_ == LAMBDA) {
delete this->f_;
}
}
bool has_value() { return this->type_ != NONE; }
T value(X... x) {
if (this->type_ == LAMBDA) {
return this->f_(x...);
return (*this->f_)(x...);
}
// return value also when none
return this->value_;
return this->type_ == VALUE ? this->value_ : T{};
}
optional<T> optional_value(X... x) {
@@ -58,14 +105,16 @@ template<typename T, typename... X> class TemplatableValue {
}
protected:
enum {
enum : uint8_t {
NONE,
VALUE,
LAMBDA,
} type_;
T value_{};
std::function<T(X...)> f_{};
union {
T value_;
std::function<T(X...)> *f_;
};
};
/** Base class for all automation conditions.

View File

@@ -303,6 +303,9 @@ uint32_t WarnIfComponentBlockingGuard::finish() {
uint32_t curr_time = millis();
uint32_t blocking_time = curr_time - this->started_;
// Record component runtime stats
runtime_stats.record_component_time(this->component_, blocking_time, curr_time);
bool should_warn;
if (this->component_ != nullptr) {
should_warn = this->component_->should_warn_of_blocking(blocking_time);

View File

@@ -6,6 +6,7 @@
#include <string>
#include "esphome/core/optional.h"
#include "esphome/core/runtime_stats.h"
namespace esphome {

View File

@@ -375,7 +375,7 @@ void ComponentIterator::advance() {
}
if (advance_platform) {
this->state_ = static_cast<IteratorState>(static_cast<uint32_t>(this->state_) + 1);
this->state_ = static_cast<IteratorState>(static_cast<uint16_t>(this->state_) + 1);
this->at_ = 0;
} else if (success) {
this->at_++;

View File

@@ -93,7 +93,9 @@ class ComponentIterator {
virtual bool on_end();
protected:
enum class IteratorState {
// Iterates over all ESPHome entities (sensors, switches, lights, etc.)
// Supports up to 256 entity types and up to 65,535 entities of each type
enum class IteratorState : uint8_t {
NONE = 0,
BEGIN,
#ifdef USE_BINARY_SENSOR
@@ -167,7 +169,7 @@ class ComponentIterator {
#endif
MAX,
} state_{IteratorState::NONE};
size_t at_{0};
uint16_t at_{0}; // Supports up to 65,535 entities per type
bool include_internal_{false};
};

View File

@@ -132,6 +132,8 @@
// ESP32-specific feature flags
#ifdef USE_ESP32
#define USE_ESPHOME_TASK_LOG_BUFFER
#define USE_BLUETOOTH_PROXY
#define USE_CAPTIVE_PORTAL
#define USE_ESP32_BLE

View File

@@ -0,0 +1,92 @@
#include "esphome/core/runtime_stats.h"
#include "esphome/core/component.h"
#include <algorithm>
namespace esphome {
RuntimeStatsCollector runtime_stats;
void RuntimeStatsCollector::record_component_time(Component *component, uint32_t duration_ms, uint32_t current_time) {
if (!this->enabled_ || component == nullptr)
return;
// Check if we have cached the name for this component
auto name_it = this->component_names_cache_.find(component);
if (name_it == this->component_names_cache_.end()) {
// First time seeing this component, cache its name
const char *source = component->get_component_source();
this->component_names_cache_[component] = source;
this->component_stats_[source].record_time(duration_ms);
} else {
// Use cached name - no string operations, just map lookup
this->component_stats_[name_it->second].record_time(duration_ms);
}
// If next_log_time_ is 0, initialize it
if (this->next_log_time_ == 0) {
this->next_log_time_ = current_time + this->log_interval_;
return;
}
// Don't print stats here anymore - let process_pending_stats handle it
}
void RuntimeStatsCollector::log_stats_() {
ESP_LOGI(RUNTIME_TAG, "Component Runtime Statistics");
ESP_LOGI(RUNTIME_TAG, "Period stats (last %" PRIu32 "ms):", this->log_interval_);
// First collect stats we want to display
std::vector<ComponentStatPair> stats_to_display;
for (const auto &it : this->component_stats_) {
const ComponentRuntimeStats &stats = it.second;
if (stats.get_period_count() > 0) {
ComponentStatPair pair = {it.first, &stats};
stats_to_display.push_back(pair);
}
}
// Sort by period runtime (descending)
std::sort(stats_to_display.begin(), stats_to_display.end(), std::greater<ComponentStatPair>());
// Log top components by period runtime
for (const auto &it : stats_to_display) {
const std::string &source = it.name;
const ComponentRuntimeStats *stats = it.stats;
ESP_LOGI(RUNTIME_TAG, " %s: count=%" PRIu32 ", avg=%.2fms, max=%" PRIu32 "ms, total=%" PRIu32 "ms", source.c_str(),
stats->get_period_count(), stats->get_period_avg_time_ms(), stats->get_period_max_time_ms(),
stats->get_period_time_ms());
}
// Log total stats since boot
ESP_LOGI(RUNTIME_TAG, "Total stats (since boot):");
// Re-sort by total runtime for all-time stats
std::sort(stats_to_display.begin(), stats_to_display.end(),
[](const ComponentStatPair &a, const ComponentStatPair &b) {
return a.stats->get_total_time_ms() > b.stats->get_total_time_ms();
});
for (const auto &it : stats_to_display) {
const std::string &source = it.name;
const ComponentRuntimeStats *stats = it.stats;
ESP_LOGI(RUNTIME_TAG, " %s: count=%" PRIu32 ", avg=%.2fms, max=%" PRIu32 "ms, total=%" PRIu32 "ms", source.c_str(),
stats->get_total_count(), stats->get_total_avg_time_ms(), stats->get_total_max_time_ms(),
stats->get_total_time_ms());
}
}
void RuntimeStatsCollector::process_pending_stats(uint32_t current_time) {
if (!this->enabled_ || this->next_log_time_ == 0)
return;
if (current_time >= this->next_log_time_) {
this->log_stats_();
this->reset_stats_();
this->next_log_time_ = current_time + this->log_interval_;
}
}
} // namespace esphome

View File

@@ -0,0 +1,121 @@
#pragma once
#include <map>
#include <string>
#include <vector>
#include <cstdint>
#include <algorithm>
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
static const char *const RUNTIME_TAG = "runtime";
class Component; // Forward declaration
class ComponentRuntimeStats {
public:
ComponentRuntimeStats()
: period_count_(0),
total_count_(0),
period_time_ms_(0),
total_time_ms_(0),
period_max_time_ms_(0),
total_max_time_ms_(0) {}
void record_time(uint32_t duration_ms) {
// Update period counters
this->period_count_++;
this->period_time_ms_ += duration_ms;
if (duration_ms > this->period_max_time_ms_)
this->period_max_time_ms_ = duration_ms;
// Update total counters
this->total_count_++;
this->total_time_ms_ += duration_ms;
if (duration_ms > this->total_max_time_ms_)
this->total_max_time_ms_ = duration_ms;
}
void reset_period_stats() {
this->period_count_ = 0;
this->period_time_ms_ = 0;
this->period_max_time_ms_ = 0;
}
// Period stats (reset each logging interval)
uint32_t get_period_count() const { return this->period_count_; }
uint32_t get_period_time_ms() const { return this->period_time_ms_; }
uint32_t get_period_max_time_ms() const { return this->period_max_time_ms_; }
float get_period_avg_time_ms() const {
return this->period_count_ > 0 ? this->period_time_ms_ / static_cast<float>(this->period_count_) : 0.0f;
}
// Total stats (persistent until reboot)
uint32_t get_total_count() const { return this->total_count_; }
uint32_t get_total_time_ms() const { return this->total_time_ms_; }
uint32_t get_total_max_time_ms() const { return this->total_max_time_ms_; }
float get_total_avg_time_ms() const {
return this->total_count_ > 0 ? this->total_time_ms_ / static_cast<float>(this->total_count_) : 0.0f;
}
protected:
// Period stats (reset each logging interval)
uint32_t period_count_;
uint32_t period_time_ms_;
uint32_t period_max_time_ms_;
// Total stats (persistent until reboot)
uint32_t total_count_;
uint32_t total_time_ms_;
uint32_t total_max_time_ms_;
};
// For sorting components by run time
struct ComponentStatPair {
std::string name;
const ComponentRuntimeStats *stats;
bool operator>(const ComponentStatPair &other) const {
// Sort by period time as that's what we're displaying in the logs
return stats->get_period_time_ms() > other.stats->get_period_time_ms();
}
};
class RuntimeStatsCollector {
public:
RuntimeStatsCollector() : log_interval_(60000), next_log_time_(0), enabled_(true) {}
void set_log_interval(uint32_t log_interval) { this->log_interval_ = log_interval; }
uint32_t get_log_interval() const { return this->log_interval_; }
void set_enabled(bool enabled) { this->enabled_ = enabled; }
bool is_enabled() const { return this->enabled_; }
void record_component_time(Component *component, uint32_t duration_ms, uint32_t current_time);
// Process any pending stats printing (should be called after component loop)
void process_pending_stats(uint32_t current_time);
protected:
void log_stats_();
void reset_stats_() {
for (auto &it : this->component_stats_) {
it.second.reset_period_stats();
}
}
// Back to string keys, but we'll cache the source name per component
std::map<std::string, ComponentRuntimeStats> component_stats_;
std::map<Component *, std::string> component_names_cache_;
uint32_t log_interval_;
uint32_t next_log_time_;
bool enabled_;
};
// Global instance for runtime stats collection
extern RuntimeStatsCollector runtime_stats;
} // namespace esphome

View File

@@ -0,0 +1,7 @@
esphome:
name: api-reboot-test
host:
api:
reboot_timeout: 0.5s # Very short timeout for fast testing
logger:
level: DEBUG

View File

@@ -0,0 +1,35 @@
"""Test API server reboot timeout functionality."""
import asyncio
import re
import pytest
from .types import RunCompiledFunction
@pytest.mark.asyncio
async def test_api_reboot_timeout(
yaml_config: str,
run_compiled: RunCompiledFunction,
) -> None:
"""Test that the device reboots when no API clients connect within the timeout."""
loop = asyncio.get_running_loop()
reboot_future = loop.create_future()
reboot_pattern = re.compile(r"No clients; rebooting")
def check_output(line: str) -> None:
"""Check output for reboot message."""
if not reboot_future.done() and reboot_pattern.search(line):
reboot_future.set_result(True)
# Run the device without connecting any API client
async with run_compiled(yaml_config, line_callback=check_output):
# Wait for reboot with timeout
# (0.5s reboot timeout + some margin for processing)
try:
await asyncio.wait_for(reboot_future, timeout=2.0)
except asyncio.TimeoutError:
pytest.fail("Device did not reboot within expected timeout")
# Test passes if we get here - reboot was detected