Compare commits

..

722 Commits

Author SHA1 Message Date
J. Nick Koston
fc6574992c [core] Optimize automation actions memory usage with std::initializer_list 2025-10-20 16:59:11 -10:00
J. Nick Koston
fe0b2daec1 Merge branch 'integration' into memory_api 2025-10-20 15:34:39 -10:00
J. Nick Koston
a91fe2c4c3 Merge branch 'integration' into memory_api 2025-10-20 13:39:58 -10:00
J. Nick Koston
e7ba19b3ea Merge branch 'integration' into memory_api 2025-10-19 12:53:45 -10:00
J. Nick Koston
82f7b7f0d5 debug 2025-10-19 09:41:04 -10:00
J. Nick Koston
db5c78acb9 preen 2025-10-19 09:36:55 -10:00
J. Nick Koston
0b45e1d608 Merge branch 'integration' into memory_api 2025-10-19 09:36:02 -10:00
J. Nick Koston
453a668cfb Merge branch 'integration' into memory_api 2025-10-19 08:46:39 -10:00
J. Nick Koston
ae0c3875fc Merge branch 'integration' into memory_api 2025-10-19 08:02:39 -10:00
J. Nick Koston
c3b652b977 Merge branch 'integration' into memory_api 2025-10-18 22:43:58 -10:00
J. Nick Koston
504c2b9dfd Merge branch 'ci_impact_analysis' into memory_api 2025-10-18 22:43:09 -10:00
J. Nick Koston
314fe77b8d Merge branch 'min_filter_ring_buffer' into memory_api 2025-10-18 22:42:55 -10:00
J. Nick Koston
8686d05d05 Merge branch 'unbound_queued_script_fix' into memory_api 2025-10-18 22:42:50 -10:00
J. Nick Koston
be3dacfe64 Merge branch 'mqtt_reduce_json_assign' into memory_api 2025-10-18 22:42:45 -10:00
J. Nick Koston
3f7e22e271 Merge branch 'integration' into memory_api 2025-10-18 22:24:32 -10:00
J. Nick Koston
1010006b5e Merge branch 'dev' into mqtt_reduce_json_assign 2025-10-18 22:16:33 -10:00
J. Nick Koston
9fc3ad1fa5 bot 2025-10-18 22:16:09 -10:00
J. Nick Koston
fc2c55b642 Merge branch 'integration' into memory_api 2025-10-18 21:26:22 -10:00
J. Nick Koston
cfcc6e22e0 Merge branch 'integration' into memory_api 2025-10-18 21:01:21 -10:00
J. Nick Koston
e52eeb528d Merge branch 'integration' into memory_api 2025-10-18 20:45:19 -10:00
J. Nick Koston
c8b2cf1ceb Merge branch 'integration' into memory_api 2025-10-18 20:21:22 -10:00
J. Nick Koston
a236f1c6d1 Merge branch 'integration' into memory_api 2025-10-18 20:05:56 -10:00
J. Nick Koston
d695c99dbe Merge branch 'integration' into memory_api 2025-10-18 19:50:34 -10:00
J. Nick Koston
eb45427d07 Merge branch 'integration' into memory_api 2025-10-18 14:58:13 -10:00
J. Nick Koston
76ad649bf9 review comments 2025-10-18 14:41:59 -10:00
J. Nick Koston
35f8dc528d Merge branch 'integration' into memory_api 2025-10-18 14:26:11 -10:00
J. Nick Koston
e7bc2b0a44 Merge branch 'light_bitmask' into memory_api 2025-10-18 12:35:33 -10:00
J. Nick Koston
f3c1733662 Merge branch 'integration' into memory_api 2025-10-18 12:07:32 -10:00
J. Nick Koston
efc98e9a2c Merge branch 'integration' into memory_api 2025-10-18 11:10:35 -10:00
J. Nick Koston
a0008d6f44 fix 2025-10-18 10:41:37 -10:00
J. Nick Koston
bb03d73106 Merge branch 'integration' into memory_api 2025-10-18 10:36:13 -10:00
J. Nick Koston
5ad22620c9 [mqtt] Reduce flash usage by optimizing ArduinoJson assignments 2025-10-17 23:35:52 -10:00
J. Nick Koston
e200f82d7a fixes 2025-10-17 21:48:03 -10:00
J. Nick Koston
d18a0888e9 Merge branch 'integration' into memory_api 2025-10-17 21:44:48 -10:00
J. Nick Koston
4dd1f51246 Merge branch 'integration' into memory_api 2025-10-17 21:41:47 -10:00
J. Nick Koston
7f38987c60 Merge branch 'integration' into memory_api 2025-10-17 19:24:31 -10:00
J. Nick Koston
54163bb520 Merge branch 'integration' into memory_api 2025-10-17 12:39:47 -10:00
J. Nick Koston
879ba5e090 Merge branch 'integration' into memory_api 2025-10-17 11:48:09 -10:00
J. Nick Koston
c26125c732 Merge branch 'integration' into memory_api 2025-10-17 11:20:53 -10:00
J. Nick Koston
63f9e1fde8 missing guard 2025-10-17 10:54:39 -10:00
J. Nick Koston
16a8645833 Merge branch 'integration' into memory_api 2025-10-17 10:41:20 -10:00
J. Nick Koston
2eeb9d097f Merge branch 'integration' into memory_api 2025-10-17 09:40:31 -10:00
J. Nick Koston
2ad80d2208 tweak 2025-10-17 09:29:58 -10:00
J. Nick Koston
7220d25a4d Merge branch 'integration' into memory_api 2025-10-17 09:07:34 -10:00
J. Nick Koston
5918db6012 Merge branch 'integration' into memory_api 2025-10-17 09:01:59 -10:00
J. Nick Koston
6e3cc21c34 Merge branch 'integration' into memory_api 2025-10-16 21:18:51 -10:00
J. Nick Koston
62e5b5cd2b Merge branch 'integration' into memory_api 2025-10-16 16:31:27 -10:00
J. Nick Koston
75b6fd4b43 Merge branch 'integration' into memory_api 2025-10-16 14:13:58 -10:00
J. Nick Koston
237504f4e8 Merge branch 'integration' into memory_api 2025-10-16 13:24:18 -10:00
J. Nick Koston
e48371a36d Merge branch 'integration' into memory_api 2025-10-16 11:18:40 -10:00
J. Nick Koston
b0cc53fe7d Merge branch 'integration' into memory_api 2025-10-16 10:29:58 -10:00
J. Nick Koston
819e155939 Merge branch 'integration' into memory_api 2025-10-16 10:19:27 -10:00
J. Nick Koston
a1b366b699 Merge branch 'integration' into memory_api 2025-10-16 09:58:42 -10:00
J. Nick Koston
da551a9257 Merge branch 'integration' into memory_api 2025-10-16 09:53:43 -10:00
J. Nick Koston
7c6351b5d7 Merge branch 'integration' into memory_api 2025-10-15 23:51:56 -10:00
J. Nick Koston
453d56fd13 Merge branch 'integration' into memory_api 2025-10-15 22:13:38 -10:00
J. Nick Koston
a49aed2dcb Merge branch 'integration' into memory_api 2025-10-15 16:19:52 -10:00
J. Nick Koston
b7fbc728f8 Merge branch 'integration' into memory_api 2025-10-15 16:08:38 -10:00
J. Nick Koston
8156c8ccf5 Merge branch 'integration' into memory_api 2025-10-15 15:51:19 -10:00
J. Nick Koston
98c1c2d9af Merge branch 'integration' into memory_api 2025-10-15 15:40:17 -10:00
J. Nick Koston
ec31f0b13b Merge branch 'memory_api' of https://github.com/esphome/esphome into memory_api 2025-10-15 15:21:05 -10:00
J. Nick Koston
d443d3037b Merge branch 'integration' into memory_api 2025-10-15 15:20:48 -10:00
J. Nick Koston
7a0ea74ec6 Merge branch 'integration' into memory_api 2025-10-15 09:45:21 -10:00
J. Nick Koston
2001ae1d41 Merge remote-tracking branch 'origin/integration' into memory_api 2025-10-15 09:45:15 -10:00
J. Nick Koston
42d53b301c Merge branch 'integration' of https://github.com/esphome/esphome into integration 2025-10-15 09:44:42 -10:00
J. Nick Koston
9355a3592c Merge remote-tracking branch 'upstream/dev' into integration 2025-10-15 09:43:58 -10:00
J. Nick Koston
9b00ab5fe4 Merge branch 'integration' into memory_api 2025-10-14 16:24:24 -10:00
J. Nick Koston
0a738b23f1 Merge branch 'integration' into memory_api 2025-10-14 16:06:16 -10:00
J. Nick Koston
e2327b46e6 Merge branch 'integration' into memory_api 2025-10-14 12:30:06 -10:00
J. Nick Koston
7598900cc2 Merge branch 'integration' into memory_api 2025-10-14 09:39:00 -10:00
J. Nick Koston
138c0fdf91 Merge branch 'integration' into memory_api 2025-10-14 09:17:14 -10:00
J. Nick Koston
41d5122a57 Merge branch 'integration' into memory_api 2025-10-13 22:51:25 -10:00
J. Nick Koston
5c3379120f Merge branch 'integration' into memory_api 2025-10-13 22:31:55 -10:00
J. Nick Koston
3dff1d02d2 Merge branch 'integration' into memory_api 2025-10-13 21:59:26 -10:00
J. Nick Koston
7572951bc5 Merge branch 'integration' into memory_api 2025-10-13 21:58:53 -10:00
J. Nick Koston
a8b6a56467 Merge branch 'integration' into memory_api 2025-10-13 21:46:14 -10:00
J. Nick Koston
66d859bfae Merge branch 'integration' into memory_api 2025-10-13 21:42:24 -10:00
J. Nick Koston
2316f3a5e3 Merge branch 'integration' into memory_api 2025-10-13 21:14:52 -10:00
J. Nick Koston
1479f48636 Merge branch 'fixed_vector_HomeassistantServiceMap' into memory_api 2025-10-13 19:31:38 -10:00
J. Nick Koston
adc0e986a2 Merge branch 'integration' into memory_api 2025-10-13 19:28:36 -10:00
J. Nick Koston
1c6dd52e9f Merge branch 'integration' into memory_api 2025-10-13 19:15:55 -10:00
J. Nick Koston
b7af2d7f4c Merge branch 'integration' into memory_api 2025-10-13 19:07:08 -10:00
J. Nick Koston
96dd348f9a Merge branch 'integration' into memory_api 2025-10-13 18:32:24 -10:00
J. Nick Koston
98132cb950 Merge branch 'integration' into memory_api 2025-10-13 17:50:00 -10:00
J. Nick Koston
a674727fa6 Merge branch 'integration' into memory_api 2025-10-13 16:51:04 -10:00
J. Nick Koston
9945c4b350 Merge branch 'integration' into memory_api 2025-10-13 15:41:57 -10:00
J. Nick Koston
044b3c07ef Merge branch 'integration' into memory_api 2025-10-13 15:30:38 -10:00
J. Nick Koston
910aff6589 Merge branch 'integration' into memory_api 2025-10-13 14:51:27 -10:00
J. Nick Koston
d82bcea106 Merge branch 'integration' into memory_api 2025-10-13 14:39:08 -10:00
J. Nick Koston
99a5a6f3a6 Merge branch 'integration' into memory_api 2025-10-13 14:20:29 -10:00
J. Nick Koston
7231b0cb5c Merge branch 'integration' into memory_api 2025-10-13 11:41:49 -10:00
J. Nick Koston
e28599b403 Merge branch 'integration' into memory_api 2025-10-12 20:22:31 -10:00
J. Nick Koston
987d616846 Merge branch 'integration' into memory_api 2025-10-12 20:20:49 -10:00
J. Nick Koston
ba408a10bb Merge branch 'integration' into memory_api 2025-10-12 20:15:20 -10:00
J. Nick Koston
767b5a11ef Merge branch 'integration' into memory_api 2025-10-12 12:55:42 -10:00
J. Nick Koston
a2254a6d55 Merge branch 'integration' into memory_api 2025-10-12 11:29:04 -10:00
J. Nick Koston
2c6828eb84 Merge branch 'integration' into memory_api 2025-10-12 11:16:36 -10:00
J. Nick Koston
a635db726d Merge branch 'integration' into memory_api 2025-10-12 10:32:11 -10:00
J. Nick Koston
dc53831b27 Merge branch 'integration' into memory_api 2025-10-11 22:03:51 -10:00
J. Nick Koston
6a0bcdf4f6 Merge branch 'integration' into memory_api 2025-10-11 21:26:37 -10:00
J. Nick Koston
cebc8a3867 Merge branch 'integration' into memory_api 2025-10-11 21:23:51 -10:00
J. Nick Koston
91dbdffea5 [mipi_rgb] Fix pin conflicts introduced by shared SPI bus in #11134 2025-10-11 19:56:05 -10:00
J. Nick Koston
2fc5afc79e Merge branch 'integration' into memory_api 2025-10-11 17:36:14 -10:00
J. Nick Koston
34d891761a Merge branch 'integration' into memory_api 2025-10-11 17:00:46 -10:00
J. Nick Koston
d6239398ed Merge branch 'loop_fix_vec' into memory_api 2025-10-11 17:00:25 -10:00
J. Nick Koston
d2a31b95c4 preen 2025-10-11 16:08:47 -10:00
J. Nick Koston
0d3489df3f Merge branch 'integration' into memory_api 2025-10-11 15:34:42 -10:00
J. Nick Koston
069893abb9 Merge branch 'integration' into memory_api 2025-10-11 13:46:07 -10:00
J. Nick Koston
b7b2b296a0 Merge branch 'integration' into memory_api 2025-10-11 12:39:33 -10:00
J. Nick Koston
c241258dfe Merge branch 'integration' into memory_api 2025-10-11 11:26:19 -10:00
J. Nick Koston
19c541f1e6 Merge branch 'integration' into memory_api 2025-10-11 11:11:46 -10:00
J. Nick Koston
245ccb02fa Merge branch 'integration' into memory_api 2025-10-11 10:52:51 -10:00
J. Nick Koston
69888af408 Merge branch 'integration' into memory_api 2025-10-11 09:32:00 -10:00
J. Nick Koston
a012557911 Merge branch 'integration' into memory_api 2025-10-11 09:31:08 -10:00
J. Nick Koston
d63af64282 Merge branch 'integration' into memory_api 2025-10-11 06:27:06 -10:00
J. Nick Koston
c524e6c2b3 Merge branch 'integration' into memory_api 2025-10-10 21:00:54 -10:00
J. Nick Koston
2184c1fde6 Merge branch 'integration' into memory_api 2025-10-10 20:04:45 -10:00
J. Nick Koston
ec44856537 Merge branch 'integration' into memory_api 2025-10-10 19:44:02 -10:00
J. Nick Koston
ee3af3904f Merge remote-tracking branch 'origin/memory_api' into memory_api 2025-10-10 17:39:56 -10:00
J. Nick Koston
02de8f9f80 merge 2025-10-10 17:39:37 -10:00
J. Nick Koston
9722c8eb60 Merge remote-tracking branch 'origin/integration' into memory_api 2025-10-10 17:39:14 -10:00
J. Nick Koston
29fb40a89f Merge branch 'integration' into memory_api 2025-10-10 17:39:01 -10:00
J. Nick Koston
e972767a11 Merge branch 'integration' into memory_api 2025-10-09 22:48:13 -10:00
J. Nick Koston
4890720c0e Merge branch 'mdns_back_compat' into integration 2025-10-09 22:48:05 -10:00
J. Nick Koston
cdc87a4445 [mdns] Restore mdns_txt_record() public API for external components 2025-10-09 22:46:45 -10:00
J. Nick Koston
06a0ab6839 Merge branch 'integration' into memory_api 2025-10-09 16:04:24 -10:00
J. Nick Koston
968d1e2647 Merge branch 'integration' into memory_api 2025-10-09 10:41:47 -10:00
J. Nick Koston
5a4f1dd2da Merge branch 'esp32_remove_esp32_ard_apis' into integration 2025-10-09 10:41:40 -10:00
J. Nick Koston
5b146e1f12 fix 2025-10-09 10:39:41 -10:00
J. Nick Koston
de8a4ff6b0 Merge branch 'integration' into memory_api 2025-10-09 10:33:08 -10:00
J. Nick Koston
c756e132a7 Merge branch 'integration' into memory_api 2025-10-08 09:28:07 -10:00
J. Nick Koston
98e68c32ee Merge branch 'integration' into memory_api 2025-10-08 05:50:03 -10:00
J. Nick Koston
2ea32635c9 Merge branch 'integration' into memory_api 2025-10-08 05:12:32 -10:00
J. Nick Koston
48799517eb Merge branch 'integration' into memory_api 2025-10-07 22:55:15 -10:00
J. Nick Koston
a0d9098f41 Merge branch 'integration' into memory_api 2025-10-07 22:34:07 -10:00
J. Nick Koston
fc0afa1793 Merge branch 'integration' into memory_api 2025-10-07 22:22:31 -10:00
J. Nick Koston
21d4e090bf Merge branch 'integration' into memory_api 2025-10-07 21:52:06 -10:00
J. Nick Koston
181f360176 Merge branch 'integration' into memory_api 2025-10-07 16:21:40 -10:00
J. Nick Koston
58796141e9 Merge branch 'integration' into memory_api 2025-10-07 16:12:17 -10:00
J. Nick Koston
76c8da03fe Merge branch 'integration' into memory_api 2025-10-07 14:10:20 -10:00
J. Nick Koston
3b06b3386f Merge branch 'integration' into memory_api 2025-10-07 17:47:36 -05:00
J. Nick Koston
6107802d69 Merge remote-tracking branch 'upstream/dev' into memory_api 2025-10-07 17:17:47 -05:00
J. Nick Koston
f59d2d5aca Merge branch 'integration' into memory_api 2025-10-07 17:16:24 -05:00
J. Nick Koston
15968cd8be Merge branch 'integration' into memory_api 2025-10-07 15:42:36 -05:00
J. Nick Koston
65b8148f2e simplify 2025-10-07 15:24:48 -05:00
J. Nick Koston
93d493004c simplify 2025-10-07 15:24:42 -05:00
J. Nick Koston
94eab93110 Merge branch 'integration' into memory_api 2025-10-07 14:59:21 -05:00
J. Nick Koston
b89c230e6a Merge branch 'integration' into memory_api 2025-10-07 11:14:50 -05:00
J. Nick Koston
4088dbfdb6 Merge branch 'integration' into memory_api 2025-10-07 09:50:11 -05:00
J. Nick Koston
b2699f5e37 Merge branch 'integration' into memory_api 2025-10-06 17:42:49 -05:00
J. Nick Koston
517f59afe4 [api] Optimize frame helpers to eliminate double-move overhead 2025-10-06 17:27:05 -05:00
J. Nick Koston
1cf5290f28 Merge branch 'integration' into memory_api 2025-10-06 17:20:32 -05:00
J. Nick Koston
cb8765a1dd Merge branch 'integration' into memory_api 2025-10-06 14:17:42 -05:00
J. Nick Koston
553b65b998 Merge branch 'integration' into memory_api 2025-10-06 12:41:56 -05:00
J. Nick Koston
5daccc92c6 Merge branch 'integration' into memory_api 2025-10-06 12:34:44 -05:00
J. Nick Koston
4b09d3a11b Merge branch 'integration' into memory_api 2025-10-06 12:00:27 -05:00
J. Nick Koston
fae8b5f16a Merge branch 'integration' into memory_api 2025-10-06 11:57:44 -05:00
J. Nick Koston
3ab91e1f8b Merge branch 'integration' into memory_api 2025-10-05 22:51:51 -05:00
J. Nick Koston
0a45aad842 Merge branch 'integration' into memory_api 2025-10-05 17:23:15 -05:00
J. Nick Koston
0e04b5ce61 Merge branch 'integration' into memory_api 2025-10-05 17:22:14 -05:00
J. Nick Koston
c9a70eb270 Merge branch 'wifi_logging_opt' into memory_api 2025-10-05 17:11:28 -05:00
J. Nick Koston
71a254a126 Merge branch 'integration' into memory_api 2025-10-05 17:10:30 -05:00
J. Nick Koston
6841d40d07 Merge branch 'integration' into memory_api 2025-10-05 14:43:26 -05:00
J. Nick Koston
4687e58b03 help bot 2025-10-04 22:02:32 -05:00
J. Nick Koston
b31f381444 wip 2025-10-04 21:19:26 -05:00
J. Nick Koston
f2a84052db Merge branch 'integration' into memory_api 2025-10-04 20:31:35 -05:00
J. Nick Koston
481c87aac3 Merge branch 'integration' into memory_api 2025-10-04 20:18:05 -05:00
J. Nick Koston
244ed9f95f Merge branch 'integration' into memory_api 2025-10-04 13:58:45 -05:00
J. Nick Koston
0178bd166d Merge branch 'integration' into memory_api 2025-10-04 13:48:39 -05:00
J. Nick Koston
1ec74583a6 Merge branch 'integration' into memory_api 2025-10-04 13:08:16 -05:00
J. Nick Koston
395aef5a13 Merge branch 'integration' into memory_api 2025-10-04 12:46:45 -05:00
J. Nick Koston
8f4decdce4 Merge branch 'integration' into memory_api 2025-10-04 12:40:48 -05:00
J. Nick Koston
76afcc7647 Merge branch 'integration' into memory_api 2025-10-04 11:25:30 -05:00
J. Nick Koston
c640792482 Merge branch 'integration' into memory_api 2025-10-04 00:26:26 -05:00
J. Nick Koston
5600e52207 Merge branch 'integration' into memory_api 2025-10-03 20:35:52 -05:00
J. Nick Koston
e27df825f8 Merge branch 'integration' into memory_api 2025-10-03 19:40:40 -05:00
J. Nick Koston
e20ad92bf7 Merge branch 'integration' into memory_api 2025-10-03 18:38:55 -05:00
J. Nick Koston
ef3ab92979 Merge branch 'integration' into memory_api 2025-10-03 18:34:25 -05:00
J. Nick Koston
944514eea4 Merge branch 'integration' into memory_api 2025-10-03 18:21:34 -05:00
J. Nick Koston
34e0620765 Merge branch 'integration' into memory_api 2025-10-03 18:11:25 -05:00
J. Nick Koston
d660207c12 Merge branch 'integration' into memory_api 2025-10-03 18:07:17 -05:00
J. Nick Koston
249080d118 Merge branch 'integration' into memory_api 2025-10-03 18:04:25 -05:00
J. Nick Koston
5e9c9e8e79 Merge branch 'integration' into memory_api 2025-10-03 17:08:07 -05:00
J. Nick Koston
7887e136d6 Merge branch 'integration' into memory_api 2025-10-03 16:56:45 -05:00
J. Nick Koston
a3f4863fdc Merge branch 'integration' into memory_api 2025-10-03 16:41:25 -05:00
J. Nick Koston
9e6dc91051 Merge branch 'integration' into memory_api 2025-10-03 16:06:56 -05:00
J. Nick Koston
d909910d6d Merge branch 'integration' into memory_api 2025-10-03 15:32:28 -05:00
J. Nick Koston
c78dc048d8 Merge branch 'integration' into memory_api 2025-10-03 14:58:25 -05:00
J. Nick Koston
7b65279021 Merge branch 'integration' into memory_api 2025-10-03 14:32:17 -05:00
J. Nick Koston
916984b616 Merge branch 'integration' into memory_api 2025-10-03 14:23:48 -05:00
J. Nick Koston
095542035e Merge branch 'integration' into memory_api 2025-10-03 14:21:34 -05:00
J. Nick Koston
29551832b3 Merge branch 'integration' into memory_api 2025-10-03 00:46:01 +02:00
J. Nick Koston
f7cbb5d647 Merge branch 'integration' into memory_api 2025-10-02 23:06:43 +02:00
J. Nick Koston
cd1b47667b preen 2025-10-02 23:02:46 +02:00
J. Nick Koston
86f2d2e5c2 Merge remote-tracking branch 'upstream/memory_api' into memory_api 2025-10-02 22:14:58 +02:00
J. Nick Koston
650f2f915a Merge branch 'webserver_string_construction_reduce' into memory_api 2025-10-02 22:14:12 +02:00
J. Nick Koston
9865320c88 Merge branch 'integration' into memory_api 2025-10-02 20:08:01 +02:00
J. Nick Koston
be4af0a977 Merge branch 'integration' into memory_api 2025-10-02 16:45:45 +02:00
J. Nick Koston
28e8750ad6 Merge branch 'integration' into memory_api 2025-10-02 14:20:41 +02:00
J. Nick Koston
bd6169ce34 Merge branch 'integration' into memory_api 2025-10-02 10:08:55 +02:00
J. Nick Koston
e845bb3404 Merge branch 'integration' into memory_api 2025-10-02 00:22:06 +02:00
J. Nick Koston
2cdc04fa27 Merge branch 'integration' into memory_api 2025-10-02 00:04:56 +02:00
J. Nick Koston
28bc3c20ad Merge branch 'integration' into memory_api 2025-10-01 23:35:54 +02:00
J. Nick Koston
fabeae554f Merge branch 'integration' into memory_api 2025-10-01 23:10:25 +02:00
J. Nick Koston
4208bad94d Merge branch 'integration' into memory_api 2025-10-01 22:49:37 +02:00
J. Nick Koston
b89d969d09 Merge branch 'integration' into memory_api 2025-10-01 21:36:07 +02:00
J. Nick Koston
829f9fb9bc style 2025-10-01 21:08:31 +02:00
J. Nick Koston
5afe2ea48e Merge branch 'integration' into memory_api 2025-10-01 20:47:48 +02:00
J. Nick Koston
b7ab9773a3 Merge branch 'integration' into memory_api 2025-10-01 18:20:48 +02:00
J. Nick Koston
0cabe6d28e Merge branch 'integration' into memory_api 2025-10-01 17:06:11 +02:00
J. Nick Koston
767753bdda Merge branch 'integration' into memory_api 2025-10-01 16:08:09 +02:00
J. Nick Koston
8c9c2ea3ec Merge branch 'integration' into memory_api 2025-10-01 15:57:57 +02:00
J. Nick Koston
8ef4fc4fe9 Merge branch 'integration' into memory_api 2025-10-01 15:20:45 +02:00
J. Nick Koston
fc2f44fa77 Merge branch 'integration' into memory_api 2025-10-01 13:36:04 +02:00
J. Nick Koston
ef0079609e Merge branch 'integration' into memory_api 2025-10-01 13:09:34 +02:00
J. Nick Koston
f0408fb121 Merge branch 'integration' into memory_api 2025-10-01 04:58:22 +02:00
J. Nick Koston
21b22ccd34 Merge branch 'integration' into memory_api 2025-10-01 02:43:56 +02:00
J. Nick Koston
c9088fcaf2 Merge branch 'integration' into memory_api 2025-09-30 18:29:15 +02:00
J. Nick Koston
212933ec29 Merge branch 'web_server_more_tables' into memory_api 2025-09-30 14:11:57 +02:00
J. Nick Koston
fa89617a56 Merge branch 'integration' into memory_api 2025-09-30 13:59:59 +02:00
J. Nick Koston
950310e49a [web_server] Optimize handler methods with lookup tables to reduce flash usage 2025-09-30 11:08:48 +02:00
J. Nick Koston
823d97d216 Merge branch 'integration' into memory_api 2025-09-30 10:31:17 +02:00
J. Nick Koston
29b6a1a6aa add comments to explain to copilot why std::vector convert is needed 2025-09-29 20:15:31 -05:00
J. Nick Koston
d381a220f7 Merge branch 'integration' into memory_api 2025-09-29 20:00:45 -05:00
J. Nick Koston
daa18bdbaf Merge branch 'integration' into memory_api 2025-09-29 19:14:40 -05:00
J. Nick Koston
0eef3caa37 Merge branch 'integration' into memory_api 2025-09-29 18:51:24 -05:00
J. Nick Koston
ee50ea31b4 Merge branch 'integration' into memory_api 2025-09-29 16:41:35 -05:00
J. Nick Koston
c1081fbff7 Merge branch 'integration' into memory_api 2025-09-29 14:05:43 -05:00
J. Nick Koston
07e230b6a7 Merge branch 'integration' into memory_api 2025-09-29 14:00:46 -05:00
J. Nick Koston
4f1c4ada9f Merge branch 'integration' into memory_api 2025-09-29 13:13:52 -05:00
J. Nick Koston
825d24f4d0 Merge branch 'integration' into memory_api 2025-09-29 10:02:05 -05:00
J. Nick Koston
0af79adb4a Merge branch 'integration' into memory_api 2025-09-28 21:34:32 -05:00
J. Nick Koston
1917d2b0af Merge branch 'integration' into memory_api 2025-09-28 18:54:40 -05:00
J. Nick Koston
4652bfa312 Merge branch 'integration' into memory_api 2025-09-28 18:50:46 -05:00
J. Nick Koston
d015457d76 Merge branch 'integration' into memory_api 2025-09-28 18:44:23 -05:00
J. Nick Koston
0945912979 Merge branch 'guard_trq_allocation' into memory_api 2025-09-28 18:43:06 -05:00
J. Nick Koston
e51b16a9c4 Merge branch 'integration' into memory_api 2025-09-28 18:27:26 -05:00
J. Nick Koston
1af6432c31 Merge branch 'integration' into memory_api 2025-09-27 13:59:32 -05:00
J. Nick Koston
4fa9c00158 Merge branch 'make_captive_portal_captive' into memory_api 2025-09-27 13:46:42 -05:00
J. Nick Koston
42c6a3b878 Merge branch 'integration' into memory_api 2025-09-27 13:46:33 -05:00
J. Nick Koston
5841dc58a9 Merge branch 'integration' into memory_api 2025-09-27 13:32:55 -05:00
J. Nick Koston
9e6a589ee4 Merge branch 'integration' into memory_api 2025-09-27 13:22:15 -05:00
J. Nick Koston
10c4d3396e Merge branch 'make_captive_portal_captive' into memory_api 2025-09-27 13:13:05 -05:00
J. Nick Koston
c787495273 Merge branch 'integration' into memory_api 2025-09-27 13:09:45 -05:00
J. Nick Koston
f3fd86a50d Merge branch 'integration' into memory_api 2025-09-27 13:07:38 -05:00
J. Nick Koston
45a8b641b9 Merge branch 'integration' into memory_api 2025-09-27 13:04:20 -05:00
J. Nick Koston
3aec19a4a6 Merge branch 'integration' into memory_api 2025-09-27 11:15:54 -05:00
J. Nick Koston
b86b186188 Merge branch 'imporv_name' into memory_api 2025-09-27 11:10:16 -05:00
J. Nick Koston
75adceb027 Merge branch 'imporv_name' into memory_api 2025-09-27 11:09:25 -05:00
J. Nick Koston
3c06dda9f0 Merge branch 'integration' into memory_api 2025-09-27 10:54:33 -05:00
J. Nick Koston
08cf92bec7 Merge branch 'ota_password_block' into memory_api 2025-09-27 09:29:15 -05:00
J. Nick Koston
b6e8eece18 Merge branch 'integration' into memory_api 2025-09-27 09:29:09 -05:00
J. Nick Koston
c526873d71 Merge branch 'integration' into memory_api 2025-09-27 00:04:03 -05:00
J. Nick Koston
d4026f9333 Merge branch 'integration' into memory_api 2025-09-26 18:19:51 -05:00
J. Nick Koston
7e52eb5ee3 cond 2025-09-26 17:54:56 -05:00
J. Nick Koston
78f64d493c Merge branch 'integration' into memory_api 2025-09-26 16:17:40 -05:00
J. Nick Koston
f4134f21ba Merge branch 'integration' into memory_api 2025-09-26 15:00:59 -05:00
J. Nick Koston
71f35c849d Merge branch 'integration' into memory_api 2025-09-26 14:27:10 -05:00
J. Nick Koston
7136993f31 Merge branch 'integration' into memory_api 2025-09-26 12:36:43 -05:00
J. Nick Koston
5ecdce9473 Merge branch 'integration' into memory_api 2025-09-26 10:32:23 -05:00
J. Nick Koston
70a0b4e1e4 Merge branch 'integration' into memory_api 2025-09-26 10:23:34 -05:00
J. Nick Koston
efe5057cc2 Merge branch 'integration' into memory_api 2025-09-26 10:18:10 -05:00
J. Nick Koston
75b9b56627 Merge branch 'integration' into memory_api 2025-09-26 09:15:34 -05:00
J. Nick Koston
ba88187353 Merge branch 'integration' into memory_api 2025-09-26 00:13:24 -05:00
J. Nick Koston
91b2140916 Merge branch 'integration' into memory_api 2025-09-25 22:50:16 -05:00
J. Nick Koston
e9f78238d4 Merge branch 'sha256_ota' into memory_api 2025-09-25 20:36:24 -05:00
J. Nick Koston
3ce430985a Merge branch 'integration' into memory_api 2025-09-25 14:48:12 -05:00
J. Nick Koston
24d034c674 Merge branch 'dependabot/pip/ruff-0.13.2' into memory_api 2025-09-25 14:37:14 -05:00
J. Nick Koston
d06175816c match pre-commit 2025-09-25 14:34:42 -05:00
J. Nick Koston
76a2725a25 Merge branch 'integration' into memory_api 2025-09-25 14:33:43 -05:00
J. Nick Koston
a4af23ebe4 Merge branch 'integration' into memory_api 2025-09-25 14:24:58 -05:00
J. Nick Koston
2d395d1f8d Merge branch 'integration' into memory_api 2025-09-25 14:20:58 -05:00
dependabot[bot]
a4991a1d96 Bump ruff from 0.13.1 to 0.13.2
Bumps [ruff](https://github.com/astral-sh/ruff) from 0.13.1 to 0.13.2.
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](https://github.com/astral-sh/ruff/compare/0.13.1...0.13.2)

---
updated-dependencies:
- dependency-name: ruff
  dependency-version: 0.13.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-25 19:01:29 +00:00
J. Nick Koston
dadc557b01 Merge branch 'integration' into memory_api 2025-09-25 11:09:51 -05:00
J. Nick Koston
c5062dab55 Merge branch 'integration' into memory_api 2025-09-25 10:56:43 -05:00
J. Nick Koston
a09ca274c3 Merge branch 'sha256_ota' into memory_api 2025-09-25 10:32:23 -05:00
J. Nick Koston
97b59f7775 Merge branch 'integration' into memory_api 2025-09-24 22:21:00 -05:00
J. Nick Koston
8c894e76e7 Merge branch 'integration' into memory_api 2025-09-24 22:07:52 -05:00
J. Nick Koston
90ffdfd650 Merge remote-tracking branch 'upstream/dev' into memory_api 2025-09-24 22:07:31 -05:00
J. Nick Koston
50b479b8f1 Merge branch 'integration' into memory_api 2025-09-24 21:20:52 -05:00
J. Nick Koston
8abdaceb51 Merge branch 'integration' into memory_api 2025-09-24 17:41:33 -05:00
J. Nick Koston
20bc9da534 Merge branch 'integration' into memory_api 2025-09-24 11:53:14 -05:00
J. Nick Koston
b91ae29142 Merge branch 'integration' into memory_api 2025-09-24 09:22:37 -05:00
J. Nick Koston
42525b981e Merge branch 'integration' into memory_api 2025-09-23 23:42:09 -05:00
J. Nick Koston
9ac0f4aa4d Merge branch 'integration' into memory_api 2025-09-23 17:59:18 -05:00
J. Nick Koston
093e70ceb8 Merge branch 'integration' into memory_api 2025-09-23 17:44:51 -05:00
J. Nick Koston
7100d65f14 Merge remote-tracking branch 'upstream/core_api_no_allocate' into memory_api 2025-09-23 15:47:08 -05:00
J. Nick Koston
886cca1095 Merge branch 'integration' into memory_api 2025-09-23 15:05:44 -05:00
J. Nick Koston
7d8c97ac3b Merge branch 'integration' into memory_api 2025-09-23 11:52:10 -05:00
J. Nick Koston
9984cfc039 Merge branch 'integration' into memory_api 2025-09-23 11:30:15 -05:00
J. Nick Koston
288b70669d Merge branch 'integration' into memory_api 2025-09-23 10:14:05 -05:00
J. Nick Koston
58add6d365 Merge branch 'integration' into memory_api 2025-09-23 09:17:25 -05:00
J. Nick Koston
1ffa10c28f Merge branch 'remove-esp32-arduino-wifi-driver' into memory_api 2025-09-23 09:16:55 -05:00
J. Nick Koston
aa687be49d Merge branch 'integration' into memory_api 2025-09-23 09:11:09 -05:00
J. Nick Koston
c800e02ee8 Merge branch 'integration' into memory_api 2025-09-23 08:41:53 -05:00
J. Nick Koston
0e85cf7ed8 Merge branch 'integration' into memory_api 2025-09-23 00:42:34 -05:00
J. Nick Koston
c0ea795348 Merge branch 'integration' into memory_api 2025-09-23 00:19:31 -05:00
J. Nick Koston
3d07a4a3c9 Merge branch 'integration' into memory_api 2025-09-22 16:23:34 -05:00
J. Nick Koston
1069ed0363 Merge branch 'sha256_ota' into memory_api 2025-09-21 15:57:03 -06:00
J. Nick Koston
0ab4f69a9c Merge branch 'integration' into memory_api 2025-09-21 15:53:38 -06:00
J. Nick Koston
199d575052 Merge branch 'integration' into memory_api 2025-09-21 15:46:50 -06:00
J. Nick Koston
05685b41cd merge 2025-09-21 12:12:53 -06:00
J. Nick Koston
7d96cf66c6 Merge branch 'integration' into memory_api 2025-09-21 12:11:12 -06:00
J. Nick Koston
8bf908a88a Merge branch 'integration' into memory_api 2025-09-21 12:06:47 -06:00
J. Nick Koston
991409d315 cleanup 2025-09-21 11:56:16 -06:00
J. Nick Koston
d5c067acfa preen 2025-09-21 11:53:37 -06:00
J. Nick Koston
85ca03345c Merge branch 'integration' into memory_api 2025-09-21 11:47:33 -06:00
J. Nick Koston
70bbe3b825 Merge branch 'integration' into memory_api 2025-09-21 11:39:44 -06:00
J. Nick Koston
863588d313 Merge branch 'integration' into memory_api 2025-09-21 11:25:51 -06:00
J. Nick Koston
cd90fb2cf7 Merge branch 'integration' into memory_api 2025-09-21 11:06:50 -06:00
J. Nick Koston
abc5604c1f Merge branch 'integration' into memory_api 2025-09-21 11:06:30 -06:00
J. Nick Koston
e5fd5425c1 Merge branch 'integration' into memory_api 2025-09-19 22:44:34 -06:00
J. Nick Koston
f8d380cb7d Merge branch 'integration' into memory_api 2025-09-17 17:45:29 -05:00
J. Nick Koston
4031dc2305 Merge branch 'integration' into memory_api 2025-09-17 17:40:18 -05:00
J. Nick Koston
c69dde4454 Merge branch 'integration' into memory_api 2025-09-17 17:25:33 -05:00
J. Nick Koston
db9b1c36f6 Merge branch 'integration' into memory_api 2025-09-16 21:31:32 -05:00
J. Nick Koston
e700ce6c61 Merge branch 'integration' into memory_api 2025-09-16 12:18:27 -05:00
J. Nick Koston
4a04f9f0f0 Merge branch 'integration' into memory_api 2025-09-16 10:53:06 -05:00
J. Nick Koston
48356ab86d Merge branch 'fix_clean_build_files_not_removing_platformio_cache' into memory_api 2025-09-16 10:52:06 -05:00
J. Nick Koston
f1f0fc858c Merge branch 'integration' into memory_api 2025-09-16 10:50:10 -05:00
J. Nick Koston
b69e6f2152 Merge branch 'integration' into memory_api 2025-09-15 23:26:46 -05:00
J. Nick Koston
f0bce77adc Merge branch 'integration' into memory_api 2025-09-15 22:01:30 -05:00
J. Nick Koston
35f50b710e preen 2025-09-15 21:28:42 -05:00
J. Nick Koston
0091a2b92b Merge branch 'integration' into memory_api 2025-09-15 21:20:26 -05:00
J. Nick Koston
5acea1252b Merge branch 'webserver_remove_lambas' into memory_api 2025-09-15 20:55:41 -05:00
J. Nick Koston
f37c833c38 Merge branch 'integration' into memory_api 2025-09-15 19:01:16 -05:00
J. Nick Koston
cd15a43d77 Merge branch 'integration' into memory_api 2025-09-15 18:45:20 -05:00
J. Nick Koston
beb80a2bb6 Merge branch 'integration' into memory_api 2025-09-15 18:00:13 -05:00
J. Nick Koston
64f267914e Merge branch 'integration' into memory_api 2025-09-15 17:45:01 -05:00
J. Nick Koston
e02f2f625f Merge branch 'integration' into memory_api 2025-09-15 12:16:10 -05:00
J. Nick Koston
d005408f71 Merge branch 'integration' into memory_api 2025-09-15 11:53:58 -05:00
J. Nick Koston
415593d05e Merge branch 'integration' into memory_api 2025-09-14 23:16:22 -05:00
J. Nick Koston
ca185cb28f Merge branch 'integration' into memory_api 2025-09-14 22:48:18 -05:00
J. Nick Koston
277b8aff73 Merge branch 'integration' into memory_api 2025-09-14 22:36:34 -05:00
J. Nick Koston
adaebf64ba Merge branch 'integration' into memory_api 2025-09-14 22:19:20 -05:00
J. Nick Koston
a8b8819826 Merge branch 'integration' into memory_api 2025-09-14 21:56:20 -05:00
J. Nick Koston
2a0567c456 Merge branch 'integration' into memory_api 2025-09-14 16:19:26 -05:00
J. Nick Koston
6378bb2ac4 Merge branch 'integration' into memory_api 2025-09-14 14:42:12 -05:00
J. Nick Koston
cb439f31b9 Merge branch 'integration' into memory_api 2025-09-14 14:21:16 -05:00
J. Nick Koston
0e9110482c Merge branch 'integration' into memory_api 2025-09-14 14:01:21 -05:00
J. Nick Koston
53ab104c6a Merge branch 'integration' into memory_api 2025-09-14 13:54:27 -05:00
J. Nick Koston
842006efa1 Merge branch 'integration' into memory_api 2025-09-14 10:50:16 -05:00
J. Nick Koston
bcf8f4ef9d comments 2025-09-14 09:25:59 -05:00
J. Nick Koston
ea26f9319b comments 2025-09-14 09:25:46 -05:00
J. Nick Koston
f78ae3aa52 Merge branch 'integration' into memory_api 2025-09-14 09:18:36 -05:00
J. Nick Koston
a8c1f6e551 Merge branch 'integration' into memory_api 2025-09-13 22:42:36 -05:00
J. Nick Koston
62c457fd87 Merge branch 'integration' into memory_api 2025-09-13 18:08:02 -05:00
J. Nick Koston
b118aa7339 Merge branch 'integration' into memory_api 2025-09-13 17:16:36 -05:00
J. Nick Koston
11691d05e2 Merge branch 'integration' into memory_api 2025-09-13 17:01:04 -05:00
J. Nick Koston
f6ebe21509 Merge branch 'integration' into memory_api 2025-09-10 21:17:19 -05:00
J. Nick Koston
185a2c2fe7 Merge branch 'integration' into memory_api 2025-09-09 17:06:57 -05:00
J. Nick Koston
af45b63d51 Merge branch 'dev' into memory_api 2025-09-09 17:06:39 -05:00
J. Nick Koston
e59928166b Merge branch 'integration' into memory_api 2025-09-08 20:31:33 -05:00
J. Nick Koston
6a7145fef8 Merge branch 'integration' into memory_api 2025-09-08 19:53:58 -05:00
J. Nick Koston
29552b951d Merge branch 'integration' into memory_api 2025-09-08 19:28:32 -05:00
J. Nick Koston
4c9fd2b800 Merge branch 'integration' into memory_api 2025-09-08 10:05:14 -05:00
J. Nick Koston
f37223c756 Merge branch 'integration' into memory_api 2025-09-07 22:03:07 -05:00
J. Nick Koston
5b4b026d1b Merge branch 'integration' into memory_api 2025-09-07 20:11:09 -05:00
J. Nick Koston
563216220d Merge branch 'integration' into memory_api 2025-09-07 18:54:50 -05:00
J. Nick Koston
d2e1135a56 Merge branch 'integration' into memory_api 2025-09-07 18:47:09 -05:00
J. Nick Koston
aec6c3ae33 Merge branch 'integration' into memory_api 2025-09-07 18:23:31 -05:00
J. Nick Koston
72a1f467f0 Merge branch 'integration' into memory_api 2025-09-07 18:05:23 -05:00
J. Nick Koston
c634ef4b12 Merge branch 'integration' into memory_api 2025-09-07 18:02:14 -05:00
J. Nick Koston
ffd607ee28 Merge branch 'integration' into memory_api 2025-09-07 17:21:57 -05:00
J. Nick Koston
1964764dd0 Merge branch 'integration' into memory_api 2025-09-07 16:50:48 -05:00
J. Nick Koston
f358f0a8f9 Merge branch 'integration' into memory_api 2025-09-07 09:48:20 -05:00
J. Nick Koston
f9d10954bb Merge branch 'integration' into memory_api 2025-09-07 09:33:29 -05:00
J. Nick Koston
68c8a67677 Merge branch 'integration' into memory_api 2025-09-07 09:02:28 -05:00
J. Nick Koston
5f8731a136 Merge branch 'integration' into memory_api 2025-09-07 08:59:12 -05:00
J. Nick Koston
2be9f42ddd Merge branch 'integration' into memory_api 2025-09-07 08:23:54 -05:00
J. Nick Koston
902619e46d Merge branch 'integration' into memory_api 2025-09-06 23:30:57 -05:00
J. Nick Koston
97957b49f1 rename 2025-09-06 23:29:11 -05:00
J. Nick Koston
871d36e303 Merge branch 'integration' into memory_api 2025-09-06 22:58:10 -05:00
J. Nick Koston
08f6286427 Merge branch 'integration' into memory_api 2025-09-06 22:53:35 -05:00
J. Nick Koston
261ab786fd Merge branch 'integration' into memory_api 2025-09-06 09:23:33 -05:00
J. Nick Koston
5e44aa2ccc Merge branch 'integration' into memory_api 2025-09-05 22:23:30 -05:00
J. Nick Koston
e9498d65c5 Merge branch 'integration' into memory_api 2025-09-05 22:15:46 -05:00
J. Nick Koston
f2dede2861 Merge branch 'integration' into memory_api 2025-09-05 22:14:32 -05:00
J. Nick Koston
a6d43b5ec9 warnings strings flash 2025-09-05 21:49:19 -05:00
J. Nick Koston
a26c769b96 Merge branch 'integration' into memory_api 2025-09-05 21:28:09 -05:00
J. Nick Koston
bf7ac26ab1 Merge branch 'integration' into memory_api 2025-09-05 17:20:13 -05:00
J. Nick Koston
8e468ff645 Merge branch 'integration' into memory_api 2025-09-05 17:11:30 -05:00
J. Nick Koston
a891b34b01 Merge branch 'integration' into memory_api 2025-09-05 14:40:56 -05:00
J. Nick Koston
48d9de7f24 Merge branch 'integration' into memory_api 2025-09-05 13:53:51 -05:00
J. Nick Koston
7ffe6459f6 Merge branch 'integration' into memory_api 2025-09-05 13:36:33 -05:00
J. Nick Koston
f6b97d3977 Merge branch 'logger_esp8266_progmem' into memory_api 2025-09-05 13:27:37 -05:00
J. Nick Koston
ce9cf27ca5 Merge branch 'integration' into memory_api 2025-09-05 13:10:56 -05:00
J. Nick Koston
2f7c1601e3 Merge branch 'integration' into memory_api 2025-09-05 13:02:18 -05:00
J. Nick Koston
bb5a973fdf Merge branch 'integration' into memory_api 2025-09-05 11:34:19 -05:00
J. Nick Koston
ba2f6027fb Merge branch 'state_class_to_string_flash' into memory_api 2025-09-05 11:34:06 -05:00
J. Nick Koston
ed7cbb9111 Merge branch 'integration' into memory_api 2025-09-05 11:20:33 -05:00
J. Nick Koston
b5dea4549c Merge branch 'integration' into memory_api 2025-09-05 11:17:17 -05:00
J. Nick Koston
c8d575aab7 revert 2025-09-05 11:15:09 -05:00
J. Nick Koston
9b882a8792 Merge branch 'integration' into memory_api 2025-09-05 11:12:43 -05:00
J. Nick Koston
210680a4cb Merge branch 'integration' into memory_api 2025-09-05 11:06:50 -05:00
J. Nick Koston
c6f8aa45eb Merge branch 'integration' into memory_api 2025-09-05 10:45:36 -05:00
J. Nick Koston
def3992af5 Merge branch 'integration' into memory_api 2025-09-05 10:43:11 -05:00
J. Nick Koston
f28691edb4 Merge branch 'integration' into memory_api 2025-09-05 10:39:27 -05:00
J. Nick Koston
4b57f1e619 beginResponse_P 2025-09-05 10:38:58 -05:00
J. Nick Koston
f9d3cddd23 Merge branch 'integration' into memory_api 2025-09-05 10:34:18 -05:00
J. Nick Koston
b7ef1cd7d4 Merge branch 'integration' into memory_api 2025-09-05 10:26:16 -05:00
J. Nick Koston
40794bca78 Merge branch 'logger_esp8266_progmem' into memory_api 2025-09-05 10:20:37 -05:00
J. Nick Koston
f4f1b9d8c3 Merge branch 'integration' into memory_api 2025-09-05 10:19:29 -05:00
J. Nick Koston
8c90675eb2 Merge branch 'integration' into memory_api 2025-09-05 10:12:10 -05:00
J. Nick Koston
51c29f7c61 Merge branch 'integration' into memory_api 2025-09-05 09:43:38 -05:00
J. Nick Koston
5760d5c995 Merge branch 'integration' into memory_api 2025-09-05 09:22:50 -05:00
J. Nick Koston
62fbda4cc3 Merge branch 'integration' into memory_api 2025-09-05 09:14:09 -05:00
J. Nick Koston
33697e95ac Merge branch 'captive_portal_progmem_8266' into memory_api 2025-09-05 08:43:02 -05:00
J. Nick Koston
894bbd27c3 Merge branch 'integration' into memory_api 2025-09-05 08:32:09 -05:00
J. Nick Koston
eb666324fe Merge branch 'integration' into memory_api 2025-09-04 23:32:23 -05:00
J. Nick Koston
2433ac4821 Merge branch 'integration' into memory_api 2025-09-04 22:58:41 -05:00
J. Nick Koston
cf7140ce1a Merge branch 'integration' into memory_api 2025-09-04 22:29:59 -05:00
J. Nick Koston
f762d3a6e2 Merge branch 'integration' into memory_api 2025-09-04 21:42:14 -05:00
J. Nick Koston
d5f855004e Merge branch 'integration' into memory_api 2025-09-04 14:45:52 -05:00
J. Nick Koston
025da5ac30 Merge branch 'integration' into memory_api 2025-09-04 14:40:26 -05:00
J. Nick Koston
ed0b1b02ed Merge branch 'integration' into memory_api 2025-09-04 13:51:40 -05:00
J. Nick Koston
2b4bf270fd Merge branch 'integration' into memory_api 2025-09-04 13:49:39 -05:00
J. Nick Koston
feecc734fb update docs 2025-09-04 13:49:10 -05:00
J. Nick Koston
a6ed4bb844 Merge branch 'integration' into memory_api 2025-09-04 13:27:13 -05:00
J. Nick Koston
56652ce763 Merge branch 'integration' into memory_api 2025-09-04 13:10:35 -05:00
J. Nick Koston
42c8624f95 Merge branch 'integration' into memory_api 2025-09-04 13:09:32 -05:00
J. Nick Koston
1528c06599 Merge branch 'integration' into memory_api 2025-09-04 13:05:31 -05:00
J. Nick Koston
2d409b017e Merge branch 'optimize_pcf8574' into memory_api 2025-09-04 13:05:15 -05:00
J. Nick Koston
20d3e4a1b8 Merge branch 'integration' into memory_api 2025-09-04 13:05:08 -05:00
J. Nick Koston
f5dbc44662 Merge branch 'optimize_pcf8574' into memory_api 2025-09-04 12:24:02 -05:00
J. Nick Koston
ec88aefebf Merge branch 'integration' into memory_api 2025-09-04 11:21:29 -05:00
J. Nick Koston
0585f05d90 Merge branch 'optimize_pcf8574' into memory_api 2025-09-04 11:20:27 -05:00
J. Nick Koston
526ba902ae Merge branch 'integration' into memory_api 2025-09-04 11:04:30 -05:00
J. Nick Koston
888a1539c5 Merge branch 'integration' into memory_api 2025-09-04 10:56:08 -05:00
J. Nick Koston
52ce1f8985 Merge branch 'integration' into memory_api 2025-09-04 10:53:37 -05:00
J. Nick Koston
30ed4717de Merge branch 'integration' into memory_api 2025-09-04 10:43:24 -05:00
J. Nick Koston
b0ec9abbc4 Merge branch 'i2c_std_vector_heap' into memory_api 2025-09-04 10:14:22 -05:00
J. Nick Koston
b1413ac0e0 Merge branch 'integration' into memory_api 2025-09-04 09:06:59 -05:00
J. Nick Koston
049606607a Merge branch 'ota_esp8266_logstr' into memory_api 2025-09-04 09:02:48 -05:00
J. Nick Koston
b5df54d900 Merge branch 'integration' into memory_api 2025-09-04 08:58:18 -05:00
J. Nick Koston
85314be7a2 Merge branch 'logger_8266_str_to_flash' into memory_api 2025-09-04 08:53:18 -05:00
J. Nick Koston
a58b7abde7 Merge branch 'integration' into memory_api 2025-09-04 08:37:41 -05:00
J. Nick Koston
85a4a61d14 [i2c] Optimize memory usage with stack allocation for small buffers 2025-09-04 08:07:22 -05:00
J. Nick Koston
e1b9b57cd7 Merge branch 'esp8266_gpio_progmem' into memory_api 2025-09-03 22:58:53 -05:00
J. Nick Koston
7587be3dfd Merge branch 'integration' into memory_api 2025-09-03 22:57:33 -05:00
J. Nick Koston
28c2af69ea Merge branch 'integration' into memory_api 2025-09-03 22:56:34 -05:00
J. Nick Koston
2d3243d631 Revert "[esp8266] Store component source strings in PROGMEM to save RAM"
This reverts commit ea01cc598b.
2025-09-03 22:16:59 -05:00
J. Nick Koston
105e94db2e Revert "preen"
This reverts commit da9a7c41d1.
2025-09-03 22:16:41 -05:00
J. Nick Koston
32fec42130 Merge branch 'integration' into memory_api 2025-09-03 22:06:30 -05:00
J. Nick Koston
4766abc461 Merge branch 'integration' into memory_api 2025-09-03 19:11:03 -05:00
J. Nick Koston
e453da6161 Merge remote-tracking branch 'origin/esp8266_prefs' into memory_api 2025-09-03 19:10:44 -05:00
J. Nick Koston
2d2ca0de52 Merge branch 'integration' into memory_api 2025-09-03 18:41:23 -05:00
J. Nick Koston
9495a885ed Merge branch 'integration' into memory_api 2025-09-03 17:56:30 -05:00
J. Nick Koston
6c3dba06ba Merge branch 'integration' into memory_api 2025-09-03 16:37:56 -05:00
J. Nick Koston
9d7f606a39 explain why its safe 2025-09-03 16:34:47 -05:00
J. Nick Koston
93c453c8f9 Merge branch 'integration' into memory_api 2025-09-03 16:23:14 -05:00
J. Nick Koston
a9bd37bacd Merge branch 'integration' into memory_api 2025-09-02 23:32:49 -05:00
J. Nick Koston
2080f881da Merge branch 'integration' into memory_api 2025-09-02 10:18:22 -05:00
J. Nick Koston
4eb744d05e Merge branch 'integration' into memory_api 2025-09-02 10:11:55 -05:00
J. Nick Koston
1855877abe Merge branch 'integration' into memory_api 2025-09-02 08:00:14 -05:00
J. Nick Koston
d738962d8d Merge branch 'integration' into memory_api 2025-09-01 23:14:34 -05:00
J. Nick Koston
64a2b688a5 Merge branch 'integration' into memory_api 2025-09-01 22:34:38 -05:00
J. Nick Koston
9ebdfde287 Merge branch 'integration' into memory_api 2025-08-29 22:11:17 -05:00
J. Nick Koston
bcea7e9fb1 Merge branch 'integration' into memory_api 2025-08-29 10:49:02 -05:00
J. Nick Koston
c828941e16 Merge branch 'integration' into memory_api 2025-08-28 13:13:27 -05:00
J. Nick Koston
c90b4c0f81 Merge branch 'integration' into memory_api 2025-08-27 09:47:26 -05:00
J. Nick Koston
2f699b3c20 Merge branch 'integration' into memory_api 2025-08-27 09:47:03 -05:00
J. Nick Koston
001d05b8f2 Merge branch 'integration' into memory_api 2025-08-26 18:22:10 -05:00
J. Nick Koston
a61300b82f Merge branch 'integration' into memory_api 2025-08-26 17:22:56 -05:00
J. Nick Koston
9e0600ec45 Merge branch 'integration' into memory_api 2025-08-26 16:34:04 -05:00
J. Nick Koston
b871456c1e Merge branch 'integration' into memory_api 2025-08-23 16:19:19 -05:00
J. Nick Koston
e44eb59e3c Merge branch 'integration' into memory_api 2025-08-22 08:52:52 -05:00
J. Nick Koston
101d98014d Merge branch 'integration' into memory_api 2025-08-21 21:47:24 -05:00
J. Nick Koston
082f795adc Merge branch 'integration' into memory_api 2025-08-21 20:27:21 -05:00
J. Nick Koston
3ac5285515 Merge branch 'integration' into memory_api 2025-08-21 09:00:18 -05:00
J. Nick Koston
2993940e1d Merge branch 'integration' into memory_api 2025-08-21 08:25:28 -05:00
J. Nick Koston
db96cfaf14 Merge branch 'integration' into memory_api 2025-08-21 08:12:51 -05:00
J. Nick Koston
bad7b0598b Merge branch 'integration' into memory_api 2025-08-20 20:26:20 -05:00
J. Nick Koston
b4f0d4b0c3 Merge branch 'integration' into memory_api 2025-08-20 20:24:31 -05:00
J. Nick Koston
df7c2473c1 Merge branch 'integration' into memory_api 2025-08-20 20:07:21 -05:00
J. Nick Koston
dde1cec3ce Merge branch 'integration' into memory_api 2025-08-20 14:54:39 -05:00
J. Nick Koston
3c6a54bb70 Merge branch 'integration' into memory_api 2025-08-20 13:26:15 -05:00
J. Nick Koston
6fd46966e2 Merge branch 'integration' into memory_api 2025-08-20 08:07:50 -05:00
J. Nick Koston
558cdda8a4 Merge branch 'integration' into memory_api 2025-08-20 08:06:47 -05:00
J. Nick Koston
856ffc2e86 Merge branch 'integration' into memory_api 2025-08-20 07:46:07 -05:00
J. Nick Koston
518443606c Merge branch 'integration' into memory_api 2025-08-19 21:53:35 -05:00
J. Nick Koston
72dd419c49 Merge branch 'integration' into memory_api 2025-08-19 21:52:31 -05:00
J. Nick Koston
997223c93e Merge branch 'integration' into memory_api 2025-08-19 21:43:20 -05:00
J. Nick Koston
d1b9902e41 Merge branch 'integration' into memory_api 2025-08-19 15:38:10 -05:00
J. Nick Koston
c5998690ba Merge branch 'integration' into memory_api 2025-08-19 09:48:25 -05:00
J. Nick Koston
db10185c7e Merge branch 'integration' into memory_api 2025-08-18 21:07:11 -05:00
J. Nick Koston
7f34757ded Merge branch 'integration' into memory_api 2025-08-18 20:03:00 -05:00
J. Nick Koston
bbd5ab1340 Merge branch 'integration' into memory_api 2025-08-18 18:33:43 -05:00
J. Nick Koston
67ae6ce00a tweak 2025-08-18 17:36:20 -05:00
J. Nick Koston
24cee8ae03 tweak 2025-08-18 17:35:21 -05:00
J. Nick Koston
58a9944601 tweak 2025-08-18 17:34:08 -05:00
J. Nick Koston
a999540d4b Merge branch 'integration' into memory_api 2025-08-18 17:13:49 -05:00
J. Nick Koston
727e04c424 Merge branch 'integration' into memory_api 2025-08-18 14:37:37 -05:00
J. Nick Koston
3d03053eb5 Merge branch 'optimize_safemode_flash' into memory_api 2025-08-18 12:42:22 -05:00
J. Nick Koston
8332893661 Merge branch 'integration' into memory_api 2025-08-18 11:06:58 -05:00
J. Nick Koston
54696eac67 Merge branch 'integration' into memory_api 2025-08-18 09:02:43 -05:00
J. Nick Koston
dbda2d5b6a Merge branch 'integration' into memory_api 2025-08-18 08:29:23 -05:00
J. Nick Koston
7e67f0432d Merge branch 'integration' into memory_api 2025-08-18 01:07:43 -05:00
J. Nick Koston
c9932342eb Merge branch 'integration' into memory_api 2025-08-18 01:01:20 -05:00
J. Nick Koston
f556ab1515 Merge branch 'frame_helper_opt' into memory_api 2025-08-18 00:57:04 -05:00
J. Nick Koston
cb157822d0 Merge branch 'decode_cleanup' into memory_api 2025-08-17 22:22:50 -05:00
J. Nick Koston
4ee84b6d53 Merge remote-tracking branch 'origin/libretiny_no_vector' into memory_api 2025-08-17 21:40:59 -05:00
J. Nick Koston
0a483012ae Update esphome/components/libretiny/preferences.cpp 2025-08-17 21:40:45 -05:00
J. Nick Koston
b12666f356 Merge branch 'integration' into memory_api 2025-08-17 21:38:18 -05:00
J. Nick Koston
a82eeabbc8 Merge branch 'libretiny_no_vector' into memory_api 2025-08-17 21:37:42 -05:00
J. Nick Koston
2b23899750 Merge branch 'integration' into memory_api 2025-08-17 21:15:01 -05:00
J. Nick Koston
b0e8aba426 Merge branch 'integration' into memory_api 2025-08-17 21:13:44 -05:00
J. Nick Koston
17467150d8 Merge branch 'integration' into memory_api 2025-08-17 08:43:10 -04:00
J. Nick Koston
1666aae105 Merge branch 'integration' into memory_api 2025-08-16 09:45:27 -04:00
J. Nick Koston
82d7255a16 Merge branch 'integration' into memory_api 2025-08-15 21:36:21 -04:00
J. Nick Koston
769a874d33 Merge branch 'integration' into memory_api 2025-08-15 21:16:44 -04:00
J. Nick Koston
15d1e1f8dd Merge branch 'integration' into memory_api 2025-08-15 15:05:19 -05:00
J. Nick Koston
e4a6dcd9c7 Merge branch 'integration' into memory_api 2025-08-15 12:33:32 -05:00
J. Nick Koston
9f8f1f5bc9 Merge branch 'integration' into memory_api 2025-08-15 09:54:57 -05:00
J. Nick Koston
57b4c65c6c Merge branch 'integration' into memory_api 2025-08-15 00:59:09 -05:00
J. Nick Koston
ce328bc9b9 Merge branch 'integration' into memory_api 2025-08-14 23:49:39 -05:00
J. Nick Koston
306f7263b2 Merge branch 'integration' into memory_api 2025-08-14 23:39:56 -05:00
J. Nick Koston
d2faac10a5 Merge branch 'integration' into memory_api 2025-08-14 23:01:08 -05:00
J. Nick Koston
7b46be523a Merge branch 'integration' into memory_api 2025-08-14 22:42:39 -05:00
J. Nick Koston
3585ed185f Merge branch 'integration' into memory_api 2025-08-14 22:22:14 -05:00
J. Nick Koston
1bd458c868 Merge branch 'integration' into memory_api 2025-08-14 22:14:23 -05:00
J. Nick Koston
9f22de449a Merge branch 'integration' into memory_api 2025-08-14 21:20:40 -05:00
J. Nick Koston
bf4070ad75 Merge branch 'integration' into memory_api 2025-08-14 21:04:59 -05:00
J. Nick Koston
00ae0fea31 Merge branch 'integration' into memory_api 2025-08-14 20:29:21 -05:00
J. Nick Koston
0d288ceef3 Merge branch 'integration' into memory_api 2025-08-14 17:46:43 -05:00
J. Nick Koston
40d35f3dff Merge branch 'integration' into memory_api 2025-08-14 16:32:51 -05:00
J. Nick Koston
4d0049aa62 Merge branch 'integration' into memory_api 2025-08-14 16:04:37 -05:00
J. Nick Koston
256f81f514 Merge branch 'clean_comp_removed' into memory_api 2025-08-14 15:59:52 -05:00
J. Nick Koston
c0c1b4738a Merge branch 'integration' into memory_api 2025-08-14 14:21:14 -05:00
J. Nick Koston
93c90124ee Merge branch 'integration' into memory_api 2025-08-14 10:07:27 -05:00
J. Nick Koston
6dd818a320 Merge branch 'integration' into memory_api 2025-08-14 09:41:12 -05:00
J. Nick Koston
6e9fb8201b Merge branch 'integration' into memory_api 2025-08-13 23:10:18 -05:00
J. Nick Koston
6fc0831762 Merge branch 'integration' into memory_api 2025-08-13 23:01:43 -05:00
J. Nick Koston
62a2b5e736 Merge branch 'integration' into memory_api 2025-08-13 22:46:00 -05:00
J. Nick Koston
3bf8624b87 Merge remote-tracking branch 'upstream/dev' into memory_api 2025-08-13 21:43:55 -05:00
J. Nick Koston
0c92dc77ad Merge branch 'integration' into memory_api 2025-08-13 11:24:22 -05:00
J. Nick Koston
bb6ce91c9d Merge branch 'integration' into memory_api 2025-08-12 21:03:55 -05:00
J. Nick Koston
9238916b32 one more 2025-08-12 20:22:10 -05:00
J. Nick Koston
ed9e717703 Merge branch 'integration' into memory_api 2025-08-12 19:33:42 -05:00
J. Nick Koston
1c409e1b51 Merge branch 'integration' into memory_api 2025-08-12 15:34:36 -05:00
J. Nick Koston
daa7633ebb Merge branch 'integration' into memory_api 2025-08-11 17:22:09 -05:00
J. Nick Koston
0813362ea8 Merge branch 'integration' into memory_api 2025-08-11 14:53:48 -05:00
J. Nick Koston
4b62f48e12 Merge branch 'integration' into memory_api 2025-08-11 14:37:19 -05:00
J. Nick Koston
11dd8ccd1e Merge branch 'integration' into memory_api 2025-08-11 13:29:31 -05:00
J. Nick Koston
073957a310 Merge branch 'integration' into memory_api 2025-08-11 00:44:05 -05:00
J. Nick Koston
cfe2bfea92 Merge branch 'integration' into memory_api 2025-08-11 00:09:51 -05:00
J. Nick Koston
336fea4251 Merge branch 'integration' into memory_api 2025-08-10 19:29:03 -05:00
J. Nick Koston
6f817fd06a Merge branch 'integration' into memory_api 2025-08-10 19:09:54 -05:00
J. Nick Koston
7c0856c460 Merge branch 'integration' into memory_api 2025-08-10 18:54:14 -05:00
J. Nick Koston
3cc916b9d8 Merge branch 'integration' into memory_api 2025-08-10 18:47:30 -05:00
J. Nick Koston
bcd4386fe4 Merge branch 'integration' into memory_api 2025-08-10 18:36:04 -05:00
J. Nick Koston
cd5f7fdc98 preen 2025-08-10 18:03:56 -05:00
J. Nick Koston
8ca671ad6f Merge branch 'integration' into memory_api 2025-08-10 17:53:46 -05:00
J. Nick Koston
3edb1e0726 Merge branch 'integration' into memory_api 2025-08-10 12:41:54 -05:00
J. Nick Koston
7ff8094be2 Merge branch 'integration' into memory_api 2025-08-10 11:12:57 -05:00
J. Nick Koston
5394ee37f9 Merge branch 'integration' into memory_api 2025-08-09 21:02:17 -05:00
J. Nick Koston
6aedcab37e Merge branch 'integration' into memory_api 2025-08-09 20:52:47 -05:00
J. Nick Koston
5b91c5f043 Merge branch 'integration' into memory_api 2025-08-09 20:46:51 -05:00
J. Nick Koston
233882e1c2 Merge branch 'integration' into memory_api 2025-08-09 14:06:01 -05:00
J. Nick Koston
48a94b686d Merge branch 'integration' into memory_api 2025-08-08 16:23:20 -05:00
J. Nick Koston
c982e35189 Merge branch 'integration' into memory_api 2025-08-08 16:05:26 -05:00
J. Nick Koston
61fbf7cd81 Merge branch 'integration' into memory_api 2025-08-07 12:55:44 -10:00
J. Nick Koston
760add7d29 Merge branch 'integration' into memory_api 2025-08-07 12:39:23 -10:00
J. Nick Koston
3f31bcacad Merge branch 'integration' into memory_api 2025-08-07 10:28:59 -10:00
J. Nick Koston
75249775ee Merge branch 'integration' into memory_api 2025-08-07 09:57:32 -10:00
J. Nick Koston
2ff418c8f9 Merge branch 'integration' into memory_api 2025-08-07 09:44:14 -10:00
J. Nick Koston
fb86bd05db Merge branch 'integration' into memory_api 2025-08-06 22:42:38 -10:00
J. Nick Koston
431a8dd374 Merge branch 'integration' into memory_api 2025-08-06 22:04:44 -10:00
J. Nick Koston
9841d7cf87 Merge branch 'integration' into memory_api 2025-08-06 17:31:53 -10:00
J. Nick Koston
28d2c86a91 Merge branch 'dev' into memory_api 2025-08-06 14:08:07 -10:00
J. Nick Koston
5e8768084b Merge branch 'integration' into memory_api 2025-08-06 10:56:41 -10:00
J. Nick Koston
e29b04bcfe Merge branch 'integration' into memory_api 2025-08-06 10:45:16 -10:00
J. Nick Koston
ddb1fcd0f9 preen 2025-08-06 10:39:44 -10:00
J. Nick Koston
2327c5bddf Merge branch 'integration' into memory_api 2025-08-06 10:28:49 -10:00
J. Nick Koston
2cdf50a025 tweak 2025-08-06 10:11:28 -10:00
J. Nick Koston
b75719d37e Merge branch 'integration' into memory_api 2025-08-06 10:03:29 -10:00
J. Nick Koston
27397593c1 Merge branch 'integration' into memory_api 2025-08-06 07:47:47 -10:00
J. Nick Koston
5511dae2fa Merge branch 'integration' into memory_api 2025-08-06 07:38:12 -10:00
J. Nick Koston
e1cb364436 Merge branch 'integration' into memory_api 2025-08-06 07:31:43 -10:00
J. Nick Koston
e5a1820708 Merge branch 'integration' into memory_api 2025-08-06 07:09:31 -10:00
J. Nick Koston
c667218f3f Merge branch 'conditional_compile_ble_adv' into memory_api 2025-08-05 22:22:42 -10:00
J. Nick Koston
2da1974a80 Merge branch 'integration' into memory_api 2025-08-05 22:02:28 -10:00
J. Nick Koston
ba713737bc Merge branch 'integration' into memory_api 2025-08-05 21:36:58 -10:00
J. Nick Koston
096b9c9651 Merge branch 'integration' into memory_api 2025-08-05 20:54:21 -10:00
J. Nick Koston
7e288126fa Merge branch 'integration' into memory_api 2025-08-05 20:30:40 -10:00
J. Nick Koston
addeec572a Merge branch 'integration' into memory_api 2025-08-05 17:26:40 -10:00
J. Nick Koston
c253eb2362 Merge branch 'integration' into memory_api 2025-08-05 17:06:06 -10:00
J. Nick Koston
eea84a43d6 Merge branch 'integration' into memory_api 2025-08-05 17:02:43 -10:00
J. Nick Koston
7344ff6941 merge 2025-08-04 21:50:36 -10:00
J. Nick Koston
2b5d035c39 Merge branch 'integration' into memory_api 2025-08-04 21:49:10 -10:00
J. Nick Koston
89cf81baaa Merge branch 'integration' into memory_api 2025-08-04 20:06:47 -10:00
J. Nick Koston
1c7200545b Merge branch 'integration' into memory_api 2025-08-04 16:52:44 -10:00
J. Nick Koston
3bbd3acea6 Merge branch 'integration' into memory_api 2025-08-04 16:26:55 -10:00
J. Nick Koston
9e1054cb87 Merge branch 'integration' into memory_api 2025-08-04 15:25:44 -10:00
J. Nick Koston
df83205997 Merge branch 'integration' into memory_api 2025-08-04 11:17:58 -10:00
J. Nick Koston
c6aeeb98b6 Merge branch 'integration' into memory_api 2025-08-04 11:10:01 -10:00
J. Nick Koston
2facd1b436 Cleanup esp32_ble_tracker 2025-08-04 09:18:30 -10:00
J. Nick Koston
d4ff83bf6e Merge branch 'integration' into memory_api 2025-08-04 08:52:25 -10:00
J. Nick Koston
15790145b2 Merge branch 'integration' into memory_api 2025-08-03 22:31:55 -10:00
J. Nick Koston
1d3cd0500e Merge branch 'integration' into memory_api 2025-08-03 22:20:15 -10:00
J. Nick Koston
5062bab73f Merge branch 'integration' into memory_api 2025-08-03 22:17:25 -10:00
J. Nick Koston
cccbe4293b Merge branch 'integration' into memory_api 2025-08-03 21:42:00 -10:00
J. Nick Koston
5126aefe13 Merge branch 'integration' into memory_api 2025-08-03 21:23:05 -10:00
J. Nick Koston
3a99929619 Merge branch 'integration' into memory_api 2025-08-03 21:02:31 -10:00
J. Nick Koston
a645c02476 Merge branch 'integration' into memory_api 2025-08-03 20:58:39 -10:00
J. Nick Koston
5ddf32b0a6 Merge branch 'integration' into memory_api 2025-08-03 19:32:46 -10:00
J. Nick Koston
85fd10f06e Merge branch 'integration' into memory_api 2025-08-03 18:06:01 -10:00
J. Nick Koston
a6c8651298 Merge branch 'integration' into memory_api 2025-08-03 16:31:11 -10:00
J. Nick Koston
7a7132f1c8 Merge branch 'integration' into memory_api 2025-08-03 16:16:40 -10:00
J. Nick Koston
367e35771e Merge branch 'integration' into memory_api 2025-08-03 15:51:48 -10:00
J. Nick Koston
27d66e5cfe Merge branch 'integration' into memory_api 2025-08-03 15:45:56 -10:00
J. Nick Koston
ef33d36d63 Merge branch 'integration' into memory_api 2025-08-03 15:04:46 -10:00
J. Nick Koston
e95baa67d5 Merge branch 'integration' into memory_api 2025-08-03 13:55:26 -10:00
J. Nick Koston
a86c0b66c8 Merge branch 'unregister_fd_flash' into memory_api 2025-08-02 22:45:29 -10:00
J. Nick Koston
f34e7a0051 Merge branch 'integration' into memory_api 2025-08-02 22:03:23 -10:00
J. Nick Koston
be701c58e1 Merge branch 'integration' into memory_api 2025-08-02 15:57:31 -10:00
J. Nick Koston
5d6a152c45 Merge branch 'integration' into memory_api 2025-08-02 15:37:27 -10:00
J. Nick Koston
7faded49d3 Merge branch 'integration' into memory_api 2025-08-02 14:23:48 -10:00
J. Nick Koston
5f0e1f8ab5 Merge branch 'integration' into memory_api 2025-08-02 13:01:22 -10:00
J. Nick Koston
3ef334c882 Merge branch 'integration' into memory_api 2025-08-02 12:54:39 -10:00
J. Nick Koston
ade5aba423 Merge branch 'integration' into memory_api 2025-08-02 12:47:00 -10:00
J. Nick Koston
178910d15e Merge branch 'integration' into memory_api 2025-08-02 12:04:28 -10:00
J. Nick Koston
18dca1a07d Merge branch 'integration' into memory_api 2025-08-02 10:30:55 -10:00
J. Nick Koston
79bc7ba206 Merge branch 'integration' into memory_api 2025-08-02 10:05:56 -10:00
J. Nick Koston
aa33baf5a2 Merge branch 'integration' into memory_api 2025-08-02 09:47:48 -10:00
J. Nick Koston
466f8d2050 remove test 2025-08-02 07:53:14 -10:00
J. Nick Koston
90b68d6fa0 Merge branch 'integration' into memory_api 2025-08-02 00:00:58 -10:00
J. Nick Koston
37827c1272 Merge branch 'integration' into memory_api 2025-08-01 23:08:30 -10:00
J. Nick Koston
9088790e63 Merge branch 'integration' into memory_api 2025-08-01 23:00:24 -10:00
J. Nick Koston
fdb437deb2 Merge branch 'integration' into memory_api 2025-08-01 22:39:01 -10:00
J. Nick Koston
9b3ee6936d Merge branch 'integration' into memory_api 2025-08-01 21:26:51 -10:00
J. Nick Koston
72d0660261 Merge branch 'integration' into memory_api 2025-08-01 21:26:29 -10:00
J. Nick Koston
9d0df67a9e Merge branch 'integration' into memory_api 2025-08-01 20:33:14 -10:00
J. Nick Koston
18fb3618a0 Merge branch 'integration' into memory_api 2025-08-01 15:41:50 -10:00
J. Nick Koston
4019bf9027 Merge branch 'integration' into memory_api 2025-08-01 15:36:16 -10:00
J. Nick Koston
082e365f82 Merge branch 'integration' into memory_api 2025-08-01 15:00:49 -10:00
J. Nick Koston
9005767b67 Merge branch 'integration' into memory_api 2025-08-01 14:13:33 -10:00
J. Nick Koston
1c469747d4 Merge branch 'integration' into memory_api 2025-08-01 13:01:25 -10:00
J. Nick Koston
7d1a816c5e Merge branch 'integration' into memory_api 2025-08-01 12:47:49 -10:00
J. Nick Koston
1a55885fc3 Merge branch 'integration' into memory_api 2025-08-01 11:21:24 -10:00
J. Nick Koston
9f52caf3b6 Merge branch 'integration' into memory_api 2025-08-01 11:19:46 -10:00
J. Nick Koston
7667325788 Merge branch 'integration' into memory_api 2025-08-01 11:17:16 -10:00
J. Nick Koston
ee7c44c3b0 Merge branch 'integration' into memory_api 2025-08-01 11:06:26 -10:00
J. Nick Koston
e079e46d99 Merge branch 'bluetooth_connection_churn' into memory_api 2025-08-01 11:06:24 -10:00
J. Nick Koston
f7c7aa9f9a Merge branch 'integration' into memory_api 2025-08-01 10:43:58 -10:00
J. Nick Koston
f3a57bd248 Merge branch 'integration' into memory_api 2025-07-31 11:51:50 -10:00
J. Nick Koston
038548500c Merge branch 'integration' into memory_api 2025-07-31 11:45:32 -10:00
J. Nick Koston
5d0323d6ea Merge branch 'btp_uuids_dynamic_services_sq' into memory_api 2025-07-31 11:45:28 -10:00
J. Nick Koston
6d40866231 Merge branch 'integration' into memory_api 2025-07-31 11:44:07 -10:00
J. Nick Koston
abb775fc53 Merge branch 'integration' into memory_api 2025-07-31 11:37:40 -10:00
J. Nick Koston
ce6aee873f Merge branch 'integration' into memory_api 2025-07-31 11:27:15 -10:00
J. Nick Koston
3988b48708 Merge branch 'btp_uuids' into memory_api 2025-07-31 05:15:41 -10:00
J. Nick Koston
ccae1bbe15 Merge branch 'integration' into memory_api 2025-07-31 04:17:55 -10:00
J. Nick Koston
6f4a8100ef Merge branch 'integration' into memory_api 2025-07-30 19:26:41 -10:00
J. Nick Koston
383f54810c Merge branch 'fix_normal_opts' into memory_api 2025-07-30 16:12:47 -10:00
J. Nick Koston
77677f3edc Merge branch 'integration' into memory_api 2025-07-30 15:39:40 -10:00
J. Nick Koston
caa9f6fdc7 Merge branch 'integration' into memory_api 2025-07-30 15:00:31 -10:00
J. Nick Koston
093e8093f5 Merge branch 'integration' into memory_api 2025-07-30 14:54:05 -10:00
J. Nick Koston
de51f0f205 Merge branch 'integration' into memory_api 2025-07-30 13:18:01 -10:00
J. Nick Koston
07bc41a900 Merge branch 'integration' into memory_api 2025-07-30 09:43:05 -10:00
J. Nick Koston
1600ab58a1 Merge branch 'integration' into memory_api 2025-07-30 00:12:36 -10:00
J. Nick Koston
24174c3eed Merge branch 'integration' into memory_api 2025-07-29 22:55:46 -10:00
J. Nick Koston
4441d0ea8d Merge branch 'integration' into memory_api 2025-07-29 15:53:43 -10:00
J. Nick Koston
4aec2a95f2 Merge branch 'integration' into memory_api 2025-07-29 14:58:15 -10:00
J. Nick Koston
579c6c57e0 Merge branch 'service_discovery' into memory_api 2025-07-29 14:32:55 -10:00
J. Nick Koston
ff1239c2a4 Merge branch 'integration' into memory_api 2025-07-29 14:27:04 -10:00
J. Nick Koston
53d68abf79 Merge branch 'integration' into memory_api 2025-07-29 10:17:46 -10:00
J. Nick Koston
11f0d0015c Merge branch 'integration' into memory_api 2025-07-29 09:55:31 -10:00
J. Nick Koston
2537c4437f cleanup 2025-07-29 07:15:12 -10:00
J. Nick Koston
9133554588 Merge branch 'integration' into memory_api 2025-07-29 07:06:56 -10:00
J. Nick Koston
e222232376 Merge branch 'integration' into memory_api 2025-07-29 07:04:07 -10:00
J. Nick Koston
aeec9a9379 Merge branch 'integration' into memory_api 2025-07-28 22:11:00 -10:00
J. Nick Koston
8b52a9a02e Merge branch 'integration' into memory_api 2025-07-28 22:10:32 -10:00
J. Nick Koston
226d465f6a Merge branch 'integration' into memory_api 2025-07-27 18:50:05 -10:00
J. Nick Koston
81cea0b2eb Merge branch 'integration' into memory_api 2025-07-27 11:36:35 -10:00
J. Nick Koston
0f5aff4d21 Merge branch 'integration' into memory_api 2025-07-27 11:08:21 -10:00
J. Nick Koston
e3b3259243 Merge branch 'integration' into memory_api 2025-07-26 22:15:15 -10:00
J. Nick Koston
9cf0535d39 Merge branch 'integration' into memory_api 2025-07-26 21:55:34 -10:00
J. Nick Koston
eb25ff0de7 Merge branch 'integration' into memory_api 2025-07-26 21:38:04 -10:00
J. Nick Koston
4f30fd9668 Merge branch 'integration' into memory_api 2025-07-26 21:36:16 -10:00
J. Nick Koston
3a49215dd6 preen 2025-07-26 21:18:39 -10:00
J. Nick Koston
dc45bed048 preen 2025-07-26 21:15:53 -10:00
J. Nick Koston
8e6a053ead preen 2025-07-26 21:11:57 -10:00
J. Nick Koston
5cf89f8594 light2 2025-07-26 21:06:39 -10:00
J. Nick Koston
825f3eee70 light2 2025-07-26 21:04:39 -10:00
J. Nick Koston
eec31846e1 Revert "Revert "light2""
This reverts commit b99b0140ae.
2025-07-26 20:50:50 -10:00
J. Nick Koston
b99b0140ae Revert "light2"
This reverts commit af7e43bbc1.
2025-07-26 20:49:51 -10:00
J. Nick Koston
af7e43bbc1 light2 2025-07-26 20:45:21 -10:00
J. Nick Koston
52750f931b light2 2025-07-26 20:42:45 -10:00
J. Nick Koston
446e9bc529 Merge branch 'integration' into memory_api 2025-07-26 20:26:28 -10:00
J. Nick Koston
d9f3ecd1a3 Merge branch 'integration' into memory_api 2025-07-26 17:14:24 -10:00
J. Nick Koston
f9d0fa1c6d Merge branch 'integration' into memory_api 2025-07-26 17:09:53 -10:00
J. Nick Koston
de6686ee14 Merge branch 'integration' into memory_api 2025-07-26 13:11:37 -10:00
J. Nick Koston
cab11086ac Merge branch 'integration' into memory_api 2025-07-25 23:38:30 -10:00
J. Nick Koston
56b00f9bb7 Merge branch 'integration' into memory_api 2025-07-25 23:31:53 -10:00
J. Nick Koston
01101380ee Merge branch 'integration' into memory_api 2025-07-25 23:15:42 -10:00
J. Nick Koston
90908996b1 Merge branch 'integration' into memory_api 2025-07-25 23:13:32 -10:00
J. Nick Koston
a57daa2656 Merge branch 'integration' into memory_api 2025-07-25 23:12:04 -10:00
J. Nick Koston
30b6a683c2 Merge branch 'integration' into memory_api 2025-07-25 21:39:46 -10:00
J. Nick Koston
7976c5e06c Merge branch 'api_ha_services' into memory_api 2025-07-25 21:39:38 -10:00
J. Nick Koston
89c47c0dae Merge branch 'integration' into memory_api 2025-07-25 18:34:24 -10:00
J. Nick Koston
63e5a0b38c Merge branch 'integration' into memory_api 2025-07-25 18:28:03 -10:00
J. Nick Koston
00266e080b merge 2025-07-25 17:39:48 -10:00
J. Nick Koston
711b153a6a revert 2025-07-25 17:32:30 -10:00
J. Nick Koston
ec9b10954c Merge branch 'integration' into memory_api 2025-07-25 17:25:55 -10:00
J. Nick Koston
f0a63313b1 Merge branch 'integration' into memory_api 2025-07-25 17:22:54 -10:00
J. Nick Koston
6c825def9e Merge branch 'integration' into memory_api 2025-07-25 17:15:23 -10:00
J. Nick Koston
793b3de3e9 revert 2025-07-25 16:50:36 -10:00
J. Nick Koston
60d988ca92 Merge branch 'ble_chars' into memory_api 2025-07-25 16:44:40 -10:00
J. Nick Koston
abf94e61f1 Revert "Revert "cleanup""
This reverts commit 7320cd24f0.
2025-07-25 16:43:28 -10:00
J. Nick Koston
7320cd24f0 Revert "cleanup"
This reverts commit 40a3232267.
2025-07-25 16:42:05 -10:00
J. Nick Koston
40a3232267 cleanup 2025-07-25 16:40:09 -10:00
J. Nick Koston
1d22bcac82 cleanup 2025-07-25 16:38:32 -10:00
J. Nick Koston
9dbcb2ea2e Merge branch 'integration' into memory_api 2025-07-25 16:17:31 -10:00
J. Nick Koston
e28c32af15 Merge branch 'integration' into memory_api 2025-07-25 15:37:20 -10:00
J. Nick Koston
ce96c3c105 Merge branch 'integration' into memory_api 2025-07-25 14:48:21 -10:00
J. Nick Koston
6b49813084 Merge branch 'integration' into memory_api 2025-07-25 14:36:23 -10:00
J. Nick Koston
662b5a452e Merge branch 'integration' into memory_api 2025-07-25 13:34:00 -10:00
J. Nick Koston
254c70a420 Merge branch 'integration' into memory_api 2025-07-25 13:28:11 -10:00
J. Nick Koston
436ba20b7f Merge branch 'integration' into memory_api 2025-07-25 12:11:11 -10:00
J. Nick Koston
b68f2b0ae4 Merge remote-tracking branch 'origin/memory_api' into memory_api 2025-07-25 12:08:26 -10:00
J. Nick Koston
4053a88c3a Merge branch 'integration' into memory_api 2025-07-25 12:08:13 -10:00
427 changed files with 3134 additions and 8166 deletions

View File

@@ -1 +1 @@
3d46b63015d761c85ca9cb77ab79a389509e5776701fb22aed16e7b79d432c0c
d7693a1e996cacd4a3d1c9a16336799c2a8cc3db02e4e74084151ce964581248

View File

@@ -53,7 +53,6 @@ jobs:
'new-target-platform',
'merging-to-release',
'merging-to-beta',
'chained-pr',
'core',
'small-pr',
'dashboard',
@@ -141,8 +140,6 @@ jobs:
labels.add('merging-to-release');
} else if (baseRef === 'beta') {
labels.add('merging-to-beta');
} else if (baseRef !== 'dev') {
labels.add('chained-pr');
}
return labels;
@@ -416,7 +413,7 @@ jobs:
}
// Generate review messages
function generateReviewMessages(finalLabels, originalLabelCount) {
function generateReviewMessages(finalLabels) {
const messages = [];
const prAuthor = context.payload.pull_request.user.login;
@@ -430,15 +427,15 @@ jobs:
.reduce((sum, file) => sum + (file.deletions || 0), 0);
const nonTestChanges = (totalAdditions - testAdditions) - (totalDeletions - testDeletions);
const tooManyLabels = originalLabelCount > MAX_LABELS;
const tooManyLabels = finalLabels.length > MAX_LABELS;
const tooManyChanges = nonTestChanges > TOO_BIG_THRESHOLD;
let message = `${TOO_BIG_MARKER}\n### 📦 Pull Request Size\n\n`;
if (tooManyLabels && tooManyChanges) {
message += `This PR is too large with ${nonTestChanges} line changes (excluding tests) and affects ${originalLabelCount} different components/areas.`;
message += `This PR is too large with ${nonTestChanges} line changes (excluding tests) and affects ${finalLabels.length} different components/areas.`;
} else if (tooManyLabels) {
message += `This PR affects ${originalLabelCount} different components/areas.`;
message += `This PR affects ${finalLabels.length} different components/areas.`;
} else {
message += `This PR is too large with ${nonTestChanges} line changes (excluding tests).`;
}
@@ -466,8 +463,8 @@ jobs:
}
// Handle reviews
async function handleReviews(finalLabels, originalLabelCount) {
const reviewMessages = generateReviewMessages(finalLabels, originalLabelCount);
async function handleReviews(finalLabels) {
const reviewMessages = generateReviewMessages(finalLabels);
const hasReviewableLabels = finalLabels.some(label =>
['too-big', 'needs-codeowners'].includes(label)
);
@@ -531,8 +528,8 @@ jobs:
const apiData = await fetchApiData();
const baseRef = context.payload.pull_request.base.ref;
// Early exit for release and beta branches only
if (baseRef === 'release' || baseRef === 'beta') {
// Early exit for non-dev branches
if (baseRef !== 'dev') {
const branchLabels = await detectMergeBranch();
const finalLabels = Array.from(branchLabels);
@@ -627,7 +624,6 @@ jobs:
// Handle too many labels (only for non-mega PRs)
const tooManyLabels = finalLabels.length > MAX_LABELS;
const originalLabelCount = finalLabels.length;
if (tooManyLabels && !isMegaPR && !finalLabels.includes('too-big')) {
finalLabels = ['too-big'];
@@ -636,7 +632,7 @@ jobs:
console.log('Computed labels:', finalLabels.join(', '));
// Handle reviews
await handleReviews(finalLabels, originalLabelCount);
await handleReviews(finalLabels);
// Apply labels
if (finalLabels.length > 0) {

View File

@@ -62,7 +62,7 @@ jobs:
run: git diff
- if: failure()
name: Archive artifacts
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: generated-proto-files
path: |

View File

@@ -114,7 +114,7 @@ jobs:
matrix:
python-version:
- "3.11"
- "3.13"
- "3.14"
os:
- ubuntu-latest
- macOS-latest
@@ -123,9 +123,9 @@ jobs:
# Minimize CI resource usage
# by only running the Python version
# version used for docker images on Windows and macOS
- python-version: "3.13"
- python-version: "3.14"
os: windows-latest
- python-version: "3.13"
- python-version: "3.14"
os: macOS-latest
runs-on: ${{ matrix.os }}
needs:
@@ -178,9 +178,6 @@ jobs:
component-test-count: ${{ steps.determine.outputs.component-test-count }}
changed-cpp-file-count: ${{ steps.determine.outputs.changed-cpp-file-count }}
memory_impact: ${{ steps.determine.outputs.memory-impact }}
cpp-unit-tests-run-all: ${{ steps.determine.outputs.cpp-unit-tests-run-all }}
cpp-unit-tests-components: ${{ steps.determine.outputs.cpp-unit-tests-components }}
component-test-batches: ${{ steps.determine.outputs.component-test-batches }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
@@ -213,9 +210,6 @@ jobs:
echo "component-test-count=$(echo "$output" | jq -r '.component_test_count')" >> $GITHUB_OUTPUT
echo "changed-cpp-file-count=$(echo "$output" | jq -r '.changed_cpp_file_count')" >> $GITHUB_OUTPUT
echo "memory-impact=$(echo "$output" | jq -c '.memory_impact')" >> $GITHUB_OUTPUT
echo "cpp-unit-tests-run-all=$(echo "$output" | jq -r '.cpp_unit_tests_run_all')" >> $GITHUB_OUTPUT
echo "cpp-unit-tests-components=$(echo "$output" | jq -c '.cpp_unit_tests_components')" >> $GITHUB_OUTPUT
echo "component-test-batches=$(echo "$output" | jq -c '.component_test_batches')" >> $GITHUB_OUTPUT
integration-tests:
name: Run integration tests
@@ -253,33 +247,6 @@ jobs:
. venv/bin/activate
pytest -vv --no-cov --tb=native -n auto tests/integration/
cpp-unit-tests:
name: Run C++ unit tests
runs-on: ubuntu-24.04
needs:
- common
- determine-jobs
if: github.event_name == 'pull_request' && (needs.determine-jobs.outputs.cpp-unit-tests-run-all == 'true' || needs.determine-jobs.outputs.cpp-unit-tests-components != '[]')
steps:
- name: Check out code from GitHub
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Restore Python
uses: ./.github/actions/restore-python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Run cpp_unit_test.py
run: |
. venv/bin/activate
if [ "${{ needs.determine-jobs.outputs.cpp-unit-tests-run-all }}" = "true" ]; then
script/cpp_unit_test.py --all
else
ARGS=$(echo '${{ needs.determine-jobs.outputs.cpp-unit-tests-components }}' | jq -r '.[] | @sh' | xargs)
script/cpp_unit_test.py $ARGS
fi
clang-tidy-single:
name: ${{ matrix.name }}
runs-on: ubuntu-24.04
@@ -460,7 +427,7 @@ jobs:
GH_TOKEN: ${{ github.token }}
strategy:
fail-fast: false
max-parallel: 2
max-parallel: 1
matrix:
include:
- id: clang-tidy
@@ -538,18 +505,59 @@ jobs:
run: script/ci-suggest-changes
if: always()
test-build-components-splitter:
name: Split components for intelligent grouping (40 weighted per batch)
runs-on: ubuntu-24.04
needs:
- common
- determine-jobs
if: github.event_name == 'pull_request' && fromJSON(needs.determine-jobs.outputs.component-test-count) > 0
outputs:
matrix: ${{ steps.split.outputs.components }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Restore Python
uses: ./.github/actions/restore-python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Split components intelligently based on bus configurations
id: split
run: |
. venv/bin/activate
# Use intelligent splitter that groups components with same bus configs
components='${{ needs.determine-jobs.outputs.changed-components-with-tests }}'
# Only isolate directly changed components when targeting dev branch
# For beta/release branches, group everything for faster CI
if [[ "${{ github.base_ref }}" == beta* ]] || [[ "${{ github.base_ref }}" == release* ]]; then
directly_changed='[]'
echo "Target branch: ${{ github.base_ref }} - grouping all components"
else
directly_changed='${{ needs.determine-jobs.outputs.directly-changed-components-with-tests }}'
echo "Target branch: ${{ github.base_ref }} - isolating directly changed components"
fi
echo "Splitting components intelligently..."
output=$(python3 script/split_components_for_ci.py --components "$components" --directly-changed "$directly_changed" --batch-size 40 --output github)
echo "$output" >> $GITHUB_OUTPUT
test-build-components-split:
name: Test components batch (${{ matrix.components }})
runs-on: ubuntu-24.04
needs:
- common
- determine-jobs
- test-build-components-splitter
if: github.event_name == 'pull_request' && fromJSON(needs.determine-jobs.outputs.component-test-count) > 0
strategy:
fail-fast: false
max-parallel: ${{ (startsWith(github.base_ref, 'beta') || startsWith(github.base_ref, 'release')) && 8 || 4 }}
matrix:
components: ${{ fromJson(needs.determine-jobs.outputs.component-test-batches) }}
components: ${{ fromJson(needs.test-build-components-splitter.outputs.matrix) }}
steps:
- name: Show disk space
run: |
@@ -810,7 +818,7 @@ jobs:
fi
- name: Upload memory analysis JSON
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: memory-analysis-target
path: memory-analysis-target.json
@@ -874,7 +882,7 @@ jobs:
--platform "$platform"
- name: Upload memory analysis JSON
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: memory-analysis-pr
path: memory-analysis-pr.json
@@ -904,13 +912,13 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Download target analysis JSON
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
name: memory-analysis-target
path: ./memory-analysis
continue-on-error: true
- name: Download PR analysis JSON
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
name: memory-analysis-pr
path: ./memory-analysis
@@ -941,6 +949,7 @@ jobs:
- clang-tidy-nosplit
- clang-tidy-split
- determine-jobs
- test-build-components-splitter
- test-build-components-split
- pre-commit-ci-lite
- memory-impact-target-branch

View File

@@ -58,7 +58,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2
uses: github/codeql-action/init@16140ae1a102900babc80a33c44059580f687047 # v4.30.9
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
@@ -86,6 +86,6 @@ jobs:
exit 1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2
uses: github/codeql-action/analyze@16140ae1a102900babc80a33c44059580f687047 # v4.30.9
with:
category: "/language:${{matrix.language}}"

View File

@@ -138,7 +138,7 @@ jobs:
# version: ${{ needs.init.outputs.tag }}
- name: Upload digests
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: digests-${{ matrix.platform.arch }}
path: /tmp/digests
@@ -171,7 +171,7 @@ jobs:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Download digests
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
pattern: digests-*
path: /tmp/digests

View File

@@ -14,7 +14,6 @@ jobs:
label:
- needs-docs
- merge-after-release
- chained-pr
steps:
- name: Check for ${{ matrix.label }} label
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0

View File

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

View File

@@ -201,7 +201,6 @@ esphome/components/havells_solar/* @sourabhjaiswal
esphome/components/hbridge/fan/* @WeekendWarrior
esphome/components/hbridge/light/* @DotNetDann
esphome/components/hbridge/switch/* @dwmw2
esphome/components/hdc2010/* @optimusprimespace @ssieb
esphome/components/he60r/* @clydebarrow
esphome/components/heatpumpir/* @rob-deutsch
esphome/components/hitachi_ac424/* @sourabhjaiswal

View File

@@ -207,14 +207,14 @@ def choose_upload_log_host(
if has_mqtt_logging():
resolved.append("MQTT")
if has_api() and has_non_ip_address() and has_resolvable_address():
if has_api() and has_non_ip_address():
resolved.extend(_resolve_with_cache(CORE.address, purpose))
elif purpose == Purpose.UPLOADING:
if has_ota() and has_mqtt_ip_lookup():
resolved.append("MQTTIP")
if has_ota() and has_non_ip_address() and has_resolvable_address():
if has_ota() and has_non_ip_address():
resolved.extend(_resolve_with_cache(CORE.address, purpose))
else:
resolved.append(device)
@@ -318,17 +318,7 @@ def has_resolvable_address() -> bool:
"""Check if CORE.address is resolvable (via mDNS, DNS, or is an IP address)."""
# Any address (IP, mDNS hostname, or regular DNS hostname) is resolvable
# The resolve_ip_address() function in helpers.py handles all types via AsyncResolver
if CORE.address is None:
return False
if has_ip_address():
return True
if has_mdns():
return True
# .local mDNS hostnames are only resolvable if mDNS is enabled
return not CORE.address.endswith(".local")
return CORE.address is not None
def mqtt_get_ip(config: ConfigType, username: str, password: str, client_id: str):

View File

@@ -1,6 +1,7 @@
"""CLI interface for memory analysis with report generation."""
from collections import defaultdict
import json
import sys
from . import (
@@ -231,22 +232,9 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
api_component = (name, mem)
break
# Also include wifi_stack and other important system components if they exist
system_components_to_include = [
# Empty list - we've finished debugging symbol categorization
# Add component names here if you need to debug their symbols
]
system_components = [
(name, mem)
for name, mem in components
if name in system_components_to_include
]
# Combine all components to analyze: top ESPHome + all external + API if not already included + system components
components_to_analyze = (
list(top_esphome_components)
+ list(top_external_components)
+ system_components
# Combine all components to analyze: top ESPHome + all external + API if not already included
components_to_analyze = list(top_esphome_components) + list(
top_external_components
)
if api_component and api_component not in components_to_analyze:
components_to_analyze.append(api_component)
@@ -283,6 +271,28 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
return "\n".join(lines)
def to_json(self) -> str:
"""Export analysis results as JSON."""
data = {
"components": {
name: {
"text": mem.text_size,
"rodata": mem.rodata_size,
"data": mem.data_size,
"bss": mem.bss_size,
"flash_total": mem.flash_total,
"ram_total": mem.ram_total,
"symbol_count": mem.symbol_count,
}
for name, mem in self.components.items()
},
"totals": {
"flash": sum(c.flash_total for c in self.components.values()),
"ram": sum(c.ram_total for c in self.components.values()),
},
}
return json.dumps(data, indent=2)
def dump_uncategorized_symbols(self, output_file: str | None = None) -> None:
"""Dump uncategorized symbols for analysis."""
# Sort by size descending

View File

@@ -127,39 +127,40 @@ SYMBOL_PATTERNS = {
"tryget_socket_unconn",
"cs_create_ctrl_sock",
"netbuf_alloc",
"tcp_", # TCP protocol functions
"udp_", # UDP protocol functions
"lwip_", # LwIP stack functions
"eagle_lwip", # ESP-specific LwIP functions
"new_linkoutput", # Link output function
"acd_", # Address Conflict Detection (ACD)
"eth_", # Ethernet functions
"mac_enable_bb", # MAC baseband enable
"reassemble_and_dispatch", # Packet reassembly
],
# dhcp must come before libc to avoid "dhcp_select" matching "select" pattern
"dhcp": ["dhcp", "handle_dhcp"],
"ipv6_stack": ["nd6_", "ip6_", "mld6_", "icmp6_", "icmp6_input"],
# Order matters! More specific categories must come before general ones.
# mdns must come before bluetooth to avoid "_mdns_disable_pcb" matching "ble_" pattern
"mdns_lib": ["mdns"],
# memory_mgmt must come before wifi_stack to catch mmu_hal_* symbols
"memory_mgmt": [
"mem_",
"memory_",
"tlsf_",
"memp_",
"pbuf_",
"pbuf_alloc",
"pbuf_copy_partial_pbuf",
"esp_mmu_map",
"mmu_hal_",
"s_do_mapping", # Memory mapping function, not WiFi
"hash_map_", # Hash map data structure
"umm_assimilate", # UMM malloc assimilation
"wifi_stack": [
"ieee80211",
"hostap",
"sta_",
"ap_",
"scan_",
"wifi_",
"wpa_",
"wps_",
"esp_wifi",
"cnx_",
"wpa3_",
"sae_",
"wDev_",
"ic_",
"mac_",
"esf_buf",
"gWpaSm",
"sm_WPA",
"eapol_",
"owe_",
"wifiLowLevelInit",
"s_do_mapping",
"gScanStruct",
"ppSearchTxframe",
"ppMapWaitTxq",
"ppFillAMPDUBar",
"ppCheckTxConnTrafficIdle",
"ppCalTkipMic",
],
# Bluetooth categories must come BEFORE wifi_stack to avoid misclassification
# Many BLE symbols contain patterns like "ble_" that would otherwise match wifi patterns
"bluetooth": ["bt_", "ble_", "l2c_", "gatt_", "gap_", "hci_", "BT_init"],
"wifi_bt_coex": ["coex"],
"bluetooth_rom": ["r_ble", "r_lld", "r_llc", "r_llm"],
"bluedroid_bt": [
"bluedroid",
@@ -206,61 +207,6 @@ SYMBOL_PATTERNS = {
"copy_extra_byte_in_db",
"parse_read_local_supported_commands_response",
],
"bluetooth": [
"bt_",
"_ble_", # More specific than "ble_" to avoid matching "able_", "enable_", "disable_"
"l2c_",
"l2ble_", # L2CAP for BLE
"gatt_",
"gap_",
"hci_",
"btsnd_hcic_", # Bluetooth HCI command send functions
"BT_init",
"BT_tx_", # Bluetooth transmit functions
"esp_ble_", # Catch esp_ble_* functions
],
"bluetooth_ll": [
"llm_", # Link layer manager
"llc_", # Link layer control
"lld_", # Link layer driver
"ld_acl_", # Link layer ACL (Asynchronous Connection-Oriented)
"llcp_", # Link layer control protocol
"lmp_", # Link manager protocol
],
"wifi_bt_coex": ["coex"],
"wifi_stack": [
"ieee80211",
"hostap",
"sta_",
"wifi_ap_", # More specific than "ap_" to avoid matching "cap_", "map_"
"wifi_scan_", # More specific than "scan_" to avoid matching "_scan_" in other contexts
"wifi_",
"wpa_",
"wps_",
"esp_wifi",
"cnx_",
"wpa3_",
"sae_",
"wDev_",
"ic_mac_", # More specific than "mac_" to avoid matching emac_
"esf_buf",
"gWpaSm",
"sm_WPA",
"eapol_",
"owe_",
"wifiLowLevelInit",
# Removed "s_do_mapping" - this is memory management, not WiFi
"gScanStruct",
"ppSearchTxframe",
"ppMapWaitTxq",
"ppFillAMPDUBar",
"ppCheckTxConnTrafficIdle",
"ppCalTkipMic",
"phy_force_wifi",
"phy_unforce_wifi",
"write_wifi_chan",
"wifi_track_pll",
],
"crypto_math": [
"ecp_",
"bignum_",
@@ -285,36 +231,13 @@ SYMBOL_PATTERNS = {
"p_256_init_curve",
"shift_sub_rows",
"rshift",
"rijndaelEncrypt", # AES Rijndael encryption
],
# System and Arduino core functions must come before libc
"esp_system": [
"system_", # ESP system functions
"postmortem_", # Postmortem reporting
],
"arduino_core": [
"pinMode",
"resetPins",
"millis",
"micros",
"delay(", # More specific - Arduino delay function with parenthesis
"delayMicroseconds",
"digitalWrite",
"digitalRead",
],
"sntp": ["sntp_", "sntp_recv"],
"scheduler": [
"run_scheduled_",
"compute_scheduled_",
"event_TaskQueue",
],
"hw_crypto": ["esp_aes", "esp_sha", "esp_rsa", "esp_bignum", "esp_mpi"],
"libc": [
"printf",
"scanf",
"malloc",
"_free", # More specific than "free" to match _free, __free_r, etc. but not arbitrary "free" substring
"umm_free", # UMM malloc free function
"free",
"memcpy",
"memset",
"strcpy",
@@ -336,7 +259,7 @@ SYMBOL_PATTERNS = {
"_setenv_r",
"_tzset_unlocked_r",
"__tzcalc_limits",
"_select", # More specific than "select" to avoid matching "dhcp_select", etc.
"select",
"scalbnf",
"strtof",
"strtof_l",
@@ -393,24 +316,8 @@ SYMBOL_PATTERNS = {
"CSWTCH$",
"dst$",
"sulp",
"_strtol_l", # String to long with locale
"__cvt", # Convert
"__utoa", # Unsigned to ASCII
"__global_locale", # Global locale
"_ctype_", # Character type
"impure_data", # Impure data
],
"string_ops": [
"strcmp",
"strncmp",
"strchr",
"strstr",
"strtok",
"strdup",
"strncasecmp_P", # String compare (case insensitive, from program memory)
"strnlen_P", # String length (from program memory)
"strncat_P", # String concatenate (from program memory)
],
"string_ops": ["strcmp", "strncmp", "strchr", "strstr", "strtok", "strdup"],
"memory_alloc": ["malloc", "calloc", "realloc", "free", "_sbrk"],
"file_io": [
"fread",
@@ -431,26 +338,10 @@ SYMBOL_PATTERNS = {
"vsscanf",
],
"cpp_anonymous": ["_GLOBAL__N_", "n$"],
# Plain C patterns only - C++ symbols will be categorized via DEMANGLED_PATTERNS
"nvs": ["nvs_"], # Plain C NVS functions
"ota": ["ota_", "OTA", "esp_ota", "app_desc"],
# cpp_runtime: Removed _ZN, _ZL to let DEMANGLED_PATTERNS categorize C++ symbols properly
# Only keep patterns that are truly runtime-specific and not categorizable by namespace
"cpp_runtime": ["__cxx", "_ZSt", "__gxx_personality", "_Z16"],
"exception_handling": [
"__cxa_",
"_Unwind_",
"__gcc_personality",
"uw_frame_state",
"search_object", # Search for exception handling object
"get_cie_encoding", # Get CIE encoding
"add_fdes", # Add frame description entries
"fde_unencoded_compare", # Compare FDEs
"fde_mixed_encoding_compare", # Compare mixed encoding FDEs
"frame_downheap", # Frame heap operations
"frame_heapsort", # Frame heap sorting
],
"cpp_runtime": ["__cxx", "_ZN", "_ZL", "_ZSt", "__gxx_personality", "_Z16"],
"exception_handling": ["__cxa_", "_Unwind_", "__gcc_personality", "uw_frame_state"],
"static_init": ["_GLOBAL__sub_I_"],
"mdns_lib": ["mdns"],
"phy_radio": [
"phy_",
"rf_",
@@ -503,47 +394,10 @@ SYMBOL_PATTERNS = {
"txcal_debuge_mode",
"ant_wifitx_cfg",
"reg_init_begin",
"tx_cap_init", # TX capacitance init
"ram_set_txcap", # RAM TX capacitance setting
"tx_atten_", # TX attenuation
"txiq_", # TX I/Q calibration
"ram_cal_", # RAM calibration
"ram_rxiq_", # RAM RX I/Q
"readvdd33", # Read VDD33
"test_tout", # Test timeout
"tsen_meas", # Temperature sensor measurement
"bbpll_cal", # Baseband PLL calibration
"set_cal_", # Set calibration
"set_rfanagain_", # Set RF analog gain
"set_txdc_", # Set TX DC
"get_vdd33_", # Get VDD33
"gen_rx_gain_table", # Generate RX gain table
"ram_ana_inf_gating_en", # RAM analog interface gating enable
"tx_cont_en", # TX continuous enable
"tx_delay_cfg", # TX delay configuration
"tx_gain_table_set", # TX gain table set
"check_and_reset_hw_deadlock", # Hardware deadlock check
"s_config", # System/hardware config
"chan14_mic_cfg", # Channel 14 MIC config
],
"wifi_phy_pp": [
"pp_",
"ppT",
"ppR",
"ppP",
"ppInstall",
"ppCalTxAMPDULength",
"ppCheckTx", # Packet processor TX check
"ppCal", # Packet processor calibration
"HdlAllBuffedEb", # Handle buffered EB
],
"wifi_phy_pp": ["pp_", "ppT", "ppR", "ppP", "ppInstall", "ppCalTxAMPDULength"],
"wifi_lmac": ["lmac"],
"wifi_device": [
"wdev",
"wDev_",
"ic_set_sta", # Set station mode
"ic_set_vif", # Set virtual interface
],
"wifi_device": ["wdev", "wDev_"],
"power_mgmt": [
"pm_",
"sleep",
@@ -552,7 +406,15 @@ SYMBOL_PATTERNS = {
"deep_sleep",
"power_down",
"g_pm",
"pmc", # Power Management Controller
],
"memory_mgmt": [
"mem_",
"memory_",
"tlsf_",
"memp_",
"pbuf_",
"pbuf_alloc",
"pbuf_copy_partial_pbuf",
],
"hal_layer": ["hal_"],
"clock_mgmt": [
@@ -577,6 +439,7 @@ SYMBOL_PATTERNS = {
"error_handling": ["panic", "abort", "assert", "error_", "fault"],
"authentication": ["auth"],
"ppp_protocol": ["ppp", "ipcp_", "lcp_", "chap_", "LcpEchoCheck"],
"dhcp": ["dhcp", "handle_dhcp"],
"ethernet_phy": [
"emac_",
"eth_phy_",
@@ -755,15 +618,7 @@ SYMBOL_PATTERNS = {
"ampdu_dispatch_upto",
],
"ieee802_11": ["ieee802_11_", "ieee802_11_parse_elems"],
"rate_control": [
"rssi_margin",
"rcGetSched",
"get_rate_fcc_index",
"rcGetRate", # Get rate
"rc_get_", # Rate control getters
"rc_set_", # Rate control setters
"rc_enable_", # Rate control enable functions
],
"rate_control": ["rssi_margin", "rcGetSched", "get_rate_fcc_index"],
"nan": ["nan_dp_", "nan_dp_post_tx", "nan_dp_delete_peer"],
"channel_mgmt": ["chm_init", "chm_set_current_channel"],
"trace": ["trc_init", "trc_onAmpduOp"],
@@ -944,18 +799,31 @@ SYMBOL_PATTERNS = {
"supports_interlaced_inquiry_scan",
"supports_reading_remote_extended_features",
],
"bluetooth_ll": [
"lld_pdu_",
"ld_acl_",
"lld_stop_ind_handler",
"lld_evt_winsize_change",
"config_lld_evt_funcs_reset",
"config_lld_funcs_reset",
"config_llm_funcs_reset",
"llm_set_long_adv_data",
"lld_retry_tx_prog",
"llc_link_sup_to_ind_handler",
"config_llc_funcs_reset",
"lld_evt_rxwin_compute",
"config_btdm_funcs_reset",
"config_ea_funcs_reset",
"llc_defalut_state_tab_reset",
"config_rwip_funcs_reset",
"ke_lmp_rx_flooding_detect",
],
}
# Demangled patterns: patterns found in demangled C++ names
DEMANGLED_PATTERNS = {
"gpio_driver": ["GPIO"],
"uart_driver": ["UART"],
# mdns_lib must come before network_stack to avoid "udp" matching "_udpReadBuffer" in MDNSResponder
"mdns_lib": [
"MDNSResponder",
"MDNSImplementation",
"MDNS",
],
"network_stack": [
"lwip",
"tcp",
@@ -968,24 +836,6 @@ DEMANGLED_PATTERNS = {
"ethernet",
"ppp",
"slip",
"UdpContext", # UDP context class
"DhcpServer", # DHCP server class
],
"arduino_core": [
"String::", # Arduino String class
"Print::", # Arduino Print class
"HardwareSerial::", # Serial class
"IPAddress::", # IP address class
"EspClass::", # ESP class
"experimental::_SPI", # Experimental SPI
],
"ota": [
"UpdaterClass",
"Updater::",
],
"wifi": [
"ESP8266WiFi",
"WiFi::",
],
"wifi_stack": ["NetworkInterface"],
"nimble_bt": [
@@ -1004,6 +854,7 @@ DEMANGLED_PATTERNS = {
"rtti": ["__type_info", "__class_type_info"],
"web_server_lib": ["AsyncWebServer", "AsyncWebHandler", "WebServer"],
"async_tcp": ["AsyncClient", "AsyncServer"],
"mdns_lib": ["mdns"],
"json_lib": [
"ArduinoJson",
"JsonDocument",

View File

@@ -16,12 +16,7 @@ from esphome.const import (
CONF_UPDATE_INTERVAL,
)
from esphome.core import ID
from esphome.cpp_generator import (
LambdaExpression,
MockObj,
MockObjClass,
TemplateArgsType,
)
from esphome.cpp_generator import MockObj, MockObjClass, TemplateArgsType
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
from esphome.types import ConfigType
from esphome.util import Registry
@@ -92,7 +87,6 @@ def validate_potentially_or_condition(value):
DelayAction = cg.esphome_ns.class_("DelayAction", Action, cg.Component)
LambdaAction = cg.esphome_ns.class_("LambdaAction", Action)
StatelessLambdaAction = cg.esphome_ns.class_("StatelessLambdaAction", Action)
IfAction = cg.esphome_ns.class_("IfAction", Action)
WhileAction = cg.esphome_ns.class_("WhileAction", Action)
RepeatAction = cg.esphome_ns.class_("RepeatAction", Action)
@@ -103,40 +97,9 @@ ResumeComponentAction = cg.esphome_ns.class_("ResumeComponentAction", Action)
Automation = cg.esphome_ns.class_("Automation")
LambdaCondition = cg.esphome_ns.class_("LambdaCondition", Condition)
StatelessLambdaCondition = cg.esphome_ns.class_("StatelessLambdaCondition", Condition)
ForCondition = cg.esphome_ns.class_("ForCondition", Condition, cg.Component)
def new_lambda_pvariable(
id_obj: ID,
lambda_expr: LambdaExpression,
stateless_class: MockObjClass,
template_arg: cg.TemplateArguments | None = None,
) -> MockObj:
"""Create Pvariable for lambda, using stateless class if applicable.
Combines ID selection and Pvariable creation in one call. For stateless
lambdas (empty capture), uses function pointer instead of std::function.
Args:
id_obj: The ID object (action_id, condition_id, or filter_id)
lambda_expr: The lambda expression object
stateless_class: The stateless class to use for stateless lambdas
template_arg: Optional template arguments (for actions/conditions)
Returns:
The created Pvariable
"""
# For stateless lambdas, use function pointer instead of std::function
if lambda_expr.capture == "":
id_obj = id_obj.copy()
id_obj.type = stateless_class
if template_arg is not None:
return cg.new_Pvariable(id_obj, template_arg, lambda_expr)
return cg.new_Pvariable(id_obj, lambda_expr)
def validate_automation(extra_schema=None, extra_validators=None, single=False):
if extra_schema is None:
extra_schema = {}
@@ -182,7 +145,7 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False):
value = cv.Schema([extra_validators])(value)
if single:
if len(value) != 1:
raise cv.Invalid("This trigger allows only a single automation")
raise cv.Invalid("Cannot have more than 1 automation for templates")
return value[0]
return value
@@ -277,9 +240,7 @@ async def lambda_condition_to_code(
args: TemplateArgsType,
) -> MockObj:
lambda_ = await cg.process_lambda(config, args, return_type=bool)
return new_lambda_pvariable(
condition_id, lambda_, StatelessLambdaCondition, template_arg
)
return cg.new_Pvariable(condition_id, template_arg, lambda_)
@register_condition(
@@ -445,7 +406,7 @@ async def lambda_action_to_code(
args: TemplateArgsType,
) -> MockObj:
lambda_ = await cg.process_lambda(config, args, return_type=cg.void)
return new_lambda_pvariable(action_id, lambda_, StatelessLambdaAction, template_arg)
return cg.new_Pvariable(action_id, template_arg, lambda_)
@register_action(

View File

@@ -62,7 +62,6 @@ from esphome.cpp_types import ( # noqa: F401
EntityBase,
EntityCategory,
ESPTime,
FixedVector,
GPIOPin,
InternalGPIOPin,
JsonObject,

View File

@@ -9,7 +9,7 @@ static const char *const TAG = "adalight_light_effect";
static const uint32_t ADALIGHT_ACK_INTERVAL = 1000;
static const uint32_t ADALIGHT_RECEIVE_TIMEOUT = 1000;
AdalightLightEffect::AdalightLightEffect(const char *name) : AddressableLightEffect(name) {}
AdalightLightEffect::AdalightLightEffect(const std::string &name) : AddressableLightEffect(name) {}
void AdalightLightEffect::start() {
AddressableLightEffect::start();

View File

@@ -11,7 +11,7 @@ namespace adalight {
class AdalightLightEffect : public light::AddressableLightEffect, public uart::UARTDevice {
public:
AdalightLightEffect(const char *name);
AdalightLightEffect(const std::string &name);
void start() override;
void stop() override;

View File

@@ -172,6 +172,12 @@ def alarm_control_panel_schema(
return _ALARM_CONTROL_PANEL_SCHEMA.extend(schema)
# Remove before 2025.11.0
ALARM_CONTROL_PANEL_SCHEMA = alarm_control_panel_schema(AlarmControlPanel)
ALARM_CONTROL_PANEL_SCHEMA.add_extra(
cv.deprecated_schema_constant("alarm_control_panel")
)
ALARM_CONTROL_PANEL_ACTION_SCHEMA = maybe_simple_id(
{
cv.GenerateID(): cv.use_id(AlarmControlPanel),

View File

@@ -71,12 +71,10 @@ SERVICE_ARG_NATIVE_TYPES = {
"int": cg.int32,
"float": float,
"string": cg.std_string,
"bool[]": cg.FixedVector.template(bool).operator("const").operator("ref"),
"int[]": cg.FixedVector.template(cg.int32).operator("const").operator("ref"),
"float[]": cg.FixedVector.template(float).operator("const").operator("ref"),
"string[]": cg.FixedVector.template(cg.std_string)
.operator("const")
.operator("ref"),
"bool[]": cg.std_vector.template(bool),
"int[]": cg.std_vector.template(cg.int32),
"float[]": cg.std_vector.template(float),
"string[]": cg.std_vector.template(cg.std_string),
}
CONF_ENCRYPTION = "encryption"
CONF_BATCH_DELAY = "batch_delay"
@@ -260,10 +258,6 @@ async def to_code(config):
if config.get(CONF_ACTIONS) or config[CONF_CUSTOM_SERVICES]:
cg.add_define("USE_API_SERVICES")
# Set USE_API_CUSTOM_SERVICES if external components need dynamic service registration
if config[CONF_CUSTOM_SERVICES]:
cg.add_define("USE_API_CUSTOM_SERVICES")
if config[CONF_HOMEASSISTANT_SERVICES]:
cg.add_define("USE_API_HOMEASSISTANT_SERVICES")
@@ -271,8 +265,6 @@ async def to_code(config):
cg.add_define("USE_API_HOMEASSISTANT_STATES")
if actions := config.get(CONF_ACTIONS, []):
# Collect all triggers first, then register all at once with initializer_list
triggers: list[cg.Pvariable] = []
for conf in actions:
template_args = []
func_args = []
@@ -286,10 +278,8 @@ async def to_code(config):
trigger = cg.new_Pvariable(
conf[CONF_TRIGGER_ID], templ, conf[CONF_ACTION], service_arg_names
)
triggers.append(trigger)
cg.add(var.register_user_service(trigger))
await automation.build_automation(trigger, func_args, conf)
# Register all services at once - single allocation, no reallocations
cg.add(var.initialize_user_services(triggers))
if CONF_ON_CLIENT_CONNECTED in config:
cg.add_define("USE_API_CLIENT_CONNECTED_TRIGGER")

View File

@@ -425,7 +425,7 @@ message ListEntitiesFanResponse {
bool disabled_by_default = 9;
string icon = 10 [(field_ifdef) = "USE_ENTITY_ICON"];
EntityCategory entity_category = 11;
repeated string supported_preset_modes = 12 [(container_pointer_no_template) = "std::vector<const char *>"];
repeated string supported_preset_modes = 12 [(container_pointer) = "std::set"];
uint32 device_id = 13 [(field_ifdef) = "USE_DEVICES"];
}
// Deprecated in API version 1.6 - only used in deprecated fields
@@ -989,7 +989,7 @@ message ListEntitiesClimateResponse {
bool supports_current_temperature = 5; // Deprecated: use feature_flags
bool supports_two_point_target_temperature = 6; // Deprecated: use feature_flags
repeated ClimateMode supported_modes = 7 [(container_pointer_no_template) = "climate::ClimateModeMask"];
repeated ClimateMode supported_modes = 7 [(container_pointer) = "std::set<climate::ClimateMode>"];
float visual_min_temperature = 8;
float visual_max_temperature = 9;
float visual_target_temperature_step = 10;
@@ -998,11 +998,11 @@ message ListEntitiesClimateResponse {
// Deprecated in API version 1.5
bool legacy_supports_away = 11 [deprecated=true];
bool supports_action = 12; // Deprecated: use feature_flags
repeated ClimateFanMode supported_fan_modes = 13 [(container_pointer_no_template) = "climate::ClimateFanModeMask"];
repeated ClimateSwingMode supported_swing_modes = 14 [(container_pointer_no_template) = "climate::ClimateSwingModeMask"];
repeated string supported_custom_fan_modes = 15 [(container_pointer_no_template) = "std::vector<const char *>"];
repeated ClimatePreset supported_presets = 16 [(container_pointer_no_template) = "climate::ClimatePresetMask"];
repeated string supported_custom_presets = 17 [(container_pointer_no_template) = "std::vector<const char *>"];
repeated ClimateFanMode supported_fan_modes = 13 [(container_pointer) = "std::set<climate::ClimateFanMode>"];
repeated ClimateSwingMode supported_swing_modes = 14 [(container_pointer) = "std::set<climate::ClimateSwingMode>"];
repeated string supported_custom_fan_modes = 15 [(container_pointer) = "std::set"];
repeated ClimatePreset supported_presets = 16 [(container_pointer) = "std::set<climate::ClimatePreset>"];
repeated string supported_custom_presets = 17 [(container_pointer) = "std::set"];
bool disabled_by_default = 18;
string icon = 19 [(field_ifdef) = "USE_ENTITY_ICON"];
EntityCategory entity_category = 20;
@@ -1143,7 +1143,7 @@ message ListEntitiesSelectResponse {
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"];
repeated string options = 6 [(container_pointer_no_template) = "FixedVector<const char *>"];
repeated string options = 6 [(container_pointer) = "std::vector"];
bool disabled_by_default = 7;
EntityCategory entity_category = 8;
uint32 device_id = 9 [(field_ifdef) = "USE_DEVICES"];

View File

@@ -410,8 +410,8 @@ uint16_t APIConnection::try_send_fan_state(EntityBase *entity, APIConnection *co
}
if (traits.supports_direction())
msg.direction = static_cast<enums::FanDirection>(fan->direction);
if (traits.supports_preset_modes() && fan->has_preset_mode())
msg.set_preset_mode(StringRef(fan->get_preset_mode()));
if (traits.supports_preset_modes())
msg.set_preset_mode(StringRef(fan->preset_mode));
return fill_and_encode_entity_state(fan, msg, FanStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
uint16_t APIConnection::try_send_fan_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
@@ -423,7 +423,7 @@ uint16_t APIConnection::try_send_fan_info(EntityBase *entity, APIConnection *con
msg.supports_speed = traits.supports_speed();
msg.supports_direction = traits.supports_direction();
msg.supported_speed_count = traits.supported_speed_count();
msg.supported_preset_modes = &traits.supported_preset_modes();
msg.supported_preset_modes = &traits.supported_preset_modes_for_api_();
return fill_and_encode_entity_info(fan, msg, ListEntitiesFanResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::fan_command(const FanCommandRequest &msg) {
@@ -486,7 +486,7 @@ uint16_t APIConnection::try_send_light_info(EntityBase *entity, APIConnection *c
if (light->supports_effects()) {
msg.effects.emplace_back("None");
for (auto *effect : light->get_effects()) {
msg.effects.emplace_back(effect->get_name());
msg.effects.push_back(effect->get_name());
}
}
return fill_and_encode_entity_info(light, msg, ListEntitiesLightResponse::MESSAGE_TYPE, conn, remaining_size,
@@ -637,14 +637,14 @@ uint16_t APIConnection::try_send_climate_state(EntityBase *entity, APIConnection
}
if (traits.get_supports_fan_modes() && climate->fan_mode.has_value())
resp.fan_mode = static_cast<enums::ClimateFanMode>(climate->fan_mode.value());
if (!traits.get_supported_custom_fan_modes().empty() && climate->has_custom_fan_mode()) {
resp.set_custom_fan_mode(StringRef(climate->get_custom_fan_mode()));
if (!traits.get_supported_custom_fan_modes().empty() && climate->custom_fan_mode.has_value()) {
resp.set_custom_fan_mode(StringRef(climate->custom_fan_mode.value()));
}
if (traits.get_supports_presets() && climate->preset.has_value()) {
resp.preset = static_cast<enums::ClimatePreset>(climate->preset.value());
}
if (!traits.get_supported_custom_presets().empty() && climate->has_custom_preset()) {
resp.set_custom_preset(StringRef(climate->get_custom_preset()));
if (!traits.get_supported_custom_presets().empty() && climate->custom_preset.has_value()) {
resp.set_custom_preset(StringRef(climate->custom_preset.value()));
}
if (traits.get_supports_swing_modes())
resp.swing_mode = static_cast<enums::ClimateSwingMode>(climate->swing_mode);
@@ -669,18 +669,18 @@ uint16_t APIConnection::try_send_climate_info(EntityBase *entity, APIConnection
msg.supports_action = traits.has_feature_flags(climate::CLIMATE_SUPPORTS_ACTION);
// Current feature flags and other supported parameters
msg.feature_flags = traits.get_feature_flags();
msg.supported_modes = &traits.get_supported_modes();
msg.supported_modes = &traits.get_supported_modes_for_api_();
msg.visual_min_temperature = traits.get_visual_min_temperature();
msg.visual_max_temperature = traits.get_visual_max_temperature();
msg.visual_target_temperature_step = traits.get_visual_target_temperature_step();
msg.visual_current_temperature_step = traits.get_visual_current_temperature_step();
msg.visual_min_humidity = traits.get_visual_min_humidity();
msg.visual_max_humidity = traits.get_visual_max_humidity();
msg.supported_fan_modes = &traits.get_supported_fan_modes();
msg.supported_custom_fan_modes = &traits.get_supported_custom_fan_modes();
msg.supported_presets = &traits.get_supported_presets();
msg.supported_custom_presets = &traits.get_supported_custom_presets();
msg.supported_swing_modes = &traits.get_supported_swing_modes();
msg.supported_fan_modes = &traits.get_supported_fan_modes_for_api_();
msg.supported_custom_fan_modes = &traits.get_supported_custom_fan_modes_for_api_();
msg.supported_presets = &traits.get_supported_presets_for_api_();
msg.supported_custom_presets = &traits.get_supported_custom_presets_for_api_();
msg.supported_swing_modes = &traits.get_supported_swing_modes_for_api_();
return fill_and_encode_entity_info(climate, msg, ListEntitiesClimateResponse::MESSAGE_TYPE, conn, remaining_size,
is_single);
}
@@ -877,7 +877,7 @@ uint16_t APIConnection::try_send_select_state(EntityBase *entity, APIConnection
bool is_single) {
auto *select = static_cast<select::Select *>(entity);
SelectStateResponse resp;
resp.set_state(StringRef(select->current_option()));
resp.set_state(StringRef(select->state));
resp.missing_state = !select->has_state();
return fill_and_encode_entity_state(select, resp, SelectStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
@@ -1572,13 +1572,7 @@ bool APIConnection::send_noise_encryption_set_key_response(const NoiseEncryption
resp.success = false;
psk_t psk{};
if (msg.key.empty()) {
if (this->parent_->clear_noise_psk(true)) {
resp.success = true;
} else {
ESP_LOGW(TAG, "Failed to clear encryption key");
}
} else if (base64_decode(msg.key, psk.data(), msg.key.size()) != psk.size()) {
if (base64_decode(msg.key, psk.data(), msg.key.size()) != psk.size()) {
ESP_LOGW(TAG, "Invalid encryption key length");
} else if (!this->parent_->save_noise_psk(psk, true)) {
ESP_LOGW(TAG, "Failed to save encryption key");

View File

@@ -142,6 +142,11 @@ APIError APINoiseFrameHelper::loop() {
* errno API_ERROR_HANDSHAKE_PACKET_LEN: Packet too big for this phase.
*/
APIError APINoiseFrameHelper::try_read_frame_() {
// Clear buffer when starting a new frame (rx_buf_len_ == 0 means not resuming after WOULD_BLOCK)
if (this->rx_buf_len_ == 0) {
this->rx_buf_.clear();
}
// read header
if (rx_header_buf_len_ < 3) {
// no header information yet
@@ -434,7 +439,8 @@ APIError APINoiseFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, st
return APIError::OK;
}
uint8_t *buffer_data = buffer.get_buffer()->data();
std::vector<uint8_t> *raw_buffer = buffer.get_buffer();
uint8_t *buffer_data = raw_buffer->data(); // Cache buffer pointer
this->reusable_iovs_.clear();
this->reusable_iovs_.reserve(packets.size());

View File

@@ -54,6 +54,11 @@ APIError APIPlaintextFrameHelper::loop() {
* error API_ERROR_BAD_INDICATOR: Bad indicator byte at start of frame.
*/
APIError APIPlaintextFrameHelper::try_read_frame_() {
// Clear buffer when starting a new frame (rx_buf_len_ == 0 means not resuming after WOULD_BLOCK)
if (this->rx_buf_len_ == 0) {
this->rx_buf_.clear();
}
// read header
while (!rx_header_parsed_) {
// Now that we know when the socket is ready, we can read up to 3 bytes
@@ -230,7 +235,8 @@ APIError APIPlaintextFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer
return APIError::OK;
}
uint8_t *buffer_data = buffer.get_buffer()->data();
std::vector<uint8_t> *raw_buffer = buffer.get_buffer();
uint8_t *buffer_data = raw_buffer->data(); // Cache buffer pointer
this->reusable_iovs_.clear();
this->reusable_iovs_.reserve(packets.size());

View File

@@ -355,8 +355,8 @@ void ListEntitiesFanResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(10, this->icon_ref_);
#endif
buffer.encode_uint32(11, static_cast<uint32_t>(this->entity_category));
for (const char *it : *this->supported_preset_modes) {
buffer.encode_string(12, it, strlen(it), true);
for (const auto &it : *this->supported_preset_modes) {
buffer.encode_string(12, it, true);
}
#ifdef USE_DEVICES
buffer.encode_uint32(13, this->device_id);
@@ -376,8 +376,8 @@ void ListEntitiesFanResponse::calculate_size(ProtoSize &size) const {
#endif
size.add_uint32(1, static_cast<uint32_t>(this->entity_category));
if (!this->supported_preset_modes->empty()) {
for (const char *it : *this->supported_preset_modes) {
size.add_length_force(1, strlen(it));
for (const auto &it : *this->supported_preset_modes) {
size.add_length_force(1, it.size());
}
}
#ifdef USE_DEVICES
@@ -1179,14 +1179,14 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const {
for (const auto &it : *this->supported_swing_modes) {
buffer.encode_uint32(14, static_cast<uint32_t>(it), true);
}
for (const char *it : *this->supported_custom_fan_modes) {
buffer.encode_string(15, it, strlen(it), true);
for (const auto &it : *this->supported_custom_fan_modes) {
buffer.encode_string(15, it, true);
}
for (const auto &it : *this->supported_presets) {
buffer.encode_uint32(16, static_cast<uint32_t>(it), true);
}
for (const char *it : *this->supported_custom_presets) {
buffer.encode_string(17, it, strlen(it), true);
for (const auto &it : *this->supported_custom_presets) {
buffer.encode_string(17, it, true);
}
buffer.encode_bool(18, this->disabled_by_default);
#ifdef USE_ENTITY_ICON
@@ -1229,8 +1229,8 @@ void ListEntitiesClimateResponse::calculate_size(ProtoSize &size) const {
}
}
if (!this->supported_custom_fan_modes->empty()) {
for (const char *it : *this->supported_custom_fan_modes) {
size.add_length_force(1, strlen(it));
for (const auto &it : *this->supported_custom_fan_modes) {
size.add_length_force(1, it.size());
}
}
if (!this->supported_presets->empty()) {
@@ -1239,8 +1239,8 @@ void ListEntitiesClimateResponse::calculate_size(ProtoSize &size) const {
}
}
if (!this->supported_custom_presets->empty()) {
for (const char *it : *this->supported_custom_presets) {
size.add_length_force(2, strlen(it));
for (const auto &it : *this->supported_custom_presets) {
size.add_length_force(2, it.size());
}
}
size.add_bool(2, this->disabled_by_default);
@@ -1475,8 +1475,8 @@ void ListEntitiesSelectResponse::encode(ProtoWriteBuffer buffer) const {
#ifdef USE_ENTITY_ICON
buffer.encode_string(5, this->icon_ref_);
#endif
for (const char *it : *this->options) {
buffer.encode_string(6, it, strlen(it), true);
for (const auto &it : *this->options) {
buffer.encode_string(6, it, true);
}
buffer.encode_bool(7, this->disabled_by_default);
buffer.encode_uint32(8, static_cast<uint32_t>(this->entity_category));
@@ -1492,8 +1492,8 @@ void ListEntitiesSelectResponse::calculate_size(ProtoSize &size) const {
size.add_length(1, this->icon_ref_.size());
#endif
if (!this->options->empty()) {
for (const char *it : *this->options) {
size.add_length_force(1, strlen(it));
for (const auto &it : *this->options) {
size.add_length_force(1, it.size());
}
}
size.add_bool(1, this->disabled_by_default);

View File

@@ -725,7 +725,7 @@ class ListEntitiesFanResponse final : public InfoResponseProtoMessage {
bool supports_speed{false};
bool supports_direction{false};
int32_t supported_speed_count{0};
const std::vector<const char *> *supported_preset_modes{};
const std::set<std::string> *supported_preset_modes{};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@@ -1377,16 +1377,16 @@ class ListEntitiesClimateResponse final : public InfoResponseProtoMessage {
#endif
bool supports_current_temperature{false};
bool supports_two_point_target_temperature{false};
const climate::ClimateModeMask *supported_modes{};
const std::set<climate::ClimateMode> *supported_modes{};
float visual_min_temperature{0.0f};
float visual_max_temperature{0.0f};
float visual_target_temperature_step{0.0f};
bool supports_action{false};
const climate::ClimateFanModeMask *supported_fan_modes{};
const climate::ClimateSwingModeMask *supported_swing_modes{};
const std::vector<const char *> *supported_custom_fan_modes{};
const climate::ClimatePresetMask *supported_presets{};
const std::vector<const char *> *supported_custom_presets{};
const std::set<climate::ClimateFanMode> *supported_fan_modes{};
const std::set<climate::ClimateSwingMode> *supported_swing_modes{};
const std::set<std::string> *supported_custom_fan_modes{};
const std::set<climate::ClimatePreset> *supported_presets{};
const std::set<std::string> *supported_custom_presets{};
float visual_current_temperature_step{0.0f};
bool supports_current_humidity{false};
bool supports_target_humidity{false};
@@ -1534,7 +1534,7 @@ class ListEntitiesSelectResponse final : public InfoResponseProtoMessage {
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "list_entities_select_response"; }
#endif
const FixedVector<const char *> *options{};
const std::vector<std::string> *options{};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP

View File

@@ -88,12 +88,6 @@ static void dump_field(std::string &out, const char *field_name, StringRef value
out.append("\n");
}
static void dump_field(std::string &out, const char *field_name, const char *value, int indent = 2) {
append_field_prefix(out, field_name, indent);
out.append("'").append(value).append("'");
out.append("\n");
}
template<typename T> static void dump_field(std::string &out, const char *field_name, T value, int indent = 2) {
append_field_prefix(out, field_name, indent);
out.append(proto_enum_to_string<T>(value));

View File

@@ -468,31 +468,6 @@ uint16_t APIServer::get_port() const { return this->port_; }
void APIServer::set_reboot_timeout(uint32_t reboot_timeout) { this->reboot_timeout_ = reboot_timeout; }
#ifdef USE_API_NOISE
bool APIServer::update_noise_psk_(const SavedNoisePsk &new_psk, const LogString *save_log_msg,
const LogString *fail_log_msg, const psk_t &active_psk, bool make_active) {
if (!this->noise_pref_.save(&new_psk)) {
ESP_LOGW(TAG, "%s", LOG_STR_ARG(fail_log_msg));
return false;
}
// ensure it's written immediately
if (!global_preferences->sync()) {
ESP_LOGW(TAG, "Failed to sync preferences");
return false;
}
ESP_LOGD(TAG, "%s", LOG_STR_ARG(save_log_msg));
if (make_active) {
this->set_timeout(100, [this, active_psk]() {
ESP_LOGW(TAG, "Disconnecting all clients to reset PSK");
this->set_noise_psk(active_psk);
for (auto &c : this->clients_) {
DisconnectRequest req;
c->send_message(req, DisconnectRequest::MESSAGE_TYPE);
}
});
}
return true;
}
bool APIServer::save_noise_psk(psk_t psk, bool make_active) {
#ifdef USE_API_NOISE_PSK_FROM_YAML
// When PSK is set from YAML, this function should never be called
@@ -507,21 +482,27 @@ bool APIServer::save_noise_psk(psk_t psk, bool make_active) {
}
SavedNoisePsk new_saved_psk{psk};
return this->update_noise_psk_(new_saved_psk, LOG_STR("Noise PSK saved"), LOG_STR("Failed to save Noise PSK"), psk,
make_active);
#endif
}
bool APIServer::clear_noise_psk(bool make_active) {
#ifdef USE_API_NOISE_PSK_FROM_YAML
// When PSK is set from YAML, this function should never be called
// but if it is, reject the change
ESP_LOGW(TAG, "Key set in YAML");
return false;
#else
SavedNoisePsk empty_psk{};
psk_t empty{};
return this->update_noise_psk_(empty_psk, LOG_STR("Noise PSK cleared"), LOG_STR("Failed to clear Noise PSK"), empty,
make_active);
if (!this->noise_pref_.save(&new_saved_psk)) {
ESP_LOGW(TAG, "Failed to save Noise PSK");
return false;
}
// ensure it's written immediately
if (!global_preferences->sync()) {
ESP_LOGW(TAG, "Failed to sync preferences");
return false;
}
ESP_LOGD(TAG, "Noise PSK saved");
if (make_active) {
this->set_timeout(100, [this, psk]() {
ESP_LOGW(TAG, "Disconnecting all clients to reset PSK");
this->set_noise_psk(psk);
for (auto &c : this->clients_) {
DisconnectRequest req;
c->send_message(req, DisconnectRequest::MESSAGE_TYPE);
}
});
}
return true;
#endif
}
#endif

View File

@@ -53,7 +53,6 @@ class APIServer : public Component, public Controller {
#ifdef USE_API_NOISE
bool save_noise_psk(psk_t psk, bool make_active = true);
bool clear_noise_psk(bool make_active = true);
void set_noise_psk(psk_t psk) { noise_ctx_->set_psk(psk); }
std::shared_ptr<APINoiseContext> get_noise_ctx() { return noise_ctx_; }
#endif // USE_API_NOISE
@@ -125,14 +124,8 @@ class APIServer : public Component, public Controller {
#endif // USE_API_HOMEASSISTANT_ACTION_RESPONSES
#endif // USE_API_HOMEASSISTANT_SERVICES
#ifdef USE_API_SERVICES
void initialize_user_services(std::initializer_list<UserServiceDescriptor *> services) {
this->user_services_.assign(services);
}
#ifdef USE_API_CUSTOM_SERVICES
// Only compile push_back method when custom_services: true (external components)
void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); }
#endif
#endif
#ifdef USE_HOMEASSISTANT_TIME
void request_time();
#endif
@@ -181,10 +174,6 @@ class APIServer : public Component, public Controller {
protected:
void schedule_reboot_timeout_();
#ifdef USE_API_NOISE
bool update_noise_psk_(const SavedNoisePsk &new_psk, const LogString *save_log_msg, const LogString *fail_log_msg,
const psk_t &active_psk, bool make_active);
#endif // USE_API_NOISE
// Pointers and pointer-like types first (4 bytes each)
std::unique_ptr<socket::Socket> socket_ = nullptr;
#ifdef USE_API_CLIENT_CONNECTED_TRIGGER

View File

@@ -53,14 +53,8 @@ class CustomAPIDevice {
template<typename T, typename... Ts>
void register_service(void (T::*callback)(Ts...), const std::string &name,
const std::array<std::string, sizeof...(Ts)> &arg_names) {
#ifdef USE_API_CUSTOM_SERVICES
auto *service = new CustomAPIDeviceService<T, Ts...>(name, arg_names, (T *) this, callback); // NOLINT
global_api_server->register_user_service(service);
#else
static_assert(
sizeof(T) == 0,
"register_service() requires 'custom_services: true' in the 'api:' section of your YAML configuration");
#endif
}
#else
template<typename T, typename... Ts>
@@ -92,14 +86,8 @@ class CustomAPIDevice {
*/
#ifdef USE_API_SERVICES
template<typename T> void register_service(void (T::*callback)(), const std::string &name) {
#ifdef USE_API_CUSTOM_SERVICES
auto *service = new CustomAPIDeviceService<T>(name, {}, (T *) this, callback); // NOLINT
global_api_server->register_user_service(service);
#else
static_assert(
sizeof(T) == 0,
"register_service() requires 'custom_services: true' in the 'api:' section of your YAML configuration");
#endif
}
#else
template<typename T> void register_service(void (T::*callback)(), const std::string &name) {

View File

@@ -11,58 +11,23 @@ template<> int32_t get_execute_arg_value<int32_t>(const ExecuteServiceArgument &
}
template<> float get_execute_arg_value<float>(const ExecuteServiceArgument &arg) { return arg.float_; }
template<> std::string get_execute_arg_value<std::string>(const ExecuteServiceArgument &arg) { return arg.string_; }
// Legacy std::vector versions for external components using custom_api_device.h - optimized with reserve
template<> std::vector<bool> get_execute_arg_value<std::vector<bool>>(const ExecuteServiceArgument &arg) {
std::vector<bool> result;
result.reserve(arg.bool_array.size());
result.insert(result.end(), arg.bool_array.begin(), arg.bool_array.end());
return result;
return std::vector<bool>(arg.bool_array.begin(), arg.bool_array.end());
}
template<> std::vector<int32_t> get_execute_arg_value<std::vector<int32_t>>(const ExecuteServiceArgument &arg) {
std::vector<int32_t> result;
result.reserve(arg.int_array.size());
result.insert(result.end(), arg.int_array.begin(), arg.int_array.end());
return result;
return std::vector<int32_t>(arg.int_array.begin(), arg.int_array.end());
}
template<> std::vector<float> get_execute_arg_value<std::vector<float>>(const ExecuteServiceArgument &arg) {
std::vector<float> result;
result.reserve(arg.float_array.size());
result.insert(result.end(), arg.float_array.begin(), arg.float_array.end());
return result;
return std::vector<float>(arg.float_array.begin(), arg.float_array.end());
}
template<> std::vector<std::string> get_execute_arg_value<std::vector<std::string>>(const ExecuteServiceArgument &arg) {
std::vector<std::string> result;
result.reserve(arg.string_array.size());
result.insert(result.end(), arg.string_array.begin(), arg.string_array.end());
return result;
}
// New FixedVector const reference versions for YAML-generated services - zero-copy
template<>
const FixedVector<bool> &get_execute_arg_value<const FixedVector<bool> &>(const ExecuteServiceArgument &arg) {
return arg.bool_array;
}
template<>
const FixedVector<int32_t> &get_execute_arg_value<const FixedVector<int32_t> &>(const ExecuteServiceArgument &arg) {
return arg.int_array;
}
template<>
const FixedVector<float> &get_execute_arg_value<const FixedVector<float> &>(const ExecuteServiceArgument &arg) {
return arg.float_array;
}
template<>
const FixedVector<std::string> &get_execute_arg_value<const FixedVector<std::string> &>(
const ExecuteServiceArgument &arg) {
return arg.string_array;
return std::vector<std::string>(arg.string_array.begin(), arg.string_array.end());
}
template<> enums::ServiceArgType to_service_arg_type<bool>() { return enums::SERVICE_ARG_TYPE_BOOL; }
template<> enums::ServiceArgType to_service_arg_type<int32_t>() { return enums::SERVICE_ARG_TYPE_INT; }
template<> enums::ServiceArgType to_service_arg_type<float>() { return enums::SERVICE_ARG_TYPE_FLOAT; }
template<> enums::ServiceArgType to_service_arg_type<std::string>() { return enums::SERVICE_ARG_TYPE_STRING; }
// Legacy std::vector versions for external components using custom_api_device.h
template<> enums::ServiceArgType to_service_arg_type<std::vector<bool>>() { return enums::SERVICE_ARG_TYPE_BOOL_ARRAY; }
template<> enums::ServiceArgType to_service_arg_type<std::vector<int32_t>>() {
return enums::SERVICE_ARG_TYPE_INT_ARRAY;
@@ -74,18 +39,4 @@ template<> enums::ServiceArgType to_service_arg_type<std::vector<std::string>>()
return enums::SERVICE_ARG_TYPE_STRING_ARRAY;
}
// New FixedVector const reference versions for YAML-generated services
template<> enums::ServiceArgType to_service_arg_type<const FixedVector<bool> &>() {
return enums::SERVICE_ARG_TYPE_BOOL_ARRAY;
}
template<> enums::ServiceArgType to_service_arg_type<const FixedVector<int32_t> &>() {
return enums::SERVICE_ARG_TYPE_INT_ARRAY;
}
template<> enums::ServiceArgType to_service_arg_type<const FixedVector<float> &>() {
return enums::SERVICE_ARG_TYPE_FLOAT_ARRAY;
}
template<> enums::ServiceArgType to_service_arg_type<const FixedVector<std::string> &>() {
return enums::SERVICE_ARG_TYPE_STRING_ARRAY;
}
} // namespace esphome::api

View File

@@ -99,8 +99,9 @@ enum BedjetCommand : uint8_t {
static const uint8_t BEDJET_FAN_SPEED_COUNT = 20;
static constexpr const char *const BEDJET_FAN_STEP_NAMES[BEDJET_FAN_SPEED_COUNT] = BEDJET_FAN_STEP_NAMES_;
static const char *const BEDJET_FAN_STEP_NAMES[BEDJET_FAN_SPEED_COUNT] = BEDJET_FAN_STEP_NAMES_;
static const std::string BEDJET_FAN_STEP_NAME_STRINGS[BEDJET_FAN_SPEED_COUNT] = BEDJET_FAN_STEP_NAMES_;
static const std::set<std::string> BEDJET_FAN_STEP_NAMES_SET BEDJET_FAN_STEP_NAMES_;
} // namespace bedjet
} // namespace esphome

View File

@@ -48,7 +48,7 @@ void BedJetClimate::dump_config() {
ESP_LOGCONFIG(TAG, " - %s", LOG_STR_ARG(climate_fan_mode_to_string(mode)));
}
for (const auto &mode : traits.get_supported_custom_fan_modes()) {
ESP_LOGCONFIG(TAG, " - %s (c)", mode);
ESP_LOGCONFIG(TAG, " - %s (c)", mode.c_str());
}
ESP_LOGCONFIG(TAG, " Supported presets:");
@@ -56,7 +56,7 @@ void BedJetClimate::dump_config() {
ESP_LOGCONFIG(TAG, " - %s", LOG_STR_ARG(climate_preset_to_string(preset)));
}
for (const auto &preset : traits.get_supported_custom_presets()) {
ESP_LOGCONFIG(TAG, " - %s (c)", preset);
ESP_LOGCONFIG(TAG, " - %s (c)", preset.c_str());
}
}
@@ -79,7 +79,7 @@ void BedJetClimate::reset_state_() {
this->target_temperature = NAN;
this->current_temperature = NAN;
this->preset.reset();
this->clear_custom_preset_();
this->custom_preset.reset();
this->publish_state();
}
@@ -120,7 +120,7 @@ void BedJetClimate::control(const ClimateCall &call) {
if (button_result) {
this->mode = mode;
// We're using (custom) preset for Turbo, EXT HT, & M1-3 presets, so changing climate mode will clear those
this->clear_custom_preset_();
this->custom_preset.reset();
this->preset.reset();
}
}
@@ -144,7 +144,8 @@ void BedJetClimate::control(const ClimateCall &call) {
if (result) {
this->mode = CLIMATE_MODE_HEAT;
this->set_preset_(CLIMATE_PRESET_BOOST);
this->preset = CLIMATE_PRESET_BOOST;
this->custom_preset.reset();
}
} else if (preset == CLIMATE_PRESET_NONE && this->preset.has_value()) {
if (this->mode == CLIMATE_MODE_HEAT && this->preset == CLIMATE_PRESET_BOOST) {
@@ -152,7 +153,7 @@ void BedJetClimate::control(const ClimateCall &call) {
result = this->parent_->send_button(heat_button(this->heating_mode_));
if (result) {
this->preset.reset();
this->clear_custom_preset_();
this->custom_preset.reset();
}
} else {
ESP_LOGD(TAG, "Ignoring preset '%s' call; with current mode '%s' and preset '%s'",
@@ -183,7 +184,8 @@ void BedJetClimate::control(const ClimateCall &call) {
}
if (result) {
this->set_custom_preset_(preset.c_str());
this->custom_preset = preset;
this->preset.reset();
}
}
@@ -205,7 +207,8 @@ void BedJetClimate::control(const ClimateCall &call) {
}
if (result) {
this->set_fan_mode_(fan_mode);
this->fan_mode = fan_mode;
this->custom_fan_mode.reset();
}
} else if (call.get_custom_fan_mode().has_value()) {
auto fan_mode = *call.get_custom_fan_mode();
@@ -215,7 +218,8 @@ void BedJetClimate::control(const ClimateCall &call) {
fan_index);
bool result = this->parent_->set_fan_index(fan_index);
if (result) {
this->set_custom_fan_mode_(fan_mode.c_str());
this->custom_fan_mode = fan_mode;
this->fan_mode.reset();
}
}
}
@@ -241,7 +245,7 @@ void BedJetClimate::on_status(const BedjetStatusPacket *data) {
const auto *fan_mode_name = bedjet_fan_step_to_fan_mode(data->fan_step);
if (fan_mode_name != nullptr) {
this->set_custom_fan_mode_(fan_mode_name->c_str());
this->custom_fan_mode = *fan_mode_name;
}
// TODO: Get biorhythm data to determine which preset (M1-3) is running, if any.
@@ -251,7 +255,7 @@ void BedJetClimate::on_status(const BedjetStatusPacket *data) {
this->mode = CLIMATE_MODE_OFF;
this->action = CLIMATE_ACTION_IDLE;
this->fan_mode = CLIMATE_FAN_OFF;
this->clear_custom_preset_();
this->custom_preset.reset();
this->preset.reset();
break;
@@ -262,7 +266,7 @@ void BedJetClimate::on_status(const BedjetStatusPacket *data) {
if (this->heating_mode_ == HEAT_MODE_EXTENDED) {
this->set_custom_preset_("LTD HT");
} else {
this->clear_custom_preset_();
this->custom_preset.reset();
}
break;
@@ -271,7 +275,7 @@ void BedJetClimate::on_status(const BedjetStatusPacket *data) {
this->action = CLIMATE_ACTION_HEATING;
this->preset.reset();
if (this->heating_mode_ == HEAT_MODE_EXTENDED) {
this->clear_custom_preset_();
this->custom_preset.reset();
} else {
this->set_custom_preset_("EXT HT");
}
@@ -280,19 +284,20 @@ void BedJetClimate::on_status(const BedjetStatusPacket *data) {
case MODE_COOL:
this->mode = CLIMATE_MODE_FAN_ONLY;
this->action = CLIMATE_ACTION_COOLING;
this->clear_custom_preset_();
this->custom_preset.reset();
this->preset.reset();
break;
case MODE_DRY:
this->mode = CLIMATE_MODE_DRY;
this->action = CLIMATE_ACTION_DRYING;
this->clear_custom_preset_();
this->custom_preset.reset();
this->preset.reset();
break;
case MODE_TURBO:
this->set_preset_(CLIMATE_PRESET_BOOST);
this->preset = CLIMATE_PRESET_BOOST;
this->custom_preset.reset();
this->mode = CLIMATE_MODE_HEAT;
this->action = CLIMATE_ACTION_HEATING;
break;

View File

@@ -43,20 +43,28 @@ class BedJetClimate : public climate::Climate, public BedJetClient, public Polli
});
// It would be better if we had a slider for the fan modes.
traits.set_supported_custom_fan_modes(BEDJET_FAN_STEP_NAMES);
traits.set_supported_custom_fan_modes(BEDJET_FAN_STEP_NAMES_SET);
traits.set_supported_presets({
// If we support NONE, then have to decide what happens if the user switches to it (turn off?)
// climate::CLIMATE_PRESET_NONE,
// Climate doesn't have a "TURBO" mode, but we can use the BOOST preset instead.
climate::CLIMATE_PRESET_BOOST,
});
// String literals are stored in rodata and valid for program lifetime
traits.set_supported_custom_presets({
this->heating_mode_ == HEAT_MODE_EXTENDED ? "LTD HT" : "EXT HT",
// We could fetch biodata from bedjet and set these names that way.
// But then we have to invert the lookup in order to send the right preset.
// For now, we can leave them as M1-3 to match the remote buttons.
// EXT HT added to match remote button.
"EXT HT",
"M1",
"M2",
"M3",
});
if (this->heating_mode_ == HEAT_MODE_EXTENDED) {
traits.add_supported_custom_preset("LTD HT");
} else {
traits.add_supported_custom_preset("EXT HT");
}
traits.set_visual_min_temperature(19.0);
traits.set_visual_max_temperature(43.0);
traits.set_visual_temperature_step(1.0);

View File

@@ -155,7 +155,6 @@ DelayedOffFilter = binary_sensor_ns.class_("DelayedOffFilter", Filter, cg.Compon
InvertFilter = binary_sensor_ns.class_("InvertFilter", Filter)
AutorepeatFilter = binary_sensor_ns.class_("AutorepeatFilter", Filter, cg.Component)
LambdaFilter = binary_sensor_ns.class_("LambdaFilter", Filter)
StatelessLambdaFilter = binary_sensor_ns.class_("StatelessLambdaFilter", Filter)
SettleFilter = binary_sensor_ns.class_("SettleFilter", Filter, cg.Component)
_LOGGER = getLogger(__name__)
@@ -265,31 +264,20 @@ async def delayed_off_filter_to_code(config, filter_id):
),
)
async def autorepeat_filter_to_code(config, filter_id):
timings = []
if len(config) > 0:
timings = [
cg.StructInitializer(
cg.MockObj("AutorepeatFilterTiming", "esphome::binary_sensor::"),
("delay", conf[CONF_DELAY]),
("time_off", conf[CONF_TIME_OFF]),
("time_on", conf[CONF_TIME_ON]),
)
timings.extend(
(conf[CONF_DELAY], conf[CONF_TIME_OFF], conf[CONF_TIME_ON])
for conf in config
]
)
else:
timings = [
cg.StructInitializer(
cg.MockObj("AutorepeatFilterTiming", "esphome::binary_sensor::"),
("delay", cv.time_period_str_unit(DEFAULT_DELAY).total_milliseconds),
(
"time_off",
cv.time_period_str_unit(DEFAULT_TIME_OFF).total_milliseconds,
),
(
"time_on",
cv.time_period_str_unit(DEFAULT_TIME_ON).total_milliseconds,
),
timings.append(
(
cv.time_period_str_unit(DEFAULT_DELAY).total_milliseconds,
cv.time_period_str_unit(DEFAULT_TIME_OFF).total_milliseconds,
cv.time_period_str_unit(DEFAULT_TIME_ON).total_milliseconds,
)
]
)
var = cg.new_Pvariable(filter_id, timings)
await cg.register_component(var, {})
return var
@@ -300,7 +288,7 @@ async def lambda_filter_to_code(config, filter_id):
lambda_ = await cg.process_lambda(
config, [(bool, "x")], return_type=cg.optional.template(bool)
)
return automation.new_lambda_pvariable(filter_id, lambda_, StatelessLambdaFilter)
return cg.new_Pvariable(filter_id, lambda_)
@register_filter(
@@ -548,6 +536,11 @@ def binary_sensor_schema(
return _BINARY_SENSOR_SCHEMA.extend(schema)
# Remove before 2025.11.0
BINARY_SENSOR_SCHEMA = binary_sensor_schema()
BINARY_SENSOR_SCHEMA.add_extra(cv.deprecated_schema_constant("binary_sensor"))
async def setup_binary_sensor_core_(var, config):
await setup_entity(var, config, "binary_sensor")

View File

@@ -2,11 +2,11 @@
#include <cinttypes>
#include <utility>
#include <vector>
#include "esphome/core/component.h"
#include "esphome/core/automation.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/components/binary_sensor/binary_sensor.h"
namespace esphome {
@@ -92,8 +92,8 @@ class DoubleClickTrigger : public Trigger<> {
class MultiClickTrigger : public Trigger<>, public Component {
public:
explicit MultiClickTrigger(BinarySensor *parent, std::initializer_list<MultiClickTriggerEvent> timing)
: parent_(parent), timing_(timing) {}
explicit MultiClickTrigger(BinarySensor *parent, std::vector<MultiClickTriggerEvent> timing)
: parent_(parent), timing_(std::move(timing)) {}
void setup() override {
this->last_state_ = this->parent_->get_state_default(false);
@@ -115,7 +115,7 @@ class MultiClickTrigger : public Trigger<>, public Component {
void trigger_();
BinarySensor *parent_;
FixedVector<MultiClickTriggerEvent> timing_;
std::vector<MultiClickTriggerEvent> timing_;
uint32_t invalid_cooldown_{1000};
optional<size_t> at_index_{};
bool last_state_{false};

View File

@@ -1,6 +1,7 @@
#include "filter.h"
#include "binary_sensor.h"
#include <utility>
namespace esphome {
@@ -67,7 +68,7 @@ float DelayedOffFilter::get_setup_priority() const { return setup_priority::HARD
optional<bool> InvertFilter::new_value(bool value) { return !value; }
AutorepeatFilter::AutorepeatFilter(std::initializer_list<AutorepeatFilterTiming> timings) : timings_(timings) {}
AutorepeatFilter::AutorepeatFilter(std::vector<AutorepeatFilterTiming> timings) : timings_(std::move(timings)) {}
optional<bool> AutorepeatFilter::new_value(bool value) {
if (value) {

View File

@@ -4,6 +4,8 @@
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include <vector>
namespace esphome {
namespace binary_sensor {
@@ -80,6 +82,11 @@ class InvertFilter : public Filter {
};
struct AutorepeatFilterTiming {
AutorepeatFilterTiming(uint32_t delay, uint32_t off, uint32_t on) {
this->delay = delay;
this->time_off = off;
this->time_on = on;
}
uint32_t delay;
uint32_t time_off;
uint32_t time_on;
@@ -87,7 +94,7 @@ struct AutorepeatFilterTiming {
class AutorepeatFilter : public Filter, public Component {
public:
explicit AutorepeatFilter(std::initializer_list<AutorepeatFilterTiming> timings);
explicit AutorepeatFilter(std::vector<AutorepeatFilterTiming> timings);
optional<bool> new_value(bool value) override;
@@ -97,7 +104,7 @@ class AutorepeatFilter : public Filter, public Component {
void next_timing_();
void next_value_(bool val);
FixedVector<AutorepeatFilterTiming> timings_;
std::vector<AutorepeatFilterTiming> timings_;
uint8_t active_timing_{0};
};
@@ -111,21 +118,6 @@ class LambdaFilter : public Filter {
std::function<optional<bool>(bool)> f_;
};
/** Optimized lambda filter for stateless lambdas (no capture).
*
* Uses function pointer instead of std::function to reduce memory overhead.
* Memory: 4 bytes (function pointer on 32-bit) vs 32 bytes (std::function).
*/
class StatelessLambdaFilter : public Filter {
public:
explicit StatelessLambdaFilter(optional<bool> (*f)(bool)) : f_(f) {}
optional<bool> new_value(bool value) override { return this->f_(value); }
protected:
optional<bool> (*f_)(bool);
};
class SettleFilter : public Filter, public Component {
public:
optional<bool> new_value(bool value) override;

View File

@@ -96,11 +96,8 @@ template<typename... Ts> class BLEClientWriteAction : public Action<Ts...>, publ
BLEClientWriteAction(BLEClient *ble_client) {
ble_client->register_ble_node(this);
ble_client_ = ble_client;
this->construct_simple_value_();
}
~BLEClientWriteAction() { this->destroy_simple_value_(); }
void set_service_uuid16(uint16_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
void set_service_uuid32(uint32_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
void set_service_uuid128(uint8_t *uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
@@ -109,18 +106,14 @@ template<typename... Ts> class BLEClientWriteAction : public Action<Ts...>, publ
void set_char_uuid32(uint32_t uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
void set_char_uuid128(uint8_t *uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
void set_value_template(std::vector<uint8_t> (*func)(Ts...)) {
this->destroy_simple_value_();
this->value_.template_func = func;
this->has_simple_value_ = false;
void set_value_template(std::function<std::vector<uint8_t>(Ts...)> func) {
this->value_template_ = std::move(func);
has_simple_value_ = false;
}
void set_value_simple(const std::vector<uint8_t> &value) {
if (!this->has_simple_value_) {
this->construct_simple_value_();
}
this->value_.simple = value;
this->has_simple_value_ = true;
this->value_simple_ = value;
has_simple_value_ = true;
}
void play(Ts... x) override {}
@@ -128,7 +121,7 @@ template<typename... Ts> class BLEClientWriteAction : public Action<Ts...>, publ
void play_complex(Ts... x) override {
this->num_running_++;
this->var_ = std::make_tuple(x...);
auto value = this->has_simple_value_ ? this->value_.simple : this->value_.template_func(x...);
auto value = this->has_simple_value_ ? this->value_simple_ : this->value_template_(x...);
// on write failure, continue the automation chain rather than stopping so that e.g. disconnect can work.
if (!write(value))
this->play_next_(x...);
@@ -201,22 +194,10 @@ template<typename... Ts> class BLEClientWriteAction : public Action<Ts...>, publ
}
private:
void construct_simple_value_() { new (&this->value_.simple) std::vector<uint8_t>(); }
void destroy_simple_value_() {
if (this->has_simple_value_) {
this->value_.simple.~vector();
}
}
BLEClient *ble_client_;
bool has_simple_value_ = true;
union Value {
std::vector<uint8_t> simple;
std::vector<uint8_t> (*template_func)(Ts...);
Value() {} // trivial constructor
~Value() {} // trivial destructor - we manage lifetime via discriminator
} value_;
std::vector<uint8_t> value_simple_;
std::function<std::vector<uint8_t>(Ts...)> value_template_{};
espbt::ESPBTUUID service_uuid_;
espbt::ESPBTUUID char_uuid_;
std::tuple<Ts...> var_{};
@@ -232,9 +213,9 @@ template<typename... Ts> class BLEClientPasskeyReplyAction : public Action<Ts...
void play(Ts... x) override {
uint32_t passkey;
if (has_simple_value_) {
passkey = this->value_.simple;
passkey = this->value_simple_;
} else {
passkey = this->value_.template_func(x...);
passkey = this->value_template_(x...);
}
if (passkey > 999999)
return;
@@ -243,23 +224,21 @@ template<typename... Ts> class BLEClientPasskeyReplyAction : public Action<Ts...
esp_ble_passkey_reply(remote_bda, true, passkey);
}
void set_value_template(uint32_t (*func)(Ts...)) {
this->value_.template_func = func;
this->has_simple_value_ = false;
void set_value_template(std::function<uint32_t(Ts...)> func) {
this->value_template_ = std::move(func);
has_simple_value_ = false;
}
void set_value_simple(const uint32_t &value) {
this->value_.simple = value;
this->has_simple_value_ = true;
this->value_simple_ = value;
has_simple_value_ = true;
}
private:
BLEClient *parent_{nullptr};
bool has_simple_value_ = true;
union {
uint32_t simple;
uint32_t (*template_func)(Ts...);
} value_{.simple = 0};
uint32_t value_simple_{0};
std::function<uint32_t(Ts...)> value_template_{};
};
template<typename... Ts> class BLEClientNumericComparisonReplyAction : public Action<Ts...> {
@@ -270,29 +249,27 @@ template<typename... Ts> class BLEClientNumericComparisonReplyAction : public Ac
esp_bd_addr_t remote_bda;
memcpy(remote_bda, parent_->get_remote_bda(), sizeof(esp_bd_addr_t));
if (has_simple_value_) {
esp_ble_confirm_reply(remote_bda, this->value_.simple);
esp_ble_confirm_reply(remote_bda, this->value_simple_);
} else {
esp_ble_confirm_reply(remote_bda, this->value_.template_func(x...));
esp_ble_confirm_reply(remote_bda, this->value_template_(x...));
}
}
void set_value_template(bool (*func)(Ts...)) {
this->value_.template_func = func;
this->has_simple_value_ = false;
void set_value_template(std::function<bool(Ts...)> func) {
this->value_template_ = std::move(func);
has_simple_value_ = false;
}
void set_value_simple(const bool &value) {
this->value_.simple = value;
this->has_simple_value_ = true;
this->value_simple_ = value;
has_simple_value_ = true;
}
private:
BLEClient *parent_{nullptr};
bool has_simple_value_ = true;
union {
bool simple;
bool (*template_func)(Ts...);
} value_{.simple = false};
bool value_simple_{false};
std::function<bool(Ts...)> value_template_{};
};
template<typename... Ts> class BLEClientRemoveBondAction : public Action<Ts...> {

View File

@@ -117,9 +117,9 @@ void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t ga
}
float BLESensor::parse_data_(uint8_t *value, uint16_t value_len) {
if (this->has_data_to_value_) {
if (this->data_to_value_func_.has_value()) {
std::vector<uint8_t> data(value, value + value_len);
return this->data_to_value_func_(data);
return (*this->data_to_value_func_)(data);
} else {
return value[0];
}

View File

@@ -15,6 +15,8 @@ namespace ble_client {
namespace espbt = esphome::esp32_ble_tracker;
using data_to_value_t = std::function<float(std::vector<uint8_t>)>;
class BLESensor : public sensor::Sensor, public PollingComponent, public BLEClientNode {
public:
void loop() override;
@@ -31,17 +33,13 @@ class BLESensor : public sensor::Sensor, public PollingComponent, public BLEClie
void set_descr_uuid16(uint16_t uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
void set_descr_uuid32(uint32_t uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
void set_descr_uuid128(uint8_t *uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
void set_data_to_value(float (*lambda)(const std::vector<uint8_t> &)) {
this->data_to_value_func_ = lambda;
this->has_data_to_value_ = true;
}
void set_data_to_value(data_to_value_t &&lambda) { this->data_to_value_func_ = lambda; }
void set_enable_notify(bool notify) { this->notify_ = notify; }
uint16_t handle;
protected:
float parse_data_(uint8_t *value, uint16_t value_len);
bool has_data_to_value_{false};
float (*data_to_value_func_)(const std::vector<uint8_t> &){};
optional<data_to_value_t> data_to_value_func_{};
bool notify_;
espbt::ESPBTUUID service_uuid_;
espbt::ESPBTUUID char_uuid_;

View File

@@ -84,6 +84,11 @@ def button_schema(
return _BUTTON_SCHEMA.extend(schema)
# Remove before 2025.11.0
BUTTON_SCHEMA = button_schema(Button)
BUTTON_SCHEMA.add_extra(cv.deprecated_schema_constant("button"))
async def setup_button_core_(var, config):
await setup_entity(var, config, "button")

View File

@@ -270,6 +270,11 @@ def climate_schema(
return _CLIMATE_SCHEMA.extend(schema)
# Remove before 2025.11.0
CLIMATE_SCHEMA = climate_schema(Climate)
CLIMATE_SCHEMA.add_extra(cv.deprecated_schema_constant("climate"))
async def setup_climate_core_(var, config):
await setup_entity(var, config, "climate")

View File

@@ -50,21 +50,21 @@ void ClimateCall::perform() {
const LogString *mode_s = climate_mode_to_string(*this->mode_);
ESP_LOGD(TAG, " Mode: %s", LOG_STR_ARG(mode_s));
}
if (this->custom_fan_mode_ != nullptr) {
if (this->custom_fan_mode_.has_value()) {
this->fan_mode_.reset();
ESP_LOGD(TAG, " Custom Fan: %s", this->custom_fan_mode_);
ESP_LOGD(TAG, " Custom Fan: %s", this->custom_fan_mode_.value().c_str());
}
if (this->fan_mode_.has_value()) {
this->custom_fan_mode_ = nullptr;
this->custom_fan_mode_.reset();
const LogString *fan_mode_s = climate_fan_mode_to_string(*this->fan_mode_);
ESP_LOGD(TAG, " Fan: %s", LOG_STR_ARG(fan_mode_s));
}
if (this->custom_preset_ != nullptr) {
if (this->custom_preset_.has_value()) {
this->preset_.reset();
ESP_LOGD(TAG, " Custom Preset: %s", this->custom_preset_);
ESP_LOGD(TAG, " Custom Preset: %s", this->custom_preset_.value().c_str());
}
if (this->preset_.has_value()) {
this->custom_preset_ = nullptr;
this->custom_preset_.reset();
const LogString *preset_s = climate_preset_to_string(*this->preset_);
ESP_LOGD(TAG, " Preset: %s", LOG_STR_ARG(preset_s));
}
@@ -96,10 +96,11 @@ void ClimateCall::validate_() {
this->mode_.reset();
}
}
if (this->custom_fan_mode_ != nullptr) {
if (!traits.supports_custom_fan_mode(this->custom_fan_mode_)) {
ESP_LOGW(TAG, " Fan Mode %s not supported", this->custom_fan_mode_);
this->custom_fan_mode_ = nullptr;
if (this->custom_fan_mode_.has_value()) {
auto custom_fan_mode = *this->custom_fan_mode_;
if (!traits.supports_custom_fan_mode(custom_fan_mode)) {
ESP_LOGW(TAG, " Fan Mode %s not supported", custom_fan_mode.c_str());
this->custom_fan_mode_.reset();
}
} else if (this->fan_mode_.has_value()) {
auto fan_mode = *this->fan_mode_;
@@ -108,10 +109,11 @@ void ClimateCall::validate_() {
this->fan_mode_.reset();
}
}
if (this->custom_preset_ != nullptr) {
if (!traits.supports_custom_preset(this->custom_preset_)) {
ESP_LOGW(TAG, " Preset %s not supported", this->custom_preset_);
this->custom_preset_ = nullptr;
if (this->custom_preset_.has_value()) {
auto custom_preset = *this->custom_preset_;
if (!traits.supports_custom_preset(custom_preset)) {
ESP_LOGW(TAG, " Preset %s not supported", custom_preset.c_str());
this->custom_preset_.reset();
}
} else if (this->preset_.has_value()) {
auto preset = *this->preset_;
@@ -184,29 +186,26 @@ ClimateCall &ClimateCall::set_mode(const std::string &mode) {
ClimateCall &ClimateCall::set_fan_mode(ClimateFanMode fan_mode) {
this->fan_mode_ = fan_mode;
this->custom_fan_mode_ = nullptr;
this->custom_fan_mode_.reset();
return *this;
}
ClimateCall &ClimateCall::set_fan_mode(const char *custom_fan_mode) {
// Check if it's a standard enum mode first
ClimateCall &ClimateCall::set_fan_mode(const std::string &fan_mode) {
for (const auto &mode_entry : CLIMATE_FAN_MODES_BY_STR) {
if (str_equals_case_insensitive(custom_fan_mode, mode_entry.str)) {
return this->set_fan_mode(static_cast<ClimateFanMode>(mode_entry.value));
if (str_equals_case_insensitive(fan_mode, mode_entry.str)) {
this->set_fan_mode(static_cast<ClimateFanMode>(mode_entry.value));
return *this;
}
}
// Find the matching pointer from parent climate device
if (const char *mode_ptr = this->parent_->find_custom_fan_mode_(custom_fan_mode)) {
this->custom_fan_mode_ = mode_ptr;
if (this->parent_->get_traits().supports_custom_fan_mode(fan_mode)) {
this->custom_fan_mode_ = fan_mode;
this->fan_mode_.reset();
return *this;
} else {
ESP_LOGW(TAG, "'%s' - Unrecognized fan mode %s", this->parent_->get_name().c_str(), fan_mode.c_str());
}
ESP_LOGW(TAG, "'%s' - Unrecognized fan mode %s", this->parent_->get_name().c_str(), custom_fan_mode);
return *this;
}
ClimateCall &ClimateCall::set_fan_mode(const std::string &fan_mode) { return this->set_fan_mode(fan_mode.c_str()); }
ClimateCall &ClimateCall::set_fan_mode(optional<std::string> fan_mode) {
if (fan_mode.has_value()) {
this->set_fan_mode(fan_mode.value());
@@ -216,29 +215,26 @@ ClimateCall &ClimateCall::set_fan_mode(optional<std::string> fan_mode) {
ClimateCall &ClimateCall::set_preset(ClimatePreset preset) {
this->preset_ = preset;
this->custom_preset_ = nullptr;
this->custom_preset_.reset();
return *this;
}
ClimateCall &ClimateCall::set_preset(const char *custom_preset) {
// Check if it's a standard enum preset first
ClimateCall &ClimateCall::set_preset(const std::string &preset) {
for (const auto &preset_entry : CLIMATE_PRESETS_BY_STR) {
if (str_equals_case_insensitive(custom_preset, preset_entry.str)) {
return this->set_preset(static_cast<ClimatePreset>(preset_entry.value));
if (str_equals_case_insensitive(preset, preset_entry.str)) {
this->set_preset(static_cast<ClimatePreset>(preset_entry.value));
return *this;
}
}
// Find the matching pointer from parent climate device
if (const char *preset_ptr = this->parent_->find_custom_preset_(custom_preset)) {
this->custom_preset_ = preset_ptr;
if (this->parent_->get_traits().supports_custom_preset(preset)) {
this->custom_preset_ = preset;
this->preset_.reset();
return *this;
} else {
ESP_LOGW(TAG, "'%s' - Unrecognized preset %s", this->parent_->get_name().c_str(), preset.c_str());
}
ESP_LOGW(TAG, "'%s' - Unrecognized preset %s", this->parent_->get_name().c_str(), custom_preset);
return *this;
}
ClimateCall &ClimateCall::set_preset(const std::string &preset) { return this->set_preset(preset.c_str()); }
ClimateCall &ClimateCall::set_preset(optional<std::string> preset) {
if (preset.has_value()) {
this->set_preset(preset.value());
@@ -291,14 +287,8 @@ const optional<ClimateMode> &ClimateCall::get_mode() const { return this->mode_;
const optional<ClimateFanMode> &ClimateCall::get_fan_mode() const { return this->fan_mode_; }
const optional<ClimateSwingMode> &ClimateCall::get_swing_mode() const { return this->swing_mode_; }
const optional<ClimatePreset> &ClimateCall::get_preset() const { return this->preset_; }
optional<std::string> ClimateCall::get_custom_fan_mode() const {
return this->custom_fan_mode_ != nullptr ? std::string(this->custom_fan_mode_) : optional<std::string>{};
}
optional<std::string> ClimateCall::get_custom_preset() const {
return this->custom_preset_ != nullptr ? std::string(this->custom_preset_) : optional<std::string>{};
}
const optional<std::string> &ClimateCall::get_custom_fan_mode() const { return this->custom_fan_mode_; }
const optional<std::string> &ClimateCall::get_custom_preset() const { return this->custom_preset_; }
ClimateCall &ClimateCall::set_target_temperature_high(optional<float> target_temperature_high) {
this->target_temperature_high_ = target_temperature_high;
@@ -327,13 +317,13 @@ ClimateCall &ClimateCall::set_mode(optional<ClimateMode> mode) {
ClimateCall &ClimateCall::set_fan_mode(optional<ClimateFanMode> fan_mode) {
this->fan_mode_ = fan_mode;
this->custom_fan_mode_ = nullptr;
this->custom_fan_mode_.reset();
return *this;
}
ClimateCall &ClimateCall::set_preset(optional<ClimatePreset> preset) {
this->preset_ = preset;
this->custom_preset_ = nullptr;
this->custom_preset_.reset();
return *this;
}
@@ -392,34 +382,30 @@ void Climate::save_state_() {
state.uses_custom_fan_mode = false;
state.fan_mode = this->fan_mode.value();
}
if (!traits.get_supported_custom_fan_modes().empty() && this->has_custom_fan_mode()) {
if (!traits.get_supported_custom_fan_modes().empty() && custom_fan_mode.has_value()) {
state.uses_custom_fan_mode = true;
const auto &supported = traits.get_supported_custom_fan_modes();
// std::vector maintains insertion order
size_t i = 0;
for (const char *mode : supported) {
if (strcmp(mode, this->custom_fan_mode_) == 0) {
std::vector<std::string> vec{supported.begin(), supported.end()};
for (size_t i = 0; i < vec.size(); i++) {
if (vec[i] == custom_fan_mode) {
state.custom_fan_mode = i;
break;
}
i++;
}
}
if (traits.get_supports_presets() && preset.has_value()) {
state.uses_custom_preset = false;
state.preset = this->preset.value();
}
if (!traits.get_supported_custom_presets().empty() && this->has_custom_preset()) {
if (!traits.get_supported_custom_presets().empty() && custom_preset.has_value()) {
state.uses_custom_preset = true;
const auto &supported = traits.get_supported_custom_presets();
// std::vector maintains insertion order
size_t i = 0;
for (const char *preset : supported) {
if (strcmp(preset, this->custom_preset_) == 0) {
std::vector<std::string> vec{supported.begin(), supported.end()};
for (size_t i = 0; i < vec.size(); i++) {
if (vec[i] == custom_preset) {
state.custom_preset = i;
break;
}
i++;
}
}
if (traits.get_supports_swing_modes()) {
@@ -440,14 +426,14 @@ void Climate::publish_state() {
if (traits.get_supports_fan_modes() && this->fan_mode.has_value()) {
ESP_LOGD(TAG, " Fan Mode: %s", LOG_STR_ARG(climate_fan_mode_to_string(this->fan_mode.value())));
}
if (!traits.get_supported_custom_fan_modes().empty() && this->has_custom_fan_mode()) {
ESP_LOGD(TAG, " Custom Fan Mode: %s", this->custom_fan_mode_);
if (!traits.get_supported_custom_fan_modes().empty() && this->custom_fan_mode.has_value()) {
ESP_LOGD(TAG, " Custom Fan Mode: %s", this->custom_fan_mode.value().c_str());
}
if (traits.get_supports_presets() && this->preset.has_value()) {
ESP_LOGD(TAG, " Preset: %s", LOG_STR_ARG(climate_preset_to_string(this->preset.value())));
}
if (!traits.get_supported_custom_presets().empty() && this->has_custom_preset()) {
ESP_LOGD(TAG, " Custom Preset: %s", this->custom_preset_);
if (!traits.get_supported_custom_presets().empty() && this->custom_preset.has_value()) {
ESP_LOGD(TAG, " Custom Preset: %s", this->custom_preset.value().c_str());
}
if (traits.get_supports_swing_modes()) {
ESP_LOGD(TAG, " Swing Mode: %s", LOG_STR_ARG(climate_swing_mode_to_string(this->swing_mode)));
@@ -534,23 +520,13 @@ ClimateCall ClimateDeviceRestoreState::to_call(Climate *climate) {
if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY)) {
call.set_target_humidity(this->target_humidity);
}
if (this->uses_custom_fan_mode) {
if (this->custom_fan_mode < traits.get_supported_custom_fan_modes().size()) {
call.fan_mode_.reset();
call.custom_fan_mode_ = traits.get_supported_custom_fan_modes()[this->custom_fan_mode];
}
} else if (traits.supports_fan_mode(this->fan_mode)) {
if (traits.get_supports_fan_modes() || !traits.get_supported_custom_fan_modes().empty()) {
call.set_fan_mode(this->fan_mode);
}
if (this->uses_custom_preset) {
if (this->custom_preset < traits.get_supported_custom_presets().size()) {
call.preset_.reset();
call.custom_preset_ = traits.get_supported_custom_presets()[this->custom_preset];
}
} else if (traits.supports_preset(this->preset)) {
if (traits.get_supports_presets() || !traits.get_supported_custom_presets().empty()) {
call.set_preset(this->preset);
}
if (traits.supports_swing_mode(this->swing_mode)) {
if (traits.get_supports_swing_modes()) {
call.set_swing_mode(this->swing_mode);
}
return call;
@@ -569,131 +545,56 @@ void ClimateDeviceRestoreState::apply(Climate *climate) {
if (traits.has_feature_flags(climate::CLIMATE_SUPPORTS_TARGET_HUMIDITY)) {
climate->target_humidity = this->target_humidity;
}
if (this->uses_custom_fan_mode) {
if (this->custom_fan_mode < traits.get_supported_custom_fan_modes().size()) {
climate->fan_mode.reset();
climate->custom_fan_mode_ = traits.get_supported_custom_fan_modes()[this->custom_fan_mode];
}
} else if (traits.supports_fan_mode(this->fan_mode)) {
if (traits.get_supports_fan_modes() && !this->uses_custom_fan_mode) {
climate->fan_mode = this->fan_mode;
climate->clear_custom_fan_mode_();
}
if (this->uses_custom_preset) {
if (this->custom_preset < traits.get_supported_custom_presets().size()) {
climate->preset.reset();
climate->custom_preset_ = traits.get_supported_custom_presets()[this->custom_preset];
if (!traits.get_supported_custom_fan_modes().empty() && this->uses_custom_fan_mode) {
// std::set has consistent order (lexicographic for strings), so this is ok
const auto &modes = traits.get_supported_custom_fan_modes();
std::vector<std::string> modes_vec{modes.begin(), modes.end()};
if (custom_fan_mode < modes_vec.size()) {
climate->custom_fan_mode = modes_vec[this->custom_fan_mode];
}
} else if (traits.supports_preset(this->preset)) {
climate->preset = this->preset;
climate->clear_custom_preset_();
}
if (traits.supports_swing_mode(this->swing_mode)) {
if (traits.get_supports_presets() && !this->uses_custom_preset) {
climate->preset = this->preset;
}
if (!traits.get_supported_custom_presets().empty() && uses_custom_preset) {
// std::set has consistent order (lexicographic for strings), so this is ok
const auto &presets = traits.get_supported_custom_presets();
std::vector<std::string> presets_vec{presets.begin(), presets.end()};
if (custom_preset < presets_vec.size()) {
climate->custom_preset = presets_vec[this->custom_preset];
}
}
if (traits.get_supports_swing_modes()) {
climate->swing_mode = this->swing_mode;
}
climate->publish_state();
}
/** Template helper for setting primary modes (fan_mode, preset) with mutual exclusion.
*
* Climate devices have mutually exclusive mode pairs:
* - fan_mode (enum) vs custom_fan_mode_ (const char*)
* - preset (enum) vs custom_preset_ (const char*)
*
* Only one mode in each pair can be active at a time. This helper ensures setting a primary
* mode automatically clears its corresponding custom mode.
*
* Example state transitions:
* Before: custom_fan_mode_="Turbo", fan_mode=nullopt
* Call: set_fan_mode_(CLIMATE_FAN_HIGH)
* After: custom_fan_mode_=nullptr, fan_mode=CLIMATE_FAN_HIGH
*
* @param primary The primary mode optional (fan_mode or preset)
* @param custom_ptr Reference to the custom mode pointer (custom_fan_mode_ or custom_preset_)
* @param value The new primary mode value to set
* @return true if state changed, false if already set to this value
*/
template<typename T> bool set_primary_mode(optional<T> &primary, const char *&custom_ptr, T value) {
// Clear the custom mode (mutual exclusion)
bool changed = custom_ptr != nullptr;
custom_ptr = nullptr;
// Set the primary mode
if (changed || !primary.has_value() || primary.value() != value) {
primary = value;
return true;
template<typename T1, typename T2> bool set_alternative(optional<T1> &dst, optional<T2> &alt, const T1 &src) {
bool is_changed = alt.has_value();
alt.reset();
if (is_changed || dst != src) {
dst = src;
is_changed = true;
}
return false;
}
/** Template helper for setting custom modes (custom_fan_mode_, custom_preset_) with mutual exclusion.
*
* This helper ensures setting a custom mode automatically clears its corresponding primary mode.
* It also validates that the custom mode exists in the device's supported modes (lifetime safety).
*
* Example state transitions:
* Before: fan_mode=CLIMATE_FAN_HIGH, custom_fan_mode_=nullptr
* Call: set_custom_fan_mode_("Turbo")
* After: fan_mode=nullopt, custom_fan_mode_="Turbo" (pointer from traits)
*
* Lifetime Safety:
* - found_ptr must come from traits.find_custom_*_mode_()
* - Only pointers found in traits are stored, ensuring they remain valid
* - Prevents dangling pointers from temporary strings
*
* @param custom_ptr Reference to the custom mode pointer to set
* @param primary The primary mode optional to clear
* @param found_ptr The validated pointer from traits (nullptr if not found)
* @param has_custom Whether a custom mode is currently active
* @return true if state changed, false otherwise
*/
template<typename T>
bool set_custom_mode(const char *&custom_ptr, optional<T> &primary, const char *found_ptr, bool has_custom) {
if (found_ptr != nullptr) {
// Clear the primary mode (mutual exclusion)
bool changed = primary.has_value();
primary.reset();
// Set the custom mode (pointer is validated by caller from traits)
if (changed || custom_ptr != found_ptr) {
custom_ptr = found_ptr;
return true;
}
return false;
}
// Mode not found in supported modes, clear it if currently set
if (has_custom) {
custom_ptr = nullptr;
return true;
}
return false;
return is_changed;
}
bool Climate::set_fan_mode_(ClimateFanMode mode) {
return set_primary_mode(this->fan_mode, this->custom_fan_mode_, mode);
return set_alternative(this->fan_mode, this->custom_fan_mode, mode);
}
bool Climate::set_custom_fan_mode_(const char *mode) {
auto traits = this->get_traits();
return set_custom_mode<ClimateFanMode>(this->custom_fan_mode_, this->fan_mode, traits.find_custom_fan_mode_(mode),
this->has_custom_fan_mode());
bool Climate::set_custom_fan_mode_(const std::string &mode) {
return set_alternative(this->custom_fan_mode, this->fan_mode, mode);
}
void Climate::clear_custom_fan_mode_() { this->custom_fan_mode_ = nullptr; }
bool Climate::set_preset_(ClimatePreset preset) { return set_alternative(this->preset, this->custom_preset, preset); }
bool Climate::set_preset_(ClimatePreset preset) { return set_primary_mode(this->preset, this->custom_preset_, preset); }
bool Climate::set_custom_preset_(const char *preset) {
auto traits = this->get_traits();
return set_custom_mode<ClimatePreset>(this->custom_preset_, this->preset, traits.find_custom_preset_(preset),
this->has_custom_preset());
}
void Climate::clear_custom_preset_() { this->custom_preset_ = nullptr; }
const char *Climate::find_custom_fan_mode_(const char *custom_fan_mode) {
return this->get_traits().find_custom_fan_mode_(custom_fan_mode);
}
const char *Climate::find_custom_preset_(const char *custom_preset) {
return this->get_traits().find_custom_preset_(custom_preset);
bool Climate::set_custom_preset_(const std::string &preset) {
return set_alternative(this->custom_preset, this->preset, preset);
}
void Climate::dump_traits_(const char *tag) {
@@ -745,8 +646,8 @@ void Climate::dump_traits_(const char *tag) {
}
if (!traits.get_supported_custom_fan_modes().empty()) {
ESP_LOGCONFIG(tag, " Supported custom fan modes:");
for (const char *s : traits.get_supported_custom_fan_modes())
ESP_LOGCONFIG(tag, " - %s", s);
for (const std::string &s : traits.get_supported_custom_fan_modes())
ESP_LOGCONFIG(tag, " - %s", s.c_str());
}
if (!traits.get_supported_presets().empty()) {
ESP_LOGCONFIG(tag, " Supported presets:");
@@ -755,8 +656,8 @@ void Climate::dump_traits_(const char *tag) {
}
if (!traits.get_supported_custom_presets().empty()) {
ESP_LOGCONFIG(tag, " Supported custom presets:");
for (const char *s : traits.get_supported_custom_presets())
ESP_LOGCONFIG(tag, " - %s", s);
for (const std::string &s : traits.get_supported_custom_presets())
ESP_LOGCONFIG(tag, " - %s", s.c_str());
}
if (!traits.get_supported_swing_modes().empty()) {
ESP_LOGCONFIG(tag, " Supported swing modes:");

View File

@@ -33,7 +33,6 @@ class Climate;
class ClimateCall {
public:
explicit ClimateCall(Climate *parent) : parent_(parent) {}
friend struct ClimateDeviceRestoreState;
/// Set the mode of the climate device.
ClimateCall &set_mode(ClimateMode mode);
@@ -77,8 +76,6 @@ class ClimateCall {
ClimateCall &set_fan_mode(const std::string &fan_mode);
/// Set the fan mode of the climate device based on a string.
ClimateCall &set_fan_mode(optional<std::string> fan_mode);
/// Set the custom fan mode of the climate device.
ClimateCall &set_fan_mode(const char *custom_fan_mode);
/// Set the swing mode of the climate device.
ClimateCall &set_swing_mode(ClimateSwingMode swing_mode);
/// Set the swing mode of the climate device.
@@ -93,8 +90,6 @@ class ClimateCall {
ClimateCall &set_preset(const std::string &preset);
/// Set the preset of the climate device based on a string.
ClimateCall &set_preset(optional<std::string> preset);
/// Set the custom preset of the climate device.
ClimateCall &set_preset(const char *custom_preset);
void perform();
@@ -107,8 +102,8 @@ class ClimateCall {
const optional<ClimateFanMode> &get_fan_mode() const;
const optional<ClimateSwingMode> &get_swing_mode() const;
const optional<ClimatePreset> &get_preset() const;
optional<std::string> get_custom_fan_mode() const;
optional<std::string> get_custom_preset() const;
const optional<std::string> &get_custom_fan_mode() const;
const optional<std::string> &get_custom_preset() const;
protected:
void validate_();
@@ -122,8 +117,8 @@ class ClimateCall {
optional<ClimateFanMode> fan_mode_;
optional<ClimateSwingMode> swing_mode_;
optional<ClimatePreset> preset_;
const char *custom_fan_mode_{nullptr};
const char *custom_preset_{nullptr};
optional<std::string> custom_fan_mode_;
optional<std::string> custom_preset_;
};
/// Struct used to save the state of the climate device in restore memory.
@@ -216,12 +211,6 @@ class Climate : public EntityBase {
void set_visual_min_humidity_override(float visual_min_humidity_override);
void set_visual_max_humidity_override(float visual_max_humidity_override);
/// Check if a custom fan mode is currently active.
bool has_custom_fan_mode() const { return this->custom_fan_mode_ != nullptr; }
/// Check if a custom preset is currently active.
bool has_custom_preset() const { return this->custom_preset_ != nullptr; }
/// The current temperature of the climate device, as reported from the integration.
float current_temperature{NAN};
@@ -248,6 +237,12 @@ class Climate : public EntityBase {
/// The active preset of the climate device.
optional<ClimatePreset> preset;
/// The active custom fan mode of the climate device.
optional<std::string> custom_fan_mode;
/// The active custom preset mode of the climate device.
optional<std::string> custom_preset;
/// The active mode of the climate device.
ClimateMode mode{CLIMATE_MODE_OFF};
@@ -257,37 +252,20 @@ class Climate : public EntityBase {
/// The active swing mode of the climate device.
ClimateSwingMode swing_mode{CLIMATE_SWING_OFF};
/// Get the active custom fan mode (read-only access).
const char *get_custom_fan_mode() const { return this->custom_fan_mode_; }
/// Get the active custom preset (read-only access).
const char *get_custom_preset() const { return this->custom_preset_; }
protected:
friend ClimateCall;
friend struct ClimateDeviceRestoreState;
/// Set fan mode. Reset custom fan mode. Return true if fan mode has been changed.
bool set_fan_mode_(ClimateFanMode mode);
/// Set custom fan mode. Reset primary fan mode. Return true if fan mode has been changed.
bool set_custom_fan_mode_(const char *mode);
/// Clear custom fan mode.
void clear_custom_fan_mode_();
bool set_custom_fan_mode_(const std::string &mode);
/// Set preset. Reset custom preset. Return true if preset has been changed.
bool set_preset_(ClimatePreset preset);
/// Set custom preset. Reset primary preset. Return true if preset has been changed.
bool set_custom_preset_(const char *preset);
/// Clear custom preset.
void clear_custom_preset_();
/// Find and return the matching custom fan mode pointer from traits, or nullptr if not found.
const char *find_custom_fan_mode_(const char *custom_fan_mode);
/// Find and return the matching custom preset pointer from traits, or nullptr if not found.
const char *find_custom_preset_(const char *custom_preset);
bool set_custom_preset_(const std::string &preset);
/** Get the default traits of this climate device.
*
@@ -324,21 +302,6 @@ class Climate : public EntityBase {
optional<float> visual_current_temperature_step_override_{};
optional<float> visual_min_humidity_override_{};
optional<float> visual_max_humidity_override_{};
private:
/** The active custom fan mode (private - enforces use of safe setters).
*
* Points to an entry in traits.supported_custom_fan_modes_ or nullptr.
* Use get_custom_fan_mode() to read, set_custom_fan_mode_() to modify.
*/
const char *custom_fan_mode_{nullptr};
/** The active custom preset (private - enforces use of safe setters).
*
* Points to an entry in traits.supported_custom_presets_ or nullptr.
* Use get_custom_preset() to read, set_custom_preset_() to modify.
*/
const char *custom_preset_{nullptr};
};
} // namespace climate

View File

@@ -7,7 +7,6 @@ namespace esphome {
namespace climate {
/// Enum for all modes a climate device can be in.
/// NOTE: If adding values, update ClimateModeMask in climate_traits.h to use the new last value
enum ClimateMode : uint8_t {
/// The climate device is off
CLIMATE_MODE_OFF = 0,
@@ -25,7 +24,7 @@ enum ClimateMode : uint8_t {
* For example, the target temperature can be adjusted based on a schedule, or learned behavior.
* The target temperature can't be adjusted when in this mode.
*/
CLIMATE_MODE_AUTO = 6 // Update ClimateModeMask in climate_traits.h if adding values after this
CLIMATE_MODE_AUTO = 6
};
/// Enum for the current action of the climate device. Values match those of ClimateMode.
@@ -44,7 +43,6 @@ enum ClimateAction : uint8_t {
CLIMATE_ACTION_FAN = 6,
};
/// NOTE: If adding values, update ClimateFanModeMask in climate_traits.h to use the new last value
enum ClimateFanMode : uint8_t {
/// The fan mode is set to On
CLIMATE_FAN_ON = 0,
@@ -65,11 +63,10 @@ enum ClimateFanMode : uint8_t {
/// The fan mode is set to Diffuse
CLIMATE_FAN_DIFFUSE = 8,
/// The fan mode is set to Quiet
CLIMATE_FAN_QUIET = 9, // Update ClimateFanModeMask in climate_traits.h if adding values after this
CLIMATE_FAN_QUIET = 9,
};
/// Enum for all modes a climate swing can be in
/// NOTE: If adding values, update ClimateSwingModeMask in climate_traits.h to use the new last value
enum ClimateSwingMode : uint8_t {
/// The swing mode is set to Off
CLIMATE_SWING_OFF = 0,
@@ -78,11 +75,10 @@ enum ClimateSwingMode : uint8_t {
/// The fan mode is set to Vertical
CLIMATE_SWING_VERTICAL = 2,
/// The fan mode is set to Horizontal
CLIMATE_SWING_HORIZONTAL = 3, // Update ClimateSwingModeMask in climate_traits.h if adding values after this
CLIMATE_SWING_HORIZONTAL = 3,
};
/// Enum for all preset modes
/// NOTE: If adding values, update ClimatePresetMask in climate_traits.h to use the new last value
enum ClimatePreset : uint8_t {
/// No preset is active
CLIMATE_PRESET_NONE = 0,
@@ -99,7 +95,7 @@ enum ClimatePreset : uint8_t {
/// Device is prepared for sleep
CLIMATE_PRESET_SLEEP = 6,
/// Device is reacting to activity (e.g., movement sensors)
CLIMATE_PRESET_ACTIVITY = 7, // Update ClimatePresetMask in climate_traits.h if adding values after this
CLIMATE_PRESET_ACTIVITY = 7,
};
enum ClimateFeature : uint32_t {

View File

@@ -1,43 +1,19 @@
#pragma once
#include <cstring>
#include <vector>
#include <set>
#include "climate_mode.h"
#include "esphome/core/finite_set_mask.h"
#include "esphome/core/helpers.h"
namespace esphome {
#ifdef USE_API
namespace api {
class APIConnection;
} // namespace api
#endif
namespace climate {
// Type aliases for climate enum bitmasks
// These replace std::set<EnumType> to eliminate red-black tree overhead
// For contiguous enums starting at 0, DefaultBitPolicy provides 1:1 mapping (enum value = bit position)
// Bitmask size is automatically calculated from the last enum value
using ClimateModeMask = FiniteSetMask<ClimateMode, DefaultBitPolicy<ClimateMode, CLIMATE_MODE_AUTO + 1>>;
using ClimateFanModeMask = FiniteSetMask<ClimateFanMode, DefaultBitPolicy<ClimateFanMode, CLIMATE_FAN_QUIET + 1>>;
using ClimateSwingModeMask =
FiniteSetMask<ClimateSwingMode, DefaultBitPolicy<ClimateSwingMode, CLIMATE_SWING_HORIZONTAL + 1>>;
using ClimatePresetMask = FiniteSetMask<ClimatePreset, DefaultBitPolicy<ClimatePreset, CLIMATE_PRESET_ACTIVITY + 1>>;
// Lightweight linear search for small vectors (1-20 items) of const char* pointers
// Avoids std::find template overhead
inline bool vector_contains(const std::vector<const char *> &vec, const char *value) {
for (const char *item : vec) {
if (strcmp(item, value) == 0)
return true;
}
return false;
}
// Find and return matching pointer from vector, or nullptr if not found
inline const char *vector_find(const std::vector<const char *> &vec, const char *value) {
for (const char *item : vec) {
if (strcmp(item, value) == 0)
return item;
}
return nullptr;
}
/** This class contains all static data for climate devices.
*
* All climate devices must support these features:
@@ -65,11 +41,7 @@ inline const char *vector_find(const std::vector<const char *> &vec, const char
* - temperature step - the step with which to increase/decrease target temperature.
* This also affects with how many decimal places the temperature is shown
*/
class Climate; // Forward declaration
class ClimateTraits {
friend class Climate; // Allow Climate to access protected find methods
public:
/// Get/set feature flags (see ClimateFeatures enum in climate_mode.h)
uint32_t get_feature_flags() const { return this->feature_flags_; }
@@ -135,74 +107,48 @@ class ClimateTraits {
}
}
void set_supported_modes(ClimateModeMask modes) { this->supported_modes_ = modes; }
void set_supported_modes(std::set<ClimateMode> modes) { this->supported_modes_ = std::move(modes); }
void add_supported_mode(ClimateMode mode) { this->supported_modes_.insert(mode); }
bool supports_mode(ClimateMode mode) const { return this->supported_modes_.count(mode); }
const ClimateModeMask &get_supported_modes() const { return this->supported_modes_; }
const std::set<ClimateMode> &get_supported_modes() const { return this->supported_modes_; }
void set_supported_fan_modes(ClimateFanModeMask modes) { this->supported_fan_modes_ = modes; }
void set_supported_fan_modes(std::set<ClimateFanMode> modes) { this->supported_fan_modes_ = std::move(modes); }
void add_supported_fan_mode(ClimateFanMode mode) { this->supported_fan_modes_.insert(mode); }
void add_supported_custom_fan_mode(const std::string &mode) { this->supported_custom_fan_modes_.insert(mode); }
bool supports_fan_mode(ClimateFanMode fan_mode) const { return this->supported_fan_modes_.count(fan_mode); }
bool get_supports_fan_modes() const {
return !this->supported_fan_modes_.empty() || !this->supported_custom_fan_modes_.empty();
}
const ClimateFanModeMask &get_supported_fan_modes() const { return this->supported_fan_modes_; }
const std::set<ClimateFanMode> &get_supported_fan_modes() const { return this->supported_fan_modes_; }
void set_supported_custom_fan_modes(std::initializer_list<const char *> modes) {
this->supported_custom_fan_modes_ = modes;
}
void set_supported_custom_fan_modes(const std::vector<const char *> &modes) {
this->supported_custom_fan_modes_ = modes;
}
template<size_t N> void set_supported_custom_fan_modes(const char *const (&modes)[N]) {
this->supported_custom_fan_modes_.assign(modes, modes + N);
}
// Deleted overloads to catch incorrect std::string usage at compile time with clear error messages
void set_supported_custom_fan_modes(const std::vector<std::string> &modes) = delete;
void set_supported_custom_fan_modes(std::initializer_list<std::string> modes) = delete;
const std::vector<const char *> &get_supported_custom_fan_modes() const { return this->supported_custom_fan_modes_; }
bool supports_custom_fan_mode(const char *custom_fan_mode) const {
return vector_contains(this->supported_custom_fan_modes_, custom_fan_mode);
void set_supported_custom_fan_modes(std::set<std::string> supported_custom_fan_modes) {
this->supported_custom_fan_modes_ = std::move(supported_custom_fan_modes);
}
const std::set<std::string> &get_supported_custom_fan_modes() const { return this->supported_custom_fan_modes_; }
bool supports_custom_fan_mode(const std::string &custom_fan_mode) const {
return this->supports_custom_fan_mode(custom_fan_mode.c_str());
return this->supported_custom_fan_modes_.count(custom_fan_mode);
}
void set_supported_presets(ClimatePresetMask presets) { this->supported_presets_ = presets; }
void set_supported_presets(std::set<ClimatePreset> presets) { this->supported_presets_ = std::move(presets); }
void add_supported_preset(ClimatePreset preset) { this->supported_presets_.insert(preset); }
void add_supported_custom_preset(const std::string &preset) { this->supported_custom_presets_.insert(preset); }
bool supports_preset(ClimatePreset preset) const { return this->supported_presets_.count(preset); }
bool get_supports_presets() const { return !this->supported_presets_.empty(); }
const ClimatePresetMask &get_supported_presets() const { return this->supported_presets_; }
const std::set<climate::ClimatePreset> &get_supported_presets() const { return this->supported_presets_; }
void set_supported_custom_presets(std::initializer_list<const char *> presets) {
this->supported_custom_presets_ = presets;
}
void set_supported_custom_presets(const std::vector<const char *> &presets) {
this->supported_custom_presets_ = presets;
}
template<size_t N> void set_supported_custom_presets(const char *const (&presets)[N]) {
this->supported_custom_presets_.assign(presets, presets + N);
}
// Deleted overloads to catch incorrect std::string usage at compile time with clear error messages
void set_supported_custom_presets(const std::vector<std::string> &presets) = delete;
void set_supported_custom_presets(std::initializer_list<std::string> presets) = delete;
const std::vector<const char *> &get_supported_custom_presets() const { return this->supported_custom_presets_; }
bool supports_custom_preset(const char *custom_preset) const {
return vector_contains(this->supported_custom_presets_, custom_preset);
void set_supported_custom_presets(std::set<std::string> supported_custom_presets) {
this->supported_custom_presets_ = std::move(supported_custom_presets);
}
const std::set<std::string> &get_supported_custom_presets() const { return this->supported_custom_presets_; }
bool supports_custom_preset(const std::string &custom_preset) const {
return this->supports_custom_preset(custom_preset.c_str());
return this->supported_custom_presets_.count(custom_preset);
}
void set_supported_swing_modes(ClimateSwingModeMask modes) { this->supported_swing_modes_ = modes; }
void set_supported_swing_modes(std::set<ClimateSwingMode> modes) { this->supported_swing_modes_ = std::move(modes); }
void add_supported_swing_mode(ClimateSwingMode mode) { this->supported_swing_modes_.insert(mode); }
bool supports_swing_mode(ClimateSwingMode swing_mode) const { return this->supported_swing_modes_.count(swing_mode); }
bool get_supports_swing_modes() const { return !this->supported_swing_modes_.empty(); }
const ClimateSwingModeMask &get_supported_swing_modes() const { return this->supported_swing_modes_; }
const std::set<ClimateSwingMode> &get_supported_swing_modes() const { return this->supported_swing_modes_; }
float get_visual_min_temperature() const { return this->visual_min_temperature_; }
void set_visual_min_temperature(float visual_min_temperature) {
@@ -233,6 +179,23 @@ class ClimateTraits {
void set_visual_max_humidity(float visual_max_humidity) { this->visual_max_humidity_ = visual_max_humidity; }
protected:
#ifdef USE_API
// The API connection is a friend class to access internal methods
friend class api::APIConnection;
// These methods return references to internal data structures.
// They are used by the API to avoid copying data when encoding messages.
// Warning: Do not use these methods outside of the API connection code.
// They return references to internal data that can be invalidated.
const std::set<ClimateMode> &get_supported_modes_for_api_() const { return this->supported_modes_; }
const std::set<ClimateFanMode> &get_supported_fan_modes_for_api_() const { return this->supported_fan_modes_; }
const std::set<std::string> &get_supported_custom_fan_modes_for_api_() const {
return this->supported_custom_fan_modes_;
}
const std::set<climate::ClimatePreset> &get_supported_presets_for_api_() const { return this->supported_presets_; }
const std::set<std::string> &get_supported_custom_presets_for_api_() const { return this->supported_custom_presets_; }
const std::set<ClimateSwingMode> &get_supported_swing_modes_for_api_() const { return this->supported_swing_modes_; }
#endif
void set_mode_support_(climate::ClimateMode mode, bool supported) {
if (supported) {
this->supported_modes_.insert(mode);
@@ -255,18 +218,6 @@ class ClimateTraits {
}
}
/// Find and return the matching custom fan mode pointer from supported modes, or nullptr if not found
/// This is protected as it's an implementation detail - use Climate::find_custom_fan_mode_() instead
const char *find_custom_fan_mode_(const char *custom_fan_mode) const {
return vector_find(this->supported_custom_fan_modes_, custom_fan_mode);
}
/// Find and return the matching custom preset pointer from supported presets, or nullptr if not found
/// This is protected as it's an implementation detail - use Climate::find_custom_preset_() instead
const char *find_custom_preset_(const char *custom_preset) const {
return vector_find(this->supported_custom_presets_, custom_preset);
}
uint32_t feature_flags_{0};
float visual_min_temperature_{10};
float visual_max_temperature_{30};
@@ -275,21 +226,12 @@ class ClimateTraits {
float visual_min_humidity_{30};
float visual_max_humidity_{99};
climate::ClimateModeMask supported_modes_{climate::CLIMATE_MODE_OFF};
climate::ClimateFanModeMask supported_fan_modes_;
climate::ClimateSwingModeMask supported_swing_modes_;
climate::ClimatePresetMask supported_presets_;
/** Custom mode storage using const char* pointers to eliminate std::string overhead.
*
* Pointers must remain valid for the ClimateTraits lifetime. Safe patterns:
* - String literals: set_supported_custom_fan_modes({"Turbo", "Silent"})
* - Static const data: static const char* MODE = "Eco";
*
* Climate class setters validate pointers are from these vectors before storing.
*/
std::vector<const char *> supported_custom_fan_modes_;
std::vector<const char *> supported_custom_presets_;
std::set<climate::ClimateMode> supported_modes_ = {climate::CLIMATE_MODE_OFF};
std::set<climate::ClimateFanMode> supported_fan_modes_;
std::set<climate::ClimateSwingMode> supported_swing_modes_;
std::set<climate::ClimatePreset> supported_presets_;
std::set<std::string> supported_custom_fan_modes_;
std::set<std::string> supported_custom_presets_;
};
} // namespace climate

View File

@@ -1,9 +1,10 @@
import logging
from esphome import core
import esphome.codegen as cg
from esphome.components import climate, remote_base, sensor
import esphome.config_validation as cv
from esphome.const import CONF_SENSOR, CONF_SUPPORTS_COOL, CONF_SUPPORTS_HEAT
from esphome.const import CONF_ID, CONF_SENSOR, CONF_SUPPORTS_COOL, CONF_SUPPORTS_HEAT
from esphome.cpp_generator import MockObjClass
_LOGGER = logging.getLogger(__name__)
@@ -51,6 +52,26 @@ def climate_ir_with_receiver_schema(
)
# Remove before 2025.11.0
def deprecated_schema_constant(config):
type: str = "unknown"
if (id := config.get(CONF_ID)) is not None and isinstance(id, core.ID):
type = str(id.type).split("::", maxsplit=1)[0]
_LOGGER.warning(
"Using `climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA` is deprecated and will be removed in ESPHome 2025.11.0. "
"Please use `climate_ir.climate_ir_with_receiver_schema(...)` instead. "
"If you are seeing this, report an issue to the external_component author and ask them to update it. "
"https://developers.esphome.io/blog/2025/05/14/_schema-deprecations/. "
"Component using this schema: %s",
type,
)
return config
CLIMATE_IR_WITH_RECEIVER_SCHEMA = climate_ir_with_receiver_schema(ClimateIR)
CLIMATE_IR_WITH_RECEIVER_SCHEMA.add_extra(deprecated_schema_constant)
async def register_climate_ir(var, config):
await cg.register_component(var, config)
await remote_base.register_transmittable(var, config)

View File

@@ -24,18 +24,16 @@ class ClimateIR : public Component,
public remote_base::RemoteTransmittable {
public:
ClimateIR(float minimum_temperature, float maximum_temperature, float temperature_step = 1.0f,
bool supports_dry = false, bool supports_fan_only = false,
climate::ClimateFanModeMask fan_modes = climate::ClimateFanModeMask(),
climate::ClimateSwingModeMask swing_modes = climate::ClimateSwingModeMask(),
climate::ClimatePresetMask presets = climate::ClimatePresetMask()) {
bool supports_dry = false, bool supports_fan_only = false, std::set<climate::ClimateFanMode> fan_modes = {},
std::set<climate::ClimateSwingMode> swing_modes = {}, std::set<climate::ClimatePreset> presets = {}) {
this->minimum_temperature_ = minimum_temperature;
this->maximum_temperature_ = maximum_temperature;
this->temperature_step_ = temperature_step;
this->supports_dry_ = supports_dry;
this->supports_fan_only_ = supports_fan_only;
this->fan_modes_ = fan_modes;
this->swing_modes_ = swing_modes;
this->presets_ = presets;
this->fan_modes_ = std::move(fan_modes);
this->swing_modes_ = std::move(swing_modes);
this->presets_ = std::move(presets);
}
void setup() override;
@@ -62,9 +60,9 @@ class ClimateIR : public Component,
bool supports_heat_{true};
bool supports_dry_{false};
bool supports_fan_only_{false};
climate::ClimateFanModeMask fan_modes_{};
climate::ClimateSwingModeMask swing_modes_{};
climate::ClimatePresetMask presets_{};
std::set<climate::ClimateFanMode> fan_modes_ = {};
std::set<climate::ClimateSwingMode> swing_modes_ = {};
std::set<climate::ClimatePreset> presets_ = {};
sensor::Sensor *sensor_{nullptr};
};

View File

@@ -12,7 +12,7 @@ void CopyFan::setup() {
this->oscillating = source_->oscillating;
this->speed = source_->speed;
this->direction = source_->direction;
this->set_preset_mode_(source_->get_preset_mode());
this->preset_mode = source_->preset_mode;
this->publish_state();
});
@@ -20,7 +20,7 @@ void CopyFan::setup() {
this->oscillating = source_->oscillating;
this->speed = source_->speed;
this->direction = source_->direction;
this->set_preset_mode_(source_->get_preset_mode());
this->preset_mode = source_->preset_mode;
this->publish_state();
}
@@ -49,7 +49,7 @@ void CopyFan::control(const fan::FanCall &call) {
call2.set_speed(*call.get_speed());
if (call.get_direction().has_value())
call2.set_direction(*call.get_direction());
if (call.has_preset_mode())
if (!call.get_preset_mode().empty())
call2.set_preset_mode(call.get_preset_mode());
call2.perform();
}

View File

@@ -7,19 +7,19 @@ namespace copy {
static const char *const TAG = "copy.select";
void CopySelect::setup() {
source_->add_on_state_callback([this](const std::string &value, size_t index) { this->publish_state(index); });
source_->add_on_state_callback([this](const std::string &value, size_t index) { this->publish_state(value); });
traits.set_options(source_->traits.get_options());
if (source_->has_state())
this->publish_state(source_->active_index().value());
this->publish_state(source_->state);
}
void CopySelect::dump_config() { LOG_SELECT("", "Copy Select", this); }
void CopySelect::control(size_t index) {
void CopySelect::control(const std::string &value) {
auto call = source_->make_call();
call.set_index(index);
call.set_option(value);
call.perform();
}

View File

@@ -13,7 +13,7 @@ class CopySelect : public select::Select, public Component {
void dump_config() override;
protected:
void control(size_t index) override;
void control(const std::string &value) override;
select::Select *source_;
};

View File

@@ -151,6 +151,11 @@ def cover_schema(
return _COVER_SCHEMA.extend(schema)
# Remove before 2025.11.0
COVER_SCHEMA = cover_schema(Cover)
COVER_SCHEMA.add_extra(cv.deprecated_schema_constant("cover"))
async def setup_cover_core_(var, config):
await setup_entity(var, config, "cover")

View File

@@ -28,16 +28,16 @@ class DemoClimate : public climate::Climate, public Component {
this->mode = climate::CLIMATE_MODE_AUTO;
this->action = climate::CLIMATE_ACTION_COOLING;
this->fan_mode = climate::CLIMATE_FAN_HIGH;
this->set_custom_preset_("My Preset");
this->custom_preset = {"My Preset"};
break;
case DemoClimateType::TYPE_3:
this->current_temperature = 21.5;
this->target_temperature_low = 21.0;
this->target_temperature_high = 22.5;
this->mode = climate::CLIMATE_MODE_HEAT_COOL;
this->set_custom_fan_mode_("Auto Low");
this->custom_fan_mode = {"Auto Low"};
this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL;
this->set_preset_(climate::CLIMATE_PRESET_AWAY);
this->preset = climate::CLIMATE_PRESET_AWAY;
break;
}
this->publish_state();
@@ -58,19 +58,23 @@ class DemoClimate : public climate::Climate, public Component {
this->target_temperature_high = *call.get_target_temperature_high();
}
if (call.get_fan_mode().has_value()) {
this->set_fan_mode_(*call.get_fan_mode());
this->fan_mode = *call.get_fan_mode();
this->custom_fan_mode.reset();
}
if (call.get_swing_mode().has_value()) {
this->swing_mode = *call.get_swing_mode();
}
if (call.get_custom_fan_mode().has_value()) {
this->set_custom_fan_mode_(call.get_custom_fan_mode()->c_str());
this->custom_fan_mode = *call.get_custom_fan_mode();
this->fan_mode.reset();
}
if (call.get_preset().has_value()) {
this->set_preset_(*call.get_preset());
this->preset = *call.get_preset();
this->custom_preset.reset();
}
if (call.get_custom_preset().has_value()) {
this->set_custom_preset_(call.get_custom_preset()->c_str());
this->custom_preset = *call.get_custom_preset();
this->preset.reset();
}
this->publish_state();
}

View File

@@ -42,7 +42,7 @@ std::string MenuItemSelect::get_value_text() const {
result = this->value_getter_.value()(this);
} else {
if (this->select_var_ != nullptr) {
result = this->select_var_->current_option();
result = this->select_var_->state;
}
}

View File

@@ -3,8 +3,6 @@
#include "e131_addressable_light_effect.h"
#include "esphome/core/log.h"
#include <algorithm>
namespace esphome {
namespace e131 {
@@ -78,14 +76,14 @@ void E131Component::loop() {
}
void E131Component::add_effect(E131AddressableLightEffect *light_effect) {
if (std::find(light_effects_.begin(), light_effects_.end(), light_effect) != light_effects_.end()) {
if (light_effects_.count(light_effect)) {
return;
}
ESP_LOGD(TAG, "Registering '%s' for universes %d-%d.", light_effect->get_name(), light_effect->get_first_universe(),
light_effect->get_last_universe());
ESP_LOGD(TAG, "Registering '%s' for universes %d-%d.", light_effect->get_name().c_str(),
light_effect->get_first_universe(), light_effect->get_last_universe());
light_effects_.push_back(light_effect);
light_effects_.insert(light_effect);
for (auto universe = light_effect->get_first_universe(); universe <= light_effect->get_last_universe(); ++universe) {
join_(universe);
@@ -93,17 +91,14 @@ void E131Component::add_effect(E131AddressableLightEffect *light_effect) {
}
void E131Component::remove_effect(E131AddressableLightEffect *light_effect) {
auto it = std::find(light_effects_.begin(), light_effects_.end(), light_effect);
if (it == light_effects_.end()) {
if (!light_effects_.count(light_effect)) {
return;
}
ESP_LOGD(TAG, "Unregistering '%s' for universes %d-%d.", light_effect->get_name(), light_effect->get_first_universe(),
light_effect->get_last_universe());
ESP_LOGD(TAG, "Unregistering '%s' for universes %d-%d.", light_effect->get_name().c_str(),
light_effect->get_first_universe(), light_effect->get_last_universe());
// Swap with last element and pop for O(1) removal (order doesn't matter)
*it = light_effects_.back();
light_effects_.pop_back();
light_effects_.erase(light_effect);
for (auto universe = light_effect->get_first_universe(); universe <= light_effect->get_last_universe(); ++universe) {
leave_(universe);

View File

@@ -7,6 +7,7 @@
#include <cinttypes>
#include <map>
#include <memory>
#include <set>
#include <vector>
namespace esphome {
@@ -46,8 +47,9 @@ class E131Component : public esphome::Component {
E131ListenMethod listen_method_{E131_MULTICAST};
std::unique_ptr<socket::Socket> socket_;
std::vector<E131AddressableLightEffect *> light_effects_;
std::set<E131AddressableLightEffect *> light_effects_;
std::map<int, int> universe_consumers_;
std::map<int, E131Packet> universe_packets_;
};
} // namespace e131

View File

@@ -9,7 +9,7 @@ namespace e131 {
static const char *const TAG = "e131_addressable_light_effect";
static const int MAX_DATA_SIZE = (sizeof(E131Packet::values) - 1);
E131AddressableLightEffect::E131AddressableLightEffect(const char *name) : AddressableLightEffect(name) {}
E131AddressableLightEffect::E131AddressableLightEffect(const std::string &name) : AddressableLightEffect(name) {}
int E131AddressableLightEffect::get_data_per_universe() const { return get_lights_per_universe() * channels_; }
@@ -58,8 +58,8 @@ bool E131AddressableLightEffect::process_(int universe, const E131Packet &packet
std::min(it->size(), std::min(output_offset + get_lights_per_universe(), output_offset + packet.count - 1));
auto *input_data = packet.values + 1;
ESP_LOGV(TAG, "Applying data for '%s' on %d universe, for %" PRId32 "-%d.", get_name(), universe, output_offset,
output_end);
ESP_LOGV(TAG, "Applying data for '%s' on %d universe, for %" PRId32 "-%d.", get_name().c_str(), universe,
output_offset, output_end);
switch (channels_) {
case E131_MONO:

View File

@@ -13,7 +13,7 @@ enum E131LightChannels { E131_MONO = 1, E131_RGB = 3, E131_RGBW = 4 };
class E131AddressableLightEffect : public light::AddressableLightEffect {
public:
E131AddressableLightEffect(const char *name);
E131AddressableLightEffect(const std::string &name);
void start() override;
void stop() override;

View File

@@ -304,13 +304,9 @@ def _format_framework_arduino_version(ver: cv.Version) -> str:
def _format_framework_espidf_version(ver: cv.Version, release: str) -> str:
# format the given espidf (https://github.com/pioarduino/esp-idf/releases) version to
# a PIO platformio/framework-espidf value
if ver == cv.Version(5, 4, 3) or ver >= cv.Version(5, 5, 1):
ext = "tar.xz"
else:
ext = "zip"
if release:
return f"pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v{str(ver)}.{release}/esp-idf-v{str(ver)}.{ext}"
return f"pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v{str(ver)}/esp-idf-v{str(ver)}.{ext}"
return f"pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v{str(ver)}.{release}/esp-idf-v{str(ver)}.zip"
return f"pioarduino/framework-espidf@https://github.com/pioarduino/esp-idf/releases/download/v{str(ver)}/esp-idf-v{str(ver)}.zip"
def _is_framework_url(source: str) -> str:
@@ -359,7 +355,6 @@ ESP_IDF_FRAMEWORK_VERSION_LOOKUP = {
ESP_IDF_PLATFORM_VERSION_LOOKUP = {
cv.Version(5, 5, 1): cv.Version(55, 3, 31, "1"),
cv.Version(5, 5, 0): cv.Version(55, 3, 31, "1"),
cv.Version(5, 4, 3): cv.Version(55, 3, 32),
cv.Version(5, 4, 2): cv.Version(54, 3, 21, "2"),
cv.Version(5, 4, 1): cv.Version(54, 3, 21, "2"),
cv.Version(5, 4, 0): cv.Version(54, 3, 21, "2"),
@@ -555,32 +550,6 @@ CONF_ENABLE_LWIP_BRIDGE_INTERFACE = "enable_lwip_bridge_interface"
CONF_ENABLE_LWIP_TCPIP_CORE_LOCKING = "enable_lwip_tcpip_core_locking"
CONF_ENABLE_LWIP_CHECK_THREAD_SAFETY = "enable_lwip_check_thread_safety"
CONF_DISABLE_LIBC_LOCKS_IN_IRAM = "disable_libc_locks_in_iram"
CONF_DISABLE_VFS_SUPPORT_TERMIOS = "disable_vfs_support_termios"
CONF_DISABLE_VFS_SUPPORT_SELECT = "disable_vfs_support_select"
CONF_DISABLE_VFS_SUPPORT_DIR = "disable_vfs_support_dir"
# VFS requirement tracking
# Components that need VFS features can call require_vfs_select() or require_vfs_dir()
KEY_VFS_SELECT_REQUIRED = "vfs_select_required"
KEY_VFS_DIR_REQUIRED = "vfs_dir_required"
def require_vfs_select() -> None:
"""Mark that VFS select support is required by a component.
Call this from components that use esp_vfs_eventfd or other VFS select features.
This prevents CONFIG_VFS_SUPPORT_SELECT from being disabled.
"""
CORE.data[KEY_VFS_SELECT_REQUIRED] = True
def require_vfs_dir() -> None:
"""Mark that VFS directory support is required by a component.
Call this from components that use directory functions (opendir, readdir, mkdir, etc.).
This prevents CONFIG_VFS_SUPPORT_DIR from being disabled.
"""
CORE.data[KEY_VFS_DIR_REQUIRED] = True
def _validate_idf_component(config: ConfigType) -> ConfigType:
@@ -646,13 +615,6 @@ FRAMEWORK_SCHEMA = cv.All(
cv.Optional(
CONF_DISABLE_LIBC_LOCKS_IN_IRAM, default=True
): cv.boolean,
cv.Optional(
CONF_DISABLE_VFS_SUPPORT_TERMIOS, default=True
): cv.boolean,
cv.Optional(
CONF_DISABLE_VFS_SUPPORT_SELECT, default=True
): cv.boolean,
cv.Optional(CONF_DISABLE_VFS_SUPPORT_DIR, default=True): cv.boolean,
cv.Optional(CONF_EXECUTE_FROM_PSRAM): cv.boolean,
}
),
@@ -882,11 +844,6 @@ async def to_code(config):
for clean_var in ("IDF_PATH", "IDF_TOOLS_PATH"):
os.environ.pop(clean_var, None)
# Set the location of the IDF component manager cache
os.environ["IDF_COMPONENT_CACHE_PATH"] = str(
CORE.relative_internal_path(".espressif")
)
add_extra_script(
"post",
"post_build.py",
@@ -1005,43 +962,6 @@ async def to_code(config):
if advanced.get(CONF_DISABLE_LIBC_LOCKS_IN_IRAM, True):
add_idf_sdkconfig_option("CONFIG_LIBC_LOCKS_PLACE_IN_IRAM", False)
# Disable VFS support for termios (terminal I/O functions)
# ESPHome doesn't use termios functions on ESP32 (only used in host UART driver).
# Saves approximately 1.8KB of flash when disabled (default).
add_idf_sdkconfig_option(
"CONFIG_VFS_SUPPORT_TERMIOS",
not advanced.get(CONF_DISABLE_VFS_SUPPORT_TERMIOS, True),
)
# Disable VFS support for select() with file descriptors
# ESPHome only uses select() with sockets via lwip_select(), which still works.
# VFS select is only needed for UART/eventfd file descriptors.
# Components that need it (e.g., openthread) call require_vfs_select().
# Saves approximately 2.7KB of flash when disabled (default).
if CORE.data.get(KEY_VFS_SELECT_REQUIRED, False):
# Component requires VFS select - force enable regardless of user setting
add_idf_sdkconfig_option("CONFIG_VFS_SUPPORT_SELECT", True)
else:
# No component needs it - allow user to control (default: disabled)
add_idf_sdkconfig_option(
"CONFIG_VFS_SUPPORT_SELECT",
not advanced.get(CONF_DISABLE_VFS_SUPPORT_SELECT, True),
)
# Disable VFS support for directory functions (opendir, readdir, mkdir, etc.)
# ESPHome doesn't use directory functions on ESP32.
# Components that need it (e.g., storage components) call require_vfs_dir().
# Saves approximately 0.5KB+ of flash when disabled (default).
if CORE.data.get(KEY_VFS_DIR_REQUIRED, False):
# Component requires VFS directory support - force enable regardless of user setting
add_idf_sdkconfig_option("CONFIG_VFS_SUPPORT_DIR", True)
else:
# No component needs it - allow user to control (default: disabled)
add_idf_sdkconfig_option(
"CONFIG_VFS_SUPPORT_DIR",
not advanced.get(CONF_DISABLE_VFS_SUPPORT_DIR, True),
)
cg.add_platformio_option("board_build.partitions", "partitions.csv")
if CONF_PARTITIONS in config:
add_extra_build_file(

View File

@@ -40,13 +40,13 @@ class ESP32InternalGPIOPin : public InternalGPIOPin {
// - 3 bytes for members below
// - 1 byte padding for alignment
// - 4 bytes for vtable pointer
uint8_t pin_; // GPIO pin number (0-255, actual max ~54 on ESP32)
gpio::Flags flags_{}; // GPIO flags (1 byte)
uint8_t pin_; // GPIO pin number (0-255, actual max ~54 on ESP32)
gpio::Flags flags_; // GPIO flags (1 byte)
struct PinFlags {
uint8_t inverted : 1; // Invert pin logic (1 bit)
uint8_t drive_strength : 2; // Drive strength 0-3 (2 bits)
uint8_t reserved : 5; // Reserved for future use (5 bits)
} pin_flags_{}; // Total: 1 byte
} pin_flags_; // Total: 1 byte
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
static bool isr_service_installed;
};

View File

@@ -223,10 +223,7 @@ async def esp32_pin_to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
num = config[CONF_NUMBER]
cg.add(var.set_pin(getattr(gpio_num_t, f"GPIO_NUM_{num}")))
# Only set if true to avoid bloating setup() function
# (inverted bit in pin_flags_ bitfield is zero-initialized to false)
if config[CONF_INVERTED]:
cg.add(var.set_inverted(True))
cg.add(var.set_inverted(config[CONF_INVERTED]))
if CONF_DRIVE_STRENGTH in config:
cg.add(var.set_drive_strength(config[CONF_DRIVE_STRENGTH]))
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))

View File

@@ -7,7 +7,6 @@ from typing import Any
from esphome import automation
import esphome.codegen as cg
from esphome.components import socket
from esphome.components.esp32 import add_idf_sdkconfig_option, const, get_esp32_variant
import esphome.config_validation as cv
from esphome.const import (
@@ -482,14 +481,6 @@ async def to_code(config):
cg.add(var.set_name(name))
await cg.register_component(var, config)
# BLE uses 1 UDP socket for event notification to wake up main loop from select()
# This enables low-latency (~12μs) BLE event processing instead of waiting for
# select() timeout (0-16ms). The socket is created in ble_setup_() and used to
# wake lwip_select() when BLE events arrive from the BLE thread.
# Note: Called during config generation, socket is created at runtime. In practice,
# always used since esp32_ble only runs on ESP32 which always has USE_SOCKET_SELECT_SUPPORT.
socket.consume_sockets(1, "esp32_ble")(config)
# Define max connections for use in C++ code (e.g., ble_server.h)
max_connections = config.get(CONF_MAX_CONNECTIONS, DEFAULT_MAX_CONNECTIONS)
cg.add_define("USE_ESP32_BLE_MAX_CONNECTIONS", max_connections)

View File

@@ -27,34 +27,10 @@ extern "C" {
#include <esp32-hal-bt.h>
#endif
#ifdef USE_SOCKET_SELECT_SUPPORT
#include <lwip/sockets.h>
#endif
namespace esphome::esp32_ble {
static const char *const TAG = "esp32_ble";
// GAP event groups for deduplication across gap_event_handler and dispatch_gap_event_
#define GAP_SCAN_COMPLETE_EVENTS \
case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: \
case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: \
case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT
#define GAP_ADV_COMPLETE_EVENTS \
case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT: \
case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT: \
case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: \
case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: \
case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT
#define GAP_SECURITY_EVENTS \
case ESP_GAP_BLE_AUTH_CMPL_EVT: \
case ESP_GAP_BLE_SEC_REQ_EVT: \
case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: \
case ESP_GAP_BLE_PASSKEY_REQ_EVT: \
case ESP_GAP_BLE_NC_REQ_EVT
void ESP32BLE::setup() {
global_ble = this;
if (!ble_pre_setup_()) {
@@ -301,21 +277,10 @@ bool ESP32BLE::ble_setup_() {
// BLE takes some time to be fully set up, 200ms should be more than enough
delay(200); // NOLINT
// Set up notification socket to wake main loop for BLE events
// This enables low-latency (~12μs) event processing instead of waiting for select() timeout
#ifdef USE_SOCKET_SELECT_SUPPORT
this->setup_event_notification_();
#endif
return true;
}
bool ESP32BLE::ble_dismantle_() {
// Clean up notification socket first before dismantling BLE stack
#ifdef USE_SOCKET_SELECT_SUPPORT
this->cleanup_event_notification_();
#endif
esp_err_t err = esp_bluedroid_disable();
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_bluedroid_disable failed: %d", err);
@@ -413,12 +378,6 @@ void ESP32BLE::loop() {
break;
}
#ifdef USE_SOCKET_SELECT_SUPPORT
// Drain any notification socket events first
// This clears the socket so it doesn't stay "ready" in subsequent select() calls
this->drain_event_notifications_();
#endif
BLEEvent *ble_event = this->ble_events_.pop();
while (ble_event != nullptr) {
switch (ble_event->type_) {
@@ -459,48 +418,60 @@ void ESP32BLE::loop() {
break;
// Scan complete events
GAP_SCAN_COMPLETE_EVENTS:
// Advertising complete events
GAP_ADV_COMPLETE_EVENTS:
// RSSI complete event
case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT:
// Security events
GAP_SECURITY_EVENTS:
case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT:
case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT:
case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT:
// All three scan complete events have the same structure with just status
// The scan_complete struct matches ESP-IDF's layout exactly, so this reinterpret_cast is safe
// This is verified at compile-time by static_assert checks in ble_event.h
// The struct already contains our copy of the status (copied in BLEEvent constructor)
ESP_LOGV(TAG, "gap_event_handler - %d", gap_event);
#ifdef ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT
{
esp_ble_gap_cb_param_t *param;
// clang-format off
switch (gap_event) {
// All three scan complete events have the same structure with just status
// The scan_complete struct matches ESP-IDF's layout exactly, so this reinterpret_cast is safe
// This is verified at compile-time by static_assert checks in ble_event.h
// The struct already contains our copy of the status (copied in BLEEvent constructor)
GAP_SCAN_COMPLETE_EVENTS:
param = reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.scan_complete);
break;
for (auto *gap_handler : this->gap_event_handlers_) {
gap_handler->gap_event_handler(
gap_event, reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.scan_complete));
}
#endif
break;
// All advertising complete events have the same structure with just status
GAP_ADV_COMPLETE_EVENTS:
param = reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.adv_complete);
break;
// Advertising complete events
case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT:
case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT:
case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
// All advertising complete events have the same structure with just status
ESP_LOGV(TAG, "gap_event_handler - %d", gap_event);
#ifdef ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT
for (auto *gap_handler : this->gap_event_handlers_) {
gap_handler->gap_event_handler(
gap_event, reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.adv_complete));
}
#endif
break;
case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT:
param = reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.read_rssi_complete);
break;
// RSSI complete event
case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT:
ESP_LOGV(TAG, "gap_event_handler - %d", gap_event);
#ifdef ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT
for (auto *gap_handler : this->gap_event_handlers_) {
gap_handler->gap_event_handler(
gap_event, reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.read_rssi_complete));
}
#endif
break;
GAP_SECURITY_EVENTS:
param = reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.security);
break;
default:
break;
}
// clang-format on
// Dispatch to all registered handlers
for (auto *gap_handler : this->gap_event_handlers_) {
gap_handler->gap_event_handler(gap_event, param);
}
// Security events
case ESP_GAP_BLE_AUTH_CMPL_EVT:
case ESP_GAP_BLE_SEC_REQ_EVT:
case ESP_GAP_BLE_PASSKEY_NOTIF_EVT:
case ESP_GAP_BLE_PASSKEY_REQ_EVT:
case ESP_GAP_BLE_NC_REQ_EVT:
ESP_LOGV(TAG, "gap_event_handler - %d", gap_event);
#ifdef ESPHOME_ESP32_BLE_GAP_EVENT_HANDLER_COUNT
for (auto *gap_handler : this->gap_event_handlers_) {
gap_handler->gap_event_handler(
gap_event, reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.security));
}
#endif
break;
@@ -580,13 +551,23 @@ void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_pa
// Queue GAP events that components need to handle
// Scanning events - used by esp32_ble_tracker
case ESP_GAP_BLE_SCAN_RESULT_EVT:
GAP_SCAN_COMPLETE_EVENTS:
case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT:
case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT:
case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT:
// Advertising events - used by esp32_ble_beacon and esp32_ble server
GAP_ADV_COMPLETE_EVENTS:
case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT:
case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT:
case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
// Connection events - used by ble_client
case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT:
// Security events - used by ble_client and bluetooth_proxy
GAP_SECURITY_EVENTS:
case ESP_GAP_BLE_AUTH_CMPL_EVT:
case ESP_GAP_BLE_SEC_REQ_EVT:
case ESP_GAP_BLE_PASSKEY_NOTIF_EVT:
case ESP_GAP_BLE_PASSKEY_REQ_EVT:
case ESP_GAP_BLE_NC_REQ_EVT:
enqueue_ble_event(event, param);
return;
@@ -607,10 +588,6 @@ void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_pa
void ESP32BLE::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t *param) {
enqueue_ble_event(event, gatts_if, param);
// Wake up main loop to process GATT event immediately
#ifdef USE_SOCKET_SELECT_SUPPORT
global_ble->notify_main_loop_();
#endif
}
#endif
@@ -618,10 +595,6 @@ void ESP32BLE::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gat
void ESP32BLE::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) {
enqueue_ble_event(event, gattc_if, param);
// Wake up main loop to process GATT event immediately
#ifdef USE_SOCKET_SELECT_SUPPORT
global_ble->notify_main_loop_();
#endif
}
#endif
@@ -661,89 +634,6 @@ void ESP32BLE::dump_config() {
}
}
#ifdef USE_SOCKET_SELECT_SUPPORT
void ESP32BLE::setup_event_notification_() {
// Create UDP socket for event notifications
this->notify_fd_ = lwip_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (this->notify_fd_ < 0) {
ESP_LOGW(TAG, "Event socket create failed: %d", errno);
return;
}
// Bind to loopback with auto-assigned port
struct sockaddr_in addr = {};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = lwip_htonl(INADDR_LOOPBACK);
addr.sin_port = 0; // Auto-assign port
if (lwip_bind(this->notify_fd_, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
ESP_LOGW(TAG, "Event socket bind failed: %d", errno);
lwip_close(this->notify_fd_);
this->notify_fd_ = -1;
return;
}
// Get the assigned address and connect to it
// Connecting a UDP socket allows using send() instead of sendto() for better performance
struct sockaddr_in notify_addr;
socklen_t len = sizeof(notify_addr);
if (lwip_getsockname(this->notify_fd_, (struct sockaddr *) &notify_addr, &len) < 0) {
ESP_LOGW(TAG, "Event socket address failed: %d", errno);
lwip_close(this->notify_fd_);
this->notify_fd_ = -1;
return;
}
// Connect to self (loopback) - allows using send() instead of sendto()
// After connect(), no need to store notify_addr - the socket remembers it
if (lwip_connect(this->notify_fd_, (struct sockaddr *) &notify_addr, sizeof(notify_addr)) < 0) {
ESP_LOGW(TAG, "Event socket connect failed: %d", errno);
lwip_close(this->notify_fd_);
this->notify_fd_ = -1;
return;
}
// Set non-blocking mode
int flags = lwip_fcntl(this->notify_fd_, F_GETFL, 0);
lwip_fcntl(this->notify_fd_, F_SETFL, flags | O_NONBLOCK);
// Register with application's select() loop
if (!App.register_socket_fd(this->notify_fd_)) {
ESP_LOGW(TAG, "Event socket register failed");
lwip_close(this->notify_fd_);
this->notify_fd_ = -1;
return;
}
ESP_LOGD(TAG, "Event socket ready");
}
void ESP32BLE::cleanup_event_notification_() {
if (this->notify_fd_ >= 0) {
App.unregister_socket_fd(this->notify_fd_);
lwip_close(this->notify_fd_);
this->notify_fd_ = -1;
ESP_LOGD(TAG, "Event socket closed");
}
}
void ESP32BLE::drain_event_notifications_() {
// Called from main loop to drain any pending notifications
// Must check is_socket_ready() to avoid blocking on empty socket
if (this->notify_fd_ >= 0 && App.is_socket_ready(this->notify_fd_)) {
char buffer[BLE_EVENT_NOTIFY_DRAIN_BUFFER_SIZE];
// Drain all pending notifications with non-blocking reads
// Multiple BLE events may have triggered multiple writes, so drain until EWOULDBLOCK
// We control both ends of this loopback socket (always write 1 byte per event),
// so no error checking needed - any errors indicate catastrophic system failure
while (lwip_recvfrom(this->notify_fd_, buffer, sizeof(buffer), 0, nullptr, nullptr) > 0) {
// Just draining, no action needed - actual BLE events are already queued
}
}
}
#endif // USE_SOCKET_SELECT_SUPPORT
uint64_t ble_addr_to_uint64(const esp_bd_addr_t address) {
uint64_t u = 0;
u |= uint64_t(address[0] & 0xFF) << 40;

View File

@@ -25,10 +25,6 @@
#include <esp_gattc_api.h>
#include <esp_gatts_api.h>
#ifdef USE_SOCKET_SELECT_SUPPORT
#include <lwip/sockets.h>
#endif
namespace esphome::esp32_ble {
// Maximum size of the BLE event queue
@@ -167,13 +163,6 @@ class ESP32BLE : public Component {
void advertising_init_();
#endif
#ifdef USE_SOCKET_SELECT_SUPPORT
void setup_event_notification_(); // Create notification socket
void cleanup_event_notification_(); // Close and unregister socket
inline void notify_main_loop_(); // Wake up select() from BLE thread (hot path - inlined)
void drain_event_notifications_(); // Read pending notifications in main loop
#endif
private:
template<typename... Args> friend void enqueue_ble_event(Args... args);
@@ -208,13 +197,6 @@ class ESP32BLE : public Component {
esp_ble_io_cap_t io_cap_{ESP_IO_CAP_NONE}; // 4 bytes (enum)
uint32_t advertising_cycle_time_{}; // 4 bytes
#ifdef USE_SOCKET_SELECT_SUPPORT
// Event notification socket for waking up main loop from BLE thread
// Uses connected UDP loopback socket to wake lwip_select() with ~12μs latency vs 0-16ms timeout
// Socket is connected during setup, allowing use of send() instead of sendto() for efficiency
int notify_fd_{-1}; // 4 bytes (file descriptor)
#endif
// 2-byte aligned members
uint16_t appearance_{0}; // 2 bytes
@@ -226,29 +208,6 @@ class ESP32BLE : public Component {
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
extern ESP32BLE *global_ble;
#ifdef USE_SOCKET_SELECT_SUPPORT
// Inline implementations for hot-path functions
// These are called from BLE thread (notify) and main loop (drain) on every event
// Small buffer for draining notification bytes (1 byte sent per BLE event)
// Size allows draining multiple notifications per recvfrom() without wasting stack
static constexpr size_t BLE_EVENT_NOTIFY_DRAIN_BUFFER_SIZE = 16;
inline void ESP32BLE::notify_main_loop_() {
// Called from BLE thread context when events are queued
// Wakes up lwip_select() in main loop by writing to connected loopback socket
if (this->notify_fd_ >= 0) {
const char dummy = 1;
// Non-blocking send - if it fails (unlikely), select() will wake on timeout anyway
// No error checking needed: we control both ends of this loopback socket, and the
// BLE event is already queued. Notification is best-effort to reduce latency.
// This is safe to call from BLE thread - send() is thread-safe in lwip
// Socket is already connected to loopback address, so send() is faster than sendto()
lwip_send(this->notify_fd_, &dummy, 1, 0);
}
}
#endif // USE_SOCKET_SELECT_SUPPORT
template<typename... Ts> class BLEEnabledCondition : public Condition<Ts...> {
public:
bool check(Ts... x) override { return global_ble->is_active(); }

View File

@@ -389,8 +389,7 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
if (this->conn_id_ != param->search_res.conn_id)
return false;
this->service_count_++;
if (this->connection_type_ == espbt::ConnectionType::V3_WITHOUT_CACHE ||
this->connection_type_ == espbt::ConnectionType::V3_WITH_CACHE) {
if (this->connection_type_ == espbt::ConnectionType::V3_WITHOUT_CACHE) {
// V3 clients don't need services initialized since
// as they use the ESP APIs to get services.
break;

View File

@@ -461,9 +461,7 @@ async def parse_value(value_config, args):
if isinstance(value, str):
value = list(value.encode(value_config[CONF_STRING_ENCODING]))
if isinstance(value, list):
# Generate initializer list {1, 2, 3} instead of std::vector<uint8_t>({1, 2, 3})
# This calls the set_value(std::initializer_list<uint8_t>) overload
return cg.ArrayInitializer(*value)
return cg.std_vector.template(cg.uint8)(value)
val = cg.RawExpression(f"{value_config[CONF_TYPE]}({cg.safe_exp(value)})")
return ByteBuffer_ns.wrap(val, value_config[CONF_ENDIANNESS])

View File

@@ -35,18 +35,13 @@ BLECharacteristic::BLECharacteristic(const ESPBTUUID uuid, uint32_t properties)
void BLECharacteristic::set_value(ByteBuffer buffer) { this->set_value(buffer.get_data()); }
void BLECharacteristic::set_value(std::vector<uint8_t> &&buffer) {
void BLECharacteristic::set_value(const std::vector<uint8_t> &buffer) {
xSemaphoreTake(this->set_value_lock_, 0L);
this->value_ = std::move(buffer);
this->value_ = buffer;
xSemaphoreGive(this->set_value_lock_);
}
void BLECharacteristic::set_value(std::initializer_list<uint8_t> data) {
this->set_value(std::vector<uint8_t>(data)); // Delegate to move overload
}
void BLECharacteristic::set_value(const std::string &buffer) {
this->set_value(std::vector<uint8_t>(buffer.begin(), buffer.end())); // Delegate to move overload
this->set_value(std::vector<uint8_t>(buffer.begin(), buffer.end()));
}
void BLECharacteristic::notify() {

View File

@@ -33,8 +33,7 @@ class BLECharacteristic {
~BLECharacteristic();
void set_value(ByteBuffer buffer);
void set_value(std::vector<uint8_t> &&buffer);
void set_value(std::initializer_list<uint8_t> data);
void set_value(const std::vector<uint8_t> &buffer);
void set_value(const std::string &buffer);
void set_broadcast_property(bool value);

View File

@@ -46,17 +46,15 @@ void BLEDescriptor::do_create(BLECharacteristic *characteristic) {
this->state_ = CREATING;
}
void BLEDescriptor::set_value(std::vector<uint8_t> &&buffer) { this->set_value_impl_(buffer.data(), buffer.size()); }
void BLEDescriptor::set_value(std::vector<uint8_t> buffer) {
size_t length = buffer.size();
void BLEDescriptor::set_value(std::initializer_list<uint8_t> data) { this->set_value_impl_(data.begin(), data.size()); }
void BLEDescriptor::set_value_impl_(const uint8_t *data, size_t length) {
if (length > this->value_.attr_max_len) {
ESP_LOGE(TAG, "Size %d too large, must be no bigger than %d", length, this->value_.attr_max_len);
return;
}
this->value_.attr_len = length;
memcpy(this->value_.attr_value, data, length);
memcpy(this->value_.attr_value, buffer.data(), length);
}
void BLEDescriptor::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,

View File

@@ -27,8 +27,7 @@ class BLEDescriptor {
void do_create(BLECharacteristic *characteristic);
ESPBTUUID get_uuid() const { return this->uuid_; }
void set_value(std::vector<uint8_t> &&buffer);
void set_value(std::initializer_list<uint8_t> data);
void set_value(std::vector<uint8_t> buffer);
void set_value(ByteBuffer buffer) { this->set_value(buffer.get_data()); }
void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
@@ -43,8 +42,6 @@ class BLEDescriptor {
}
protected:
void set_value_impl_(const uint8_t *data, size_t length);
BLECharacteristic *characteristic_{nullptr};
ESPBTUUID uuid_;
uint16_t handle_{0xFFFF};

View File

@@ -15,7 +15,10 @@ Trigger<std::vector<uint8_t>, uint16_t> *BLETriggers::create_characteristic_on_w
Trigger<std::vector<uint8_t>, uint16_t> *on_write_trigger = // NOLINT(cppcoreguidelines-owning-memory)
new Trigger<std::vector<uint8_t>, uint16_t>();
characteristic->on_write([on_write_trigger](std::span<const uint8_t> data, uint16_t id) {
// Convert span to vector for trigger
// Convert span to vector for trigger - copy is necessary because:
// 1. Trigger stores the data for use in automation actions that execute later
// 2. The span is only valid during this callback (points to temporary BLE stack data)
// 3. User lambdas in automations need persistent data they can access asynchronously
on_write_trigger->trigger(std::vector<uint8_t>(data.begin(), data.end()), id);
});
return on_write_trigger;
@@ -27,7 +30,10 @@ Trigger<std::vector<uint8_t>, uint16_t> *BLETriggers::create_descriptor_on_write
Trigger<std::vector<uint8_t>, uint16_t> *on_write_trigger = // NOLINT(cppcoreguidelines-owning-memory)
new Trigger<std::vector<uint8_t>, uint16_t>();
descriptor->on_write([on_write_trigger](std::span<const uint8_t> data, uint16_t id) {
// Convert span to vector for trigger
// Convert span to vector for trigger - copy is necessary because:
// 1. Trigger stores the data for use in automation actions that execute later
// 2. The span is only valid during this callback (points to temporary BLE stack data)
// 3. User lambdas in automations need persistent data they can access asynchronously
on_write_trigger->trigger(std::vector<uint8_t>(data.begin(), data.end()), id);
});
return on_write_trigger;

View File

@@ -112,7 +112,7 @@ async def to_code(config):
cg.add_define("USE_IMPROV")
await improv_base.setup_improv_core(var, config, "esp32_improv")
await improv_base.setup_improv_core(var, config)
cg.add(var.set_identify_duration(config[CONF_IDENTIFY_DURATION]))
cg.add(var.set_authorized_duration(config[CONF_AUTHORIZED_DURATION]))

View File

@@ -270,8 +270,8 @@ void ESP32ImprovComponent::set_error_(improv::Error error) {
}
}
void ESP32ImprovComponent::send_response_(std::vector<uint8_t> &&response) {
this->rpc_response_->set_value(std::move(response));
void ESP32ImprovComponent::send_response_(std::vector<uint8_t> &response) {
this->rpc_response_->set_value(ByteBuffer::wrap(response));
if (this->state_ != improv::STATE_STOPPED)
this->rpc_response_->notify();
}
@@ -389,13 +389,11 @@ void ESP32ImprovComponent::check_wifi_connection_() {
std::string url_strings[3];
size_t url_count = 0;
#ifdef USE_ESP32_IMPROV_NEXT_URL
// Add next_url if configured (should be first per Improv BLE spec)
std::string next_url = this->get_formatted_next_url_();
if (!next_url.empty()) {
url_strings[url_count++] = std::move(next_url);
}
#endif
// Add default URLs for backward compatibility
url_strings[url_count++] = ESPHOME_MY_LINK;
@@ -409,8 +407,10 @@ void ESP32ImprovComponent::check_wifi_connection_() {
}
}
#endif
this->send_response_(improv::build_rpc_response(improv::WIFI_SETTINGS,
std::vector<std::string>(url_strings, url_strings + url_count)));
// Pass to build_rpc_response using vector constructor from iterators to avoid extra copies
std::vector<uint8_t> data = improv::build_rpc_response(
improv::WIFI_SETTINGS, std::vector<std::string>(url_strings, url_strings + url_count));
this->send_response_(data);
} else if (this->is_active() && this->state_ != improv::STATE_PROVISIONED) {
ESP_LOGD(TAG, "WiFi provisioned externally");
}

View File

@@ -109,7 +109,7 @@ class ESP32ImprovComponent : public Component, public improv_base::ImprovBase {
void set_state_(improv::State state, bool update_advertising = true);
void set_error_(improv::Error error);
improv::State get_initial_state_() const;
void send_response_(std::vector<uint8_t> &&response);
void send_response_(std::vector<uint8_t> &response);
void process_incoming_data_();
void on_wifi_connect_timeout_();
void check_wifi_connection_();

View File

@@ -29,8 +29,8 @@ class ESP8266GPIOPin : public InternalGPIOPin {
void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
uint8_t pin_;
bool inverted_{};
gpio::Flags flags_{};
bool inverted_;
gpio::Flags flags_;
};
} // namespace esp8266

View File

@@ -165,10 +165,7 @@ async def esp8266_pin_to_code(config):
num = config[CONF_NUMBER]
mode = config[CONF_MODE]
cg.add(var.set_pin(num))
# Only set if true to avoid bloating setup() function
# (inverted bit in pin_flags_ bitfield is zero-initialized to false)
if config[CONF_INVERTED]:
cg.add(var.set_inverted(True))
cg.add(var.set_inverted(config[CONF_INVERTED]))
cg.add(var.set_flags(pins.gpio_flags_expr(mode)))
if num < 16:
initial_state: PinInitialState = CORE.data[KEY_ESP8266][KEY_PIN_INITIAL_STATES][

View File

@@ -281,15 +281,19 @@ void ESPHomeOTAComponent::handle_data_() {
#endif
// Acknowledge auth OK - 1 byte
this->write_byte_(ota::OTA_RESPONSE_AUTH_OK);
buf[0] = ota::OTA_RESPONSE_AUTH_OK;
this->writeall_(buf, 1);
// Read size, 4 bytes MSB first
if (!this->readall_(buf, 4)) {
this->log_read_error_(LOG_STR("size"));
goto error; // NOLINT(cppcoreguidelines-avoid-goto)
}
ota_size = (static_cast<size_t>(buf[0]) << 24) | (static_cast<size_t>(buf[1]) << 16) |
(static_cast<size_t>(buf[2]) << 8) | buf[3];
ota_size = 0;
for (uint8_t i = 0; i < 4; i++) {
ota_size <<= 8;
ota_size |= buf[i];
}
ESP_LOGV(TAG, "Size is %u bytes", ota_size);
// Now that we've passed authentication and are actually
@@ -309,7 +313,8 @@ void ESPHomeOTAComponent::handle_data_() {
update_started = true;
// Acknowledge prepare OK - 1 byte
this->write_byte_(ota::OTA_RESPONSE_UPDATE_PREPARE_OK);
buf[0] = ota::OTA_RESPONSE_UPDATE_PREPARE_OK;
this->writeall_(buf, 1);
// Read binary MD5, 32 bytes
if (!this->readall_(buf, 32)) {
@@ -321,7 +326,8 @@ void ESPHomeOTAComponent::handle_data_() {
this->backend_->set_update_md5(sbuf);
// Acknowledge MD5 OK - 1 byte
this->write_byte_(ota::OTA_RESPONSE_BIN_MD5_OK);
buf[0] = ota::OTA_RESPONSE_BIN_MD5_OK;
this->writeall_(buf, 1);
while (total < ota_size) {
// TODO: timeout check
@@ -348,7 +354,8 @@ void ESPHomeOTAComponent::handle_data_() {
total += read;
#if USE_OTA_VERSION == 2
while (size_acknowledged + OTA_BLOCK_SIZE <= total || (total == ota_size && size_acknowledged < ota_size)) {
this->write_byte_(ota::OTA_RESPONSE_CHUNK_OK);
buf[0] = ota::OTA_RESPONSE_CHUNK_OK;
this->writeall_(buf, 1);
size_acknowledged += OTA_BLOCK_SIZE;
}
#endif
@@ -367,7 +374,8 @@ void ESPHomeOTAComponent::handle_data_() {
}
// Acknowledge receive OK - 1 byte
this->write_byte_(ota::OTA_RESPONSE_RECEIVE_OK);
buf[0] = ota::OTA_RESPONSE_RECEIVE_OK;
this->writeall_(buf, 1);
error_code = this->backend_->end();
if (error_code != ota::OTA_RESPONSE_OK) {
@@ -376,7 +384,8 @@ void ESPHomeOTAComponent::handle_data_() {
}
// Acknowledge Update end OK - 1 byte
this->write_byte_(ota::OTA_RESPONSE_UPDATE_END_OK);
buf[0] = ota::OTA_RESPONSE_UPDATE_END_OK;
this->writeall_(buf, 1);
// Read ACK
if (!this->readall_(buf, 1) || buf[0] != ota::OTA_RESPONSE_OK) {
@@ -395,7 +404,8 @@ void ESPHomeOTAComponent::handle_data_() {
App.safe_reboot();
error:
this->write_byte_(static_cast<uint8_t>(error_code));
buf[0] = static_cast<uint8_t>(error_code);
this->writeall_(buf, 1);
this->cleanup_connection_();
if (this->backend_ != nullptr && update_started) {

View File

@@ -53,7 +53,6 @@ class ESPHomeOTAComponent : public ota::OTAComponent {
#endif // USE_OTA_PASSWORD
bool readall_(uint8_t *buf, size_t len);
bool writeall_(const uint8_t *buf, size_t len);
inline bool write_byte_(uint8_t byte) { return this->writeall_(&byte, 1); }
bool try_read_(size_t to_read, const LogString *desc);
bool try_write_(size_t to_write, const LogString *desc);

View File

@@ -14,7 +14,7 @@ from esphome.components.esp32.const import (
VARIANT_ESP32S2,
VARIANT_ESP32S3,
)
from esphome.components.network import ip_address_literal
from esphome.components.network import IPAddress
from esphome.components.spi import CONF_INTERFACE_INDEX, get_spi_interface
import esphome.config_validation as cv
from esphome.const import (
@@ -32,7 +32,6 @@ from esphome.const import (
CONF_MISO_PIN,
CONF_MODE,
CONF_MOSI_PIN,
CONF_NUMBER,
CONF_PAGE_ID,
CONF_PIN,
CONF_POLLING_INTERVAL,
@@ -53,36 +52,12 @@ from esphome.core import (
coroutine_with_priority,
)
import esphome.final_validate as fv
from esphome.types import ConfigType
CONFLICTS_WITH = ["wifi"]
DEPENDENCIES = ["esp32"]
AUTO_LOAD = ["network"]
LOGGER = logging.getLogger(__name__)
# RMII pins that are hardcoded on ESP32 classic and cannot be changed
# These pins are used by the internal Ethernet MAC when using RMII PHYs
ESP32_RMII_FIXED_PINS = {
19: "EMAC_TXD0",
21: "EMAC_TX_EN",
22: "EMAC_TXD1",
25: "EMAC_RXD0",
26: "EMAC_RXD1",
27: "EMAC_RX_CRS_DV",
}
# RMII default pins for ESP32-P4
# These are the default pins used by ESP-IDF and are configurable in principle,
# but ESPHome's ethernet component currently has no way to change them
ESP32P4_RMII_DEFAULT_PINS = {
34: "EMAC_TXD0",
35: "EMAC_TXD1",
28: "EMAC_RX_CRS_DV",
29: "EMAC_RXD0",
30: "EMAC_RXD1",
49: "EMAC_TX_EN",
}
ethernet_ns = cg.esphome_ns.namespace("ethernet")
PHYRegister = ethernet_ns.struct("PHYRegister")
CONF_PHY_ADDR = "phy_addr"
@@ -298,7 +273,7 @@ CONFIG_SCHEMA = cv.All(
)
def _final_validate_spi(config):
def _final_validate(config):
if config[CONF_TYPE] not in SPI_ETHERNET_TYPES:
return
if spi_configs := fv.full_config.get().get(CONF_SPI):
@@ -317,14 +292,17 @@ def _final_validate_spi(config):
)
FINAL_VALIDATE_SCHEMA = _final_validate
def manual_ip(config):
return cg.StructInitializer(
ManualIP,
("static_ip", ip_address_literal(config[CONF_STATIC_IP])),
("gateway", ip_address_literal(config[CONF_GATEWAY])),
("subnet", ip_address_literal(config[CONF_SUBNET])),
("dns1", ip_address_literal(config[CONF_DNS1])),
("dns2", ip_address_literal(config[CONF_DNS2])),
("static_ip", IPAddress(str(config[CONF_STATIC_IP]))),
("gateway", IPAddress(str(config[CONF_GATEWAY]))),
("subnet", IPAddress(str(config[CONF_SUBNET]))),
("dns1", IPAddress(str(config[CONF_DNS1]))),
("dns2", IPAddress(str(config[CONF_DNS2]))),
)
@@ -405,57 +383,3 @@ async def to_code(config):
if CORE.using_arduino:
cg.add_library("WiFi", None)
def _final_validate_rmii_pins(config: ConfigType) -> None:
"""Validate that RMII pins are not used by other components."""
# Only validate for RMII-based PHYs on ESP32/ESP32P4
if config[CONF_TYPE] in SPI_ETHERNET_TYPES or config[CONF_TYPE] == "OPENETH":
return # SPI and OPENETH don't use RMII
variant = get_esp32_variant()
if variant == VARIANT_ESP32:
rmii_pins = ESP32_RMII_FIXED_PINS
is_configurable = False
elif variant == VARIANT_ESP32P4:
rmii_pins = ESP32P4_RMII_DEFAULT_PINS
is_configurable = True
else:
return # No RMII validation needed for other variants
# Check all used pins against RMII reserved pins
for pin_list in pins.PIN_SCHEMA_REGISTRY.pins_used.values():
for pin_path, _, pin_config in pin_list:
pin_num = pin_config.get(CONF_NUMBER)
if pin_num not in rmii_pins:
continue
# Found a conflict - show helpful error message
pin_function = rmii_pins[pin_num]
component_path = ".".join(str(p) for p in pin_path)
if is_configurable:
error_msg = (
f"GPIO{pin_num} is used by Ethernet RMII "
f"({pin_function}) with the current default "
f"configuration. This conflicts with '{component_path}'. "
f"Please choose a different GPIO pin for "
f"'{component_path}'."
)
else:
error_msg = (
f"GPIO{pin_num} is reserved for Ethernet RMII "
f"({pin_function}) and cannot be used. This pin is "
f"hardcoded by ESP-IDF and cannot be changed when using "
f"RMII Ethernet PHYs. Please choose a different GPIO pin "
f"for '{component_path}'."
)
raise cv.Invalid(error_msg, path=pin_path)
def _final_validate(config: ConfigType) -> ConfigType:
"""Final validation for Ethernet component."""
_final_validate_spi(config)
_final_validate_rmii_pins(config)
return config
FINAL_VALIDATE_SCHEMA = _final_validate

View File

@@ -85,6 +85,11 @@ def event_schema(
return _EVENT_SCHEMA.extend(schema)
# Remove before 2025.11.0
EVENT_SCHEMA = event_schema()
EVENT_SCHEMA.add_extra(cv.deprecated_schema_constant("event"))
async def setup_event_core_(var, config, *, event_types: list[str]):
await setup_entity(var, config, "event")

View File

@@ -8,19 +8,12 @@ namespace event {
static const char *const TAG = "event";
void Event::trigger(const std::string &event_type) {
// Linear search - faster than std::set for small datasets (1-5 items typical)
const std::string *found = nullptr;
for (const auto &type : this->types_) {
if (type == event_type) {
found = &type;
break;
}
}
if (found == nullptr) {
auto found = types_.find(event_type);
if (found == types_.end()) {
ESP_LOGE(TAG, "'%s': invalid event type for trigger(): %s", this->get_name().c_str(), event_type.c_str());
return;
}
last_event_type = found;
last_event_type = &(*found);
ESP_LOGD(TAG, "'%s' Triggered event '%s'", this->get_name().c_str(), last_event_type->c_str());
this->event_callback_.call(event_type);
}

View File

@@ -1,5 +1,6 @@
#pragma once
#include <set>
#include <string>
#include "esphome/core/component.h"
@@ -25,13 +26,13 @@ class Event : public EntityBase, public EntityBase_DeviceClass {
const std::string *last_event_type;
void trigger(const std::string &event_type);
void set_event_types(const std::initializer_list<std::string> &event_types) { this->types_ = event_types; }
const FixedVector<std::string> &get_event_types() const { return this->types_; }
void set_event_types(const std::set<std::string> &event_types) { this->types_ = event_types; }
std::set<std::string> get_event_types() const { return this->types_; }
void add_on_event_callback(std::function<void(const std::string &event_type)> &&callback);
protected:
CallbackManager<void(const std::string &event_type)> event_callback_;
FixedVector<std::string> types_;
std::set<std::string> types_;
};
} // namespace event

View File

@@ -189,6 +189,10 @@ def fan_schema(
return _FAN_SCHEMA.extend(schema)
# Remove before 2025.11.0
FAN_SCHEMA = fan_schema(Fan)
FAN_SCHEMA.add_extra(cv.deprecated_schema_constant("fan"))
_PRESET_MODES_SCHEMA = cv.All(
cv.ensure_list(cv.string_strict),
cv.Length(min=1),

View File

@@ -212,18 +212,18 @@ class FanPresetSetTrigger : public Trigger<std::string> {
public:
FanPresetSetTrigger(Fan *state) {
state->add_on_state_callback([this, state]() {
const auto *preset_mode = state->get_preset_mode();
auto preset_mode = state->preset_mode;
auto should_trigger = preset_mode != this->last_preset_mode_;
this->last_preset_mode_ = preset_mode;
if (should_trigger && preset_mode != nullptr) {
if (should_trigger) {
this->trigger(preset_mode);
}
});
this->last_preset_mode_ = state->get_preset_mode();
this->last_preset_mode_ = state->preset_mode;
}
protected:
const char *last_preset_mode_{nullptr};
std::string last_preset_mode_;
};
} // namespace fan

View File

@@ -17,27 +17,6 @@ const LogString *fan_direction_to_string(FanDirection direction) {
}
}
FanCall &FanCall::set_preset_mode(const std::string &preset_mode) { return this->set_preset_mode(preset_mode.c_str()); }
FanCall &FanCall::set_preset_mode(const char *preset_mode) {
if (preset_mode == nullptr || strlen(preset_mode) == 0) {
this->preset_mode_ = nullptr;
return *this;
}
// Find and validate pointer from traits immediately
auto traits = this->parent_.get_traits();
const char *validated_mode = traits.find_preset_mode(preset_mode);
if (validated_mode != nullptr) {
this->preset_mode_ = validated_mode; // Store pointer from traits
} else {
// Preset mode not found in traits - log warning and don't set
ESP_LOGW(TAG, "%s: Preset mode '%s' not supported", this->parent_.get_name().c_str(), preset_mode);
this->preset_mode_ = nullptr;
}
return *this;
}
void FanCall::perform() {
ESP_LOGD(TAG, "'%s' - Setting:", this->parent_.get_name().c_str());
this->validate_();
@@ -53,8 +32,8 @@ void FanCall::perform() {
if (this->direction_.has_value()) {
ESP_LOGD(TAG, " Direction: %s", LOG_STR_ARG(fan_direction_to_string(*this->direction_)));
}
if (this->has_preset_mode()) {
ESP_LOGD(TAG, " Preset Mode: %s", this->preset_mode_);
if (!this->preset_mode_.empty()) {
ESP_LOGD(TAG, " Preset Mode: %s", this->preset_mode_.c_str());
}
this->parent_.control(*this);
}
@@ -67,15 +46,23 @@ void FanCall::validate_() {
// https://developers.home-assistant.io/docs/core/entity/fan/#preset-modes
// "Manually setting a speed must disable any set preset mode"
this->preset_mode_ = nullptr;
this->preset_mode_.clear();
}
if (!this->preset_mode_.empty()) {
const auto &preset_modes = traits.supported_preset_modes();
if (preset_modes.find(this->preset_mode_) == preset_modes.end()) {
ESP_LOGW(TAG, "%s: Preset mode '%s' not supported", this->parent_.get_name().c_str(), this->preset_mode_.c_str());
this->preset_mode_.clear();
}
}
// when turning on...
if (!this->parent_.state && this->binary_state_.has_value() &&
*this->binary_state_
// ..,and no preset mode will be active...
&& !this->has_preset_mode() &&
this->parent_.get_preset_mode() == nullptr
&& this->preset_mode_.empty() &&
this->parent_.preset_mode.empty()
// ...and neither current nor new speed is available...
&& traits.supports_speed() && this->parent_.speed == 0 && !this->speed_.has_value()) {
// ...set speed to 100%
@@ -105,12 +92,11 @@ FanCall FanRestoreState::to_call(Fan &fan) {
call.set_speed(this->speed);
call.set_direction(this->direction);
auto traits = fan.get_traits();
if (traits.supports_preset_modes()) {
if (fan.get_traits().supports_preset_modes()) {
// Use stored preset index to get preset name
const auto &preset_modes = traits.supported_preset_modes();
const auto &preset_modes = fan.get_traits().supported_preset_modes();
if (this->preset_mode < preset_modes.size()) {
call.set_preset_mode(preset_modes[this->preset_mode]);
call.set_preset_mode(*std::next(preset_modes.begin(), this->preset_mode));
}
}
return call;
@@ -121,12 +107,13 @@ void FanRestoreState::apply(Fan &fan) {
fan.speed = this->speed;
fan.direction = this->direction;
// Use stored preset index to get preset name from traits
const auto &preset_modes = fan.get_traits().supported_preset_modes();
if (this->preset_mode < preset_modes.size()) {
fan.set_preset_mode_(preset_modes[this->preset_mode]);
if (fan.get_traits().supports_preset_modes()) {
// Use stored preset index to get preset name
const auto &preset_modes = fan.get_traits().supported_preset_modes();
if (this->preset_mode < preset_modes.size()) {
fan.preset_mode = *std::next(preset_modes.begin(), this->preset_mode);
}
}
fan.publish_state();
}
@@ -135,29 +122,6 @@ FanCall Fan::turn_off() { return this->make_call().set_state(false); }
FanCall Fan::toggle() { return this->make_call().set_state(!this->state); }
FanCall Fan::make_call() { return FanCall(*this); }
const char *Fan::find_preset_mode_(const char *preset_mode) { return this->get_traits().find_preset_mode(preset_mode); }
bool Fan::set_preset_mode_(const char *preset_mode) {
if (preset_mode == nullptr) {
// Treat nullptr as clearing the preset mode
if (this->preset_mode_ == nullptr) {
return false; // No change
}
this->clear_preset_mode_();
return true;
}
const char *validated = this->find_preset_mode_(preset_mode);
if (validated == nullptr || this->preset_mode_ == validated) {
return false; // Preset mode not supported or no change
}
this->preset_mode_ = validated;
return true;
}
bool Fan::set_preset_mode_(const std::string &preset_mode) { return this->set_preset_mode_(preset_mode.c_str()); }
void Fan::clear_preset_mode_() { this->preset_mode_ = nullptr; }
void Fan::add_on_state_callback(std::function<void()> &&callback) { this->state_callback_.add(std::move(callback)); }
void Fan::publish_state() {
auto traits = this->get_traits();
@@ -173,9 +137,8 @@ void Fan::publish_state() {
if (traits.supports_direction()) {
ESP_LOGD(TAG, " Direction: %s", LOG_STR_ARG(fan_direction_to_string(this->direction)));
}
const char *preset = this->get_preset_mode();
if (traits.supports_preset_modes() && preset != nullptr) {
ESP_LOGD(TAG, " Preset Mode: %s", preset);
if (traits.supports_preset_modes() && !this->preset_mode.empty()) {
ESP_LOGD(TAG, " Preset Mode: %s", this->preset_mode.c_str());
}
this->state_callback_.call();
this->save_state_();
@@ -219,24 +182,18 @@ void Fan::save_state_() {
return;
}
auto traits = this->get_traits();
FanRestoreState state{};
state.state = this->state;
state.oscillating = this->oscillating;
state.speed = this->speed;
state.direction = this->direction;
const char *preset = this->get_preset_mode();
if (preset != nullptr) {
const auto &preset_modes = traits.supported_preset_modes();
// Find index of current preset mode (pointer comparison is safe since preset is from traits)
for (size_t i = 0; i < preset_modes.size(); i++) {
if (preset_modes[i] == preset) {
state.preset_mode = i;
break;
}
}
if (this->get_traits().supports_preset_modes() && !this->preset_mode.empty()) {
const auto &preset_modes = this->get_traits().supported_preset_modes();
// Store index of current preset mode
auto preset_iterator = preset_modes.find(this->preset_mode);
if (preset_iterator != preset_modes.end())
state.preset_mode = std::distance(preset_modes.begin(), preset_iterator);
}
this->rtc_.save(&state);
@@ -259,8 +216,8 @@ void Fan::dump_traits_(const char *tag, const char *prefix) {
}
if (traits.supports_preset_modes()) {
ESP_LOGCONFIG(tag, "%s Supported presets:", prefix);
for (const char *s : traits.supported_preset_modes())
ESP_LOGCONFIG(tag, "%s - %s", prefix, s);
for (const std::string &s : traits.supported_preset_modes())
ESP_LOGCONFIG(tag, "%s - %s", prefix, s.c_str());
}
}

View File

@@ -60,6 +60,8 @@ class FanCall {
this->speed_ = speed;
return *this;
}
ESPDEPRECATED("set_speed() with string argument is deprecated, use integer argument instead.", "2021.9")
FanCall &set_speed(const char *legacy_speed);
optional<int> get_speed() const { return this->speed_; }
FanCall &set_direction(FanDirection direction) {
this->direction_ = direction;
@@ -70,10 +72,11 @@ class FanCall {
return *this;
}
optional<FanDirection> get_direction() const { return this->direction_; }
FanCall &set_preset_mode(const std::string &preset_mode);
FanCall &set_preset_mode(const char *preset_mode);
const char *get_preset_mode() const { return this->preset_mode_; }
bool has_preset_mode() const { return this->preset_mode_ != nullptr; }
FanCall &set_preset_mode(const std::string &preset_mode) {
this->preset_mode_ = preset_mode;
return *this;
}
std::string get_preset_mode() const { return this->preset_mode_; }
void perform();
@@ -85,7 +88,7 @@ class FanCall {
optional<bool> oscillating_;
optional<int> speed_;
optional<FanDirection> direction_{};
const char *preset_mode_{nullptr}; // Pointer to string in traits (after validation)
std::string preset_mode_{};
};
struct FanRestoreState {
@@ -111,6 +114,8 @@ class Fan : public EntityBase {
int speed{0};
/// The current direction of the fan
FanDirection direction{FanDirection::FORWARD};
// The current preset mode of the fan
std::string preset_mode{};
FanCall turn_on();
FanCall turn_off();
@@ -127,15 +132,8 @@ class Fan : public EntityBase {
/// Set the restore mode of this fan.
void set_restore_mode(FanRestoreMode restore_mode) { this->restore_mode_ = restore_mode; }
/// Get the current preset mode (returns pointer to string stored in traits, or nullptr if not set)
const char *get_preset_mode() const { return this->preset_mode_; }
/// Check if a preset mode is currently active
bool has_preset_mode() const { return this->preset_mode_ != nullptr; }
protected:
friend FanCall;
friend struct FanRestoreState;
virtual void control(const FanCall &call) = 0;
@@ -144,19 +142,9 @@ class Fan : public EntityBase {
void dump_traits_(const char *tag, const char *prefix);
/// Set the preset mode (finds and stores pointer from traits). Returns true if changed.
bool set_preset_mode_(const char *preset_mode);
/// Set the preset mode (finds and stores pointer from traits). Returns true if changed.
bool set_preset_mode_(const std::string &preset_mode);
/// Clear the preset mode
void clear_preset_mode_();
/// Find and return the matching preset mode pointer from traits, or nullptr if not found.
const char *find_preset_mode_(const char *preset_mode);
CallbackManager<void()> state_callback_{};
ESPPreferenceObject rtc_;
FanRestoreMode restore_mode_;
const char *preset_mode_{nullptr};
};
} // namespace fan

View File

@@ -1,11 +1,16 @@
#include <set>
#include <utility>
#pragma once
#include <cstring>
#include <vector>
#include <initializer_list>
namespace esphome {
#ifdef USE_API
namespace api {
class APIConnection;
} // namespace api
#endif
namespace fan {
class FanTraits {
@@ -31,38 +36,27 @@ class FanTraits {
/// Set whether this fan supports changing direction
void set_direction(bool direction) { this->direction_ = direction; }
/// Return the preset modes supported by the fan.
const std::vector<const char *> &supported_preset_modes() const { return this->preset_modes_; }
/// Set the preset modes supported by the fan (from initializer list).
void set_supported_preset_modes(std::initializer_list<const char *> preset_modes) {
this->preset_modes_ = preset_modes;
}
/// Set the preset modes supported by the fan (from vector).
void set_supported_preset_modes(const std::vector<const char *> &preset_modes) { this->preset_modes_ = preset_modes; }
// Deleted overloads to catch incorrect std::string usage at compile time with clear error messages
void set_supported_preset_modes(const std::vector<std::string> &preset_modes) = delete;
void set_supported_preset_modes(std::initializer_list<std::string> preset_modes) = delete;
std::set<std::string> supported_preset_modes() const { return this->preset_modes_; }
/// Set the preset modes supported by the fan.
void set_supported_preset_modes(const std::set<std::string> &preset_modes) { this->preset_modes_ = preset_modes; }
/// Return if preset modes are supported
bool supports_preset_modes() const { return !this->preset_modes_.empty(); }
/// Find and return the matching preset mode pointer from supported modes, or nullptr if not found.
const char *find_preset_mode(const char *preset_mode) const {
if (preset_mode == nullptr)
return nullptr;
for (const char *mode : this->preset_modes_) {
if (strcmp(mode, preset_mode) == 0) {
return mode; // Return pointer from traits
}
}
return nullptr;
}
protected:
#ifdef USE_API
// The API connection is a friend class to access internal methods
friend class api::APIConnection;
// This method returns a reference to the internal preset modes set.
// It is used by the API to avoid copying data when encoding messages.
// Warning: Do not use this method outside of the API connection code.
// It returns a reference to internal data that can be invalidated.
const std::set<std::string> &supported_preset_modes_for_api_() const { return this->preset_modes_; }
#endif
bool oscillation_{false};
bool speed_{false};
bool direction_{false};
int speed_count_{};
std::vector<const char *> preset_modes_{};
std::set<std::string> preset_modes_{};
};
} // namespace fan

View File

@@ -94,8 +94,6 @@ async def to_code(config):
)
use_interrupt = False
cg.add(var.set_use_interrupt(use_interrupt))
if use_interrupt:
cg.add(var.set_interrupt_type(config[CONF_INTERRUPT_TYPE]))
else:
# Only generate call when disabling interrupts (default is true)
cg.add(var.set_use_interrupt(use_interrupt))

View File

@@ -67,7 +67,7 @@ void GPIOSwitch::write_state(bool state) {
this->pin_->digital_write(state);
this->publish_state(state);
}
void GPIOSwitch::set_interlock(const std::initializer_list<Switch *> &interlock) { this->interlock_ = interlock; }
void GPIOSwitch::set_interlock(const std::vector<Switch *> &interlock) { this->interlock_ = interlock; }
} // namespace gpio
} // namespace esphome

View File

@@ -2,9 +2,10 @@
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/components/switch/switch.h"
#include <vector>
namespace esphome {
namespace gpio {
@@ -18,14 +19,14 @@ class GPIOSwitch : public switch_::Switch, public Component {
void setup() override;
void dump_config() override;
void set_interlock(const std::initializer_list<Switch *> &interlock);
void set_interlock(const std::vector<Switch *> &interlock);
void set_interlock_wait_time(uint32_t interlock_wait_time) { interlock_wait_time_ = interlock_wait_time; }
protected:
void write_state(bool state) override;
GPIOPin *pin_;
FixedVector<Switch *> interlock_;
std::vector<Switch *> interlock_;
uint32_t interlock_wait_time_{0};
};

View File

@@ -171,7 +171,7 @@ void HaierClimateBase::toggle_power() {
PendingAction({ActionRequest::TOGGLE_POWER, esphome::optional<haier_protocol::HaierMessage>()});
}
void HaierClimateBase::set_supported_swing_modes(climate::ClimateSwingModeMask modes) {
void HaierClimateBase::set_supported_swing_modes(const std::set<climate::ClimateSwingMode> &modes) {
this->traits_.set_supported_swing_modes(modes);
if (!modes.empty())
this->traits_.add_supported_swing_mode(climate::CLIMATE_SWING_OFF);
@@ -179,13 +179,13 @@ void HaierClimateBase::set_supported_swing_modes(climate::ClimateSwingModeMask m
void HaierClimateBase::set_answer_timeout(uint32_t timeout) { this->haier_protocol_.set_answer_timeout(timeout); }
void HaierClimateBase::set_supported_modes(climate::ClimateModeMask modes) {
void HaierClimateBase::set_supported_modes(const std::set<climate::ClimateMode> &modes) {
this->traits_.set_supported_modes(modes);
this->traits_.add_supported_mode(climate::CLIMATE_MODE_OFF); // Always available
this->traits_.add_supported_mode(climate::CLIMATE_MODE_HEAT_COOL); // Always available
}
void HaierClimateBase::set_supported_presets(climate::ClimatePresetMask presets) {
void HaierClimateBase::set_supported_presets(const std::set<climate::ClimatePreset> &presets) {
this->traits_.set_supported_presets(presets);
if (!presets.empty())
this->traits_.add_supported_preset(climate::CLIMATE_PRESET_NONE);

View File

@@ -1,6 +1,7 @@
#pragma once
#include <chrono>
#include <set>
#include "esphome/components/climate/climate.h"
#include "esphome/components/uart/uart.h"
#include "esphome/core/automation.h"
@@ -59,9 +60,9 @@ class HaierClimateBase : public esphome::Component,
void send_power_off_command();
void toggle_power();
void reset_protocol() { this->reset_protocol_request_ = true; };
void set_supported_modes(esphome::climate::ClimateModeMask modes);
void set_supported_swing_modes(esphome::climate::ClimateSwingModeMask modes);
void set_supported_presets(esphome::climate::ClimatePresetMask presets);
void set_supported_modes(const std::set<esphome::climate::ClimateMode> &modes);
void set_supported_swing_modes(const std::set<esphome::climate::ClimateSwingMode> &modes);
void set_supported_presets(const std::set<esphome::climate::ClimatePreset> &presets);
bool valid_connection() const { return this->protocol_phase_ >= ProtocolPhases::IDLE; };
size_t available() noexcept override { return esphome::uart::UARTDevice::available(); };
size_t read_array(uint8_t *data, size_t len) noexcept override {

View File

@@ -1033,9 +1033,9 @@ haier_protocol::HandlerError HonClimate::process_status_message_(const uint8_t *
{
// Swing mode
ClimateSwingMode old_swing_mode = this->swing_mode;
const auto &swing_modes = traits_.get_supported_swing_modes();
bool vertical_swing_supported = swing_modes.count(CLIMATE_SWING_VERTICAL);
bool horizontal_swing_supported = swing_modes.count(CLIMATE_SWING_HORIZONTAL);
const std::set<ClimateSwingMode> &swing_modes = traits_.get_supported_swing_modes();
bool vertical_swing_supported = swing_modes.find(CLIMATE_SWING_VERTICAL) != swing_modes.end();
bool horizontal_swing_supported = swing_modes.find(CLIMATE_SWING_HORIZONTAL) != swing_modes.end();
if (horizontal_swing_supported &&
(packet.control.horizontal_swing_mode == (uint8_t) hon_protocol::HorizontalSwingMode::AUTO)) {
if (vertical_swing_supported &&
@@ -1218,13 +1218,13 @@ void HonClimate::fill_control_messages_queue_() {
(uint8_t) hon_protocol::DataParameters::QUIET_MODE,
quiet_mode_buf, 2);
}
if ((fast_mode_buf[1] != 0xFF) && presets.count(climate::ClimatePreset::CLIMATE_PRESET_BOOST)) {
if ((fast_mode_buf[1] != 0xFF) && ((presets.find(climate::ClimatePreset::CLIMATE_PRESET_BOOST) != presets.end()))) {
this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL,
(uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
(uint8_t) hon_protocol::DataParameters::FAST_MODE,
fast_mode_buf, 2);
}
if ((away_mode_buf[1] != 0xFF) && presets.count(climate::ClimatePreset::CLIMATE_PRESET_AWAY)) {
if ((away_mode_buf[1] != 0xFF) && ((presets.find(climate::ClimatePreset::CLIMATE_PRESET_AWAY) != presets.end()))) {
this->control_messages_queue_.emplace(haier_protocol::FrameType::CONTROL,
(uint16_t) hon_protocol::SubcommandsControl::SET_SINGLE_PARAMETER +
(uint8_t) hon_protocol::DataParameters::TEN_DEGREE,

View File

@@ -57,7 +57,7 @@ void HBridgeFan::control(const fan::FanCall &call) {
this->oscillating = *call.get_oscillating();
if (call.get_direction().has_value())
this->direction = *call.get_direction();
this->set_preset_mode_(call.get_preset_mode());
this->preset_mode = call.get_preset_mode();
this->write_state_();
this->publish_state();

View File

@@ -1,5 +1,7 @@
#pragma once
#include <set>
#include "esphome/core/automation.h"
#include "esphome/components/output/binary_output.h"
#include "esphome/components/output/float_output.h"
@@ -20,7 +22,7 @@ class HBridgeFan : public Component, public fan::Fan {
void set_pin_a(output::FloatOutput *pin_a) { pin_a_ = pin_a; }
void set_pin_b(output::FloatOutput *pin_b) { pin_b_ = pin_b; }
void set_enable_pin(output::FloatOutput *enable) { enable_ = enable; }
void set_preset_modes(std::initializer_list<const char *> presets) { preset_modes_ = presets; }
void set_preset_modes(const std::set<std::string> &presets) { preset_modes_ = presets; }
void setup() override;
void dump_config() override;
@@ -36,7 +38,7 @@ class HBridgeFan : public Component, public fan::Fan {
int speed_count_{};
DecayMode decay_mode_{DECAY_MODE_SLOW};
fan::FanTraits traits_;
std::vector<const char *> preset_modes_{};
std::set<std::string> preset_modes_{};
void control(const fan::FanCall &call) override;
void write_state_();

View File

@@ -1 +0,0 @@
CODEOWNERS = ["@optimusprimespace", "@ssieb"]

View File

@@ -1,111 +0,0 @@
#include "esphome/core/hal.h"
#include "hdc2010.h"
// https://github.com/vigsterkr/homebridge-hdc2010/blob/main/src/hdc2010.js
// https://github.com/lime-labs/HDC2080-Arduino/blob/master/src/HDC2080.cpp
namespace esphome {
namespace hdc2010 {
static const char *const TAG = "hdc2010";
static const uint8_t HDC2010_ADDRESS = 0x40; // 0b1000000 or 0b1000001 from datasheet
static const uint8_t HDC2010_CMD_CONFIGURATION_MEASUREMENT = 0x8F;
static const uint8_t HDC2010_CMD_START_MEASUREMENT = 0xF9;
static const uint8_t HDC2010_CMD_TEMPERATURE_LOW = 0x00;
static const uint8_t HDC2010_CMD_TEMPERATURE_HIGH = 0x01;
static const uint8_t HDC2010_CMD_HUMIDITY_LOW = 0x02;
static const uint8_t HDC2010_CMD_HUMIDITY_HIGH = 0x03;
static const uint8_t CONFIG = 0x0E;
static const uint8_t MEASUREMENT_CONFIG = 0x0F;
void HDC2010Component::setup() {
ESP_LOGCONFIG(TAG, "Running setup");
const uint8_t data[2] = {
0b00000000, // resolution 14bit for both humidity and temperature
0b00000000 // reserved
};
if (!this->write_bytes(HDC2010_CMD_CONFIGURATION_MEASUREMENT, data, 2)) {
ESP_LOGW(TAG, "Initial config instruction error");
this->status_set_warning();
return;
}
// Set measurement mode to temperature and humidity
uint8_t config_contents;
this->read_register(MEASUREMENT_CONFIG, &config_contents, 1);
config_contents = (config_contents & 0xF9); // Always set to TEMP_AND_HUMID mode
this->write_bytes(MEASUREMENT_CONFIG, &config_contents, 1);
// Set rate to manual
this->read_register(CONFIG, &config_contents, 1);
config_contents &= 0x8F;
this->write_bytes(CONFIG, &config_contents, 1);
// Set temperature resolution to 14bit
this->read_register(CONFIG, &config_contents, 1);
config_contents &= 0x3F;
this->write_bytes(CONFIG, &config_contents, 1);
// Set humidity resolution to 14bit
this->read_register(CONFIG, &config_contents, 1);
config_contents &= 0xCF;
this->write_bytes(CONFIG, &config_contents, 1);
}
void HDC2010Component::dump_config() {
ESP_LOGCONFIG(TAG, "HDC2010:");
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
}
LOG_UPDATE_INTERVAL(this);
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
}
void HDC2010Component::update() {
// Trigger measurement
uint8_t config_contents;
this->read_register(CONFIG, &config_contents, 1);
config_contents |= 0x01;
this->write_bytes(MEASUREMENT_CONFIG, &config_contents, 1);
// 1ms delay after triggering the sample
set_timeout(1, [this]() {
if (this->temperature_sensor_ != nullptr) {
float temp = this->read_temp();
this->temperature_sensor_->publish_state(temp);
ESP_LOGD(TAG, "Temp=%.1f°C", temp);
}
if (this->humidity_sensor_ != nullptr) {
float humidity = this->read_humidity();
this->humidity_sensor_->publish_state(humidity);
ESP_LOGD(TAG, "Humidity=%.1f%%", humidity);
}
});
}
float HDC2010Component::read_temp() {
uint8_t byte[2];
this->read_register(HDC2010_CMD_TEMPERATURE_LOW, &byte[0], 1);
this->read_register(HDC2010_CMD_TEMPERATURE_HIGH, &byte[1], 1);
uint16_t temp = encode_uint16(byte[1], byte[0]);
return (float) temp * 0.0025177f - 40.0f;
}
float HDC2010Component::read_humidity() {
uint8_t byte[2];
this->read_register(HDC2010_CMD_HUMIDITY_LOW, &byte[0], 1);
this->read_register(HDC2010_CMD_HUMIDITY_HIGH, &byte[1], 1);
uint16_t humidity = encode_uint16(byte[1], byte[0]);
return (float) humidity * 0.001525879f;
}
} // namespace hdc2010
} // namespace esphome

View File

@@ -1,32 +0,0 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h"
namespace esphome {
namespace hdc2010 {
class HDC2010Component : public PollingComponent, public i2c::I2CDevice {
public:
void set_temperature_sensor(sensor::Sensor *temperature) { this->temperature_sensor_ = temperature; }
void set_humidity_sensor(sensor::Sensor *humidity) { this->humidity_sensor_ = humidity; }
/// Setup the sensor and check for connection.
void setup() override;
void dump_config() override;
/// Retrieve the latest sensor values. This operation takes approximately 16ms.
void update() override;
float read_temp();
float read_humidity();
protected:
sensor::Sensor *temperature_sensor_{nullptr};
sensor::Sensor *humidity_sensor_{nullptr};
};
} // namespace hdc2010
} // namespace esphome

View File

@@ -1,56 +0,0 @@
import esphome.codegen as cg
from esphome.components import i2c, sensor
import esphome.config_validation as cv
from esphome.const import (
CONF_HUMIDITY,
CONF_ID,
CONF_TEMPERATURE,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
UNIT_PERCENT,
)
DEPENDENCIES = ["i2c"]
hdc2010_ns = cg.esphome_ns.namespace("hdc2010")
HDC2010Component = hdc2010_ns.class_(
"HDC2010Component", cg.PollingComponent, i2c.I2CDevice
)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(HDC2010Component),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=0,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x40))
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
if temperature_config := config.get(CONF_TEMPERATURE):
sens = await sensor.new_sensor(temperature_config)
cg.add(var.set_temperature_sensor(sens))
if humidity_config := config.get(CONF_HUMIDITY):
sens = await sensor.new_sensor(humidity_config)
cg.add(var.set_humidity_sensor(sens))

View File

@@ -97,11 +97,12 @@ const float TEMP_MAX = 100; // Celsius
class HeatpumpIRClimate : public climate_ir::ClimateIR {
public:
HeatpumpIRClimate()
: climate_ir::ClimateIR(TEMP_MIN, TEMP_MAX, 1.0f, true, true,
{climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM, climate::CLIMATE_FAN_HIGH,
climate::CLIMATE_FAN_AUTO},
{climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_HORIZONTAL,
climate::CLIMATE_SWING_VERTICAL, climate::CLIMATE_SWING_BOTH}) {}
: climate_ir::ClimateIR(
TEMP_MIN, TEMP_MAX, 1.0f, true, true,
std::set<climate::ClimateFanMode>{climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM,
climate::CLIMATE_FAN_HIGH, climate::CLIMATE_FAN_AUTO},
std::set<climate::ClimateSwingMode>{climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_HORIZONTAL,
climate::CLIMATE_SWING_VERTICAL, climate::CLIMATE_SWING_BOTH}) {}
void setup() override;
void set_protocol(Protocol protocol) { this->protocol_ = protocol; }
void set_horizontal_default(HorizontalDirection horizontal_direction) {

View File

@@ -28,8 +28,8 @@ class HostGPIOPin : public InternalGPIOPin {
void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
uint8_t pin_;
bool inverted_{};
gpio::Flags flags_{};
bool inverted_;
gpio::Flags flags_;
};
} // namespace host

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