1
0
mirror of https://github.com/esphome/esphome.git synced 2025-06-15 14:56:59 +02:00

Compare commits

...

268 Commits

Author SHA1 Message Date
J. Nick Koston
07cf6e723b
Fix unbound BLE event queue growth and reduce memory usage (#9052) 2025-06-15 04:45:41 +00:00
J. Nick Koston
78e3c6333f
Optimize Application area_ from std::string to const char* (#9085) 2025-06-14 22:46:40 -05:00
J. Nick Koston
98e2684107
Fix API message encoding to return actual size instead of calculated size (#9073) 2025-06-15 15:46:02 +12:00
J. Nick Koston
cb019fff9a
Optimize memory usage by lazy-allocating raw callbacks in sensors (#9077) 2025-06-15 15:28:15 +12:00
J. Nick Koston
4305c44440
Reduce entity memory usage by eliminating field shadowing and bit-packing (#9076) 2025-06-15 15:21:55 +12:00
J. Nick Koston
a1e4143600
Small optimizations to api buffer helper (#9071) 2025-06-15 14:55:03 +12:00
J. Nick Koston
374c33e8dc
Optimize Component and Application state storage from uint32_t to uint8_t (#9082) 2025-06-15 14:48:53 +12:00
J. Nick Koston
dcfe7af9d3
Make ParseOnOffState enum uint8_t (#9083) 2025-06-15 14:44:45 +12:00
Keith Burzinski
049c7e00ca
Move some consts to `const.py` (#9084) 2025-06-14 23:23:52 +00:00
Jimmy Hedman
ee37d2f9c8
Build with C++17 (#8603)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-06-14 08:21:39 -05:00
J. Nick Koston
92ea697119
Fix captive_portal loading entire web_server (#9066) 2025-06-14 08:19:41 -05:00
dependabot[bot]
1c488d375f
Bump pytest-asyncio from 0.26.0 to 1.0.0 (#9067)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-13 18:40:18 -05:00
Jesse Hills
1a03b4949f
[esp32] Dynamically set default framework based on variant (#9060) 2025-06-14 11:17:06 +12:00
Jesse Hills
731b7808cd
[prometheus] Remove `cv.only_with_arduino` (#9061) 2025-06-14 11:08:07 +12:00
J. Nick Koston
d9da4cf24d
Fix misleading comment in API (#9069) 2025-06-14 09:10:33 +12:00
Nate Clark
666a3ee5e9
Fix BYPASS_AUTO feature to work with or without an arming delay (#9051) 2025-06-13 13:31:00 -05:00
Nico B
02469c2d4c
ina219: powerdown the sensor on shutdown (#9053) 2025-06-13 18:17:38 +00:00
Edward Firmo
2a629cae93
[nextion] Remove upload flags reset from success path to prevent TFT corruption (#9064) 2025-06-13 13:39:32 +12:00
dependabot[bot]
1f14c316a3
Bump pytest-cov from 6.1.1 to 6.2.1 (#9063)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-12 18:16:37 -05:00
J. Nick Koston
dac738a916
Always perform select() when loop duration exceeds interval (#9058) 2025-06-12 03:27:10 +00:00
Clyde Stubbs
261b561bb2
[binary_sensor] Add action to invalidate state and pass to HA (#8961)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-06-12 09:16:20 +10:00
J. Nick Koston
0228379a2e
Fix dashboard logging being escaped before parser (#9054) 2025-06-11 16:17:47 -05:00
Jesse Hills
da79215bc3
Merge branch 'beta' into dev 2025-06-12 07:56:24 +12:00
Jesse Hills
44323dc285
Merge pull request #9049 from esphome/bump-2025.6.0b1
2025.6.0b1
2025-06-12 07:55:49 +12:00
Thomas Rupprecht
a59e1c7011
[core/pins] improve pins types (#8848)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-06-11 18:06:41 +00:00
Jesse Hills
abb4d991ad
Bump version to 2025.6.0b1 2025-06-11 23:16:56 +12:00
Jesse Hills
f467c79a20
Bump version to 2025.7.0-dev 2025-06-11 23:16:56 +12:00
Keith Burzinski
dcf41db878
[sgp4x] Shorten log messages, various clean-up (#9048) 2025-06-11 11:11:11 +00:00
Jesse Hills
c3c3a27af2
Openthread code updates (#9047) 2025-06-11 22:41:38 +12:00
Stanislav Meduna
052f558131
Add support for custom request headers in online_image component (#8985) 2025-06-11 22:14:02 +12:00
J. Nick Koston
e8aa7cff36
Improve shutdown reliability when tx buffer is full (#9043) 2025-06-11 22:08:23 +12:00
J. Nick Koston
3411e45a0a
Reserve memory for component and platform vectors (#9042) 2025-06-11 22:05:42 +12:00
Clyde Stubbs
a488c8cd5c
[spi] Restrict octal spi to S3/S2/P4 (#9041) 2025-06-11 19:45:26 +10:00
Keith Burzinski
9652b1a556
[application] Fix build error on some IDF versions (#9045) 2025-06-11 21:44:49 +12:00
Keith Burzinski
69f2c79ccb
[shtcx] Shorten log messages (#9046) 2025-06-11 21:44:10 +12:00
Mathieu Rene
9d9d210176
Add OpenThread support on ESP-IDF (#7506)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-06-11 17:27:58 +12:00
Thomas Rupprecht
487e1f871f
use `encode_uintXX` (#8847)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-11 17:06:45 +12:00
J. Nick Koston
0e27ac281f
Ensure components only powerdown after teardown (#9044) 2025-06-11 04:21:22 +00:00
Jonathan Swoboda
ad37f103fa
[esp32_rmt] Add variant validation for use_dma (#8897) 2025-06-11 14:00:59 +12:00
Jesse Hills
b579bbf03b
[esp32] Use release zip from pioarduino/platform-espressif32 instead of git tag (#8975) 2025-06-11 13:53:55 +12:00
Citric Li
7f4d2534aa
Fix: Seeed Studio MR60FDA2 threshold height could not be set (#9011) 2025-06-11 12:44:51 +12:00
Clyde Stubbs
9e26daeb94
[esp_ldo] Implement support for ESP32-P4 LDO (#9009)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-06-11 12:43:19 +12:00
Keith Burzinski
da6af184a6
Use a define for log message constants (#8952) 2025-06-11 12:32:51 +12:00
Jonathan Swoboda
4d347f1cc6
[core] Include esp_mac.h on Arduino too (#9040) 2025-06-11 00:08:51 +00:00
Jesse Hills
84e57b8136
[inkplate] Remove arduino dependency (#9031) 2025-06-11 12:01:11 +12:00
J. Nick Koston
63882c4a74
Reduce Bluetooth overhead by disabling unused logging categories (#8945) 2025-06-11 11:57:43 +12:00
J. Nick Koston
2ed5611a08
Replace API deferred queue with efficient message batching system (#9012) 2025-06-11 11:49:15 +12:00
Clyde Stubbs
1467b704b8
[lvgl] Fix templated argument to lvgl.is_idle (#9014) 2025-06-10 03:47:08 -05:00
Edward Firmo
94848e4811
[nextion] Add configurable limit for commands processed per loop (#8972)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-06-10 07:27:46 +00:00
Edward Firmo
3174f7ae86
[nextion] Optimize log messages to reduce memory usage (#9039) 2025-06-09 18:51:08 -05:00
dependabot[bot]
962a339a8a
Bump ruamel-yaml from 0.18.13 to 0.18.14 (#9037)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-09 18:45:05 -05:00
dependabot[bot]
6a76e6ae4a
Bump aioesphomeapi from 32.2.0 to 32.2.1 (#9038)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-09 18:44:53 -05:00
Edward Firmo
ce4371a80d
[globals] Prevent redundant oversized string checks in loop (#9001)
Co-authored-by: J. Nick Koston <nick+github@koston.org>
2025-06-09 11:19:24 -05:00
J. Nick Koston
b7ca6e087a
Add LWIP optimization options to reduce flash usage (#8946)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-06-09 06:08:10 +00:00
pseud0sphere
368a0eea8a
Change RP2040 PIO SK6812 timings (#9020) 2025-06-09 17:46:27 +12:00
Keith Burzinski
99c368fe62
[tsl2561, tsl2591] Shorten log messages (#9034) 2025-06-09 17:45:17 +12:00
Keith Burzinski
ff406f8e11
[max7219digit, servo, tsl2591] ESP_LOGCONFIG call reduction (Extend #9026) (#9033) 2025-06-09 04:19:20 +00:00
Edward Firmo
b98165e077
[nextion] Use safe restart to properly handle globals and restart logging (#9010) 2025-06-08 21:10:36 -05:00
Clyde Stubbs
e2a9cced94
[psram] Add P4 support (#8545)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: clydeps <U5yx99dok9>
2025-06-09 01:07:54 +00:00
Jon Krause
cdae06e571
Force socket ready when high frequency looping (#9032) 2025-06-09 00:40:25 +00:00
J. Nick Koston
c0b05ada1a
Reduce ESP_LOGCONFIG calls (#9026) 2025-06-09 00:02:30 +00:00
esphomebot
80dddb4cae
Update webserver local assets to 20250608-225410 (#9030) 2025-06-08 23:41:35 +00:00
J. Nick Koston
245c89a6c1
Disable ruff rule UP038 (#9029) 2025-06-08 23:15:46 +00:00
Clyde Stubbs
4d044d4ac9
[config] Clean build on ESP-IDF when component/platform combos change (#9028) 2025-06-09 08:39:02 +10:00
J. Nick Koston
9cc2a04d54
Implement proper API connection teardown before deep sleep/reboot (#9008) 2025-06-09 10:29:26 +12:00
dependabot[bot]
50cdec19dd
Bump aioesphomeapi from 32.1.0 to 32.2.0 (#9025)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-08 16:28:39 +00:00
dependabot[bot]
6d587278bd
Bump aioesphomeapi from 32.0.0 to 32.1.0 (#9024)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-08 15:26:04 +00:00
Keith Burzinski
dde63e7459
[esp32] Add config vars for compiler (#9023) 2025-06-08 07:38:15 -05:00
Keith Burzinski
8894f5030a
[qwiic_pir] Clean-up, shorten some log messages (#8951) 2025-06-07 22:44:35 -05:00
Clyde Stubbs
9e862b8b53
[list-components.py] Only add platforms that are actually platforms. (#9005) 2025-06-07 21:25:24 -05:00
dependabot[bot]
24d4ada841
Bump ruamel-yaml from 0.18.12 to 0.18.13 (#9018)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-07 13:48:41 -05:00
dependabot[bot]
b1a8887548
Bump ruff from 0.11.11 to 0.11.13 (#9017)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-07 13:47:51 -05:00
Jonathan Swoboda
d19997a056
[api] Fix build error in IDF 5.5 (#9007) 2025-06-04 21:19:58 +00:00
Stanislav Meduna
de7591882d
Move CONF_REQUEST_HEADERS to const.py (#9002) 2025-06-04 15:26:30 +01:00
dependabot[bot]
a00fc75c77
Bump aioesphomeapi from 31.1.0 to 32.0.0 (#9004) 2025-06-04 12:33:02 +01:00
Edward Firmo
80fd827f8b
[nextion] Add optional max_queue_size limit to prevent queue overflows (#8976) 2025-06-04 06:13:35 -05:00
Hannah_GBS
1dd3c6de90
[sdl] Add config for SDL window flags (#8998)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
2025-06-04 19:49:32 +10:00
tomaszduda23
c8c43f13fd
[ci, nrf52] make zephyr clang mandatory (#8992) 2025-06-03 22:03:32 +01:00
Keith Burzinski
518bce50a5
[mqtt] Remove redundant "mqtt" from log messages (#8968) 2025-06-03 21:54:58 +01:00
Keith Burzinski
4f87bea788
[api] Streamline some log strings (#8962) 2025-06-03 21:53:32 +01:00
Keith Burzinski
8054c9b4f5
[bmp581] Shorten some log messages (#8948) 2025-06-03 21:51:51 +01:00
Keith Burzinski
935e0a365f
[sps30] Shorten log messages (#8971) 2025-06-03 21:50:22 +01:00
dependabot[bot]
b39a9924d8
Bump ruamel-yaml from 0.18.11 to 0.18.12 (#8977)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-03 21:49:14 +01:00
Jonathan Swoboda
19ec922e28
[dashboard] Fix logging colors (#8984) 2025-06-03 21:48:22 +01:00
Keith Burzinski
a225d6881f
[wireguard] Remove redundant "wireguard" from log messages (#8963) 2025-06-03 21:46:53 +01:00
Keith Burzinski
6675e99862
Remove unnecessary ellipsis (#8964) 2025-06-03 21:46:10 +01:00
Keith Burzinski
8cbe2b41f6
[bmp3xx] Remove redundant "bmp3xx" from log messages (#8965) 2025-06-03 21:45:29 +01:00
Keith Burzinski
6a225cb4c0
[ethernet] Remove redundant "ethernet" from log messages (#8966) 2025-06-03 21:43:39 +01:00
Keith Burzinski
e62d8bfabe
[sdp3x] Remove redundant "sdp3x" from log messages (#8969) 2025-06-03 21:43:14 +01:00
Keith Burzinski
d4cea84b1b
[spi] Remove redundant "SPI" from log messages (#8970) 2025-06-03 21:39:21 +01:00
Jesse Hills
b63f90a6c0
[core] Update defines.h esp-idf version (#8974) 2025-06-03 21:38:35 +01:00
Hannah_GBS
4370b6695e
[const] Move CONF_X and CONF_Y to const.py (#8999) 2025-06-03 21:28:06 +01:00
Jesse Hills
589f13f0f7
[mdns] Set up only after API is set up (#9000) 2025-06-03 17:38:59 +00:00
Jesse Hills
367017b352
Merge branch 'release' into dev 2025-06-03 20:45:32 +12:00
Jesse Hills
ec26d31499
Merge pull request #8996 from esphome/bump-2025.5.2
2025.5.2
2025-06-03 20:45:09 +12:00
Jesse Hills
1bbc6db1c3
Bump version to 2025.5.2 2025-06-03 20:04:39 +12:00
J. Nick Koston
162472bdc2
Fix logger stack overflow (#8988) 2025-06-03 20:04:39 +12:00
Clyde Stubbs
aecac15809
[debug] Make sensors work without logger debug level (#8980) 2025-06-03 20:04:39 +12:00
Samuel Sieb
6554af21b9
[esp8266] fix isr pin (#8981)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2025-06-03 20:04:39 +12:00
Samuel Sieb
8583466c6a
[rp2040] use low-level control for ISR gpio and add IRAM_ATTR (#8950)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2025-06-03 20:04:39 +12:00
Kevin Ahrendt
6666604069
[i2s-audio] ensure mic task isn't pinned to a core (#8879) 2025-06-03 20:04:39 +12:00
dependabot[bot]
13e7aacc9d
Bump pytest from 8.3.5 to 8.4.0 (#8993)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-02 23:47:54 +01:00
J. Nick Koston
737d502614
Fix logger stack overflow (#8988) 2025-06-03 04:46:17 +12:00
Pat Satyshur
67dd649d00
[lc709203f] Add battery monitor (#8037)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-06-01 15:24:55 -05:00
Clyde Stubbs
b2fc51367b
[debug] Make sensors work without logger debug level (#8980) 2025-05-31 14:27:48 -05:00
Keith Burzinski
5771bb4907
[preferences] Shorten log messages (#8982) 2025-05-31 14:23:47 -05:00
tronikos
9ba9674437
Add missing icons and device classes to BME680 sensors (#8960) 2025-05-31 14:22:37 -05:00
Samuel Sieb
acb1532e34
[esp8266] fix isr pin (#8981)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2025-05-31 14:21:08 -05:00
Jesse Hills
e2093c34da
[esp32c6] Add test base file and platformio env (#8973) 2025-05-31 22:50:45 +12:00
Edward Firmo
a2e4ad90ba
[nextion] Allocate NextionQueue in PSRAM (if available) (#8979) 2025-05-31 02:52:42 -05:00
Keith Burzinski
32e69c67f2
[max9611] Remove redundant "max9611" from log messages (#8967) 2025-05-31 00:29:51 +12:00
Samuel Sieb
df0b5a187e
[rp2040] use low-level control for ISR gpio and add IRAM_ATTR (#8950)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2025-05-30 23:38:50 +12:00
Tim Laurence
cee0e5379b
Allow Weikai to pass data_bit validation (#8917) 2025-05-30 23:37:27 +12:00
Javier Peletier
daf2bd7e66
Have ESPHome's YAML dumper comply with its own yamllint rules (#8957) 2025-05-30 08:43:05 +12:00
Keith Burzinski
4031077f6d
[dht] Clean-up, shorten some log messages (#8949) 2025-05-29 21:37:41 +12:00
J. Nick Koston
fd72a64053
Redundant Log Messages Cleanup (#8944)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-05-29 09:36:23 +00:00
Jesse Hills
959a8b91bd
[demo] FIx some of the entities (#8943) 2025-05-29 03:22:46 -05:00
Keith Burzinski
44f1ff10e6
Introduce "communication failed" log macro (#8939) 2025-05-29 01:59:38 -05:00
Jesse Hills
64e4589f4e
[esp32] Regenerate boards from recommended platform version (#8938) 2025-05-29 05:21:05 +00:00
Kuba Szczodrzyński
20aba45cbe
[rp2040] Allow changing watchdog timeout (#8868) 2025-05-29 17:15:26 +12:00
gotnone
0b1c5b825e
[modbus] [modbus_controller] Fix server role read coil 0x1 crc (#8859)
Co-authored-by: Stanley Pinchak <stanley.pinchak@gmail.com>
2025-05-29 17:12:58 +12:00
Leicas
455624105b
Add flip X and Y on inkplate6 component (#7904)
Co-authored-by: Antoine Weill--Duflos <antoine@haply.co>
Co-authored-by: David Sichau <sichau@inf.ethz.ch>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: David Sichau <DavidSichau@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-29 17:10:06 +12:00
Jonathan Swoboda
7ac5746e0d
Fix colors in update all (#8854) 2025-05-29 15:00:27 +12:00
Thomas Rupprecht
12997451f6
particle matter improvements (#8846) 2025-05-29 14:57:20 +12:00
J. Nick Koston
8c77e40695
Fix select() logging flood in very verbose mode (#8942) 2025-05-29 02:29:37 +00:00
Nate Clark
2ddd91acf2
[alarm_control_panel] BYPASS_AUTO option for Template Alarm Control Panel sensors left open when armed (#8795) 2025-05-29 14:22:26 +12:00
Keith Burzinski
729e49cdc3
[gcja5] Remove unused setup() method (#8935) 2025-05-29 14:01:00 +12:00
J. Nick Koston
d64b49cc13
Optimize plaintext API header reading to reduce system calls (#8941)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-28 20:46:23 -05:00
Keith Burzinski
cfa8b3b272
[tmp102] Remove setup(), optimize logging (#8937) 2025-05-29 11:44:31 +12:00
Keith Burzinski
51981335d5
[pmwcs3] Optimize logging (#8936) 2025-05-29 11:44:03 +12:00
J. Nick Koston
70c5e1bbf1
Improve logging in integration tests when port does not open (#8932)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-29 11:42:19 +12:00
J. Nick Koston
43e88af28a
Optimize socket operations by checking readiness in the main loop (#8918) 2025-05-28 18:16:37 -05:00
J. Nick Koston
ffc66f539f
Migrate wifi component to use App.get_loop_component_start_time (#8931) 2025-05-29 09:33:54 +12:00
Jesse Hills
c4cb694d77
Add more demo platforms (#8903) 2025-05-28 15:23:45 -05:00
Jesse Hills
3fb9577ad9
[i2s_audio] Bump esphome/ESP32-audioI2S to 2.2.0 (#8920) 2025-05-29 08:12:03 +12:00
mrtoy-me
34169491ac
[speaker mediaplayer] Yaml config initial volume (on first boot) (#8898) 2025-05-28 15:37:25 -04:00
Keith Burzinski
8eac859bab
Streamline setup() logging (s, t, u, v, w, x, y, z) (#8930) 2025-05-29 07:28:41 +12:00
Keith Burzinski
d99e3237f9
Streamline setup() logging (n, o, p, q, r) (#8929) 2025-05-29 07:23:53 +12:00
Keith Burzinski
d9a9e0aea3
Streamline setup() logging (k, l, m) (#8928) 2025-05-29 06:57:58 +12:00
Keith Burzinski
0ce03ae26b
Streamline setup() logging (g, h, i) (#8927) 2025-05-29 06:55:02 +12:00
Keith Burzinski
18653f8f69
Streamline setup() logging (e, f) (#8926) 2025-05-28 12:12:46 +00:00
Keith Burzinski
6e0523109a
Streamline setup() logging (c, d) (#8925) 2025-05-28 22:54:38 +12:00
Keith Burzinski
b6fa4f641d
Streamline setup() logging (a, b) (#8924) 2025-05-28 22:53:51 +12:00
Keith Burzinski
ca6295d1bd
[ledc] Various optimizations/clean-up (#8922) 2025-05-28 22:50:48 +12:00
Keith Burzinski
18a1d31845
[rtttl] Various optimizations/clean-up (#8923) 2025-05-28 22:48:54 +12:00
Keith Burzinski
c5239a63ab
[aht10] Various optimizations/clean-up (#8921) 2025-05-28 22:22:05 +12:00
Craig Andrews
1911269dc9
[online_image] Last-Modified-Date and ETag response caching (#8782) 2025-05-28 17:17:57 +12:00
Thomas SAMTER
04ee1a87e9
Add es8388 audio_dac (#8342)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-05-28 14:00:00 +12:00
Kevin Ahrendt
a8fdb6db4d
[i2s-audio] ensure mic task isn't pinned to a core (#8879) 2025-05-28 08:47:42 +12:00
dependabot[bot]
8860c74f0c
Bump docker/build-push-action from 6.17.0 to 6.18.0 in /.github/actions/build-image (#8919)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-28 08:45:24 +12:00
Jesse Hills
d585440d54
Merge branch 'release' into dev 2025-05-27 21:02:03 +12:00
Jesse Hills
f74f89c6b5
Merge pull request #8913 from esphome/bump-2025.5.1
2025.5.1
2025-05-27 21:01:19 +12:00
dependabot[bot]
7d049a61bb
Bump pytest-xdist from 3.6.1 to 3.7.0 (#8916)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-27 06:55:47 +00:00
dependabot[bot]
f2e4dc7907
Bump setuptools from 80.8.0 to 80.9.0 (#8915)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-27 06:54:33 +00:00
dependabot[bot]
0c7589caeb
Bump pytest-mock from 3.14.0 to 3.14.1 (#8909)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-27 17:26:14 +12:00
dependabot[bot]
321411e355
Bump ruamel-yaml from 0.18.10 to 0.18.11 (#8910)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-27 17:26:08 +12:00
Samuel Sieb
361de22370
[sx1509] add support for keys (#8413)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-05-27 17:16:27 +12:00
Jesse Hills
95a17387a8
Bump actions/checkout from 4.1.7 to 4.2.2 (#8904) 2025-05-27 16:26:01 +12:00
J. Nick Koston
caf9930ff9
Fix flakey tests (#8914) 2025-05-27 16:20:14 +12:00
Jesse Hills
42390faf4a
Bump version to 2025.5.1 2025-05-27 14:31:38 +12:00
Jesse Hills
fdc6c4a219
[web_server] Fix download list where external_components has a substitution value (#8911)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-27 14:31:38 +12:00
Jesse Hills
6c08f5e343
[api] Fix crash with gcc compiler on host (#8902) 2025-05-27 14:31:38 +12:00
Keith Burzinski
e0e4ba9592
[esp32] Fix building on IDF 4 (#8892) 2025-05-27 14:31:38 +12:00
Jesse Hills
ad20825f31
[logger] Fix options in select (#8875)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
2025-05-27 14:31:38 +12:00
Kevin Ahrendt
e4f3a952d5
[speaker] ensure the pipeline returns an error state before returning its stopped (#8878) 2025-05-27 14:31:38 +12:00
Kevin Ahrendt
90e3c5bba2
[micro_wake_word] avoid duplicated detections from same event (#8877) 2025-05-27 14:31:38 +12:00
Clyde Stubbs
b1d5ad27f3
[lvgl] Improve error messages from text validation (#8872) 2025-05-27 14:31:38 +12:00
Jesse Hills
5c54f75b7a
[online_image] Allocate pngle manually to potentially use psram (#8354)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-05-27 14:31:38 +12:00
Cossid
a5f85b4437
[tuya_select] - Fix datapoint config error. (#8871) 2025-05-27 14:31:38 +12:00
Jesse Hills
da4e710249
[core] Add some missing includes (#8864) 2025-05-27 14:31:38 +12:00
J. Nick Koston
4ac433fddb
Add integration tests for host (#8912) 2025-05-26 21:31:32 -05:00
Jesse Hills
73771d5c50
[web_server] Fix download list where external_components has a substitution value (#8911)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-27 09:08:16 +12:00
Jesse Hills
af7b1a3a23
[api] Fix crash with gcc compiler on host (#8902) 2025-05-27 06:46:51 +12:00
dependabot[bot]
430f63fcbb
Bump pyupgrade from 3.19.1 to 3.20.0 (#8891)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-05-26 08:13:45 +00:00
Emmanuel Ferdman
5921a9cd68
Resolve regex library warnings (#8890) 2025-05-26 19:45:47 +12:00
Jonathan Swoboda
ca0037d076
[esp32, logger, core] Add initial c5 support (#8895) 2025-05-26 13:33:41 +12:00
Jesse Hills
1e18d0b06c
[i2s_audio] Add basic support for esp32-p4 (#8887) 2025-05-26 11:55:51 +12:00
luar123
4b5c3e7e2b
[bme68x_bsec2_i2c] Remove arduino dependency (#7815) 2025-05-25 03:08:51 -05:00
Keith Burzinski
d4c4b75eb3
[esp32] Fix building on IDF 4 (#8892) 2025-05-25 02:15:24 +12:00
Jesse Hills
9dd4045984
[const] Move `CONF_RESET` to const.py (#8889) 2025-05-23 21:54:06 -05:00
gotnone
19e2460af2
[modbus_controller] Add assumed_state to switch (#8880)
Co-authored-by: Stanley Pinchak <stanley.pinchak@gmail.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-05-23 21:34:10 +12:00
Rodrigo Martín
149f787035
feat: wifi.configure now emits error after reconnecting to old AP (#8653) 2025-05-23 21:32:47 +12:00
J. Nick Koston
2ab1fe1abf
Use UINT16_MAX instead of hard coded 65535 in api (#8884)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-22 23:51:06 +00:00
Jesse Hills
926b42ba1c
[logger] Fix options in select (#8875)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
2025-05-23 09:33:38 +10:00
J. Nick Koston
377ed2e212
Optimize API frame helper buffer management (#8805)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-05-22 21:11:16 +00:00
esphomebot
42912447fb
Synchronise Device Classes from Home Assistant (#8874) 2025-05-23 08:50:31 +12:00
Pi57
25ead44f1c
Add const DEVICE_CLASS_WIND_DIRECTION (#8870)
Co-authored-by: PierreYvesHB <pierre-yves.henius-beck@act-blue.eu>
2025-05-23 08:49:37 +12:00
dependabot[bot]
03b003af47
Bump ruff from 0.11.10 to 0.11.11 (#8883)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-22 20:22:04 +00:00
dependabot[bot]
5baccf0ce7
Bump tornado from 6.4.2 to 6.5.1 (#8882)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-22 20:21:46 +00:00
Kevin Ahrendt
e95c92773c
[speaker] ensure the pipeline returns an error state before returning its stopped (#8878) 2025-05-23 07:20:15 +12:00
Kevin Ahrendt
c23ea384fb
[micro_wake_word] avoid duplicated detections from same event (#8877) 2025-05-23 07:19:16 +12:00
Lưu Oa Oa (宰相劉羅鍋)
69da17742f
OTA: Close and clean up client when setsockopt fails (#8865)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
2025-05-22 11:37:42 -05:00
Clyde Stubbs
1ec57a74b5
[usb_uart] Implement USB Host mode UART (#8334) 2025-05-22 13:54:40 +12:00
Clyde Stubbs
d1e55252d0
[lvgl] Improve error messages from text validation (#8872) 2025-05-22 13:49:56 +12:00
Clyde Stubbs
090feb55e9
[lvgl] Add content styling to tabview (#8823) 2025-05-22 13:47:38 +12:00
Clyde Stubbs
6109acb6f3
[lvgl] Try to allocate smaller buffer on failure (#8814)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-05-22 13:45:56 +12:00
Jesse Hills
5aa13db815
[online_image] Allocate pngle manually to potentially use psram (#8354)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-05-22 13:40:53 +12:00
Jesse Hills
1b67dd4232
[sync] Update and fix sync workflow (#8873) 2025-05-22 13:32:19 +12:00
Cossid
ba6efcedcb
[tuya_select] - Fix datapoint config error. (#8871) 2025-05-22 13:26:19 +12:00
Jesse Hills
bd7c2a680c
Updates for development environment (#8801) 2025-05-22 13:24:34 +12:00
Andrew J.Swan
1466aa7703
Add CUBIC CM1106 Single Beam NDIR CO2 Sensor Module (#8293)
Co-authored-by: Djordje <6750655+DjordjeMandic@users.noreply.github.com>
Co-authored-by: Patrick <info@patagona.dev>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-05-22 13:23:54 +12:00
Jonathan Swoboda
787f4860db
[esp32, logger] Add initial P4 support (#8439)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-05-22 13:22:01 +12:00
Thomas Rupprecht
aeb4e63950
update minimal python version to 3.10 (#8850) 2025-05-22 13:21:43 +12:00
Jonathan Swoboda
026f47bfb3
[esp32] Use IDF 5.3.2 as default for IDF builds (#8464)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
2025-05-22 13:21:27 +12:00
Jesse Hills
dd47d063b5
Merge branch 'release' into dev 2025-05-21 20:33:34 +12:00
Jesse Hills
cdcd1cd292
Merge pull request #8863 from esphome/bump-2025.5.0
2025.5.0
2025-05-21 20:32:40 +12:00
Jesse Hills
a6fa963605
[core] Add some missing includes (#8864) 2025-05-21 20:02:14 +12:00
Jesse Hills
1cba22175f
Bump version to 2025.5.0 2025-05-21 15:26:55 +12:00
Jesse Hills
f2d7720a4e
Merge branch 'beta' into dev 2025-05-21 13:09:35 +12:00
Jesse Hills
e9d832d64a
[api-docs] Move netlify.toml to root (#8861) 2025-05-21 11:43:19 +12:00
Jesse Hills
f8f09bca02
Merge branch 'beta' into dev 2025-05-21 11:26:20 +12:00
dependabot[bot]
c5d809b3dd
Bump setuptools from 80.7.1 to 80.8.0 (#8858)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-20 16:27:53 -04:00
Thomas Rupprecht
b1cf08b261
add python 3.13 to ci pipeline (#8855) 2025-05-20 10:04:09 -04:00
Gustavo Ambrozio
6ae83dfe3d
[at581x] Fix issue with methods not being public (#8852) 2025-05-20 02:36:44 -05:00
Thomas Rupprecht
0932e83b15
update ruff version to `0.11.10 in .pre-commit-config.yaml` (#8851) 2025-05-20 00:42:43 -04:00
Clyde Stubbs
86670c4d39
Fix #ifdefs (#8853) 2025-05-20 13:19:24 +10:00
dependabot[bot]
4ce55b94ec
Bump aioesphomeapi from 31.0.1 to 31.1.0 (#8849) 2025-05-19 20:30:30 -04:00
Jesse Hills
1c5dc63eb4
Merge branch 'beta' into dev 2025-05-20 01:19:32 +12:00
Jesse Hills
ef7a22ff04
[api-docs] Run using netlify builders (#8842)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-19 19:48:04 +12:00
Jesse Hills
dfda0e5c7c
[docker] Update pip on build (#8835)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-05-19 18:22:24 +12:00
J. Nick Koston
78c63311c6
Fix ethernet connection timeout issue caused by incorrect time value during setup (#8841) 2025-05-19 00:25:21 -05:00
Jesse Hills
1ac51e7b3e
Merge branch 'beta' into dev 2025-05-19 16:03:18 +12:00
J. Nick Koston
5b552b9ec5
Fix API connection sending ping too early after connection establishment (#8840) 2025-05-19 15:22:36 +12:00
Jesse Hills
d36ce7c010
[release] Don't wait for docker to be finished before deploying schema (#8838) 2025-05-19 14:17:01 +12:00
Jesse Hills
b8a96f59f0
[release] Fix output value (#8839) 2025-05-19 14:16:39 +12:00
Jesse Hills
2e15ee232d
Deploy doxygen docs to netlify (#8837) 2025-05-19 14:09:38 +12:00
Jesse Hills
904495e1b8
Fix api doc homepage (#8836)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-19 13:57:41 +12:00
Fexiven
99c4f88c3f
Update esp32-camera library version (#8832) 2025-05-19 11:01:31 +12:00
DanielV
87a9dd18c8
Improve stability for a test that crashes intermittently in CI (#8699)
Co-authored-by: NP v/d Spek <github_mail@lumensoft.nl>
2025-05-19 10:01:30 +12:00
Thomas Rupprecht
dbce54477a
unify and add missing metric suffixes (#8816) 2025-05-18 21:44:33 +00:00
dependabot[bot]
660030d157
Bump docker/build-push-action from 6.16.0 to 6.17.0 in /.github/actions/build-image (#8810)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-19 09:20:29 +12:00
dependabot[bot]
24fbe602dd
Bump codecov/codecov-action from 5.4.2 to 5.4.3 (#8820)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-19 09:13:20 +12:00
J. Nick Koston
b0c1e0e28c
Refactor API frame helpers to enable buffer reuse (#8825) 2025-05-19 09:05:20 +12:00
J. Nick Koston
574aabdede
Reduce number of calls to fetch time in the main loop (#8804) 2025-05-19 07:48:57 +12:00
J. Nick Koston
e47741d471
Fix ESP32 console logging corruption and message loss in multi-task (#8806) 2025-05-19 07:43:41 +12:00
Kent Gibson
a78bea78f9
Fix misspelling of climate in climate_ir.climate_ir_with_receiver_schema (#8829) 2025-05-18 03:45:12 +00:00
Clyde Stubbs
44470f31f6
Revert "[binary_sensor] initial state refactor" (#8828) 2025-05-18 03:30:08 +00:00
Anton Sergunov
18ac1b7c54
Fix the case of single error (#8824) 2025-05-18 15:11:09 +12:00
Keith Burzinski
e87b659483
[sen5x] Fix validation for values read from hardware (#8769) 2025-05-18 15:05:03 +12:00
J. Nick Koston
fefcb45e1f
Bump cryptography to 45.0.1 (#8826) 2025-05-18 14:50:06 +12:00
Clyde Stubbs
5c92367ca2
[script] Use local import for zephyr (#8822) 2025-05-16 23:41:19 +00:00
dependabot[bot]
b469a504e4
Bump cairosvg from 2.8.1 to 2.8.2 (#8817)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-16 15:42:51 -04:00
dependabot[bot]
218f8e0caf
Bump ruff from 0.11.9 to 0.11.10 (#8818)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-16 15:42:44 -04:00
J. Nick Koston
7965558d5e
Fix ESP32 Camera class inheritance (#8811) 2025-05-16 11:42:54 +12:00
dependabot[bot]
d9b860088e
Bump setuptools from 80.4.0 to 80.7.1 (#8808)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-15 15:45:01 -05:00
dependabot[bot]
115975c409
Bump aioesphomeapi from 31.0.0 to 31.0.1 (#8809)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-15 15:44:47 -05:00
Thomas Rupprecht
4761ffe023
[gps] update lib, improve code/tests/config (#8768) 2025-05-15 22:07:41 +12:00
Thomas Rupprecht
88edddf07a
[log] improve/refactor log (#8708) 2025-05-15 21:45:07 +12:00
J. Nick Koston
0b77cb1d16
Logger Recursion Guard per Task on ESP32 (#8765) 2025-05-15 21:36:28 +12:00
J. Nick Koston
efa6745a5e
Optimize protobuf varint decoder for ESPHome use case (#8791) 2025-05-15 17:16:25 +12:00
J. Nick Koston
dd8d8ad952
Use fixed buffer for plaintext protocol like noise protocol (#8800) 2025-05-15 17:16:08 +12:00
dependabot[bot]
57284b1ac3
Bump cairosvg from 2.8.0 to 2.8.1 (#8799)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-14 23:26:28 -05:00
Jesse Hills
1a651ce66d
Update some sensor schemas to be Optional (#8803) 2025-05-15 02:40:11 +00:00
Jesse Hills
730441c120
[api] Update api proto to add legacy value (#8802) 2025-05-14 21:26:21 -05:00
J. Nick Koston
bb1f24ab43
Avoid protobuf message construction when tx buffer is full (#8787)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-05-14 21:25:44 -05:00
NP v/d Spek
edb8d187be
add actions to the MAX7219Component (#6462)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-05-15 09:15:04 +12:00
Jesse Hills
e7b6081c5c
Merge branch 'beta' into dev 2025-05-15 06:51:16 +12:00
dependabot[bot]
5454500024
Bump cairosvg from 2.7.1 to 2.8.0 (#8780)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-14 11:20:22 -05:00
Jesse Hills
191afd3e69
Bump esphome-dashboard to 20250514.0 (#8790) 2025-05-14 02:08:16 -05:00
Jesse Hills
de27ce79dc
[climate] Update components to use `climate_schema(...)` (#8788) 2025-05-14 05:36:21 +00:00
Jesse Hills
a12bd78ceb
Fix release to pypi (#8789) 2025-05-14 16:35:30 +12:00
J. Nick Koston
ddb986b4fa
Improve batching of BLE advertisements for better airtime efficiency (#8778) 2025-05-14 04:34:33 +00:00
Jesse Hills
c98c78e368
Merge branch 'beta' into dev 2025-05-14 15:55:25 +12:00
dependabot[bot]
5570a788fd
Bump aioesphomeapi from 30.2.0 to 31.0.0 (#8779)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-13 21:23:32 -05:00
Jesse Hills
42c355e6d7
[fan] Update components to use `fan_schema(...)` (#8786) 2025-05-13 20:30:11 -05:00
Jesse Hills
a835ab48bc
[schema] Get component name if available for deprecation warning (#8785) 2025-05-13 20:25:21 -05:00
Jesse Hills
f28a373898
[media_player] Deprecate `MEDIA_PLAYER_SCHEMA` (#8784) 2025-05-13 23:48:54 +00:00
Jesse Hills
28e29efd98
Bump version to 2025.6.0-dev 2025-05-14 09:54:26 +12:00
907 changed files with 16818 additions and 6811 deletions

View File

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

37
.devcontainer/Dockerfile Normal file
View File

@ -0,0 +1,37 @@
ARG BUILD_BASE_VERSION=2025.04.0
FROM ghcr.io/esphome/docker-base:debian-${BUILD_BASE_VERSION} AS base
RUN git config --system --add safe.directory "*"
RUN apt update \
&& apt install -y \
protobuf-compiler
RUN pip install uv
RUN useradd esphome -m
USER esphome
ENV VIRTUAL_ENV=/home/esphome/.local/esphome-venv
RUN uv venv $VIRTUAL_ENV
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
# Override this set to true in the docker-base image
ENV UV_SYSTEM_PYTHON=false
WORKDIR /tmp
COPY requirements.txt ./
RUN uv pip install -r requirements.txt
COPY requirements_dev.txt requirements_test.txt ./
RUN uv pip install -r requirements_dev.txt -r requirements_test.txt
RUN \
platformio settings set enable_telemetry No \
&& platformio settings set check_platformio_interval 1000000
COPY script/platformio_install_deps.py platformio.ini ./
RUN ./platformio_install_deps.py platformio.ini --libraries --platforms --tools
WORKDIR /workspaces

View File

@ -1,18 +1,17 @@
{
"name": "ESPHome Dev",
"image": "ghcr.io/esphome/esphome-lint:dev",
"context": "..",
"dockerFile": "Dockerfile",
"postCreateCommand": [
"script/devcontainer-post-create"
],
"containerEnv": {
"DEVCONTAINER": "1",
"PIP_BREAK_SYSTEM_PACKAGES": "1",
"PIP_ROOT_USER_ACTION": "ignore"
"features": {
"ghcr.io/devcontainers/features/github-cli:1": {}
},
"runArgs": [
"--privileged",
"-e",
"ESPHOME_DASHBOARD_USE_PING=1"
"GIT_EDITOR=code --wait"
// uncomment and edit the path in order to pass though local USB serial to the conatiner
// , "--device=/dev/ttyACM0"
],

View File

@ -47,7 +47,7 @@ runs:
- name: Build and push to ghcr by digest
id: build-ghcr
uses: docker/build-push-action@v6.17.0
uses: docker/build-push-action@v6.18.0
env:
DOCKER_BUILD_SUMMARY: false
DOCKER_BUILD_RECORD_UPLOAD: false
@ -73,7 +73,7 @@ runs:
- name: Build and push to dockerhub by digest
id: build-dockerhub
uses: docker/build-push-action@v6.17.0
uses: docker/build-push-action@v6.18.0
env:
DOCKER_BUILD_SUMMARY: false
DOCKER_BUILD_RECORD_UPLOAD: false

View File

@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4.1.7
uses: actions/checkout@v4.2.2
- name: Set up Python
uses: actions/setup-python@v5.6.0
with:

View File

@ -43,11 +43,11 @@ jobs:
- "docker"
# - "lint"
steps:
- uses: actions/checkout@v4.1.7
- uses: actions/checkout@v4.2.2
- name: Set up Python
uses: actions/setup-python@v5.6.0
with:
python-version: "3.9"
python-version: "3.10"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.10.0

View File

@ -20,8 +20,8 @@ permissions:
contents: read
env:
DEFAULT_PYTHON: "3.9"
PYUPGRADE_TARGET: "--py39-plus"
DEFAULT_PYTHON: "3.10"
PYUPGRADE_TARGET: "--py310-plus"
concurrency:
# yamllint disable-line rule:line-length
@ -36,7 +36,7 @@ jobs:
cache-key: ${{ steps.cache-key.outputs.key }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.1.7
uses: actions/checkout@v4.2.2
- name: Generate cache-key
id: cache-key
run: echo key="${{ hashFiles('requirements.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT
@ -68,7 +68,7 @@ jobs:
- common
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.1.7
uses: actions/checkout@v4.2.2
- name: Restore Python
uses: ./.github/actions/restore-python
with:
@ -89,7 +89,7 @@ jobs:
- common
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.1.7
uses: actions/checkout@v4.2.2
- name: Restore Python
uses: ./.github/actions/restore-python
with:
@ -110,7 +110,7 @@ jobs:
- common
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.1.7
uses: actions/checkout@v4.2.2
- name: Restore Python
uses: ./.github/actions/restore-python
with:
@ -131,7 +131,7 @@ jobs:
- common
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.1.7
uses: actions/checkout@v4.2.2
- name: Restore Python
uses: ./.github/actions/restore-python
with:
@ -152,7 +152,7 @@ jobs:
- common
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.1.7
uses: actions/checkout@v4.2.2
- name: Restore Python
uses: ./.github/actions/restore-python
with:
@ -173,10 +173,10 @@ jobs:
fail-fast: false
matrix:
python-version:
- "3.9"
- "3.10"
- "3.11"
- "3.12"
- "3.13"
os:
- ubuntu-latest
- macOS-latest
@ -185,24 +185,24 @@ jobs:
# Minimize CI resource usage
# by only running the Python version
# version used for docker images on Windows and macOS
- python-version: "3.13"
os: windows-latest
- python-version: "3.12"
os: windows-latest
- python-version: "3.10"
os: windows-latest
- python-version: "3.9"
os: windows-latest
- python-version: "3.13"
os: macOS-latest
- python-version: "3.12"
os: macOS-latest
- python-version: "3.10"
os: macOS-latest
- python-version: "3.9"
os: macOS-latest
runs-on: ${{ matrix.os }}
needs:
- common
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.1.7
uses: actions/checkout@v4.2.2
- name: Restore Python
uses: ./.github/actions/restore-python
with:
@ -214,14 +214,14 @@ jobs:
if: matrix.os == 'windows-latest'
run: |
./venv/Scripts/activate
pytest -vv --cov-report=xml --tb=native tests
pytest -vv --cov-report=xml --tb=native -n auto tests
- name: Run pytest
if: matrix.os == 'ubuntu-latest' || matrix.os == 'macOS-latest'
run: |
. venv/bin/activate
pytest -vv --cov-report=xml --tb=native tests
pytest -vv --cov-report=xml --tb=native -n auto tests
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5.4.2
uses: codecov/codecov-action@v5.4.3
with:
token: ${{ secrets.CODECOV_TOKEN }}
@ -232,7 +232,7 @@ jobs:
- common
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.1.7
uses: actions/checkout@v4.2.2
- name: Restore Python
uses: ./.github/actions/restore-python
with:
@ -296,11 +296,11 @@ jobs:
name: Run script/clang-tidy for ZEPHYR
options: --environment nrf52-tidy --grep USE_ZEPHYR
pio_cache_key: tidy-zephyr
ignore_errors: true
ignore_errors: false
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.1.7
uses: actions/checkout@v4.2.2
- name: Restore Python
uses: ./.github/actions/restore-python
with:
@ -356,7 +356,7 @@ jobs:
count: ${{ steps.list-components.outputs.count }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.1.7
uses: actions/checkout@v4.2.2
with:
# Fetch enough history so `git merge-base refs/remotes/origin/dev HEAD` works.
fetch-depth: 500
@ -406,7 +406,7 @@ jobs:
sudo apt-get install libsdl2-dev
- name: Check out code from GitHub
uses: actions/checkout@v4.1.7
uses: actions/checkout@v4.2.2
- name: Restore Python
uses: ./.github/actions/restore-python
with:
@ -432,7 +432,7 @@ jobs:
matrix: ${{ steps.split.outputs.components }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.1.7
uses: actions/checkout@v4.2.2
- name: Split components into 20 groups
id: split
run: |
@ -462,7 +462,7 @@ jobs:
sudo apt-get install libsdl2-dev
- name: Check out code from GitHub
uses: actions/checkout@v4.1.7
uses: actions/checkout@v4.2.2
- name: Restore Python
uses: ./.github/actions/restore-python
with:

View File

@ -20,7 +20,7 @@ jobs:
branch_build: ${{ steps.tag.outputs.branch_build }}
deploy_env: ${{ steps.tag.outputs.deploy_env }}
steps:
- uses: actions/checkout@v4.1.7
- uses: actions/checkout@v4.2.2
- name: Get tag
id: tag
# yamllint disable rule:line-length
@ -60,7 +60,7 @@ jobs:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4.1.7
- uses: actions/checkout@v4.2.2
- name: Set up Python
uses: actions/setup-python@v5.6.0
with:
@ -92,11 +92,11 @@ jobs:
os: "ubuntu-24.04-arm"
steps:
- uses: actions/checkout@v4.1.7
- uses: actions/checkout@v4.2.2
- name: Set up Python
uses: actions/setup-python@v5.6.0
with:
python-version: "3.9"
python-version: "3.10"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.10.0
@ -168,7 +168,7 @@ jobs:
- ghcr
- dockerhub
steps:
- uses: actions/checkout@v4.1.7
- uses: actions/checkout@v4.2.2
- name: Download digests
uses: actions/download-artifact@v4.3.0

View File

@ -13,10 +13,10 @@ jobs:
if: github.repository == 'esphome/esphome'
steps:
- name: Checkout
uses: actions/checkout@v4.1.7
uses: actions/checkout@v4.2.2
- name: Checkout Home Assistant
uses: actions/checkout@v4.1.7
uses: actions/checkout@v4.2.2
with:
repository: home-assistant/core
path: lib/home-assistant
@ -24,7 +24,7 @@ jobs:
- name: Setup Python
uses: actions/setup-python@v5.6.0
with:
python-version: 3.12
python-version: 3.13
- name: Install Home Assistant
run: |

View File

@ -18,7 +18,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code from GitHub
uses: actions/checkout@v4.1.7
uses: actions/checkout@v4.2.2
- name: Run yamllint
uses: frenck/action-yamllint@v1.5.0
with:

View File

@ -4,7 +4,7 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.11.9
rev: v0.11.10
hooks:
# Run the linter.
- id: ruff
@ -28,10 +28,10 @@ repos:
- --branch=release
- --branch=beta
- repo: https://github.com/asottile/pyupgrade
rev: v3.15.2
rev: v3.20.0
hooks:
- id: pyupgrade
args: [--py39-plus]
args: [--py310-plus]
- repo: https://github.com/adrienverge/yamllint.git
rev: v1.37.1
hooks:

View File

@ -96,6 +96,7 @@ esphome/components/ch422g/* @clydebarrow @jesterret
esphome/components/chsc6x/* @kkosik20
esphome/components/climate/* @esphome/core
esphome/components/climate_ir/* @glmnet
esphome/components/cm1106/* @andrewjswan
esphome/components/color_temperature/* @jesserockz
esphome/components/combination/* @Cat-Ion @kahrendt
esphome/components/const/* @esphome/core
@ -138,6 +139,7 @@ esphome/components/es7210/* @kahrendt
esphome/components/es7243e/* @kbx81
esphome/components/es8156/* @kbx81
esphome/components/es8311/* @kahrendt @kroimon
esphome/components/es8388/* @P4uLT
esphome/components/esp32/* @esphome/core
esphome/components/esp32_ble/* @Rapsssito @jesserockz
esphome/components/esp32_ble_client/* @jesserockz
@ -148,6 +150,7 @@ esphome/components/esp32_improv/* @jesserockz
esphome/components/esp32_rmt/* @jesserockz
esphome/components/esp32_rmt_led_strip/* @jesserockz
esphome/components/esp8266/* @esphome/core
esphome/components/esp_ldo/* @clydebarrow
esphome/components/ethernet_info/* @gtjadsonsantos
esphome/components/event/* @nohat
esphome/components/event_emitter/* @Rapsssito
@ -233,6 +236,7 @@ esphome/components/kamstrup_kmp/* @cfeenstra1024
esphome/components/key_collector/* @ssieb
esphome/components/key_provider/* @ssieb
esphome/components/kuntze/* @ssieb
esphome/components/lc709203f/* @ilikecake
esphome/components/lcd_menu/* @numo68
esphome/components/ld2410/* @regevbr @sebcaps
esphome/components/ld2420/* @descipher
@ -318,6 +322,7 @@ esphome/components/number/* @esphome/core
esphome/components/one_wire/* @ssieb
esphome/components/online_image/* @clydebarrow @guillempages
esphome/components/opentherm/* @olegtarasov
esphome/components/openthread/* @mrene
esphome/components/ota/* @esphome/core
esphome/components/output/* @esphome/core
esphome/components/packet_transport/* @clydebarrow
@ -478,6 +483,8 @@ esphome/components/ufire_ise/* @pvizeli
esphome/components/ultrasonic/* @OttoWinter
esphome/components/update/* @jesserockz
esphome/components/uponor_smatrix/* @kroimon
esphome/components/usb_host/* @clydebarrow
esphome/components/usb_uart/* @clydebarrow
esphome/components/valve/* @esphome/core
esphome/components/vbus/* @ssieb
esphome/components/veml3235/* @kbx81

View File

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

View File

@ -134,6 +134,7 @@ def get_port_type(port):
def run_miniterm(config, port, args):
from aioesphomeapi import LogParser
import serial
from esphome import platformio_api
@ -158,6 +159,7 @@ def run_miniterm(config, port, args):
ser.dtr = False
ser.rts = False
parser = LogParser()
tries = 0
while tries < 5:
try:
@ -174,8 +176,7 @@ def run_miniterm(config, port, args):
.decode("utf8", "backslashreplace")
)
time_str = datetime.now().time().strftime("[%H:%M:%S]")
message = time_str + line
safe_print(message)
safe_print(parser.parse_line(line, time_str))
backtrace_state = platformio_api.process_stacktrace(
config, line, backtrace_state=backtrace_state
@ -593,15 +594,20 @@ def command_update_all(args):
middle_text = f" {middle_text} "
width = len(click.unstyle(middle_text))
half_line = "=" * ((twidth - width) // 2)
click.echo(f"{half_line}{middle_text}{half_line}")
safe_print(f"{half_line}{middle_text}{half_line}")
for f in files:
print(f"Updating {color(AnsiFore.CYAN, f)}")
print("-" * twidth)
print()
rc = run_external_process(
"esphome", "--dashboard", "run", f, "--no-logs", "--device", "OTA"
)
safe_print(f"Updating {color(AnsiFore.CYAN, f)}")
safe_print("-" * twidth)
safe_print()
if CORE.dashboard:
rc = run_external_process(
"esphome", "--dashboard", "run", f, "--no-logs", "--device", "OTA"
)
else:
rc = run_external_process(
"esphome", "run", f, "--no-logs", "--device", "OTA"
)
if rc == 0:
print_bar(f"[{color(AnsiFore.BOLD_GREEN, 'SUCCESS')}] {f}")
success[f] = True
@ -609,17 +615,17 @@ def command_update_all(args):
print_bar(f"[{color(AnsiFore.BOLD_RED, 'ERROR')}] {f}")
success[f] = False
print()
print()
print()
safe_print()
safe_print()
safe_print()
print_bar(f"[{color(AnsiFore.BOLD_WHITE, 'SUMMARY')}]")
failed = 0
for f in files:
if success[f]:
print(f" - {f}: {color(AnsiFore.GREEN, 'SUCCESS')}")
safe_print(f" - {f}: {color(AnsiFore.GREEN, 'SUCCESS')}")
else:
print(f" - {f}: {color(AnsiFore.BOLD_RED, 'FAILED')}")
safe_print(f" - {f}: {color(AnsiFore.BOLD_RED, 'FAILED')}")
failed += 1
return failed

View File

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

View File

@ -7,7 +7,7 @@ namespace a4988 {
static const char *const TAG = "a4988.stepper";
void A4988::setup() {
ESP_LOGCONFIG(TAG, "Setting up A4988...");
ESP_LOGCONFIG(TAG, "Running setup");
if (this->sleep_pin_ != nullptr) {
this->sleep_pin_->setup();
this->sleep_pin_->digital_write(false);

View File

@ -7,7 +7,7 @@ namespace absolute_humidity {
static const char *const TAG = "absolute_humidity.sensor";
void AbsoluteHumidityComponent::setup() {
ESP_LOGCONFIG(TAG, "Setting up absolute humidity '%s'...", this->get_name().c_str());
ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->get_name().c_str());
ESP_LOGD(TAG, " Added callback for temperature '%s'", this->temperature_sensor_->get_name().c_str());
this->temperature_sensor_->add_on_state_callback([this](float state) { this->temperature_callback_(state); });
@ -40,9 +40,11 @@ void AbsoluteHumidityComponent::dump_config() {
break;
}
ESP_LOGCONFIG(TAG, "Sources");
ESP_LOGCONFIG(TAG, " Temperature: '%s'", this->temperature_sensor_->get_name().c_str());
ESP_LOGCONFIG(TAG, " Relative Humidity: '%s'", this->humidity_sensor_->get_name().c_str());
ESP_LOGCONFIG(TAG,
"Sources\n"
" Temperature: '%s'\n"
" Relative Humidity: '%s'",
this->temperature_sensor_->get_name().c_str(), this->humidity_sensor_->get_name().c_str());
}
float AbsoluteHumidityComponent::get_setup_priority() const { return setup_priority::DATA; }

View File

@ -214,8 +214,10 @@ void AcDimmer::dump_config() {
ESP_LOGCONFIG(TAG, "AcDimmer:");
LOG_PIN(" Output Pin: ", this->gate_pin_);
LOG_PIN(" Zero-Cross Pin: ", this->zero_cross_pin_);
ESP_LOGCONFIG(TAG, " Min Power: %.1f%%", this->store_.min_power / 10.0f);
ESP_LOGCONFIG(TAG, " Init with half cycle: %s", YESNO(this->init_with_half_cycle_));
ESP_LOGCONFIG(TAG,
" Min Power: %.1f%%\n"
" Init with half cycle: %s",
this->store_.min_power / 10.0f, YESNO(this->init_with_half_cycle_));
if (method_ == DIM_METHOD_LEADING_PULSE) {
ESP_LOGCONFIG(TAG, " Method: leading pulse");
} else if (method_ == DIM_METHOD_LEADING) {

View File

@ -22,7 +22,7 @@ static const int ADC_MAX = (1 << SOC_ADC_RTC_MAX_BITWIDTH) - 1;
static const int ADC_HALF = (1 << SOC_ADC_RTC_MAX_BITWIDTH) >> 1;
void ADCSensor::setup() {
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->get_name().c_str());
if (this->channel1_ != ADC1_CHANNEL_MAX) {
adc1_config_width(ADC_WIDTH_MAX_SOC_BITS);
@ -77,8 +77,10 @@ void ADCSensor::dump_config() {
break;
}
}
ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_);
ESP_LOGCONFIG(TAG, " Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
ESP_LOGCONFIG(TAG,
" Samples: %i\n"
" Sampling mode: %s",
this->sample_count_, LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
LOG_UPDATE_INTERVAL(this);
}

View File

@ -17,7 +17,7 @@ namespace adc {
static const char *const TAG = "adc.esp8266";
void ADCSensor::setup() {
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->get_name().c_str());
#ifndef USE_ADC_SENSOR_VCC
this->pin_->setup();
#endif
@ -30,8 +30,10 @@ void ADCSensor::dump_config() {
#else
LOG_PIN(" Pin: ", this->pin_);
#endif // USE_ADC_SENSOR_VCC
ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_);
ESP_LOGCONFIG(TAG, " Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
ESP_LOGCONFIG(TAG,
" Samples: %i\n"
" Sampling mode: %s",
this->sample_count_, LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
LOG_UPDATE_INTERVAL(this);
}

View File

@ -9,7 +9,7 @@ namespace adc {
static const char *const TAG = "adc.libretiny";
void ADCSensor::setup() {
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->get_name().c_str());
#ifndef USE_ADC_SENSOR_VCC
this->pin_->setup();
#endif // !USE_ADC_SENSOR_VCC
@ -22,8 +22,10 @@ void ADCSensor::dump_config() {
#else // USE_ADC_SENSOR_VCC
LOG_PIN(" Pin: ", this->pin_);
#endif // USE_ADC_SENSOR_VCC
ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_);
ESP_LOGCONFIG(TAG, " Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
ESP_LOGCONFIG(TAG,
" Samples: %i\n"
" Sampling mode: %s",
this->sample_count_, LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
LOG_UPDATE_INTERVAL(this);
}

View File

@ -14,7 +14,7 @@ namespace adc {
static const char *const TAG = "adc.rp2040";
void ADCSensor::setup() {
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->get_name().c_str());
static bool initialized = false;
if (!initialized) {
adc_init();
@ -33,8 +33,10 @@ void ADCSensor::dump_config() {
LOG_PIN(" Pin: ", this->pin_);
#endif // USE_ADC_SENSOR_VCC
}
ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_);
ESP_LOGCONFIG(TAG, " Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
ESP_LOGCONFIG(TAG,
" Samples: %i\n"
" Sampling mode: %s",
this->sample_count_, LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
LOG_UPDATE_INTERVAL(this);
}

View File

@ -9,7 +9,7 @@ static const char *const TAG = "adc128s102";
float ADC128S102::get_setup_priority() const { return setup_priority::HARDWARE; }
void ADC128S102::setup() {
ESP_LOGCONFIG(TAG, "Setting up adc128s102");
ESP_LOGCONFIG(TAG, "Running setup");
this->spi_setup();
}

View File

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

View File

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

View File

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

View File

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

View File

@ -10,15 +10,13 @@ static const uint8_t ADS1115_REGISTER_CONVERSION = 0x00;
static const uint8_t ADS1115_REGISTER_CONFIG = 0x01;
void ADS1115Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up ADS1115...");
ESP_LOGCONFIG(TAG, "Running setup");
uint16_t value;
if (!this->read_byte_16(ADS1115_REGISTER_CONVERSION, &value)) {
this->mark_failed();
return;
}
ESP_LOGCONFIG(TAG, "Configuring ADS1115...");
uint16_t config = 0;
// Clear single-shot bit
// 0b0xxxxxxxxxxxxxxx
@ -68,10 +66,10 @@ void ADS1115Component::setup() {
this->prev_config_ = config;
}
void ADS1115Component::dump_config() {
ESP_LOGCONFIG(TAG, "Setting up ADS1115...");
ESP_LOGCONFIG(TAG, "ADS1115:");
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, "Communication with ADS1115 failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
}
}
float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1115Gain gain,

View File

@ -1,4 +1,5 @@
#include "ads1118.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
@ -8,7 +9,7 @@ static const char *const TAG = "ads1118";
static const uint8_t ADS1118_DATA_RATE_860_SPS = 0b111;
void ADS1118::setup() {
ESP_LOGCONFIG(TAG, "Setting up ads1118");
ESP_LOGCONFIG(TAG, "Running setup");
this->spi_setup();
this->config_ = 0;

View File

@ -1,4 +1,5 @@
#include "ags10.h"
#include "esphome/core/helpers.h"
#include <cinttypes>
@ -23,7 +24,7 @@ static const uint16_t ZP_CURRENT = 0x0000;
static const uint16_t ZP_DEFAULT = 0xFFFF;
void AGS10Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up ags10...");
ESP_LOGCONFIG(TAG, "Running setup");
auto version = this->read_version_();
if (version) {
@ -65,7 +66,7 @@ void AGS10Component::dump_config() {
case NONE:
break;
case COMMUNICATION_FAILED:
ESP_LOGE(TAG, "Communication with AGS10 failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
break;
case CRC_CHECK_FAILED:
ESP_LOGE(TAG, "The crc check failed");

View File

@ -13,8 +13,9 @@
// results making successive requests; the current implementation makes 3 attempts with a delay of 30ms each time.
#include "aht10.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace aht10 {
@ -34,57 +35,59 @@ static const uint8_t AHT10_INIT_ATTEMPTS = 10;
static const uint8_t AHT10_STATUS_BUSY = 0x80;
static const float AHT10_DIVISOR = 1048576.0f; // 2^20, used for temperature and humidity calculations
void AHT10Component::setup() {
ESP_LOGCONFIG(TAG, "Running setup");
if (this->write(AHT10_SOFTRESET_CMD, sizeof(AHT10_SOFTRESET_CMD)) != i2c::ERROR_OK) {
ESP_LOGE(TAG, "Reset AHT10 failed!");
ESP_LOGE(TAG, "Reset failed");
}
delay(AHT10_SOFTRESET_DELAY);
i2c::ErrorCode error_code = i2c::ERROR_INVALID_ARGUMENT;
switch (this->variant_) {
case AHT10Variant::AHT20:
ESP_LOGCONFIG(TAG, "Setting up AHT20");
error_code = this->write(AHT20_INITIALIZE_CMD, sizeof(AHT20_INITIALIZE_CMD));
break;
case AHT10Variant::AHT10:
ESP_LOGCONFIG(TAG, "Setting up AHT10");
error_code = this->write(AHT10_INITIALIZE_CMD, sizeof(AHT10_INITIALIZE_CMD));
break;
}
if (error_code != i2c::ERROR_OK) {
ESP_LOGE(TAG, "Communication with AHT10 failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
this->mark_failed();
return;
}
uint8_t cal_attempts = 0;
uint8_t data = AHT10_STATUS_BUSY;
int cal_attempts = 0;
while (data & AHT10_STATUS_BUSY) {
delay(AHT10_DEFAULT_DELAY);
if (this->read(&data, 1) != i2c::ERROR_OK) {
ESP_LOGE(TAG, "Communication with AHT10 failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
this->mark_failed();
return;
}
++cal_attempts;
if (cal_attempts > AHT10_INIT_ATTEMPTS) {
ESP_LOGE(TAG, "AHT10 initialization timed out!");
ESP_LOGE(TAG, "Initialization timed out");
this->mark_failed();
return;
}
}
if ((data & 0x68) != 0x08) { // Bit[6:5] = 0b00, NORMAL mode and Bit[3] = 0b1, CALIBRATED
ESP_LOGE(TAG, "AHT10 initialization failed!");
ESP_LOGE(TAG, "Initialization failed");
this->mark_failed();
return;
}
ESP_LOGV(TAG, "AHT10 initialization");
ESP_LOGV(TAG, "Initialization complete");
}
void AHT10Component::restart_read_() {
if (this->read_count_ == AHT10_ATTEMPTS) {
this->read_count_ = 0;
this->status_set_error("Measurements reading timed-out!");
this->status_set_error("Reading timed out");
return;
}
this->read_count_++;
@ -97,24 +100,24 @@ void AHT10Component::read_data_() {
ESP_LOGD(TAG, "Read attempt %d at %ums", this->read_count_, (unsigned) (millis() - this->start_time_));
}
if (this->read(data, 6) != i2c::ERROR_OK) {
this->status_set_warning("AHT10 read failed, retrying soon");
this->status_set_warning("Read failed, will retry");
this->restart_read_();
return;
}
if ((data[0] & 0x80) == 0x80) { // Bit[7] = 0b1, device is busy
ESP_LOGD(TAG, "AHT10 is busy, waiting...");
ESP_LOGD(TAG, "Device busy, will retry");
this->restart_read_();
return;
}
if (data[1] == 0x0 && data[2] == 0x0 && (data[3] >> 4) == 0x0) {
// Unrealistic humidity (0x0)
// Invalid humidity (0x0)
if (this->humidity_sensor_ == nullptr) {
ESP_LOGV(TAG, "ATH10 Unrealistic humidity (0x0), but humidity is not required");
ESP_LOGV(TAG, "Invalid humidity (reading not required)");
} else {
ESP_LOGD(TAG, "ATH10 Unrealistic humidity (0x0), retrying...");
ESP_LOGD(TAG, "Invalid humidity, retrying");
if (this->write(AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD)) != i2c::ERROR_OK) {
this->status_set_warning("Communication with AHT10 failed!");
this->status_set_warning(ESP_LOG_MSG_COMM_FAIL);
}
this->restart_read_();
return;
@ -123,22 +126,17 @@ void AHT10Component::read_data_() {
if (this->read_count_ > 1) {
ESP_LOGD(TAG, "Success at %ums", (unsigned) (millis() - this->start_time_));
}
uint32_t raw_temperature = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];
uint32_t raw_humidity = ((data[1] << 16) | (data[2] << 8) | data[3]) >> 4;
uint32_t raw_temperature = encode_uint24(data[3] & 0xF, data[4], data[5]);
uint32_t raw_humidity = encode_uint24(data[1], data[2], data[3]) >> 4;
if (this->temperature_sensor_ != nullptr) {
float temperature = ((200.0f * (float) raw_temperature) / 1048576.0f) - 50.0f;
float temperature = ((200.0f * static_cast<float>(raw_temperature)) / AHT10_DIVISOR) - 50.0f;
this->temperature_sensor_->publish_state(temperature);
}
if (this->humidity_sensor_ != nullptr) {
float humidity;
if (raw_humidity == 0) { // unrealistic value
humidity = NAN;
} else {
humidity = (float) raw_humidity * 100.0f / 1048576.0f;
}
float humidity = raw_humidity == 0 ? NAN : static_cast<float>(raw_humidity) * 100.0f / AHT10_DIVISOR;
if (std::isnan(humidity)) {
ESP_LOGW(TAG, "Invalid humidity! Sensor reported 0%% Hum");
ESP_LOGW(TAG, "Invalid humidity reading (0%%), ");
}
this->humidity_sensor_->publish_state(humidity);
}
@ -150,7 +148,7 @@ void AHT10Component::update() {
return;
this->start_time_ = millis();
if (this->write(AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD)) != i2c::ERROR_OK) {
this->status_set_warning("Communication with AHT10 failed!");
this->status_set_warning(ESP_LOG_MSG_COMM_FAIL);
return;
}
this->restart_read_();
@ -162,7 +160,7 @@ void AHT10Component::dump_config() {
ESP_LOGCONFIG(TAG, "AHT10:");
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, "Communication with AHT10 failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
}
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);

View File

@ -17,7 +17,7 @@ static const char *const TAG = "aic3204";
}
void AIC3204::setup() {
ESP_LOGCONFIG(TAG, "Setting up AIC3204...");
ESP_LOGCONFIG(TAG, "Running setup");
// Set register page to 0
ERROR_CHECK(this->write_byte(AIC3204_PAGE_CTRL, 0x00), "Set page 0 failed");
@ -113,7 +113,7 @@ void AIC3204::dump_config() {
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, "Communication with AIC3204 failed");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
}
}

View File

@ -235,6 +235,7 @@ async def register_alarm_control_panel(var, config):
if not CORE.has_id(config[CONF_ID]):
var = cg.Pvariable(config[CONF_ID], var)
cg.add(cg.App.register_alarm_control_panel(var))
CORE.register_platform_component("alarm_control_panel", var)
await setup_alarm_control_panel_core_(var, config)

View File

@ -90,7 +90,7 @@ bool AM2315C::convert_(uint8_t *data, float &humidity, float &temperature) {
}
void AM2315C::setup() {
ESP_LOGCONFIG(TAG, "Setting up AM2315C...");
ESP_LOGCONFIG(TAG, "Running setup");
// get status
uint8_t status = 0;
@ -188,7 +188,7 @@ void AM2315C::dump_config() {
ESP_LOGCONFIG(TAG, "AM2315C:");
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, "Communication with AM2315C failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
}
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);

View File

@ -34,7 +34,7 @@ void AM2320Component::update() {
this->status_clear_warning();
}
void AM2320Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up AM2320...");
ESP_LOGCONFIG(TAG, "Running setup");
uint8_t data[8];
data[0] = 0;
data[1] = 4;
@ -47,7 +47,7 @@ void AM2320Component::dump_config() {
ESP_LOGD(TAG, "AM2320:");
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, "Communication with AM2320 failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
}
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);

View File

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

View File

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

View File

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

View File

@ -54,7 +54,7 @@ enum { // APDS9306 registers
}
void APDS9306::setup() {
ESP_LOGCONFIG(TAG, "Setting up APDS9306...");
ESP_LOGCONFIG(TAG, "Running setup");
uint8_t id;
if (!this->read_byte(APDS9306_PART_ID, &id)) { // Part ID register
@ -97,7 +97,7 @@ void APDS9306::dump_config() {
if (this->is_failed()) {
switch (this->error_code_) {
case COMMUNICATION_FAILED:
ESP_LOGE(TAG, "Communication with APDS9306 failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
break;
case WRONG_ID:
ESP_LOGE(TAG, "APDS9306 has invalid id!");
@ -108,9 +108,12 @@ void APDS9306::dump_config() {
}
}
ESP_LOGCONFIG(TAG, " Gain: %u", AMBIENT_LIGHT_GAIN_VALUES[this->gain_]);
ESP_LOGCONFIG(TAG, " Measurement rate: %u", MEASUREMENT_RATE_VALUES[this->measurement_rate_]);
ESP_LOGCONFIG(TAG, " Measurement Resolution/Bit width: %d", MEASUREMENT_BIT_WIDTH_VALUES[this->bit_width_]);
ESP_LOGCONFIG(TAG,
" Gain: %u\n"
" Measurement rate: %u\n"
" Measurement Resolution/Bit width: %d",
AMBIENT_LIGHT_GAIN_VALUES[this->gain_], MEASUREMENT_RATE_VALUES[this->measurement_rate_],
MEASUREMENT_BIT_WIDTH_VALUES[this->bit_width_]);
LOG_UPDATE_INTERVAL(this);
}

View File

@ -15,7 +15,7 @@ static const char *const TAG = "apds9960";
#define APDS9960_WRITE_BYTE(reg, value) APDS9960_ERROR_CHECK(this->write_byte(reg, value));
void APDS9960::setup() {
ESP_LOGCONFIG(TAG, "Setting up APDS9960...");
ESP_LOGCONFIG(TAG, "Running setup");
uint8_t id;
if (!this->read_byte(0x92, &id)) { // ID register
this->error_code_ = COMMUNICATION_FAILED;
@ -141,7 +141,7 @@ void APDS9960::dump_config() {
if (this->is_failed()) {
switch (this->error_code_) {
case COMMUNICATION_FAILED:
ESP_LOGE(TAG, "Communication with APDS9960 failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
break;
case WRONG_ID:
ESP_LOGE(TAG, "APDS9960 has invalid id!");

View File

@ -49,6 +49,7 @@ SERVICE_ARG_NATIVE_TYPES = {
"string[]": cg.std_vector.template(cg.std_string),
}
CONF_ENCRYPTION = "encryption"
CONF_BATCH_DELAY = "batch_delay"
def validate_encryption_key(value):
@ -109,6 +110,9 @@ CONFIG_SCHEMA = cv.All(
): ACTIONS_SCHEMA,
cv.Exclusive(CONF_ACTIONS, group_of_exclusion=CONF_ACTIONS): ACTIONS_SCHEMA,
cv.Optional(CONF_ENCRYPTION): _encryption_schema,
cv.Optional(
CONF_BATCH_DELAY, default="100ms"
): cv.positive_time_period_milliseconds,
cv.Optional(CONF_ON_CLIENT_CONNECTED): automation.validate_automation(
single=True
),
@ -129,6 +133,7 @@ async def to_code(config):
cg.add(var.set_port(config[CONF_PORT]))
cg.add(var.set_password(config[CONF_PASSWORD]))
cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT]))
cg.add(var.set_batch_delay(config[CONF_BATCH_DELAY]))
for conf in config.get(CONF_ACTIONS, []):
template_args = []

View File

@ -432,7 +432,8 @@ message FanCommandRequest {
enum ColorMode {
COLOR_MODE_UNKNOWN = 0;
COLOR_MODE_ON_OFF = 1;
COLOR_MODE_BRIGHTNESS = 2;
COLOR_MODE_LEGACY_BRIGHTNESS = 2;
COLOR_MODE_BRIGHTNESS = 3;
COLOR_MODE_WHITE = 7;
COLOR_MODE_COLOR_TEMPERATURE = 11;
COLOR_MODE_COLD_WARM_WHITE = 19;

View File

@ -3,12 +3,14 @@
#include <cerrno>
#include <cinttypes>
#include <utility>
#include <functional>
#include <limits>
#include "esphome/components/network/util.h"
#include "esphome/core/application.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
#include "esphome/core/version.h"
#include "esphome/core/application.h"
#ifdef USE_DEEP_SLEEP
#include "esphome/components/deep_sleep/deep_sleep_component.h"
@ -29,40 +31,8 @@ namespace api {
static const char *const TAG = "api.connection";
static const int ESP32_CAMERA_STOP_STREAM = 5000;
// helper for allowing only unique entries in the queue
void DeferredMessageQueue::dmq_push_back_with_dedup_(void *source, send_message_t send_message) {
DeferredMessage item(source, send_message);
auto iter = std::find_if(this->deferred_queue_.begin(), this->deferred_queue_.end(),
[&item](const DeferredMessage &test) -> bool { return test == item; });
if (iter != this->deferred_queue_.end()) {
(*iter) = item;
} else {
this->deferred_queue_.push_back(item);
}
}
void DeferredMessageQueue::process_queue() {
while (!deferred_queue_.empty()) {
DeferredMessage &de = deferred_queue_.front();
if ((this->api_connection_->*(de.send_message_))(de.source_)) {
// O(n) but memory efficiency is more important than speed here which is why std::vector was chosen
deferred_queue_.erase(deferred_queue_.begin());
} else {
break;
}
}
}
void DeferredMessageQueue::defer(void *source, send_message_t send_message) {
this->dmq_push_back_with_dedup_(source, send_message);
}
APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *parent)
: parent_(parent), deferred_message_queue_(this), initial_state_iterator_(this), list_entities_iterator_(this) {
this->proto_write_buffer_.reserve(64);
: parent_(parent), initial_state_iterator_(this), list_entities_iterator_(this) {
#if defined(USE_API_PLAINTEXT) && defined(USE_API_NOISE)
auto noise_ctx = parent->get_noise_ctx();
if (noise_ctx->has_psk()) {
@ -78,6 +48,9 @@ APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *pa
#error "No frame helper defined"
#endif
}
uint32_t APIConnection::get_batch_delay_ms_() const { return this->parent_->get_batch_delay(); }
void APIConnection::start() {
this->last_traffic_ = App.get_loop_component_start_time();
@ -118,7 +91,7 @@ void APIConnection::loop() {
// when network is disconnected force disconnect immediately
// don't wait for timeout
this->on_fatal_error();
ESP_LOGW(TAG, "%s: Network unavailable, disconnecting", this->client_combined_info_.c_str());
ESP_LOGW(TAG, "%s: Network unavailable; disconnecting", this->client_combined_info_.c_str());
return;
}
if (this->next_close_) {
@ -135,31 +108,41 @@ void APIConnection::loop() {
api_error_to_str(err), errno);
return;
}
ReadPacketBuffer buffer;
err = this->helper_->read_packet(&buffer);
if (err == APIError::WOULD_BLOCK) {
// pass
} else if (err != APIError::OK) {
on_fatal_error();
if (err == APIError::SOCKET_READ_FAILED && errno == ECONNRESET) {
ESP_LOGW(TAG, "%s: Connection reset", this->client_combined_info_.c_str());
} else if (err == APIError::CONNECTION_CLOSED) {
ESP_LOGW(TAG, "%s: Connection closed", this->client_combined_info_.c_str());
} else {
ESP_LOGW(TAG, "%s: Reading failed: %s errno=%d", this->client_combined_info_.c_str(), api_error_to_str(err),
errno);
}
return;
} else {
this->last_traffic_ = App.get_loop_component_start_time();
// read a packet
this->read_message(buffer.data_len, buffer.type, &buffer.container[buffer.data_offset]);
if (this->remove_)
// Check if socket has data ready before attempting to read
if (this->helper_->is_socket_ready()) {
ReadPacketBuffer buffer;
err = this->helper_->read_packet(&buffer);
if (err == APIError::WOULD_BLOCK) {
// pass
} else if (err != APIError::OK) {
on_fatal_error();
if (err == APIError::SOCKET_READ_FAILED && errno == ECONNRESET) {
ESP_LOGW(TAG, "%s: Connection reset", this->client_combined_info_.c_str());
} else if (err == APIError::CONNECTION_CLOSED) {
ESP_LOGW(TAG, "%s: Connection closed", this->client_combined_info_.c_str());
} else {
ESP_LOGW(TAG, "%s: Reading failed: %s errno=%d", this->client_combined_info_.c_str(), api_error_to_str(err),
errno);
}
return;
} else {
this->last_traffic_ = App.get_loop_component_start_time();
// read a packet
if (buffer.data_len > 0) {
this->read_message(buffer.data_len, buffer.type, &buffer.container[buffer.data_offset]);
} else {
this->read_message(0, buffer.type, nullptr);
}
if (this->remove_)
return;
}
}
if (!this->deferred_message_queue_.empty() && this->helper_->can_write_without_blocking()) {
this->deferred_message_queue_.process_queue();
// Process deferred batch if scheduled
if (this->deferred_batch_.batch_scheduled &&
App.get_loop_component_start_time() - this->deferred_batch_.batch_start_time >= this->get_batch_delay_ms_()) {
this->process_batch_();
}
if (!this->list_entities_iterator_.completed())
@ -174,42 +157,30 @@ void APIConnection::loop() {
// Disconnect if not responded within 2.5*keepalive
if (now - this->last_traffic_ > (KEEPALIVE_TIMEOUT_MS * 5) / 2) {
on_fatal_error();
ESP_LOGW(TAG, "%s didn't respond to ping request in time. Disconnecting...", this->client_combined_info_.c_str());
ESP_LOGW(TAG, "%s is unresponsive; disconnecting", this->client_combined_info_.c_str());
}
} else if (now - this->last_traffic_ > KEEPALIVE_TIMEOUT_MS && now > this->next_ping_retry_) {
ESP_LOGVV(TAG, "Sending keepalive PING...");
this->sent_ping_ = this->send_ping_request(PingRequest());
ESP_LOGVV(TAG, "Sending keepalive PING");
this->sent_ping_ = this->send_message(PingRequest());
if (!this->sent_ping_) {
this->next_ping_retry_ = now + ping_retry_interval;
this->ping_retries_++;
std::string warn_str = str_sprintf("%s: Sending keepalive failed %u time(s);",
this->client_combined_info_.c_str(), this->ping_retries_);
if (this->ping_retries_ >= max_ping_retries) {
on_fatal_error();
ESP_LOGE(TAG, "%s: Sending keepalive failed %d time(s). Disconnecting...", this->client_combined_info_.c_str(),
this->ping_retries_);
ESP_LOGE(TAG, "%s disconnecting", warn_str.c_str());
} else if (this->ping_retries_ >= 10) {
ESP_LOGW(TAG, "%s: Sending keepalive failed %d time(s), will retry in %d ms",
this->client_combined_info_.c_str(), this->ping_retries_, ping_retry_interval);
ESP_LOGW(TAG, "%s retrying in %u ms", warn_str.c_str(), ping_retry_interval);
} else {
ESP_LOGD(TAG, "%s: Sending keepalive failed %d time(s), will retry in %d ms",
this->client_combined_info_.c_str(), this->ping_retries_, ping_retry_interval);
ESP_LOGD(TAG, "%s retrying in %u ms", warn_str.c_str(), ping_retry_interval);
}
}
}
#ifdef USE_ESP32_CAMERA
if (this->image_reader_.available() && this->helper_->can_write_without_blocking()) {
// Message will use 8 more bytes than the minimum size, and typical
// MTU is 1500. Sometimes users will see as low as 1460 MTU.
// If its IPv6 the header is 40 bytes, and if its IPv4
// the header is 20 bytes. So we have 1460 - 40 = 1420 bytes
// available for the payload. But we also need to add the size of
// the protobuf overhead, which is 8 bytes.
//
// To be safe we pick 1390 bytes as the maximum size
// to send in one go. This is the maximum size of a single packet
// that can be sent over the network.
// This is to avoid fragmentation of the packet.
uint32_t to_send = std::min((size_t) 1390, this->image_reader_.available());
uint32_t to_send = std::min((size_t) MAX_PACKET_SIZE, this->image_reader_.available());
bool done = this->image_reader_.available() == to_send;
uint32_t msg_size = 0;
ProtoSize::add_fixed_field<4>(msg_size, 1, true);
@ -247,7 +218,7 @@ void APIConnection::loop() {
resp.entity_id = it.entity_id;
resp.attribute = it.attribute.value();
resp.once = it.once;
if (this->send_subscribe_home_assistant_state_response(resp)) {
if (this->send_message(resp)) {
state_subs_at_++;
}
}
@ -262,54 +233,100 @@ DisconnectResponse APIConnection::disconnect(const DisconnectRequest &msg) {
// remote initiated disconnect_client
// don't close yet, we still need to send the disconnect response
// close will happen on next loop
ESP_LOGD(TAG, "%s requested disconnected", this->client_combined_info_.c_str());
ESP_LOGD(TAG, "%s disconnected", this->client_combined_info_.c_str());
this->next_close_ = true;
DisconnectResponse resp;
return resp;
}
void APIConnection::on_disconnect_response(const DisconnectResponse &value) {
// pass
this->helper_->close();
this->remove_ = true;
}
// Encodes a message to the buffer and returns the total number of bytes used,
// including header and footer overhead. Returns 0 if the message doesn't fit.
uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint16_t message_type, APIConnection *conn,
uint32_t remaining_size, bool is_single) {
// Calculate size
uint32_t calculated_size = 0;
msg.calculate_size(calculated_size);
// Cache frame sizes to avoid repeated virtual calls
const uint8_t header_padding = conn->helper_->frame_header_padding();
const uint8_t footer_size = conn->helper_->frame_footer_size();
// Calculate total size with padding for buffer allocation
size_t total_calculated_size = calculated_size + header_padding + footer_size;
// Check if it fits
if (total_calculated_size > remaining_size) {
return 0; // Doesn't fit
}
// Allocate buffer space - pass payload size, allocation functions add header/footer space
ProtoWriteBuffer buffer = is_single ? conn->allocate_single_message_buffer(calculated_size)
: conn->allocate_batch_message_buffer(calculated_size);
// Get buffer size after allocation (which includes header padding)
std::vector<uint8_t> &shared_buf = conn->parent_->get_shared_buffer_ref();
size_t size_before_encode = shared_buf.size();
// Encode directly into buffer
msg.encode(buffer);
// Calculate actual encoded size (not including header that was already added)
size_t actual_payload_size = shared_buf.size() - size_before_encode;
// Return actual total size (header + actual payload + footer)
size_t actual_total_size = header_padding + actual_payload_size + footer_size;
// Verify that calculate_size() returned the correct value
assert(calculated_size == actual_payload_size);
return static_cast<uint16_t>(actual_total_size);
}
#ifdef USE_BINARY_SENSOR
bool APIConnection::send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor, bool state) {
return this->send_state_with_value_(binary_sensor, &APIConnection::try_send_binary_sensor_state_,
&APIConnection::try_send_binary_sensor_state_, state);
bool APIConnection::send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor) {
return this->schedule_message_(binary_sensor, &APIConnection::try_send_binary_sensor_state,
BinarySensorStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor) {
this->send_info_(static_cast<EntityBase *>(binary_sensor),
reinterpret_cast<send_message_t>(&APIConnection::try_send_binary_sensor_info_));
this->schedule_message_(binary_sensor, &APIConnection::try_send_binary_sensor_info,
ListEntitiesBinarySensorResponse::MESSAGE_TYPE);
}
bool APIConnection::try_send_binary_sensor_state_(binary_sensor::BinarySensor *binary_sensor) {
return this->try_send_binary_sensor_state_(binary_sensor, binary_sensor->state);
uint16_t APIConnection::try_send_binary_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *binary_sensor = static_cast<binary_sensor::BinarySensor *>(entity);
BinarySensorStateResponse resp;
resp.state = binary_sensor->state;
resp.missing_state = !binary_sensor->has_state();
resp.key = binary_sensor->get_object_id_hash();
return encode_message_to_buffer(resp, BinarySensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_binary_sensor_state_(binary_sensor::BinarySensor *binary_sensor, bool state) {
BinarySensorStateResponse msg;
msg.state = state;
msg.missing_state = !binary_sensor->has_state();
msg.key = binary_sensor->get_object_id_hash();
return this->send_binary_sensor_state_response(msg);
}
bool APIConnection::try_send_binary_sensor_info_(binary_sensor::BinarySensor *binary_sensor) {
uint16_t APIConnection::try_send_binary_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *binary_sensor = static_cast<binary_sensor::BinarySensor *>(entity);
ListEntitiesBinarySensorResponse msg;
msg.device_class = binary_sensor->get_device_class();
msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor();
msg.unique_id = get_default_unique_id("binary_sensor", binary_sensor);
return this->try_send_entity_info_(static_cast<EntityBase *>(binary_sensor), msg,
&APIConnection::send_list_entities_binary_sensor_response);
fill_entity_info_base(binary_sensor, msg);
return encode_message_to_buffer(msg, ListEntitiesBinarySensorResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
#endif
#ifdef USE_COVER
bool APIConnection::send_cover_state(cover::Cover *cover) {
return this->send_state_(static_cast<EntityBase *>(cover),
reinterpret_cast<send_message_t>(&APIConnection::try_send_cover_state_));
return this->schedule_message_(cover, &APIConnection::try_send_cover_state, CoverStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_cover_info(cover::Cover *cover) {
this->send_info_(static_cast<EntityBase *>(cover),
reinterpret_cast<send_message_t>(&APIConnection::try_send_cover_info_));
this->schedule_message_(cover, &APIConnection::try_send_cover_info, ListEntitiesCoverResponse::MESSAGE_TYPE);
}
bool APIConnection::try_send_cover_state_(cover::Cover *cover) {
uint16_t APIConnection::try_send_cover_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *cover = static_cast<cover::Cover *>(entity);
CoverStateResponse msg;
auto traits = cover->get_traits();
msg.legacy_state =
@ -319,9 +336,11 @@ bool APIConnection::try_send_cover_state_(cover::Cover *cover) {
msg.tilt = cover->tilt;
msg.current_operation = static_cast<enums::CoverOperation>(cover->current_operation);
msg.key = cover->get_object_id_hash();
return this->send_cover_state_response(msg);
return encode_message_to_buffer(msg, CoverStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_cover_info_(cover::Cover *cover) {
uint16_t APIConnection::try_send_cover_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *cover = static_cast<cover::Cover *>(entity);
ListEntitiesCoverResponse msg;
auto traits = cover->get_traits();
msg.assumed_state = traits.get_is_assumed_state();
@ -330,8 +349,8 @@ bool APIConnection::try_send_cover_info_(cover::Cover *cover) {
msg.supports_stop = traits.get_supports_stop();
msg.device_class = cover->get_device_class();
msg.unique_id = get_default_unique_id("cover", cover);
return this->try_send_entity_info_(static_cast<EntityBase *>(cover), msg,
&APIConnection::send_list_entities_cover_response);
fill_entity_info_base(cover, msg);
return encode_message_to_buffer(msg, ListEntitiesCoverResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::cover_command(const CoverCommandRequest &msg) {
cover::Cover *cover = App.get_cover_by_key(msg.key);
@ -364,14 +383,14 @@ void APIConnection::cover_command(const CoverCommandRequest &msg) {
#ifdef USE_FAN
bool APIConnection::send_fan_state(fan::Fan *fan) {
return this->send_state_(static_cast<EntityBase *>(fan),
reinterpret_cast<send_message_t>(&APIConnection::try_send_fan_state_));
return this->schedule_message_(fan, &APIConnection::try_send_fan_state, FanStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_fan_info(fan::Fan *fan) {
this->send_info_(static_cast<EntityBase *>(fan),
reinterpret_cast<send_message_t>(&APIConnection::try_send_fan_info_));
this->schedule_message_(fan, &APIConnection::try_send_fan_info, ListEntitiesFanResponse::MESSAGE_TYPE);
}
bool APIConnection::try_send_fan_state_(fan::Fan *fan) {
uint16_t APIConnection::try_send_fan_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *fan = static_cast<fan::Fan *>(entity);
FanStateResponse msg;
auto traits = fan->get_traits();
msg.state = fan->state;
@ -385,9 +404,11 @@ bool APIConnection::try_send_fan_state_(fan::Fan *fan) {
if (traits.supports_preset_modes())
msg.preset_mode = fan->preset_mode;
msg.key = fan->get_object_id_hash();
return this->send_fan_state_response(msg);
return encode_message_to_buffer(msg, FanStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_fan_info_(fan::Fan *fan) {
uint16_t APIConnection::try_send_fan_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *fan = static_cast<fan::Fan *>(entity);
ListEntitiesFanResponse msg;
auto traits = fan->get_traits();
msg.supports_oscillation = traits.supports_oscillation();
@ -397,8 +418,8 @@ bool APIConnection::try_send_fan_info_(fan::Fan *fan) {
for (auto const &preset : traits.supported_preset_modes())
msg.supported_preset_modes.push_back(preset);
msg.unique_id = get_default_unique_id("fan", fan);
return this->try_send_entity_info_(static_cast<EntityBase *>(fan), msg,
&APIConnection::send_list_entities_fan_response);
fill_entity_info_base(fan, msg);
return encode_message_to_buffer(msg, ListEntitiesFanResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::fan_command(const FanCommandRequest &msg) {
fan::Fan *fan = App.get_fan_by_key(msg.key);
@ -424,14 +445,14 @@ void APIConnection::fan_command(const FanCommandRequest &msg) {
#ifdef USE_LIGHT
bool APIConnection::send_light_state(light::LightState *light) {
return this->send_state_(static_cast<EntityBase *>(light),
reinterpret_cast<send_message_t>(&APIConnection::try_send_light_state_));
return this->schedule_message_(light, &APIConnection::try_send_light_state, LightStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_light_info(light::LightState *light) {
this->send_info_(static_cast<EntityBase *>(light),
reinterpret_cast<send_message_t>(&APIConnection::try_send_light_info_));
this->schedule_message_(light, &APIConnection::try_send_light_info, ListEntitiesLightResponse::MESSAGE_TYPE);
}
bool APIConnection::try_send_light_state_(light::LightState *light) {
uint16_t APIConnection::try_send_light_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *light = static_cast<light::LightState *>(entity);
LightStateResponse resp;
auto traits = light->get_traits();
auto values = light->remote_values;
@ -450,9 +471,11 @@ bool APIConnection::try_send_light_state_(light::LightState *light) {
if (light->supports_effects())
resp.effect = light->get_effect_name();
resp.key = light->get_object_id_hash();
return this->send_light_state_response(resp);
return encode_message_to_buffer(resp, LightStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_light_info_(light::LightState *light) {
uint16_t APIConnection::try_send_light_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *light = static_cast<light::LightState *>(entity);
ListEntitiesLightResponse msg;
auto traits = light->get_traits();
for (auto mode : traits.get_supported_color_modes())
@ -475,8 +498,8 @@ bool APIConnection::try_send_light_info_(light::LightState *light) {
}
}
msg.unique_id = get_default_unique_id("light", light);
return this->try_send_entity_info_(static_cast<EntityBase *>(light), msg,
&APIConnection::send_list_entities_light_response);
fill_entity_info_base(light, msg);
return encode_message_to_buffer(msg, ListEntitiesLightResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::light_command(const LightCommandRequest &msg) {
light::LightState *light = App.get_light_by_key(msg.key);
@ -516,26 +539,26 @@ void APIConnection::light_command(const LightCommandRequest &msg) {
#endif
#ifdef USE_SENSOR
bool APIConnection::send_sensor_state(sensor::Sensor *sensor, float state) {
return this->send_state_with_value_(sensor, &APIConnection::try_send_sensor_state_,
&APIConnection::try_send_sensor_state_, state);
bool APIConnection::send_sensor_state(sensor::Sensor *sensor) {
return this->schedule_message_(sensor, &APIConnection::try_send_sensor_state, SensorStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_sensor_info(sensor::Sensor *sensor) {
this->send_info_(static_cast<EntityBase *>(sensor),
reinterpret_cast<send_message_t>(&APIConnection::try_send_sensor_info_));
this->schedule_message_(sensor, &APIConnection::try_send_sensor_info, ListEntitiesSensorResponse::MESSAGE_TYPE);
}
bool APIConnection::try_send_sensor_state_(sensor::Sensor *sensor) {
return this->try_send_sensor_state_(sensor, sensor->state);
}
bool APIConnection::try_send_sensor_state_(sensor::Sensor *sensor, float state) {
SensorStateResponse resp;
resp.state = state;
resp.missing_state = !sensor->has_state();
uint16_t APIConnection::try_send_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *sensor = static_cast<sensor::Sensor *>(entity);
SensorStateResponse resp;
resp.state = sensor->state;
resp.missing_state = !sensor->has_state();
resp.key = sensor->get_object_id_hash();
return this->send_sensor_state_response(resp);
return encode_message_to_buffer(resp, SensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_sensor_info_(sensor::Sensor *sensor) {
uint16_t APIConnection::try_send_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *sensor = static_cast<sensor::Sensor *>(entity);
ListEntitiesSensorResponse msg;
msg.unit_of_measurement = sensor->get_unit_of_measurement();
msg.accuracy_decimals = sensor->get_accuracy_decimals();
@ -545,37 +568,37 @@ bool APIConnection::try_send_sensor_info_(sensor::Sensor *sensor) {
msg.unique_id = sensor->unique_id();
if (msg.unique_id.empty())
msg.unique_id = get_default_unique_id("sensor", sensor);
return this->try_send_entity_info_(static_cast<EntityBase *>(sensor), msg,
&APIConnection::send_list_entities_sensor_response);
fill_entity_info_base(sensor, msg);
return encode_message_to_buffer(msg, ListEntitiesSensorResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
#endif
#ifdef USE_SWITCH
bool APIConnection::send_switch_state(switch_::Switch *a_switch, bool state) {
return this->send_state_with_value_(a_switch, &APIConnection::try_send_switch_state_,
&APIConnection::try_send_switch_state_, state);
bool APIConnection::send_switch_state(switch_::Switch *a_switch) {
return this->schedule_message_(a_switch, &APIConnection::try_send_switch_state, SwitchStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_switch_info(switch_::Switch *a_switch) {
this->send_info_(static_cast<EntityBase *>(a_switch),
reinterpret_cast<send_message_t>(&APIConnection::try_send_switch_info_));
this->schedule_message_(a_switch, &APIConnection::try_send_switch_info, ListEntitiesSwitchResponse::MESSAGE_TYPE);
}
bool APIConnection::try_send_switch_state_(switch_::Switch *a_switch) {
return this->try_send_switch_state_(a_switch, a_switch->state);
}
bool APIConnection::try_send_switch_state_(switch_::Switch *a_switch, bool state) {
SwitchStateResponse resp;
resp.state = state;
uint16_t APIConnection::try_send_switch_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *a_switch = static_cast<switch_::Switch *>(entity);
SwitchStateResponse resp;
resp.state = a_switch->state;
resp.key = a_switch->get_object_id_hash();
return this->send_switch_state_response(resp);
return encode_message_to_buffer(resp, SwitchStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_switch_info_(switch_::Switch *a_switch) {
uint16_t APIConnection::try_send_switch_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *a_switch = static_cast<switch_::Switch *>(entity);
ListEntitiesSwitchResponse msg;
msg.assumed_state = a_switch->assumed_state();
msg.device_class = a_switch->get_device_class();
msg.unique_id = get_default_unique_id("switch", a_switch);
return this->try_send_entity_info_(static_cast<EntityBase *>(a_switch), msg,
&APIConnection::send_list_entities_switch_response);
fill_entity_info_base(a_switch, msg);
return encode_message_to_buffer(msg, ListEntitiesSwitchResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::switch_command(const SwitchCommandRequest &msg) {
switch_::Switch *a_switch = App.get_switch_by_key(msg.key);
@ -591,46 +614,44 @@ void APIConnection::switch_command(const SwitchCommandRequest &msg) {
#endif
#ifdef USE_TEXT_SENSOR
bool APIConnection::send_text_sensor_state(text_sensor::TextSensor *text_sensor, std::string state) {
return this->send_state_with_value_(text_sensor, &APIConnection::try_send_text_sensor_state_,
&APIConnection::try_send_text_sensor_state_, std::move(state));
bool APIConnection::send_text_sensor_state(text_sensor::TextSensor *text_sensor) {
return this->schedule_message_(text_sensor, &APIConnection::try_send_text_sensor_state,
TextSensorStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_text_sensor_info(text_sensor::TextSensor *text_sensor) {
this->send_info_(static_cast<EntityBase *>(text_sensor),
reinterpret_cast<send_message_t>(&APIConnection::try_send_text_sensor_info_));
this->schedule_message_(text_sensor, &APIConnection::try_send_text_sensor_info,
ListEntitiesTextSensorResponse::MESSAGE_TYPE);
}
bool APIConnection::try_send_text_sensor_state_(text_sensor::TextSensor *text_sensor) {
return this->try_send_text_sensor_state_(text_sensor, text_sensor->state);
}
bool APIConnection::try_send_text_sensor_state_(text_sensor::TextSensor *text_sensor, std::string state) {
TextSensorStateResponse resp;
resp.state = std::move(state);
resp.missing_state = !text_sensor->has_state();
uint16_t APIConnection::try_send_text_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *text_sensor = static_cast<text_sensor::TextSensor *>(entity);
TextSensorStateResponse resp;
resp.state = text_sensor->state;
resp.missing_state = !text_sensor->has_state();
resp.key = text_sensor->get_object_id_hash();
return this->send_text_sensor_state_response(resp);
return encode_message_to_buffer(resp, TextSensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_text_sensor_info_(text_sensor::TextSensor *text_sensor) {
uint16_t APIConnection::try_send_text_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *text_sensor = static_cast<text_sensor::TextSensor *>(entity);
ListEntitiesTextSensorResponse msg;
msg.device_class = text_sensor->get_device_class();
msg.unique_id = text_sensor->unique_id();
if (msg.unique_id.empty())
msg.unique_id = get_default_unique_id("text_sensor", text_sensor);
return this->try_send_entity_info_(static_cast<EntityBase *>(text_sensor), msg,
&APIConnection::send_list_entities_text_sensor_response);
fill_entity_info_base(text_sensor, msg);
return encode_message_to_buffer(msg, ListEntitiesTextSensorResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
#endif
#ifdef USE_CLIMATE
bool APIConnection::send_climate_state(climate::Climate *climate) {
return this->send_state_(static_cast<EntityBase *>(climate),
reinterpret_cast<send_message_t>(&APIConnection::try_send_climate_state_));
return this->schedule_message_(climate, &APIConnection::try_send_climate_state, ClimateStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_climate_info(climate::Climate *climate) {
this->send_info_(static_cast<EntityBase *>(climate),
reinterpret_cast<send_message_t>(&APIConnection::try_send_climate_info_));
}
bool APIConnection::try_send_climate_state_(climate::Climate *climate) {
uint16_t APIConnection::try_send_climate_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *climate = static_cast<climate::Climate *>(entity);
ClimateStateResponse resp;
resp.key = climate->get_object_id_hash();
auto traits = climate->get_traits();
@ -659,9 +680,14 @@ bool APIConnection::try_send_climate_state_(climate::Climate *climate) {
resp.current_humidity = climate->current_humidity;
if (traits.get_supports_target_humidity())
resp.target_humidity = climate->target_humidity;
return this->send_climate_state_response(resp);
return encode_message_to_buffer(resp, ClimateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_climate_info_(climate::Climate *climate) {
void APIConnection::send_climate_info(climate::Climate *climate) {
this->schedule_message_(climate, &APIConnection::try_send_climate_info, ListEntitiesClimateResponse::MESSAGE_TYPE);
}
uint16_t APIConnection::try_send_climate_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *climate = static_cast<climate::Climate *>(entity);
ListEntitiesClimateResponse msg;
auto traits = climate->get_traits();
msg.supports_current_temperature = traits.get_supports_current_temperature();
@ -689,8 +715,8 @@ bool APIConnection::try_send_climate_info_(climate::Climate *climate) {
for (auto swing_mode : traits.get_supported_swing_modes())
msg.supported_swing_modes.push_back(static_cast<enums::ClimateSwingMode>(swing_mode));
msg.unique_id = get_default_unique_id("climate", climate);
return this->try_send_entity_info_(static_cast<EntityBase *>(climate), msg,
&APIConnection::send_list_entities_climate_response);
fill_entity_info_base(climate, msg);
return encode_message_to_buffer(msg, ListEntitiesClimateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::climate_command(const ClimateCommandRequest &msg) {
climate::Climate *climate = App.get_climate_by_key(msg.key);
@ -723,26 +749,26 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) {
#endif
#ifdef USE_NUMBER
bool APIConnection::send_number_state(number::Number *number, float state) {
return this->send_state_with_value_(number, &APIConnection::try_send_number_state_,
&APIConnection::try_send_number_state_, state);
bool APIConnection::send_number_state(number::Number *number) {
return this->schedule_message_(number, &APIConnection::try_send_number_state, NumberStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_number_info(number::Number *number) {
this->send_info_(static_cast<EntityBase *>(number),
reinterpret_cast<send_message_t>(&APIConnection::try_send_number_info_));
this->schedule_message_(number, &APIConnection::try_send_number_info, ListEntitiesNumberResponse::MESSAGE_TYPE);
}
bool APIConnection::try_send_number_state_(number::Number *number) {
return this->try_send_number_state_(number, number->state);
}
bool APIConnection::try_send_number_state_(number::Number *number, float state) {
NumberStateResponse resp;
resp.state = state;
resp.missing_state = !number->has_state();
uint16_t APIConnection::try_send_number_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *number = static_cast<number::Number *>(entity);
NumberStateResponse resp;
resp.state = number->state;
resp.missing_state = !number->has_state();
resp.key = number->get_object_id_hash();
return this->send_number_state_response(resp);
return encode_message_to_buffer(resp, NumberStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_number_info_(number::Number *number) {
uint16_t APIConnection::try_send_number_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *number = static_cast<number::Number *>(entity);
ListEntitiesNumberResponse msg;
msg.unit_of_measurement = number->traits.get_unit_of_measurement();
msg.mode = static_cast<enums::NumberMode>(number->traits.get_mode());
@ -751,8 +777,8 @@ bool APIConnection::try_send_number_info_(number::Number *number) {
msg.max_value = number->traits.get_max_value();
msg.step = number->traits.get_step();
msg.unique_id = get_default_unique_id("number", number);
return this->try_send_entity_info_(static_cast<EntityBase *>(number), msg,
&APIConnection::send_list_entities_number_response);
fill_entity_info_base(number, msg);
return encode_message_to_buffer(msg, ListEntitiesNumberResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::number_command(const NumberCommandRequest &msg) {
number::Number *number = App.get_number_by_key(msg.key);
@ -767,28 +793,29 @@ void APIConnection::number_command(const NumberCommandRequest &msg) {
#ifdef USE_DATETIME_DATE
bool APIConnection::send_date_state(datetime::DateEntity *date) {
return this->send_state_(static_cast<EntityBase *>(date),
reinterpret_cast<send_message_t>(&APIConnection::try_send_date_state_));
return this->schedule_message_(date, &APIConnection::try_send_date_state, DateStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_date_info(datetime::DateEntity *date) {
this->send_info_(static_cast<EntityBase *>(date),
reinterpret_cast<send_message_t>(&APIConnection::try_send_date_info_));
}
bool APIConnection::try_send_date_state_(datetime::DateEntity *date) {
uint16_t APIConnection::try_send_date_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *date = static_cast<datetime::DateEntity *>(entity);
DateStateResponse resp;
resp.missing_state = !date->has_state();
resp.year = date->year;
resp.month = date->month;
resp.day = date->day;
resp.key = date->get_object_id_hash();
return this->send_date_state_response(resp);
return encode_message_to_buffer(resp, DateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_date_info_(datetime::DateEntity *date) {
void APIConnection::send_date_info(datetime::DateEntity *date) {
this->schedule_message_(date, &APIConnection::try_send_date_info, ListEntitiesDateResponse::MESSAGE_TYPE);
}
uint16_t APIConnection::try_send_date_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *date = static_cast<datetime::DateEntity *>(entity);
ListEntitiesDateResponse msg;
msg.unique_id = get_default_unique_id("date", date);
return this->try_send_entity_info_(static_cast<EntityBase *>(date), msg,
&APIConnection::send_list_entities_date_response);
fill_entity_info_base(date, msg);
return encode_message_to_buffer(msg, ListEntitiesDateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::date_command(const DateCommandRequest &msg) {
datetime::DateEntity *date = App.get_date_by_key(msg.key);
@ -803,28 +830,29 @@ void APIConnection::date_command(const DateCommandRequest &msg) {
#ifdef USE_DATETIME_TIME
bool APIConnection::send_time_state(datetime::TimeEntity *time) {
return this->send_state_(static_cast<EntityBase *>(time),
reinterpret_cast<send_message_t>(&APIConnection::try_send_time_state_));
return this->schedule_message_(time, &APIConnection::try_send_time_state, TimeStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_time_info(datetime::TimeEntity *time) {
this->send_info_(static_cast<EntityBase *>(time),
reinterpret_cast<send_message_t>(&APIConnection::try_send_time_info_));
}
bool APIConnection::try_send_time_state_(datetime::TimeEntity *time) {
uint16_t APIConnection::try_send_time_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *time = static_cast<datetime::TimeEntity *>(entity);
TimeStateResponse resp;
resp.missing_state = !time->has_state();
resp.hour = time->hour;
resp.minute = time->minute;
resp.second = time->second;
resp.key = time->get_object_id_hash();
return this->send_time_state_response(resp);
return encode_message_to_buffer(resp, TimeStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_time_info_(datetime::TimeEntity *time) {
void APIConnection::send_time_info(datetime::TimeEntity *time) {
this->schedule_message_(time, &APIConnection::try_send_time_info, ListEntitiesTimeResponse::MESSAGE_TYPE);
}
uint16_t APIConnection::try_send_time_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *time = static_cast<datetime::TimeEntity *>(entity);
ListEntitiesTimeResponse msg;
msg.unique_id = get_default_unique_id("time", time);
return this->try_send_entity_info_(static_cast<EntityBase *>(time), msg,
&APIConnection::send_list_entities_time_response);
fill_entity_info_base(time, msg);
return encode_message_to_buffer(msg, ListEntitiesTimeResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::time_command(const TimeCommandRequest &msg) {
datetime::TimeEntity *time = App.get_time_by_key(msg.key);
@ -839,29 +867,31 @@ void APIConnection::time_command(const TimeCommandRequest &msg) {
#ifdef USE_DATETIME_DATETIME
bool APIConnection::send_datetime_state(datetime::DateTimeEntity *datetime) {
return this->send_state_(static_cast<EntityBase *>(datetime),
reinterpret_cast<send_message_t>(&APIConnection::try_send_datetime_state_));
return this->schedule_message_(datetime, &APIConnection::try_send_datetime_state,
DateTimeStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_datetime_info(datetime::DateTimeEntity *datetime) {
this->send_info_(static_cast<EntityBase *>(datetime),
reinterpret_cast<send_message_t>(&APIConnection::try_send_datetime_info_));
}
bool APIConnection::try_send_datetime_state_(datetime::DateTimeEntity *datetime) {
uint16_t APIConnection::try_send_datetime_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *datetime = static_cast<datetime::DateTimeEntity *>(entity);
DateTimeStateResponse resp;
resp.missing_state = !datetime->has_state();
if (datetime->has_state()) {
ESPTime state = datetime->state_as_esptime();
resp.epoch_seconds = state.timestamp;
}
resp.key = datetime->get_object_id_hash();
return this->send_date_time_state_response(resp);
return encode_message_to_buffer(resp, DateTimeStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_datetime_info_(datetime::DateTimeEntity *datetime) {
void APIConnection::send_datetime_info(datetime::DateTimeEntity *datetime) {
this->schedule_message_(datetime, &APIConnection::try_send_datetime_info, ListEntitiesDateTimeResponse::MESSAGE_TYPE);
}
uint16_t APIConnection::try_send_datetime_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *datetime = static_cast<datetime::DateTimeEntity *>(entity);
ListEntitiesDateTimeResponse msg;
msg.unique_id = get_default_unique_id("datetime", datetime);
return this->try_send_entity_info_(static_cast<EntityBase *>(datetime), msg,
&APIConnection::send_list_entities_date_time_response);
fill_entity_info_base(datetime, msg);
return encode_message_to_buffer(msg, ListEntitiesDateTimeResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::datetime_command(const DateTimeCommandRequest &msg) {
datetime::DateTimeEntity *datetime = App.get_datetime_by_key(msg.key);
@ -875,32 +905,34 @@ void APIConnection::datetime_command(const DateTimeCommandRequest &msg) {
#endif
#ifdef USE_TEXT
bool APIConnection::send_text_state(text::Text *text, std::string state) {
return this->send_state_with_value_(text, &APIConnection::try_send_text_state_, &APIConnection::try_send_text_state_,
std::move(state));
bool APIConnection::send_text_state(text::Text *text) {
return this->schedule_message_(text, &APIConnection::try_send_text_state, TextStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_text_info(text::Text *text) {
this->send_info_(static_cast<EntityBase *>(text),
reinterpret_cast<send_message_t>(&APIConnection::try_send_text_info_));
this->schedule_message_(text, &APIConnection::try_send_text_info, ListEntitiesTextResponse::MESSAGE_TYPE);
}
bool APIConnection::try_send_text_state_(text::Text *text) { return this->try_send_text_state_(text, text->state); }
bool APIConnection::try_send_text_state_(text::Text *text, std::string state) {
TextStateResponse resp;
resp.state = std::move(state);
resp.missing_state = !text->has_state();
uint16_t APIConnection::try_send_text_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *text = static_cast<text::Text *>(entity);
TextStateResponse resp;
resp.state = text->state;
resp.missing_state = !text->has_state();
resp.key = text->get_object_id_hash();
return this->send_text_state_response(resp);
return encode_message_to_buffer(resp, TextStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_text_info_(text::Text *text) {
uint16_t APIConnection::try_send_text_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *text = static_cast<text::Text *>(entity);
ListEntitiesTextResponse msg;
msg.mode = static_cast<enums::TextMode>(text->traits.get_mode());
msg.min_length = text->traits.get_min_length();
msg.max_length = text->traits.get_max_length();
msg.pattern = text->traits.get_pattern();
msg.unique_id = get_default_unique_id("text", text);
return this->try_send_entity_info_(static_cast<EntityBase *>(text), msg,
&APIConnection::send_list_entities_text_response);
fill_entity_info_base(text, msg);
return encode_message_to_buffer(msg, ListEntitiesTextResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::text_command(const TextCommandRequest &msg) {
text::Text *text = App.get_text_by_key(msg.key);
@ -914,32 +946,32 @@ void APIConnection::text_command(const TextCommandRequest &msg) {
#endif
#ifdef USE_SELECT
bool APIConnection::send_select_state(select::Select *select, std::string state) {
return this->send_state_with_value_(select, &APIConnection::try_send_select_state_,
&APIConnection::try_send_select_state_, std::move(state));
bool APIConnection::send_select_state(select::Select *select) {
return this->schedule_message_(select, &APIConnection::try_send_select_state, SelectStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_select_info(select::Select *select) {
this->send_info_(static_cast<EntityBase *>(select),
reinterpret_cast<send_message_t>(&APIConnection::try_send_select_info_));
this->schedule_message_(select, &APIConnection::try_send_select_info, ListEntitiesSelectResponse::MESSAGE_TYPE);
}
bool APIConnection::try_send_select_state_(select::Select *select) {
return this->try_send_select_state_(select, select->state);
}
bool APIConnection::try_send_select_state_(select::Select *select, std::string state) {
SelectStateResponse resp;
resp.state = std::move(state);
resp.missing_state = !select->has_state();
uint16_t APIConnection::try_send_select_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *select = static_cast<select::Select *>(entity);
SelectStateResponse resp;
resp.state = select->state;
resp.missing_state = !select->has_state();
resp.key = select->get_object_id_hash();
return this->send_select_state_response(resp);
return encode_message_to_buffer(resp, SelectStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_select_info_(select::Select *select) {
uint16_t APIConnection::try_send_select_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *select = static_cast<select::Select *>(entity);
ListEntitiesSelectResponse msg;
for (const auto &option : select->traits.get_options())
msg.options.push_back(option);
msg.unique_id = get_default_unique_id("select", select);
return this->try_send_entity_info_(static_cast<EntityBase *>(select), msg,
&APIConnection::send_list_entities_select_response);
fill_entity_info_base(select, msg);
return encode_message_to_buffer(msg, ListEntitiesSelectResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::select_command(const SelectCommandRequest &msg) {
select::Select *select = App.get_select_by_key(msg.key);
@ -954,15 +986,16 @@ void APIConnection::select_command(const SelectCommandRequest &msg) {
#ifdef USE_BUTTON
void esphome::api::APIConnection::send_button_info(button::Button *button) {
this->send_info_(static_cast<EntityBase *>(button),
reinterpret_cast<send_message_t>(&APIConnection::try_send_button_info_));
this->schedule_message_(button, &APIConnection::try_send_button_info, ListEntitiesButtonResponse::MESSAGE_TYPE);
}
bool esphome::api::APIConnection::try_send_button_info_(button::Button *button) {
uint16_t APIConnection::try_send_button_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *button = static_cast<button::Button *>(entity);
ListEntitiesButtonResponse msg;
msg.device_class = button->get_device_class();
msg.unique_id = get_default_unique_id("button", button);
return this->try_send_entity_info_(static_cast<EntityBase *>(button), msg,
&APIConnection::send_list_entities_button_response);
fill_entity_info_base(button, msg);
return encode_message_to_buffer(msg, ListEntitiesButtonResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void esphome::api::APIConnection::button_command(const ButtonCommandRequest &msg) {
button::Button *button = App.get_button_by_key(msg.key);
@ -974,32 +1007,32 @@ void esphome::api::APIConnection::button_command(const ButtonCommandRequest &msg
#endif
#ifdef USE_LOCK
bool APIConnection::send_lock_state(lock::Lock *a_lock, lock::LockState state) {
return this->send_state_with_value_(a_lock, &APIConnection::try_send_lock_state_,
&APIConnection::try_send_lock_state_, state);
bool APIConnection::send_lock_state(lock::Lock *a_lock) {
return this->schedule_message_(a_lock, &APIConnection::try_send_lock_state, LockStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_lock_info(lock::Lock *a_lock) {
this->send_info_(static_cast<EntityBase *>(a_lock),
reinterpret_cast<send_message_t>(&APIConnection::try_send_lock_info_));
this->schedule_message_(a_lock, &APIConnection::try_send_lock_info, ListEntitiesLockResponse::MESSAGE_TYPE);
}
bool APIConnection::try_send_lock_state_(lock::Lock *a_lock) {
return this->try_send_lock_state_(a_lock, a_lock->state);
}
bool APIConnection::try_send_lock_state_(lock::Lock *a_lock, lock::LockState state) {
LockStateResponse resp;
resp.state = static_cast<enums::LockState>(state);
uint16_t APIConnection::try_send_lock_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *a_lock = static_cast<lock::Lock *>(entity);
LockStateResponse resp;
resp.state = static_cast<enums::LockState>(a_lock->state);
resp.key = a_lock->get_object_id_hash();
return this->send_lock_state_response(resp);
return encode_message_to_buffer(resp, LockStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_lock_info_(lock::Lock *a_lock) {
uint16_t APIConnection::try_send_lock_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *a_lock = static_cast<lock::Lock *>(entity);
ListEntitiesLockResponse msg;
msg.assumed_state = a_lock->traits.get_assumed_state();
msg.supports_open = a_lock->traits.get_supports_open();
msg.requires_code = a_lock->traits.get_requires_code();
msg.unique_id = get_default_unique_id("lock", a_lock);
return this->try_send_entity_info_(static_cast<EntityBase *>(a_lock), msg,
&APIConnection::send_list_entities_lock_response);
fill_entity_info_base(a_lock, msg);
return encode_message_to_buffer(msg, ListEntitiesLockResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::lock_command(const LockCommandRequest &msg) {
lock::Lock *a_lock = App.get_lock_by_key(msg.key);
@ -1022,22 +1055,23 @@ void APIConnection::lock_command(const LockCommandRequest &msg) {
#ifdef USE_VALVE
bool APIConnection::send_valve_state(valve::Valve *valve) {
return this->send_state_(static_cast<EntityBase *>(valve),
reinterpret_cast<send_message_t>(&APIConnection::try_send_valve_state_));
return this->schedule_message_(valve, &APIConnection::try_send_valve_state, ValveStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_valve_info(valve::Valve *valve) {
this->send_info_(static_cast<EntityBase *>(valve),
reinterpret_cast<send_message_t>(&APIConnection::try_send_valve_info_));
}
bool APIConnection::try_send_valve_state_(valve::Valve *valve) {
uint16_t APIConnection::try_send_valve_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *valve = static_cast<valve::Valve *>(entity);
ValveStateResponse resp;
resp.position = valve->position;
resp.current_operation = static_cast<enums::ValveOperation>(valve->current_operation);
resp.key = valve->get_object_id_hash();
return this->send_valve_state_response(resp);
return encode_message_to_buffer(resp, ValveStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_valve_info_(valve::Valve *valve) {
void APIConnection::send_valve_info(valve::Valve *valve) {
this->schedule_message_(valve, &APIConnection::try_send_valve_info, ListEntitiesValveResponse::MESSAGE_TYPE);
}
uint16_t APIConnection::try_send_valve_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *valve = static_cast<valve::Valve *>(entity);
ListEntitiesValveResponse msg;
auto traits = valve->get_traits();
msg.device_class = valve->get_device_class();
@ -1045,8 +1079,8 @@ bool APIConnection::try_send_valve_info_(valve::Valve *valve) {
msg.supports_position = traits.get_supports_position();
msg.supports_stop = traits.get_supports_stop();
msg.unique_id = get_default_unique_id("valve", valve);
return this->try_send_entity_info_(static_cast<EntityBase *>(valve), msg,
&APIConnection::send_list_entities_valve_response);
fill_entity_info_base(valve, msg);
return encode_message_to_buffer(msg, ListEntitiesValveResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::valve_command(const ValveCommandRequest &msg) {
valve::Valve *valve = App.get_valve_by_key(msg.key);
@ -1064,14 +1098,12 @@ void APIConnection::valve_command(const ValveCommandRequest &msg) {
#ifdef USE_MEDIA_PLAYER
bool APIConnection::send_media_player_state(media_player::MediaPlayer *media_player) {
return this->send_state_(static_cast<EntityBase *>(media_player),
reinterpret_cast<send_message_t>(&APIConnection::try_send_media_player_state_));
return this->schedule_message_(media_player, &APIConnection::try_send_media_player_state,
MediaPlayerStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_media_player_info(media_player::MediaPlayer *media_player) {
this->send_info_(static_cast<EntityBase *>(media_player),
reinterpret_cast<send_message_t>(&APIConnection::try_send_media_player_info_));
}
bool APIConnection::try_send_media_player_state_(media_player::MediaPlayer *media_player) {
uint16_t APIConnection::try_send_media_player_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *media_player = static_cast<media_player::MediaPlayer *>(entity);
MediaPlayerStateResponse resp;
media_player::MediaPlayerState report_state = media_player->state == media_player::MEDIA_PLAYER_STATE_ANNOUNCING
? media_player::MEDIA_PLAYER_STATE_PLAYING
@ -1079,11 +1111,16 @@ bool APIConnection::try_send_media_player_state_(media_player::MediaPlayer *medi
resp.state = static_cast<enums::MediaPlayerState>(report_state);
resp.volume = media_player->volume;
resp.muted = media_player->is_muted();
resp.key = media_player->get_object_id_hash();
return this->send_media_player_state_response(resp);
return encode_message_to_buffer(resp, MediaPlayerStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_media_player_info_(media_player::MediaPlayer *media_player) {
void APIConnection::send_media_player_info(media_player::MediaPlayer *media_player) {
this->schedule_message_(media_player, &APIConnection::try_send_media_player_info,
ListEntitiesMediaPlayerResponse::MESSAGE_TYPE);
}
uint16_t APIConnection::try_send_media_player_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *media_player = static_cast<media_player::MediaPlayer *>(entity);
ListEntitiesMediaPlayerResponse msg;
auto traits = media_player->get_traits();
msg.supports_pause = traits.get_supports_pause();
@ -1097,8 +1134,8 @@ bool APIConnection::try_send_media_player_info_(media_player::MediaPlayer *media
msg.supported_formats.push_back(media_format);
}
msg.unique_id = get_default_unique_id("media_player", media_player);
return this->try_send_entity_info_(static_cast<EntityBase *>(media_player), msg,
&APIConnection::send_list_entities_media_player_response);
fill_entity_info_base(media_player, msg);
return encode_message_to_buffer(msg, ListEntitiesMediaPlayerResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::media_player_command(const MediaPlayerCommandRequest &msg) {
media_player::MediaPlayer *media_player = App.get_media_player_by_key(msg.key);
@ -1133,14 +1170,15 @@ void APIConnection::set_camera_state(std::shared_ptr<esp32_camera::CameraImage>
this->image_reader_.set_image(std::move(image));
}
void APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) {
this->send_info_(static_cast<EntityBase *>(camera),
reinterpret_cast<send_message_t>(&APIConnection::try_send_camera_info_));
this->schedule_message_(camera, &APIConnection::try_send_camera_info, ListEntitiesCameraResponse::MESSAGE_TYPE);
}
bool APIConnection::try_send_camera_info_(esp32_camera::ESP32Camera *camera) {
uint16_t APIConnection::try_send_camera_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *camera = static_cast<esp32_camera::ESP32Camera *>(entity);
ListEntitiesCameraResponse msg;
msg.unique_id = get_default_unique_id("camera", camera);
return this->try_send_entity_info_(static_cast<EntityBase *>(camera), msg,
&APIConnection::send_list_entities_camera_response);
fill_entity_info_base(camera, msg);
return encode_message_to_buffer(msg, ListEntitiesCameraResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::camera_image(const CameraImageRequest &msg) {
if (esp32_camera::global_esp32_camera == nullptr)
@ -1183,9 +1221,9 @@ bool APIConnection::send_bluetooth_le_advertisement(const BluetoothLEAdvertiseme
manufacturer_data.legacy_data.assign(manufacturer_data.data.begin(), manufacturer_data.data.end());
manufacturer_data.data.clear();
}
return this->send_bluetooth_le_advertisement_response(resp);
return this->send_message(resp);
}
return this->send_bluetooth_le_advertisement_response(msg);
return this->send_message(msg);
}
void APIConnection::bluetooth_device_request(const BluetoothDeviceRequest &msg) {
bluetooth_proxy::global_bluetooth_proxy->bluetooth_device_request(msg);
@ -1329,28 +1367,32 @@ void APIConnection::voice_assistant_set_configuration(const VoiceAssistantSetCon
#ifdef USE_ALARM_CONTROL_PANEL
bool APIConnection::send_alarm_control_panel_state(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
return this->send_state_(static_cast<EntityBase *>(a_alarm_control_panel),
reinterpret_cast<send_message_t>(&APIConnection::try_send_alarm_control_panel_state_));
return this->schedule_message_(a_alarm_control_panel, &APIConnection::try_send_alarm_control_panel_state,
AlarmControlPanelStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
this->send_info_(static_cast<EntityBase *>(a_alarm_control_panel),
reinterpret_cast<send_message_t>(&APIConnection::try_send_alarm_control_panel_info_));
}
bool APIConnection::try_send_alarm_control_panel_state_(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
uint16_t APIConnection::try_send_alarm_control_panel_state(EntityBase *entity, APIConnection *conn,
uint32_t remaining_size, bool is_single) {
auto *a_alarm_control_panel = static_cast<alarm_control_panel::AlarmControlPanel *>(entity);
AlarmControlPanelStateResponse resp;
resp.state = static_cast<enums::AlarmControlPanelState>(a_alarm_control_panel->get_state());
resp.key = a_alarm_control_panel->get_object_id_hash();
return this->send_alarm_control_panel_state_response(resp);
return encode_message_to_buffer(resp, AlarmControlPanelStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_alarm_control_panel_info_(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
void APIConnection::send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
this->schedule_message_(a_alarm_control_panel, &APIConnection::try_send_alarm_control_panel_info,
ListEntitiesAlarmControlPanelResponse::MESSAGE_TYPE);
}
uint16_t APIConnection::try_send_alarm_control_panel_info(EntityBase *entity, APIConnection *conn,
uint32_t remaining_size, bool is_single) {
auto *a_alarm_control_panel = static_cast<alarm_control_panel::AlarmControlPanel *>(entity);
ListEntitiesAlarmControlPanelResponse msg;
msg.supported_features = a_alarm_control_panel->get_supported_features();
msg.requires_code = a_alarm_control_panel->get_requires_code();
msg.requires_code_to_arm = a_alarm_control_panel->get_requires_code_to_arm();
msg.unique_id = get_default_unique_id("alarm_control_panel", a_alarm_control_panel);
return this->try_send_entity_info_(static_cast<EntityBase *>(a_alarm_control_panel), msg,
&APIConnection::send_list_entities_alarm_control_panel_response);
fill_entity_info_base(a_alarm_control_panel, msg);
return encode_message_to_buffer(msg, ListEntitiesAlarmControlPanelResponse::MESSAGE_TYPE, conn, remaining_size,
is_single);
}
void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) {
alarm_control_panel::AlarmControlPanel *a_alarm_control_panel = App.get_alarm_control_panel_by_key(msg.key);
@ -1387,45 +1429,40 @@ void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRe
#endif
#ifdef USE_EVENT
void APIConnection::send_event(event::Event *event, std::string event_type) {
this->send_state_with_value_(event, &APIConnection::try_send_event_, &APIConnection::try_send_event_,
std::move(event_type));
void APIConnection::send_event(event::Event *event, const std::string &event_type) {
this->schedule_message_(event, MessageCreator(event_type, EventResponse::MESSAGE_TYPE), EventResponse::MESSAGE_TYPE);
}
void APIConnection::send_event_info(event::Event *event) {
this->send_info_(static_cast<EntityBase *>(event),
reinterpret_cast<send_message_t>(&APIConnection::try_send_event_info_));
this->schedule_message_(event, &APIConnection::try_send_event_info, ListEntitiesEventResponse::MESSAGE_TYPE);
}
bool APIConnection::try_send_event_(event::Event *event) {
return this->try_send_event_(event, *(event->last_event_type));
}
bool APIConnection::try_send_event_(event::Event *event, std::string event_type) {
uint16_t APIConnection::try_send_event_response(event::Event *event, const std::string &event_type, APIConnection *conn,
uint32_t remaining_size, bool is_single) {
EventResponse resp;
resp.event_type = std::move(event_type);
resp.event_type = event_type;
resp.key = event->get_object_id_hash();
return this->send_event_response(resp);
return encode_message_to_buffer(resp, EventResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_event_info_(event::Event *event) {
uint16_t APIConnection::try_send_event_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *event = static_cast<event::Event *>(entity);
ListEntitiesEventResponse msg;
msg.device_class = event->get_device_class();
for (const auto &event_type : event->get_event_types())
msg.event_types.push_back(event_type);
msg.unique_id = get_default_unique_id("event", event);
return this->try_send_entity_info_(static_cast<EntityBase *>(event), msg,
&APIConnection::send_list_entities_event_response);
fill_entity_info_base(event, msg);
return encode_message_to_buffer(msg, ListEntitiesEventResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
#endif
#ifdef USE_UPDATE
bool APIConnection::send_update_state(update::UpdateEntity *update) {
return this->send_state_(static_cast<EntityBase *>(update),
reinterpret_cast<send_message_t>(&APIConnection::try_send_update_state_));
return this->schedule_message_(update, &APIConnection::try_send_update_state, UpdateStateResponse::MESSAGE_TYPE);
}
void APIConnection::send_update_info(update::UpdateEntity *update) {
this->send_info_(static_cast<EntityBase *>(update),
reinterpret_cast<send_message_t>(&APIConnection::try_send_update_info_));
}
bool APIConnection::try_send_update_state_(update::UpdateEntity *update) {
uint16_t APIConnection::try_send_update_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *update = static_cast<update::UpdateEntity *>(entity);
UpdateStateResponse resp;
resp.missing_state = !update->has_state();
if (update->has_state()) {
@ -1440,16 +1477,20 @@ bool APIConnection::try_send_update_state_(update::UpdateEntity *update) {
resp.release_summary = update->update_info.summary;
resp.release_url = update->update_info.release_url;
}
resp.key = update->get_object_id_hash();
return this->send_update_state_response(resp);
return encode_message_to_buffer(resp, UpdateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
bool APIConnection::try_send_update_info_(update::UpdateEntity *update) {
void APIConnection::send_update_info(update::UpdateEntity *update) {
this->schedule_message_(update, &APIConnection::try_send_update_info, ListEntitiesUpdateResponse::MESSAGE_TYPE);
}
uint16_t APIConnection::try_send_update_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
auto *update = static_cast<update::UpdateEntity *>(entity);
ListEntitiesUpdateResponse msg;
msg.device_class = update->get_device_class();
msg.unique_id = get_default_unique_id("update", update);
return this->try_send_entity_info_(static_cast<EntityBase *>(update), msg,
&APIConnection::send_list_entities_update_response);
fill_entity_info_base(update, msg);
return encode_message_to_buffer(msg, ListEntitiesUpdateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
void APIConnection::update_command(const UpdateCommandRequest &msg) {
update::UpdateEntity *update = App.get_update_by_key(msg.key);
@ -1464,7 +1505,7 @@ void APIConnection::update_command(const UpdateCommandRequest &msg) {
update->check();
break;
case enums::UPDATE_COMMAND_NONE:
ESP_LOGE(TAG, "UPDATE_COMMAND_NONE not handled. Check client is sending the correct command");
ESP_LOGE(TAG, "UPDATE_COMMAND_NONE not handled; confirm command is correct");
break;
default:
ESP_LOGW(TAG, "Unknown update command: %" PRIu32, msg.command);
@ -1526,7 +1567,7 @@ ConnectResponse APIConnection::connect(const ConnectRequest &msg) {
// bool invalid_password = 1;
resp.invalid_password = !correct;
if (correct) {
ESP_LOGD(TAG, "%s: Connected successfully", this->client_combined_info_.c_str());
ESP_LOGD(TAG, "%s connected", this->client_combined_info_.c_str());
this->connection_state_ = ConnectionState::AUTHENTICATED;
this->parent_->get_client_connected_trigger()->trigger(this->client_info_, this->client_peername_);
#ifdef USE_HOMEASSISTANT_TIME
@ -1597,7 +1638,7 @@ void APIConnection::execute_service(const ExecuteServiceRequest &msg) {
}
}
if (!found) {
ESP_LOGV(TAG, "Could not find matching service!");
ESP_LOGV(TAG, "Could not find service");
}
}
#ifdef USE_API_NOISE
@ -1643,7 +1684,7 @@ bool APIConnection::try_to_clear_buffer(bool log_out_of_space) {
}
return false;
}
bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) {
bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint16_t message_type) {
if (!this->try_to_clear_buffer(message_type != 29)) { // SubscribeLogsResponse
return false;
}
@ -1666,17 +1707,350 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type)
}
void APIConnection::on_unauthenticated_access() {
this->on_fatal_error();
ESP_LOGD(TAG, "%s: tried to access without authentication.", this->client_combined_info_.c_str());
ESP_LOGD(TAG, "%s requested access without authentication", this->client_combined_info_.c_str());
}
void APIConnection::on_no_setup_connection() {
this->on_fatal_error();
ESP_LOGD(TAG, "%s: tried to access without full connection.", this->client_combined_info_.c_str());
ESP_LOGD(TAG, "%s requested access without full connection", this->client_combined_info_.c_str());
}
void APIConnection::on_fatal_error() {
this->helper_->close();
this->remove_ = true;
}
void APIConnection::DeferredBatch::add_item(EntityBase *entity, MessageCreator creator, uint16_t message_type) {
// Check if we already have a message of this type for this entity
// This provides deduplication per entity/message_type combination
// O(n) but optimized for RAM and not performance.
for (auto &item : items) {
if (item.entity == entity && item.message_type == message_type) {
// Update the existing item with the new creator
item.creator = std::move(creator);
return;
}
}
// No existing item found, add new one
items.emplace_back(entity, std::move(creator), message_type);
}
bool APIConnection::schedule_batch_() {
if (!this->deferred_batch_.batch_scheduled) {
this->deferred_batch_.batch_scheduled = true;
this->deferred_batch_.batch_start_time = App.get_loop_component_start_time();
}
return true;
}
ProtoWriteBuffer APIConnection::allocate_single_message_buffer(uint16_t size) { return this->create_buffer(size); }
ProtoWriteBuffer APIConnection::allocate_batch_message_buffer(uint16_t size) {
ProtoWriteBuffer result = this->prepare_message_buffer(size, this->batch_first_message_);
this->batch_first_message_ = false;
return result;
}
void APIConnection::process_batch_() {
if (this->deferred_batch_.empty()) {
this->deferred_batch_.batch_scheduled = false;
return;
}
// Try to clear buffer first
if (!this->try_to_clear_buffer(true)) {
// Can't write now, we'll try again later
return;
}
size_t num_items = this->deferred_batch_.items.size();
// Fast path for single message - allocate exact size needed
if (num_items == 1) {
const auto &item = this->deferred_batch_.items[0];
// Let the creator calculate size and encode if it fits
uint16_t payload_size = item.creator(item.entity, this, std::numeric_limits<uint16_t>::max(), true);
if (payload_size > 0 &&
this->send_buffer(ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, item.message_type)) {
this->deferred_batch_.clear();
} else if (payload_size == 0) {
// Message too large
ESP_LOGW(TAG, "Message too large to send: type=%u", item.message_type);
this->deferred_batch_.clear();
}
return;
}
// Pre-allocate storage for packet info
std::vector<PacketInfo> packet_info;
packet_info.reserve(num_items);
// Cache these values to avoid repeated virtual calls
const uint8_t header_padding = this->helper_->frame_header_padding();
const uint8_t footer_size = this->helper_->frame_footer_size();
// Initialize buffer and tracking variables
this->parent_->get_shared_buffer_ref().clear();
// Pre-calculate exact buffer size needed based on message types
uint32_t total_estimated_size = 0;
for (const auto &item : this->deferred_batch_.items) {
total_estimated_size += get_estimated_message_size(item.message_type);
}
// Calculate total overhead for all messages
uint32_t total_overhead = (header_padding + footer_size) * num_items;
// Reserve based on estimated size (much more accurate than 24-byte worst-case)
this->parent_->get_shared_buffer_ref().reserve(total_estimated_size + total_overhead);
this->batch_first_message_ = true;
size_t items_processed = 0;
uint32_t remaining_size = MAX_PACKET_SIZE;
// Track where each message's header padding begins in the buffer
// For plaintext: this is where the 6-byte header padding starts
// For noise: this is where the 7-byte header padding starts
// The actual message data follows after the header padding
uint32_t current_offset = 0;
// Process items and encode directly to buffer
for (const auto &item : this->deferred_batch_.items) {
// Try to encode message
// The creator will calculate overhead to determine if the message fits
uint16_t payload_size = item.creator(item.entity, this, remaining_size, false);
if (payload_size == 0) {
// Message won't fit, stop processing
break;
}
// Message was encoded successfully
// payload_size is header_padding + actual payload size + footer_size
uint16_t proto_payload_size = payload_size - header_padding - footer_size;
packet_info.emplace_back(item.message_type, current_offset, proto_payload_size);
// Update tracking variables
remaining_size -= payload_size;
// Calculate where the next message's header padding will start
// Current buffer size + footer space (that prepare_message_buffer will add for this message)
current_offset = this->parent_->get_shared_buffer_ref().size() + footer_size;
items_processed++;
}
if (items_processed == 0) {
this->deferred_batch_.clear();
return;
}
// Add footer space for the last message (for Noise protocol MAC)
if (footer_size > 0) {
auto &shared_buf = this->parent_->get_shared_buffer_ref();
shared_buf.resize(shared_buf.size() + footer_size);
}
// Send all collected packets
APIError err =
this->helper_->write_protobuf_packets(ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, packet_info);
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
on_fatal_error();
if (err == APIError::SOCKET_WRITE_FAILED && errno == ECONNRESET) {
ESP_LOGW(TAG, "%s: Connection reset during batch write", this->client_combined_info_.c_str());
} else {
ESP_LOGW(TAG, "%s: Batch write failed %s errno=%d", this->client_combined_info_.c_str(), api_error_to_str(err),
errno);
}
}
// Handle remaining items more efficiently
if (items_processed < this->deferred_batch_.items.size()) {
// Remove processed items from the beginning
this->deferred_batch_.items.erase(this->deferred_batch_.items.begin(),
this->deferred_batch_.items.begin() + items_processed);
// Reschedule for remaining items
this->schedule_batch_();
} else {
// All items processed
this->deferred_batch_.clear();
}
}
uint16_t APIConnection::MessageCreator::operator()(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) const {
switch (message_type_) {
case 0: // Function pointer
return data_.ptr(entity, conn, remaining_size, is_single);
#ifdef USE_EVENT
case EventResponse::MESSAGE_TYPE: {
auto *e = static_cast<event::Event *>(entity);
return APIConnection::try_send_event_response(e, *data_.string_ptr, conn, remaining_size, is_single);
}
#endif
default:
// Should not happen, return 0 to indicate no message
return 0;
}
}
uint16_t APIConnection::try_send_list_info_done(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
ListEntitiesDoneResponse resp;
return encode_message_to_buffer(resp, ListEntitiesDoneResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
uint16_t APIConnection::try_send_disconnect_request(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) {
DisconnectRequest req;
return encode_message_to_buffer(req, DisconnectRequest::MESSAGE_TYPE, conn, remaining_size, is_single);
}
uint16_t APIConnection::get_estimated_message_size(uint16_t message_type) {
// Use generated ESTIMATED_SIZE constants from each message type
switch (message_type) {
#ifdef USE_BINARY_SENSOR
case BinarySensorStateResponse::MESSAGE_TYPE:
return BinarySensorStateResponse::ESTIMATED_SIZE;
case ListEntitiesBinarySensorResponse::MESSAGE_TYPE:
return ListEntitiesBinarySensorResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_SENSOR
case SensorStateResponse::MESSAGE_TYPE:
return SensorStateResponse::ESTIMATED_SIZE;
case ListEntitiesSensorResponse::MESSAGE_TYPE:
return ListEntitiesSensorResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_SWITCH
case SwitchStateResponse::MESSAGE_TYPE:
return SwitchStateResponse::ESTIMATED_SIZE;
case ListEntitiesSwitchResponse::MESSAGE_TYPE:
return ListEntitiesSwitchResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_TEXT_SENSOR
case TextSensorStateResponse::MESSAGE_TYPE:
return TextSensorStateResponse::ESTIMATED_SIZE;
case ListEntitiesTextSensorResponse::MESSAGE_TYPE:
return ListEntitiesTextSensorResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_NUMBER
case NumberStateResponse::MESSAGE_TYPE:
return NumberStateResponse::ESTIMATED_SIZE;
case ListEntitiesNumberResponse::MESSAGE_TYPE:
return ListEntitiesNumberResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_TEXT
case TextStateResponse::MESSAGE_TYPE:
return TextStateResponse::ESTIMATED_SIZE;
case ListEntitiesTextResponse::MESSAGE_TYPE:
return ListEntitiesTextResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_SELECT
case SelectStateResponse::MESSAGE_TYPE:
return SelectStateResponse::ESTIMATED_SIZE;
case ListEntitiesSelectResponse::MESSAGE_TYPE:
return ListEntitiesSelectResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_LOCK
case LockStateResponse::MESSAGE_TYPE:
return LockStateResponse::ESTIMATED_SIZE;
case ListEntitiesLockResponse::MESSAGE_TYPE:
return ListEntitiesLockResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_EVENT
case EventResponse::MESSAGE_TYPE:
return EventResponse::ESTIMATED_SIZE;
case ListEntitiesEventResponse::MESSAGE_TYPE:
return ListEntitiesEventResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_COVER
case CoverStateResponse::MESSAGE_TYPE:
return CoverStateResponse::ESTIMATED_SIZE;
case ListEntitiesCoverResponse::MESSAGE_TYPE:
return ListEntitiesCoverResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_FAN
case FanStateResponse::MESSAGE_TYPE:
return FanStateResponse::ESTIMATED_SIZE;
case ListEntitiesFanResponse::MESSAGE_TYPE:
return ListEntitiesFanResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_LIGHT
case LightStateResponse::MESSAGE_TYPE:
return LightStateResponse::ESTIMATED_SIZE;
case ListEntitiesLightResponse::MESSAGE_TYPE:
return ListEntitiesLightResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_CLIMATE
case ClimateStateResponse::MESSAGE_TYPE:
return ClimateStateResponse::ESTIMATED_SIZE;
case ListEntitiesClimateResponse::MESSAGE_TYPE:
return ListEntitiesClimateResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_ESP32_CAMERA
case ListEntitiesCameraResponse::MESSAGE_TYPE:
return ListEntitiesCameraResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_BUTTON
case ListEntitiesButtonResponse::MESSAGE_TYPE:
return ListEntitiesButtonResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_MEDIA_PLAYER
case MediaPlayerStateResponse::MESSAGE_TYPE:
return MediaPlayerStateResponse::ESTIMATED_SIZE;
case ListEntitiesMediaPlayerResponse::MESSAGE_TYPE:
return ListEntitiesMediaPlayerResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_ALARM_CONTROL_PANEL
case AlarmControlPanelStateResponse::MESSAGE_TYPE:
return AlarmControlPanelStateResponse::ESTIMATED_SIZE;
case ListEntitiesAlarmControlPanelResponse::MESSAGE_TYPE:
return ListEntitiesAlarmControlPanelResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_DATETIME_DATE
case DateStateResponse::MESSAGE_TYPE:
return DateStateResponse::ESTIMATED_SIZE;
case ListEntitiesDateResponse::MESSAGE_TYPE:
return ListEntitiesDateResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_DATETIME_TIME
case TimeStateResponse::MESSAGE_TYPE:
return TimeStateResponse::ESTIMATED_SIZE;
case ListEntitiesTimeResponse::MESSAGE_TYPE:
return ListEntitiesTimeResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_DATETIME_DATETIME
case DateTimeStateResponse::MESSAGE_TYPE:
return DateTimeStateResponse::ESTIMATED_SIZE;
case ListEntitiesDateTimeResponse::MESSAGE_TYPE:
return ListEntitiesDateTimeResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_VALVE
case ValveStateResponse::MESSAGE_TYPE:
return ValveStateResponse::ESTIMATED_SIZE;
case ListEntitiesValveResponse::MESSAGE_TYPE:
return ListEntitiesValveResponse::ESTIMATED_SIZE;
#endif
#ifdef USE_UPDATE
case UpdateStateResponse::MESSAGE_TYPE:
return UpdateStateResponse::ESTIMATED_SIZE;
case ListEntitiesUpdateResponse::MESSAGE_TYPE:
return ListEntitiesUpdateResponse::ESTIMATED_SIZE;
#endif
case ListEntitiesServicesResponse::MESSAGE_TYPE:
return ListEntitiesServicesResponse::ESTIMATED_SIZE;
case ListEntitiesDoneResponse::MESSAGE_TYPE:
return ListEntitiesDoneResponse::ESTIMATED_SIZE;
case DisconnectRequest::MESSAGE_TYPE:
return DisconnectRequest::ESTIMATED_SIZE;
default:
// Fallback for unknown message types
return 24;
}
}
} // namespace api
} // namespace esphome
#endif

View File

@ -11,6 +11,7 @@
#include "esphome/core/entity_base.h"
#include <vector>
#include <functional>
namespace esphome {
namespace api {
@ -18,49 +19,9 @@ namespace api {
// Keepalive timeout in milliseconds
static constexpr uint32_t KEEPALIVE_TIMEOUT_MS = 60000;
using send_message_t = bool (APIConnection::*)(void *);
/*
This class holds a pointer to the source component that wants to publish a message, and a pointer to a function that
will lazily publish that message. The two pointers allow dedup in the deferred queue if multiple publishes for the
same component are backed up, and take up only 8 bytes of memory. The entry in the deferred queue (a std::vector) is
the DeferredMessage instance itself (not a pointer to one elsewhere in heap) so still only 8 bytes per entry. Even
100 backed up messages (you'd have to have at least 100 sensors publishing because of dedup) would take up only 0.8
kB.
*/
class DeferredMessageQueue {
struct DeferredMessage {
friend class DeferredMessageQueue;
protected:
void *source_;
send_message_t send_message_;
public:
DeferredMessage(void *source, send_message_t send_message) : source_(source), send_message_(send_message) {}
bool operator==(const DeferredMessage &test) const {
return (source_ == test.source_ && send_message_ == test.send_message_);
}
} __attribute__((packed));
protected:
// vector is used very specifically for its zero memory overhead even though items are popped from the front (memory
// footprint is more important than speed here)
std::vector<DeferredMessage> deferred_queue_;
APIConnection *api_connection_;
// helper for allowing only unique entries in the queue
void dmq_push_back_with_dedup_(void *source, send_message_t send_message);
public:
DeferredMessageQueue(APIConnection *api_connection) : api_connection_(api_connection) {}
void process_queue();
void defer(void *source, send_message_t send_message);
bool empty() const { return deferred_queue_.empty(); }
};
class APIConnection : public APIServerConnection {
public:
friend class APIServer;
APIConnection(std::unique_ptr<socket::Socket> socket, APIServer *parent);
virtual ~APIConnection();
@ -68,225 +29,105 @@ class APIConnection : public APIServerConnection {
void loop();
bool send_list_info_done() {
ListEntitiesDoneResponse resp;
return this->send_list_entities_done_response(resp);
return this->schedule_message_(nullptr, &APIConnection::try_send_list_info_done,
ListEntitiesDoneResponse::MESSAGE_TYPE);
}
#ifdef USE_BINARY_SENSOR
bool send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor, bool state);
bool send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor);
void send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor);
protected:
bool try_send_binary_sensor_state_(binary_sensor::BinarySensor *binary_sensor);
bool try_send_binary_sensor_state_(binary_sensor::BinarySensor *binary_sensor, bool state);
bool try_send_binary_sensor_info_(binary_sensor::BinarySensor *binary_sensor);
public:
#endif
#ifdef USE_COVER
bool send_cover_state(cover::Cover *cover);
void send_cover_info(cover::Cover *cover);
void cover_command(const CoverCommandRequest &msg) override;
protected:
bool try_send_cover_state_(cover::Cover *cover);
bool try_send_cover_info_(cover::Cover *cover);
public:
#endif
#ifdef USE_FAN
bool send_fan_state(fan::Fan *fan);
void send_fan_info(fan::Fan *fan);
void fan_command(const FanCommandRequest &msg) override;
protected:
bool try_send_fan_state_(fan::Fan *fan);
bool try_send_fan_info_(fan::Fan *fan);
public:
#endif
#ifdef USE_LIGHT
bool send_light_state(light::LightState *light);
void send_light_info(light::LightState *light);
void light_command(const LightCommandRequest &msg) override;
protected:
bool try_send_light_state_(light::LightState *light);
bool try_send_light_info_(light::LightState *light);
public:
#endif
#ifdef USE_SENSOR
bool send_sensor_state(sensor::Sensor *sensor, float state);
bool send_sensor_state(sensor::Sensor *sensor);
void send_sensor_info(sensor::Sensor *sensor);
protected:
bool try_send_sensor_state_(sensor::Sensor *sensor);
bool try_send_sensor_state_(sensor::Sensor *sensor, float state);
bool try_send_sensor_info_(sensor::Sensor *sensor);
public:
#endif
#ifdef USE_SWITCH
bool send_switch_state(switch_::Switch *a_switch, bool state);
bool send_switch_state(switch_::Switch *a_switch);
void send_switch_info(switch_::Switch *a_switch);
void switch_command(const SwitchCommandRequest &msg) override;
protected:
bool try_send_switch_state_(switch_::Switch *a_switch);
bool try_send_switch_state_(switch_::Switch *a_switch, bool state);
bool try_send_switch_info_(switch_::Switch *a_switch);
public:
#endif
#ifdef USE_TEXT_SENSOR
bool send_text_sensor_state(text_sensor::TextSensor *text_sensor, std::string state);
bool send_text_sensor_state(text_sensor::TextSensor *text_sensor);
void send_text_sensor_info(text_sensor::TextSensor *text_sensor);
protected:
bool try_send_text_sensor_state_(text_sensor::TextSensor *text_sensor);
bool try_send_text_sensor_state_(text_sensor::TextSensor *text_sensor, std::string state);
bool try_send_text_sensor_info_(text_sensor::TextSensor *text_sensor);
public:
#endif
#ifdef USE_ESP32_CAMERA
void set_camera_state(std::shared_ptr<esp32_camera::CameraImage> image);
void send_camera_info(esp32_camera::ESP32Camera *camera);
void camera_image(const CameraImageRequest &msg) override;
protected:
bool try_send_camera_info_(esp32_camera::ESP32Camera *camera);
public:
#endif
#ifdef USE_CLIMATE
bool send_climate_state(climate::Climate *climate);
void send_climate_info(climate::Climate *climate);
void climate_command(const ClimateCommandRequest &msg) override;
protected:
bool try_send_climate_state_(climate::Climate *climate);
bool try_send_climate_info_(climate::Climate *climate);
public:
#endif
#ifdef USE_NUMBER
bool send_number_state(number::Number *number, float state);
bool send_number_state(number::Number *number);
void send_number_info(number::Number *number);
void number_command(const NumberCommandRequest &msg) override;
protected:
bool try_send_number_state_(number::Number *number);
bool try_send_number_state_(number::Number *number, float state);
bool try_send_number_info_(number::Number *number);
public:
#endif
#ifdef USE_DATETIME_DATE
bool send_date_state(datetime::DateEntity *date);
void send_date_info(datetime::DateEntity *date);
void date_command(const DateCommandRequest &msg) override;
protected:
bool try_send_date_state_(datetime::DateEntity *date);
bool try_send_date_info_(datetime::DateEntity *date);
public:
#endif
#ifdef USE_DATETIME_TIME
bool send_time_state(datetime::TimeEntity *time);
void send_time_info(datetime::TimeEntity *time);
void time_command(const TimeCommandRequest &msg) override;
protected:
bool try_send_time_state_(datetime::TimeEntity *time);
bool try_send_time_info_(datetime::TimeEntity *time);
public:
#endif
#ifdef USE_DATETIME_DATETIME
bool send_datetime_state(datetime::DateTimeEntity *datetime);
void send_datetime_info(datetime::DateTimeEntity *datetime);
void datetime_command(const DateTimeCommandRequest &msg) override;
protected:
bool try_send_datetime_state_(datetime::DateTimeEntity *datetime);
bool try_send_datetime_info_(datetime::DateTimeEntity *datetime);
public:
#endif
#ifdef USE_TEXT
bool send_text_state(text::Text *text, std::string state);
bool send_text_state(text::Text *text);
void send_text_info(text::Text *text);
void text_command(const TextCommandRequest &msg) override;
protected:
bool try_send_text_state_(text::Text *text);
bool try_send_text_state_(text::Text *text, std::string state);
bool try_send_text_info_(text::Text *text);
public:
#endif
#ifdef USE_SELECT
bool send_select_state(select::Select *select, std::string state);
bool send_select_state(select::Select *select);
void send_select_info(select::Select *select);
void select_command(const SelectCommandRequest &msg) override;
protected:
bool try_send_select_state_(select::Select *select);
bool try_send_select_state_(select::Select *select, std::string state);
bool try_send_select_info_(select::Select *select);
public:
#endif
#ifdef USE_BUTTON
void send_button_info(button::Button *button);
void button_command(const ButtonCommandRequest &msg) override;
protected:
bool try_send_button_info_(button::Button *button);
public:
#endif
#ifdef USE_LOCK
bool send_lock_state(lock::Lock *a_lock, lock::LockState state);
bool send_lock_state(lock::Lock *a_lock);
void send_lock_info(lock::Lock *a_lock);
void lock_command(const LockCommandRequest &msg) override;
protected:
bool try_send_lock_state_(lock::Lock *a_lock);
bool try_send_lock_state_(lock::Lock *a_lock, lock::LockState state);
bool try_send_lock_info_(lock::Lock *a_lock);
public:
#endif
#ifdef USE_VALVE
bool send_valve_state(valve::Valve *valve);
void send_valve_info(valve::Valve *valve);
void valve_command(const ValveCommandRequest &msg) override;
protected:
bool try_send_valve_state_(valve::Valve *valve);
bool try_send_valve_info_(valve::Valve *valve);
public:
#endif
#ifdef USE_MEDIA_PLAYER
bool send_media_player_state(media_player::MediaPlayer *media_player);
void send_media_player_info(media_player::MediaPlayer *media_player);
void media_player_command(const MediaPlayerCommandRequest &msg) override;
protected:
bool try_send_media_player_state_(media_player::MediaPlayer *media_player);
bool try_send_media_player_info_(media_player::MediaPlayer *media_player);
public:
#endif
bool try_send_log_message(int level, const char *tag, const char *line);
void send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
if (!this->service_call_subscription_)
return;
this->send_homeassistant_service_response(call);
this->send_message(call);
}
#ifdef USE_BLUETOOTH_PROXY
void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
@ -308,7 +149,7 @@ class APIConnection : public APIServerConnection {
#ifdef USE_HOMEASSISTANT_TIME
void send_time_request() {
GetTimeRequest req;
this->send_get_time_request(req);
this->send_message(req);
}
#endif
@ -328,36 +169,17 @@ class APIConnection : public APIServerConnection {
bool send_alarm_control_panel_state(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel);
void send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel);
void alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) override;
protected:
bool try_send_alarm_control_panel_state_(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel);
bool try_send_alarm_control_panel_info_(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel);
public:
#endif
#ifdef USE_EVENT
void send_event(event::Event *event, std::string event_type);
void send_event(event::Event *event, const std::string &event_type);
void send_event_info(event::Event *event);
protected:
bool try_send_event_(event::Event *event);
bool try_send_event_(event::Event *event, std::string event_type);
bool try_send_event_info_(event::Event *event);
public:
#endif
#ifdef USE_UPDATE
bool send_update_state(update::UpdateEntity *update);
void send_update_info(update::UpdateEntity *update);
void update_command(const UpdateCommandRequest &msg) override;
protected:
bool try_send_update_state_(update::UpdateEntity *update);
bool try_send_update_info_(update::UpdateEntity *update);
public:
#endif
void on_disconnect_response(const DisconnectResponse &value) override;
@ -407,102 +229,61 @@ class APIConnection : public APIServerConnection {
void on_no_setup_connection() override;
ProtoWriteBuffer create_buffer(uint32_t reserve_size) override {
// FIXME: ensure no recursive writes can happen
this->proto_write_buffer_.clear();
// Get header padding size - used for both reserve and insert
uint8_t header_padding = this->helper_->frame_header_padding();
// Get shared buffer from parent server
std::vector<uint8_t> &shared_buf = this->parent_->get_shared_buffer_ref();
shared_buf.clear();
// Reserve space for header padding + message + footer
// - Header padding: space for protocol headers (7 bytes for Noise, 6 for Plaintext)
// - Footer: space for MAC (16 bytes for Noise, 0 for Plaintext)
this->proto_write_buffer_.reserve(reserve_size + header_padding + this->helper_->frame_footer_size());
// Insert header padding bytes so message encoding starts at the correct position
this->proto_write_buffer_.insert(this->proto_write_buffer_.begin(), header_padding, 0);
return {&this->proto_write_buffer_};
shared_buf.reserve(reserve_size + header_padding + this->helper_->frame_footer_size());
// Resize to add header padding so message encoding starts at the correct position
shared_buf.resize(header_padding);
return {&shared_buf};
}
// Prepare buffer for next message in batch
ProtoWriteBuffer prepare_message_buffer(uint16_t message_size, bool is_first_message) {
// Get reference to shared buffer (it maintains state between batch messages)
std::vector<uint8_t> &shared_buf = this->parent_->get_shared_buffer_ref();
if (is_first_message) {
shared_buf.clear();
}
size_t current_size = shared_buf.size();
// Calculate padding to add:
// - First message: just header padding
// - Subsequent messages: footer for previous message + header padding for this message
size_t padding_to_add = is_first_message
? this->helper_->frame_header_padding()
: this->helper_->frame_header_padding() + this->helper_->frame_footer_size();
// Reserve space for padding + message
shared_buf.reserve(current_size + padding_to_add + message_size);
// Resize to add the padding bytes
shared_buf.resize(current_size + padding_to_add);
return {&shared_buf};
}
bool try_to_clear_buffer(bool log_out_of_space);
bool send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) override;
bool send_buffer(ProtoWriteBuffer buffer, uint16_t message_type) override;
std::string get_client_combined_info() const { return this->client_combined_info_; }
// Buffer allocator methods for batch processing
ProtoWriteBuffer allocate_single_message_buffer(uint16_t size);
ProtoWriteBuffer allocate_batch_message_buffer(uint16_t size);
protected:
friend APIServer;
/**
* Generic send entity state method to reduce code duplication.
* Only attempts to build and send the message if the transmit buffer is available.
*
* This is the base version for entities that use their current state.
*
* @param entity The entity to send state for
* @param try_send_func The function that tries to send the state
* @return True on success or message deferred, false if subscription check failed
*/
bool send_state_(esphome::EntityBase *entity, send_message_t try_send_func) {
if (!this->state_subscription_)
return false;
if (this->try_to_clear_buffer(true) && (this->*try_send_func)(entity)) {
return true;
}
this->deferred_message_queue_.defer(entity, try_send_func);
return true;
}
/**
* Send entity state method that handles explicit state values.
* Only attempts to build and send the message if the transmit buffer is available.
*
* This method accepts a state parameter to be used instead of the entity's current state.
* It attempts to send the state with the provided value first, and if that fails due to buffer constraints,
* it defers the entity for later processing using the entity-only function.
*
* @tparam EntityT The entity type
* @tparam StateT Type of the state parameter
* @tparam Args Additional argument types (if any)
* @param entity The entity to send state for
* @param try_send_entity_func The function that tries to send the state with entity pointer only
* @param try_send_state_func The function that tries to send the state with entity and state parameters
* @param state The state value to send
* @param args Additional arguments to pass to the try_send_state_func
* @return True on success or message deferred, false if subscription check failed
*/
template<typename EntityT, typename StateT, typename... Args>
bool send_state_with_value_(EntityT *entity, bool (APIConnection::*try_send_entity_func)(EntityT *),
bool (APIConnection::*try_send_state_func)(EntityT *, StateT, Args...), StateT state,
Args... args) {
if (!this->state_subscription_)
return false;
if (this->try_to_clear_buffer(true) && (this->*try_send_state_func)(entity, state, args...)) {
return true;
}
this->deferred_message_queue_.defer(entity, reinterpret_cast<send_message_t>(try_send_entity_func));
return true;
}
/**
* Generic send entity info method to reduce code duplication.
* Only attempts to build and send the message if the transmit buffer is available.
*
* @param entity The entity to send info for
* @param try_send_func The function that tries to send the info
*/
void send_info_(esphome::EntityBase *entity, send_message_t try_send_func) {
if (this->try_to_clear_buffer(true) && (this->*try_send_func)(entity)) {
return;
}
this->deferred_message_queue_.defer(entity, try_send_func);
}
/**
* Generic function for generating entity info response messages.
* This is used to reduce duplication in the try_send_*_info functions.
*
* @param entity The entity to generate info for
* @param response The response object
* @param send_response_func Function pointer to send the response
* @return True if the message was sent successfully
*/
template<typename ResponseT>
bool try_send_entity_info_(esphome::EntityBase *entity, ResponseT &response,
bool (APIServerConnectionBase::*send_response_func)(const ResponseT &)) {
// Helper function to fill common entity fields
template<typename ResponseT> static void fill_entity_info_base(esphome::EntityBase *entity, ResponseT &response) {
// Set common fields that are shared by all entity types
response.key = entity->get_object_id_hash();
response.object_id = entity->get_object_id();
@ -514,12 +295,137 @@ class APIConnection : public APIServerConnection {
response.icon = entity->get_icon();
response.disabled_by_default = entity->is_disabled_by_default();
response.entity_category = static_cast<enums::EntityCategory>(entity->get_entity_category());
// Send the response using the provided send method
return (this->*send_response_func)(response);
}
bool send_(const void *buf, size_t len, bool force);
// Non-template helper to encode any ProtoMessage
static uint16_t encode_message_to_buffer(ProtoMessage &msg, uint16_t message_type, APIConnection *conn,
uint32_t remaining_size, bool is_single);
#ifdef USE_BINARY_SENSOR
static uint16_t try_send_binary_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_binary_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
#endif
#ifdef USE_COVER
static uint16_t try_send_cover_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_cover_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
#endif
#ifdef USE_FAN
static uint16_t try_send_fan_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
static uint16_t try_send_fan_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
#endif
#ifdef USE_LIGHT
static uint16_t try_send_light_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_light_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
#endif
#ifdef USE_SENSOR
static uint16_t try_send_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
#endif
#ifdef USE_SWITCH
static uint16_t try_send_switch_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_switch_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
#endif
#ifdef USE_TEXT_SENSOR
static uint16_t try_send_text_sensor_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_text_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
#endif
#ifdef USE_CLIMATE
static uint16_t try_send_climate_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_climate_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
#endif
#ifdef USE_NUMBER
static uint16_t try_send_number_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_number_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
#endif
#ifdef USE_DATETIME_DATE
static uint16_t try_send_date_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
static uint16_t try_send_date_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
#endif
#ifdef USE_DATETIME_TIME
static uint16_t try_send_time_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
static uint16_t try_send_time_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
#endif
#ifdef USE_DATETIME_DATETIME
static uint16_t try_send_datetime_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_datetime_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
#endif
#ifdef USE_TEXT
static uint16_t try_send_text_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
static uint16_t try_send_text_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
#endif
#ifdef USE_SELECT
static uint16_t try_send_select_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_select_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
#endif
#ifdef USE_BUTTON
static uint16_t try_send_button_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
#endif
#ifdef USE_LOCK
static uint16_t try_send_lock_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
static uint16_t try_send_lock_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
#endif
#ifdef USE_VALVE
static uint16_t try_send_valve_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_valve_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
#endif
#ifdef USE_MEDIA_PLAYER
static uint16_t try_send_media_player_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_media_player_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
#endif
#ifdef USE_ALARM_CONTROL_PANEL
static uint16_t try_send_alarm_control_panel_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_alarm_control_panel_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
#endif
#ifdef USE_EVENT
static uint16_t try_send_event_response(event::Event *event, const std::string &event_type, APIConnection *conn,
uint32_t remaining_size, bool is_single);
static uint16_t try_send_event_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
#endif
#ifdef USE_UPDATE
static uint16_t try_send_update_state(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
static uint16_t try_send_update_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
#endif
#ifdef USE_ESP32_CAMERA
static uint16_t try_send_camera_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
#endif
// Method for ListEntitiesDone batching
static uint16_t try_send_list_info_done(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
// Method for DisconnectRequest batching
static uint16_t try_send_disconnect_request(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single);
// Helper function to get estimated message size for buffer pre-allocation
static uint16_t get_estimated_message_size(uint16_t message_type);
enum class ConnectionState {
WAITING_FOR_HELLO,
@ -529,9 +435,6 @@ class APIConnection : public APIServerConnection {
bool remove_{false};
// Buffer used to encode proto messages
// Re-use to prevent allocations
std::vector<uint8_t> proto_write_buffer_;
std::unique_ptr<APIFrameHelper> helper_;
std::string client_info_;
@ -552,10 +455,160 @@ class APIConnection : public APIServerConnection {
bool service_call_subscription_{false};
bool next_close_ = false;
APIServer *parent_;
DeferredMessageQueue deferred_message_queue_;
InitialStateIterator initial_state_iterator_;
ListEntitiesIterator list_entities_iterator_;
int state_subs_at_ = -1;
// Function pointer type for message encoding
using MessageCreatorPtr = uint16_t (*)(EntityBase *, APIConnection *, uint32_t remaining_size, bool is_single);
// Optimized MessageCreator class using union dispatch
class MessageCreator {
public:
// Constructor for function pointer (message_type = 0)
MessageCreator(MessageCreatorPtr ptr) : message_type_(0) { data_.ptr = ptr; }
// Constructor for string state capture
MessageCreator(const std::string &value, uint16_t msg_type) : message_type_(msg_type) {
data_.string_ptr = new std::string(value);
}
// Destructor
~MessageCreator() {
// Clean up string data for string-based message types
if (uses_string_data_()) {
delete data_.string_ptr;
}
}
// Copy constructor
MessageCreator(const MessageCreator &other) : message_type_(other.message_type_) {
if (message_type_ == 0) {
data_.ptr = other.data_.ptr;
} else if (uses_string_data_()) {
data_.string_ptr = new std::string(*other.data_.string_ptr);
} else {
data_ = other.data_; // For POD types
}
}
// Move constructor
MessageCreator(MessageCreator &&other) noexcept : data_(other.data_), message_type_(other.message_type_) {
other.message_type_ = 0; // Reset other to function pointer type
other.data_.ptr = nullptr;
}
// Assignment operators (needed for batch deduplication)
MessageCreator &operator=(const MessageCreator &other) {
if (this != &other) {
// Clean up current string data if needed
if (uses_string_data_()) {
delete data_.string_ptr;
}
// Copy new data
message_type_ = other.message_type_;
if (other.message_type_ == 0) {
data_.ptr = other.data_.ptr;
} else if (other.uses_string_data_()) {
data_.string_ptr = new std::string(*other.data_.string_ptr);
} else {
data_ = other.data_;
}
}
return *this;
}
MessageCreator &operator=(MessageCreator &&other) noexcept {
if (this != &other) {
// Clean up current string data if needed
if (uses_string_data_()) {
delete data_.string_ptr;
}
// Move data
message_type_ = other.message_type_;
data_ = other.data_;
// Reset other to safe state
other.message_type_ = 0;
other.data_.ptr = nullptr;
}
return *this;
}
// Call operator
uint16_t operator()(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single) const;
private:
// Helper to check if this message type uses heap-allocated strings
bool uses_string_data_() const { return message_type_ == EventResponse::MESSAGE_TYPE; }
union CreatorData {
MessageCreatorPtr ptr; // 8 bytes
std::string *string_ptr; // 8 bytes
} data_; // 8 bytes
uint16_t message_type_; // 2 bytes (0 = function ptr, >0 = state capture)
};
// Generic batching mechanism for both state updates and entity info
struct DeferredBatch {
struct BatchItem {
EntityBase *entity; // Entity pointer
MessageCreator creator; // Function that creates the message when needed
uint16_t message_type; // Message type for overhead calculation
// Constructor for creating BatchItem
BatchItem(EntityBase *entity, MessageCreator creator, uint16_t message_type)
: entity(entity), creator(std::move(creator)), message_type(message_type) {}
};
std::vector<BatchItem> items;
uint32_t batch_start_time{0};
bool batch_scheduled{false};
DeferredBatch() {
// Pre-allocate capacity for typical batch sizes to avoid reallocation
items.reserve(8);
}
// Add item to the batch
void add_item(EntityBase *entity, MessageCreator creator, uint16_t message_type);
void clear() {
items.clear();
batch_scheduled = false;
batch_start_time = 0;
}
bool empty() const { return items.empty(); }
};
DeferredBatch deferred_batch_;
uint32_t get_batch_delay_ms_() const;
// Message will use 8 more bytes than the minimum size, and typical
// MTU is 1500. Sometimes users will see as low as 1460 MTU.
// If its IPv6 the header is 40 bytes, and if its IPv4
// the header is 20 bytes. So we have 1460 - 40 = 1420 bytes
// available for the payload. But we also need to add the size of
// the protobuf overhead, which is 8 bytes.
//
// To be safe we pick 1390 bytes as the maximum size
// to send in one go. This is the maximum size of a single packet
// that can be sent over the network.
// This is to avoid fragmentation of the packet.
static constexpr size_t MAX_PACKET_SIZE = 1390; // MTU
bool schedule_batch_();
void process_batch_();
// State for batch buffer allocation
bool batch_first_message_{false};
// Helper function to schedule a deferred message with known message type
bool schedule_message_(EntityBase *entity, MessageCreator creator, uint16_t message_type) {
this->deferred_batch_.add_item(entity, std::move(creator), message_type);
return this->schedule_batch_();
}
// Overload for function pointers (for info messages and current state reads)
bool schedule_message_(EntityBase *entity, MessageCreatorPtr function_ptr, uint16_t message_type) {
return schedule_message_(entity, MessageCreator(function_ptr), message_type);
}
};
} // namespace api

View File

@ -1,26 +1,19 @@
#include "api_frame_helper.h"
#ifdef USE_API
#include "esphome/core/log.h"
#include "esphome/core/application.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/application.h"
#include "esphome/core/log.h"
#include "proto.h"
#include "api_pb2_size.h"
#include <cstring>
#include <cinttypes>
namespace esphome {
namespace api {
static const char *const TAG = "api.socket";
/// Is the given return value (from write syscalls) a wouldblock error?
bool is_would_block(ssize_t ret) {
if (ret == -1) {
return errno == EWOULDBLOCK || errno == EAGAIN;
}
return ret == 0;
}
const char *api_error_to_str(APIError err) {
// not using switch to ensure compiler doesn't try to build a big table out of it
if (err == APIError::OK) {
@ -73,92 +66,154 @@ const char *api_error_to_str(APIError err) {
return "UNKNOWN";
}
// Common implementation for writing raw data to socket
template<typename StateEnum>
APIError APIFrameHelper::write_raw_(const struct iovec *iov, int iovcnt, socket::Socket *socket,
std::vector<uint8_t> &tx_buf, const std::string &info, StateEnum &state,
StateEnum failed_state) {
// This method writes data to socket or buffers it
// Helper method to buffer data from IOVs
void APIFrameHelper::buffer_data_from_iov_(const struct iovec *iov, int iovcnt, uint16_t total_write_len) {
SendBuffer buffer;
buffer.data.reserve(total_write_len);
for (int i = 0; i < iovcnt; i++) {
const uint8_t *data = reinterpret_cast<uint8_t *>(iov[i].iov_base);
buffer.data.insert(buffer.data.end(), data, data + iov[i].iov_len);
}
this->tx_buf_.push_back(std::move(buffer));
}
// This method writes data to socket or buffers it
APIError APIFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
// Returns APIError::OK if successful (or would block, but data has been buffered)
// Returns APIError::SOCKET_WRITE_FAILED if socket write failed, and sets state to failed_state
// Returns APIError::SOCKET_WRITE_FAILED if socket write failed, and sets state to FAILED
if (iovcnt == 0)
return APIError::OK; // Nothing to do, success
size_t total_write_len = 0;
uint16_t total_write_len = 0;
for (int i = 0; i < iovcnt; i++) {
#ifdef HELPER_LOG_PACKETS
ESP_LOGVV(TAG, "Sending raw: %s",
format_hex_pretty(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
#endif
total_write_len += iov[i].iov_len;
total_write_len += static_cast<uint16_t>(iov[i].iov_len);
}
if (!tx_buf.empty()) {
// try to empty tx_buf first
while (!tx_buf.empty()) {
ssize_t sent = socket->write(tx_buf.data(), tx_buf.size());
if (is_would_block(sent)) {
break;
} else if (sent == -1) {
ESP_LOGVV(TAG, "%s: Socket write failed with errno %d", info.c_str(), errno);
state = failed_state;
return APIError::SOCKET_WRITE_FAILED; // Socket write failed
}
// TODO: inefficient if multiple packets in txbuf
// replace with deque of buffers
tx_buf.erase(tx_buf.begin(), tx_buf.begin() + sent);
// Try to send any existing buffered data first if there is any
if (!this->tx_buf_.empty()) {
APIError send_result = try_send_tx_buf_();
// If real error occurred (not just WOULD_BLOCK), return it
if (send_result != APIError::OK && send_result != APIError::WOULD_BLOCK) {
return send_result;
}
// If there is still data in the buffer, we can't send, buffer
// the new data and return
if (!this->tx_buf_.empty()) {
this->buffer_data_from_iov_(iov, iovcnt, total_write_len);
return APIError::OK; // Success, data buffered
}
}
if (!tx_buf.empty()) {
// tx buf not empty, can't write now because then stream would be inconsistent
// Reserve space upfront to avoid multiple reallocations
tx_buf.reserve(tx_buf.size() + total_write_len);
for (int i = 0; i < iovcnt; i++) {
tx_buf.insert(tx_buf.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
}
return APIError::OK; // Success, data buffered
}
// Try to send directly if no buffered data
ssize_t sent = this->socket_->writev(iov, iovcnt);
ssize_t sent = socket->writev(iov, iovcnt);
if (is_would_block(sent)) {
// operation would block, add buffer to tx_buf
// Reserve space upfront to avoid multiple reallocations
tx_buf.reserve(tx_buf.size() + total_write_len);
for (int i = 0; i < iovcnt; i++) {
tx_buf.insert(tx_buf.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
if (sent == -1) {
if (errno == EWOULDBLOCK || errno == EAGAIN) {
// Socket would block, buffer the data
this->buffer_data_from_iov_(iov, iovcnt, total_write_len);
return APIError::OK; // Success, data buffered
}
return APIError::OK; // Success, data buffered
} else if (sent == -1) {
// an error occurred
ESP_LOGVV(TAG, "%s: Socket write failed with errno %d", info.c_str(), errno);
state = failed_state;
// Socket error
ESP_LOGVV(TAG, "%s: Socket write failed with errno %d", this->info_.c_str(), errno);
this->state_ = State::FAILED;
return APIError::SOCKET_WRITE_FAILED; // Socket write failed
} else if ((size_t) sent != total_write_len) {
// partially sent, add end to tx_buf
size_t remaining = total_write_len - sent;
// Reserve space upfront to avoid multiple reallocations
tx_buf.reserve(tx_buf.size() + remaining);
} else if (static_cast<uint16_t>(sent) < total_write_len) {
// Partially sent, buffer the remaining data
SendBuffer buffer;
uint16_t to_consume = static_cast<uint16_t>(sent);
uint16_t remaining = total_write_len - static_cast<uint16_t>(sent);
buffer.data.reserve(remaining);
size_t to_consume = sent;
for (int i = 0; i < iovcnt; i++) {
if (to_consume >= iov[i].iov_len) {
to_consume -= iov[i].iov_len;
// This segment was fully sent
to_consume -= static_cast<uint16_t>(iov[i].iov_len);
} else {
tx_buf.insert(tx_buf.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base) + to_consume,
reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
// This segment was partially sent or not sent at all
const uint8_t *data = reinterpret_cast<uint8_t *>(iov[i].iov_base) + to_consume;
uint16_t len = static_cast<uint16_t>(iov[i].iov_len) - to_consume;
buffer.data.insert(buffer.data.end(), data, data + len);
to_consume = 0;
}
}
return APIError::OK; // Success, data buffered
this->tx_buf_.push_back(std::move(buffer));
}
return APIError::OK; // Success, all data sent
return APIError::OK; // Success, all data sent or buffered
}
#define HELPER_LOG(msg, ...) ESP_LOGVV(TAG, "%s: " msg, info_.c_str(), ##__VA_ARGS__)
// Common implementation for trying to send buffered data
// IMPORTANT: Caller MUST ensure tx_buf_ is not empty before calling this method
APIError APIFrameHelper::try_send_tx_buf_() {
// Try to send from tx_buf - we assume it's not empty as it's the caller's responsibility to check
bool tx_buf_empty = false;
while (!tx_buf_empty) {
// Get the first buffer in the queue
SendBuffer &front_buffer = this->tx_buf_.front();
// Try to send the remaining data in this buffer
ssize_t sent = this->socket_->write(front_buffer.current_data(), front_buffer.remaining());
if (sent == -1) {
if (errno != EWOULDBLOCK && errno != EAGAIN) {
// Real socket error (not just would block)
ESP_LOGVV(TAG, "%s: Socket write failed with errno %d", this->info_.c_str(), errno);
this->state_ = State::FAILED;
return APIError::SOCKET_WRITE_FAILED; // Socket write failed
}
// Socket would block, we'll try again later
return APIError::WOULD_BLOCK;
} else if (sent == 0) {
// Nothing sent but not an error
return APIError::WOULD_BLOCK;
} else if (static_cast<uint16_t>(sent) < front_buffer.remaining()) {
// Partially sent, update offset
// Cast to ensure no overflow issues with uint16_t
front_buffer.offset += static_cast<uint16_t>(sent);
return APIError::WOULD_BLOCK; // Stop processing more buffers if we couldn't send a complete buffer
} else {
// Buffer completely sent, remove it from the queue
this->tx_buf_.pop_front();
// Update empty status for the loop condition
tx_buf_empty = this->tx_buf_.empty();
// Continue loop to try sending the next buffer
}
}
return APIError::OK; // All buffers sent successfully
}
APIError APIFrameHelper::init_common_() {
if (state_ != State::INITIALIZE || this->socket_ == nullptr) {
ESP_LOGVV(TAG, "%s: Bad state for init %d", this->info_.c_str(), (int) state_);
return APIError::BAD_STATE;
}
int err = this->socket_->setblocking(false);
if (err != 0) {
state_ = State::FAILED;
ESP_LOGVV(TAG, "%s: Setting nonblocking failed with errno %d", this->info_.c_str(), errno);
return APIError::TCP_NONBLOCKING_FAILED;
}
int enable = 1;
err = this->socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
if (err != 0) {
state_ = State::FAILED;
ESP_LOGVV(TAG, "%s: Setting nodelay failed with errno %d", this->info_.c_str(), errno);
return APIError::TCP_NODELAY_FAILED;
}
return APIError::OK;
}
#define HELPER_LOG(msg, ...) ESP_LOGVV(TAG, "%s: " msg, this->info_.c_str(), ##__VA_ARGS__)
// uncomment to log raw packets
//#define HELPER_LOG_PACKETS
@ -206,23 +261,9 @@ std::string noise_err_to_str(int err) {
/// Initialize the frame helper, returns OK if successful.
APIError APINoiseFrameHelper::init() {
if (state_ != State::INITIALIZE || socket_ == nullptr) {
HELPER_LOG("Bad state for init %d", (int) state_);
return APIError::BAD_STATE;
}
int err = socket_->setblocking(false);
if (err != 0) {
state_ = State::FAILED;
HELPER_LOG("Setting nonblocking failed with errno %d", errno);
return APIError::TCP_NONBLOCKING_FAILED;
}
int enable = 1;
err = socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
if (err != 0) {
state_ = State::FAILED;
HELPER_LOG("Setting nodelay failed with errno %d", errno);
return APIError::TCP_NODELAY_FAILED;
APIError err = init_common_();
if (err != APIError::OK) {
return err;
}
// init prologue
@ -234,17 +275,16 @@ APIError APINoiseFrameHelper::init() {
/// Run through handshake messages (if in that phase)
APIError APINoiseFrameHelper::loop() {
APIError err = state_action_();
if (err == APIError::WOULD_BLOCK)
return APIError::OK;
if (err != APIError::OK)
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
return err;
if (!tx_buf_.empty()) {
}
if (!this->tx_buf_.empty()) {
err = try_send_tx_buf_();
if (err != APIError::OK) {
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
return err;
}
}
return APIError::OK;
return APIError::OK; // Convert WOULD_BLOCK to OK to avoid connection termination
}
/** Read a packet into the rx_buf_. If successful, stores frame data in the frame parameter
@ -270,8 +310,8 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
// read header
if (rx_header_buf_len_ < 3) {
// no header information yet
size_t to_read = 3 - rx_header_buf_len_;
ssize_t received = socket_->read(&rx_header_buf_[rx_header_buf_len_], to_read);
uint8_t to_read = 3 - rx_header_buf_len_;
ssize_t received = this->socket_->read(&rx_header_buf_[rx_header_buf_len_], to_read);
if (received == -1) {
if (errno == EWOULDBLOCK || errno == EAGAIN) {
return APIError::WOULD_BLOCK;
@ -284,8 +324,8 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
HELPER_LOG("Connection closed");
return APIError::CONNECTION_CLOSED;
}
rx_header_buf_len_ += received;
if ((size_t) received != to_read) {
rx_header_buf_len_ += static_cast<uint8_t>(received);
if (static_cast<uint8_t>(received) != to_read) {
// not a full read
return APIError::WOULD_BLOCK;
}
@ -317,8 +357,8 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
if (rx_buf_len_ < msg_size) {
// more data to read
size_t to_read = msg_size - rx_buf_len_;
ssize_t received = socket_->read(&rx_buf_[rx_buf_len_], to_read);
uint16_t to_read = msg_size - rx_buf_len_;
ssize_t received = this->socket_->read(&rx_buf_[rx_buf_len_], to_read);
if (received == -1) {
if (errno == EWOULDBLOCK || errno == EAGAIN) {
return APIError::WOULD_BLOCK;
@ -331,8 +371,8 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
HELPER_LOG("Connection closed");
return APIError::CONNECTION_CLOSED;
}
rx_buf_len_ += received;
if ((size_t) received != to_read) {
rx_buf_len_ += static_cast<uint16_t>(received);
if (static_cast<uint16_t>(received) != to_read) {
// not all read
return APIError::WOULD_BLOCK;
}
@ -381,6 +421,8 @@ APIError APINoiseFrameHelper::state_action_() {
if (aerr != APIError::OK)
return aerr;
// ignore contents, may be used in future for flags
// Reserve space for: existing prologue + 2 size bytes + frame data
prologue_.reserve(prologue_.size() + 2 + frame.msg.size());
prologue_.push_back((uint8_t) (frame.msg.size() >> 8));
prologue_.push_back((uint8_t) frame.msg.size());
prologue_.insert(prologue_.end(), frame.msg.begin(), frame.msg.end());
@ -389,16 +431,20 @@ APIError APINoiseFrameHelper::state_action_() {
}
if (state_ == State::SERVER_HELLO) {
// send server hello
const std::string &name = App.get_name();
const std::string &mac = get_mac_address();
std::vector<uint8_t> msg;
// Reserve space for: 1 byte proto + name + null + mac + null
msg.reserve(1 + name.size() + 1 + mac.size() + 1);
// chosen proto
msg.push_back(0x01);
// node name, terminated by null byte
const std::string &name = App.get_name();
const uint8_t *name_ptr = reinterpret_cast<const uint8_t *>(name.c_str());
msg.insert(msg.end(), name_ptr, name_ptr + name.size() + 1);
// node mac, terminated by null byte
const std::string &mac = get_mac_address();
const uint8_t *mac_ptr = reinterpret_cast<const uint8_t *>(mac.c_str());
msg.insert(msg.end(), mac_ptr, mac_ptr + mac.size() + 1);
@ -505,7 +551,6 @@ void APINoiseFrameHelper::send_explicit_handshake_reject_(const std::string &rea
write_frame_(data.data(), data.size());
state_ = orig_state;
}
APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) {
int err;
APIError aerr;
@ -533,7 +578,7 @@ APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) {
return APIError::CIPHERSTATE_DECRYPT_FAILED;
}
size_t msg_size = mbuf.size;
uint16_t msg_size = mbuf.size;
uint8_t *msg_data = frame.msg.data();
if (msg_size < 4) {
state_ = State::FAILED;
@ -559,11 +604,22 @@ APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) {
buffer->type = type;
return APIError::OK;
}
bool APINoiseFrameHelper::can_write_without_blocking() { return state_ == State::DATA && tx_buf_.empty(); }
APIError APINoiseFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) {
int err;
APIError aerr;
aerr = state_action_();
std::vector<uint8_t> *raw_buffer = buffer.get_buffer();
uint16_t payload_len = static_cast<uint16_t>(raw_buffer->size() - frame_header_padding_);
// Resize to include MAC space (required for Noise encryption)
raw_buffer->resize(raw_buffer->size() + frame_footer_size_);
// Use write_protobuf_packets with a single packet
std::vector<PacketInfo> packets;
packets.emplace_back(type, 0, payload_len);
return write_protobuf_packets(buffer, packets);
}
APIError APINoiseFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, const std::vector<PacketInfo> &packets) {
APIError aerr = state_action_();
if (aerr != APIError::OK) {
return aerr;
}
@ -572,77 +628,67 @@ APIError APINoiseFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuf
return APIError::WOULD_BLOCK;
}
if (packets.empty()) {
return APIError::OK;
}
std::vector<uint8_t> *raw_buffer = buffer.get_buffer();
// Message data starts after padding
size_t payload_len = raw_buffer->size() - frame_header_padding_;
size_t padding = 0;
size_t msg_len = 4 + payload_len + padding;
this->reusable_iovs_.clear();
this->reusable_iovs_.reserve(packets.size());
// We need to resize to include MAC space, but we already reserved it in create_buffer
raw_buffer->resize(raw_buffer->size() + frame_footer_size_);
// We need to encrypt each packet in place
for (const auto &packet : packets) {
uint16_t type = packet.message_type;
uint16_t offset = packet.offset;
uint16_t payload_len = packet.payload_size;
uint16_t msg_len = 4 + payload_len; // type(2) + data_len(2) + payload
// Write the noise header in the padded area
// Buffer layout:
// [0] - 0x01 indicator byte
// [1-2] - Size of encrypted payload (filled after encryption)
// [3-4] - Message type (encrypted)
// [5-6] - Payload length (encrypted)
// [7...] - Actual payload data (encrypted)
uint8_t *buf_start = raw_buffer->data();
buf_start[0] = 0x01; // indicator
// buf_start[1], buf_start[2] to be set later after encryption
const uint8_t msg_offset = 3;
buf_start[msg_offset + 0] = (uint8_t) (type >> 8); // type high byte
buf_start[msg_offset + 1] = (uint8_t) type; // type low byte
buf_start[msg_offset + 2] = (uint8_t) (payload_len >> 8); // data_len high byte
buf_start[msg_offset + 3] = (uint8_t) payload_len; // data_len low byte
// payload data is already in the buffer starting at position 7
// The buffer already has padding at offset
uint8_t *buf_start = raw_buffer->data() + offset;
NoiseBuffer mbuf;
noise_buffer_init(mbuf);
// The capacity parameter should be msg_len + frame_footer_size_ (MAC length) to allow space for encryption
noise_buffer_set_inout(mbuf, buf_start + msg_offset, msg_len, msg_len + frame_footer_size_);
err = noise_cipherstate_encrypt(send_cipher_, &mbuf);
if (err != 0) {
state_ = State::FAILED;
HELPER_LOG("noise_cipherstate_encrypt failed: %s", noise_err_to_str(err).c_str());
return APIError::CIPHERSTATE_ENCRYPT_FAILED;
}
// Write noise header
buf_start[0] = 0x01; // indicator
// buf_start[1], buf_start[2] to be set after encryption
size_t total_len = 3 + mbuf.size;
buf_start[1] = (uint8_t) (mbuf.size >> 8);
buf_start[2] = (uint8_t) mbuf.size;
// Write message header (to be encrypted)
const uint8_t msg_offset = 3;
buf_start[msg_offset + 0] = (uint8_t) (type >> 8); // type high byte
buf_start[msg_offset + 1] = (uint8_t) type; // type low byte
buf_start[msg_offset + 2] = (uint8_t) (payload_len >> 8); // data_len high byte
buf_start[msg_offset + 3] = (uint8_t) payload_len; // data_len low byte
// payload data is already in the buffer starting at offset + 7
struct iovec iov;
// Point iov_base to the beginning of the buffer (no unused padding in Noise)
// We send the entire frame: indicator + size + encrypted(type + data_len + payload + MAC)
iov.iov_base = buf_start;
iov.iov_len = total_len;
// Make sure we have space for MAC
// The buffer should already have been sized appropriately
// write raw to not have two packets sent if NAGLE disabled
return write_raw_(&iov, 1);
}
APIError APINoiseFrameHelper::try_send_tx_buf_() {
// try send from tx_buf
while (state_ != State::CLOSED && !tx_buf_.empty()) {
ssize_t sent = socket_->write(tx_buf_.data(), tx_buf_.size());
if (sent == -1) {
if (errno == EWOULDBLOCK || errno == EAGAIN)
break;
// Encrypt the message in place
NoiseBuffer mbuf;
noise_buffer_init(mbuf);
noise_buffer_set_inout(mbuf, buf_start + msg_offset, msg_len, msg_len + frame_footer_size_);
int err = noise_cipherstate_encrypt(send_cipher_, &mbuf);
if (err != 0) {
state_ = State::FAILED;
HELPER_LOG("Socket write failed with errno %d", errno);
return APIError::SOCKET_WRITE_FAILED;
} else if (sent == 0) {
break;
HELPER_LOG("noise_cipherstate_encrypt failed: %s", noise_err_to_str(err).c_str());
return APIError::CIPHERSTATE_ENCRYPT_FAILED;
}
// TODO: inefficient if multiple packets in txbuf
// replace with deque of buffers
tx_buf_.erase(tx_buf_.begin(), tx_buf_.begin() + sent);
// Fill in the encrypted size
buf_start[1] = (uint8_t) (mbuf.size >> 8);
buf_start[2] = (uint8_t) mbuf.size;
// Add iovec for this encrypted packet
struct iovec iov;
iov.iov_base = buf_start;
iov.iov_len = 3 + mbuf.size; // indicator + size + encrypted data
this->reusable_iovs_.push_back(iov);
}
return APIError::OK;
// Send all encrypted packets in one writev call
return this->write_raw_(this->reusable_iovs_.data(), this->reusable_iovs_.size());
}
APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, size_t len) {
APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, uint16_t len) {
uint8_t header[3];
header[0] = 0x01; // indicator
header[1] = (uint8_t) (len >> 8);
@ -652,12 +698,12 @@ APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, size_t len) {
iov[0].iov_base = header;
iov[0].iov_len = 3;
if (len == 0) {
return write_raw_(iov, 1);
return this->write_raw_(iov, 1);
}
iov[1].iov_base = const_cast<uint8_t *>(data);
iov[1].iov_len = len;
return write_raw_(iov, 2);
return this->write_raw_(iov, 2);
}
/** Initiate the data structures for the handshake.
@ -752,58 +798,25 @@ APINoiseFrameHelper::~APINoiseFrameHelper() {
}
}
APIError APINoiseFrameHelper::close() {
state_ = State::CLOSED;
int err = socket_->close();
if (err == -1)
return APIError::CLOSE_FAILED;
return APIError::OK;
}
APIError APINoiseFrameHelper::shutdown(int how) {
int err = socket_->shutdown(how);
if (err == -1)
return APIError::SHUTDOWN_FAILED;
if (how == SHUT_RDWR) {
state_ = State::CLOSED;
}
return APIError::OK;
}
extern "C" {
// declare how noise generates random bytes (here with a good HWRNG based on the RF system)
void noise_rand_bytes(void *output, size_t len) {
if (!esphome::random_bytes(reinterpret_cast<uint8_t *>(output), len)) {
ESP_LOGE(TAG, "Failed to acquire random bytes, rebooting!");
ESP_LOGE(TAG, "Acquiring random bytes failed; rebooting");
arch_restart();
}
}
}
// Explicit template instantiation for Noise
template APIError APIFrameHelper::write_raw_<APINoiseFrameHelper::State>(
const struct iovec *iov, int iovcnt, socket::Socket *socket, std::vector<uint8_t> &tx_buf_, const std::string &info,
APINoiseFrameHelper::State &state, APINoiseFrameHelper::State failed_state);
#endif // USE_API_NOISE
#ifdef USE_API_PLAINTEXT
/// Initialize the frame helper, returns OK if successful.
APIError APIPlaintextFrameHelper::init() {
if (state_ != State::INITIALIZE || socket_ == nullptr) {
HELPER_LOG("Bad state for init %d", (int) state_);
return APIError::BAD_STATE;
}
int err = socket_->setblocking(false);
if (err != 0) {
state_ = State::FAILED;
HELPER_LOG("Setting nonblocking failed with errno %d", errno);
return APIError::TCP_NONBLOCKING_FAILED;
}
int enable = 1;
err = socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
if (err != 0) {
state_ = State::FAILED;
HELPER_LOG("Setting nodelay failed with errno %d", errno);
return APIError::TCP_NODELAY_FAILED;
APIError err = init_common_();
if (err != APIError::OK) {
return err;
}
state_ = State::DATA;
@ -814,14 +827,13 @@ APIError APIPlaintextFrameHelper::loop() {
if (state_ != State::DATA) {
return APIError::BAD_STATE;
}
// try send pending TX data
if (!tx_buf_.empty()) {
if (!this->tx_buf_.empty()) {
APIError err = try_send_tx_buf_();
if (err != APIError::OK) {
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
return err;
}
}
return APIError::OK;
return APIError::OK; // Convert WOULD_BLOCK to OK to avoid connection termination
}
/** Read a packet into the rx_buf_. If successful, stores frame data in the frame parameter
@ -841,12 +853,15 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
// read header
while (!rx_header_parsed_) {
uint8_t data;
// Reading one byte at a time is fastest in practice for ESP32 when
// there is no data on the wire (which is the common case).
// This results in faster failure detection compared to
// attempting to read multiple bytes at once.
ssize_t received = socket_->read(&data, 1);
// Now that we know when the socket is ready, we can read up to 3 bytes
// into the rx_header_buf_ before we have to switch back to reading
// one byte at a time to ensure we don't read past the message and
// into the next one.
// Read directly into rx_header_buf_ at the current position
// Try to get to at least 3 bytes total (indicator + 2 varint bytes), then read one byte at a time
ssize_t received =
this->socket_->read(&rx_header_buf_[rx_header_buf_pos_], rx_header_buf_pos_ < 3 ? 3 - rx_header_buf_pos_ : 1);
if (received == -1) {
if (errno == EWOULDBLOCK || errno == EAGAIN) {
return APIError::WOULD_BLOCK;
@ -860,64 +875,74 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
return APIError::CONNECTION_CLOSED;
}
// Successfully read a byte
// Process byte according to current buffer position
if (rx_header_buf_pos_ == 0) { // Case 1: First byte (indicator byte)
if (data != 0x00) {
// If this was the first read, validate the indicator byte
if (rx_header_buf_pos_ == 0 && received > 0) {
if (rx_header_buf_[0] != 0x00) {
state_ = State::FAILED;
HELPER_LOG("Bad indicator byte %u", data);
HELPER_LOG("Bad indicator byte %u", rx_header_buf_[0]);
return APIError::BAD_INDICATOR;
}
// We don't store the indicator byte, just increment position
rx_header_buf_pos_ = 1; // Set to 1 directly
continue; // Need more bytes before we can parse
}
// Check buffer overflow before storing
if (rx_header_buf_pos_ == 5) { // Case 2: Buffer would overflow (5 bytes is max allowed)
rx_header_buf_pos_ += received;
// Check for buffer overflow
if (rx_header_buf_pos_ >= sizeof(rx_header_buf_)) {
state_ = State::FAILED;
HELPER_LOG("Header buffer overflow");
return APIError::BAD_DATA_PACKET;
}
// Store byte in buffer (adjust index to account for skipped indicator byte)
rx_header_buf_[rx_header_buf_pos_ - 1] = data;
// Increment position after storing
rx_header_buf_pos_++;
// Case 3: If we only have one varint byte, we need more
if (rx_header_buf_pos_ == 2) { // Have read indicator + 1 byte
continue; // Need more bytes before we can parse
// Need at least 3 bytes total (indicator + 2 varint bytes) before trying to parse
if (rx_header_buf_pos_ < 3) {
continue;
}
// At this point, we have at least 3 bytes total:
// - Validated indicator byte (0x00) but not stored
// - Validated indicator byte (0x00) stored at position 0
// - At least 2 bytes in the buffer for the varints
// Buffer layout:
// First 1-3 bytes: Message size varint (variable length)
// - 2 bytes would only allow up to 16383, which is less than noise's 65535
// [0]: indicator byte (0x00)
// [1-3]: Message size varint (variable length)
// - 2 bytes would only allow up to 16383, which is less than noise's UINT16_MAX (65535)
// - 3 bytes allows up to 2097151, ensuring we support at least as much as noise
// Remaining 1-2 bytes: Message type varint (variable length)
// [2-5]: Message type varint (variable length)
// We now attempt to parse both varints. If either is incomplete,
// we'll continue reading more bytes.
// Skip indicator byte at position 0
uint8_t varint_pos = 1;
uint32_t consumed = 0;
auto msg_size_varint = ProtoVarInt::parse(&rx_header_buf_[0], rx_header_buf_pos_ - 1, &consumed);
auto msg_size_varint = ProtoVarInt::parse(&rx_header_buf_[varint_pos], rx_header_buf_pos_ - varint_pos, &consumed);
if (!msg_size_varint.has_value()) {
// not enough data there yet
continue;
}
rx_header_parsed_len_ = msg_size_varint->as_uint32();
if (msg_size_varint->as_uint32() > std::numeric_limits<uint16_t>::max()) {
state_ = State::FAILED;
HELPER_LOG("Bad packet: message size %" PRIu32 " exceeds maximum %u", msg_size_varint->as_uint32(),
std::numeric_limits<uint16_t>::max());
return APIError::BAD_DATA_PACKET;
}
rx_header_parsed_len_ = msg_size_varint->as_uint16();
auto msg_type_varint = ProtoVarInt::parse(&rx_header_buf_[consumed], rx_header_buf_pos_ - 1 - consumed, &consumed);
// Move to next varint position
varint_pos += consumed;
auto msg_type_varint = ProtoVarInt::parse(&rx_header_buf_[varint_pos], rx_header_buf_pos_ - varint_pos, &consumed);
if (!msg_type_varint.has_value()) {
// not enough data there yet
continue;
}
rx_header_parsed_type_ = msg_type_varint->as_uint32();
if (msg_type_varint->as_uint32() > std::numeric_limits<uint16_t>::max()) {
state_ = State::FAILED;
HELPER_LOG("Bad packet: message type %" PRIu32 " exceeds maximum %u", msg_type_varint->as_uint32(),
std::numeric_limits<uint16_t>::max());
return APIError::BAD_DATA_PACKET;
}
rx_header_parsed_type_ = msg_type_varint->as_uint16();
rx_header_parsed_ = true;
}
// header reading done
@ -929,8 +954,8 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
if (rx_buf_len_ < rx_header_parsed_len_) {
// more data to read
size_t to_read = rx_header_parsed_len_ - rx_buf_len_;
ssize_t received = socket_->read(&rx_buf_[rx_buf_len_], to_read);
uint16_t to_read = rx_header_parsed_len_ - rx_buf_len_;
ssize_t received = this->socket_->read(&rx_buf_[rx_buf_len_], to_read);
if (received == -1) {
if (errno == EWOULDBLOCK || errno == EAGAIN) {
return APIError::WOULD_BLOCK;
@ -943,8 +968,8 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
HELPER_LOG("Connection closed");
return APIError::CONNECTION_CLOSED;
}
rx_buf_len_ += received;
if ((size_t) received != to_read) {
rx_buf_len_ += static_cast<uint16_t>(received);
if (static_cast<uint16_t>(received) != to_read) {
// not all read
return APIError::WOULD_BLOCK;
}
@ -962,7 +987,6 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
rx_header_parsed_ = false;
return APIError::OK;
}
APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
APIError aerr;
@ -990,7 +1014,7 @@ APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
"Bad indicator byte";
iov[0].iov_base = (void *) msg;
iov[0].iov_len = 19;
write_raw_(iov, 1);
this->write_raw_(iov, 1);
}
return aerr;
}
@ -1001,108 +1025,89 @@ APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
buffer->type = rx_header_parsed_type_;
return APIError::OK;
}
bool APIPlaintextFrameHelper::can_write_without_blocking() { return state_ == State::DATA && tx_buf_.empty(); }
APIError APIPlaintextFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) {
std::vector<uint8_t> *raw_buffer = buffer.get_buffer();
uint16_t payload_len = static_cast<uint16_t>(raw_buffer->size() - frame_header_padding_);
// Use write_protobuf_packets with a single packet
std::vector<PacketInfo> packets;
packets.emplace_back(type, 0, payload_len);
return write_protobuf_packets(buffer, packets);
}
APIError APIPlaintextFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer,
const std::vector<PacketInfo> &packets) {
if (state_ != State::DATA) {
return APIError::BAD_STATE;
}
if (packets.empty()) {
return APIError::OK;
}
std::vector<uint8_t> *raw_buffer = buffer.get_buffer();
// Message data starts after padding (frame_header_padding_ = 6)
size_t payload_len = raw_buffer->size() - frame_header_padding_;
this->reusable_iovs_.clear();
this->reusable_iovs_.reserve(packets.size());
// Calculate varint sizes for header components
size_t size_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(payload_len));
size_t type_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(type));
size_t total_header_len = 1 + size_varint_len + type_varint_len;
for (const auto &packet : packets) {
uint16_t type = packet.message_type;
uint16_t offset = packet.offset;
uint16_t payload_len = packet.payload_size;
if (total_header_len > frame_header_padding_) {
// Header is too large to fit in the padding
return APIError::BAD_ARG;
// Calculate varint sizes for header layout
uint8_t size_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(payload_len));
uint8_t type_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(type));
uint8_t total_header_len = 1 + size_varint_len + type_varint_len;
// Calculate where to start writing the header
// The header starts at the latest possible position to minimize unused padding
//
// Example 1 (small values): total_header_len = 3, header_offset = 6 - 3 = 3
// [0-2] - Unused padding
// [3] - 0x00 indicator byte
// [4] - Payload size varint (1 byte, for sizes 0-127)
// [5] - Message type varint (1 byte, for types 0-127)
// [6...] - Actual payload data
//
// Example 2 (medium values): total_header_len = 4, header_offset = 6 - 4 = 2
// [0-1] - Unused padding
// [2] - 0x00 indicator byte
// [3-4] - Payload size varint (2 bytes, for sizes 128-16383)
// [5] - Message type varint (1 byte, for types 0-127)
// [6...] - Actual payload data
//
// Example 3 (large values): total_header_len = 6, header_offset = 6 - 6 = 0
// [0] - 0x00 indicator byte
// [1-3] - Payload size varint (3 bytes, for sizes 16384-2097151)
// [4-5] - Message type varint (2 bytes, for types 128-32767)
// [6...] - Actual payload data
//
// The message starts at offset + frame_header_padding_
// So we write the header starting at offset + frame_header_padding_ - total_header_len
uint8_t *buf_start = raw_buffer->data() + offset;
uint32_t header_offset = frame_header_padding_ - total_header_len;
// Write the plaintext header
buf_start[header_offset] = 0x00; // indicator
// Encode size varint directly into buffer
ProtoVarInt(payload_len).encode_to_buffer_unchecked(buf_start + header_offset + 1, size_varint_len);
// Encode type varint directly into buffer
ProtoVarInt(type).encode_to_buffer_unchecked(buf_start + header_offset + 1 + size_varint_len, type_varint_len);
// Add iovec for this packet (header + payload)
struct iovec iov;
iov.iov_base = buf_start + header_offset;
iov.iov_len = total_header_len + payload_len;
this->reusable_iovs_.push_back(iov);
}
// Calculate where to start writing the header
// The header starts at the latest possible position to minimize unused padding
//
// Example 1 (small values): total_header_len = 3, header_offset = 6 - 3 = 3
// [0-2] - Unused padding
// [3] - 0x00 indicator byte
// [4] - Payload size varint (1 byte, for sizes 0-127)
// [5] - Message type varint (1 byte, for types 0-127)
// [6...] - Actual payload data
//
// Example 2 (medium values): total_header_len = 4, header_offset = 6 - 4 = 2
// [0-1] - Unused padding
// [2] - 0x00 indicator byte
// [3-4] - Payload size varint (2 bytes, for sizes 128-16383)
// [5] - Message type varint (1 byte, for types 0-127)
// [6...] - Actual payload data
//
// Example 3 (large values): total_header_len = 6, header_offset = 6 - 6 = 0
// [0] - 0x00 indicator byte
// [1-3] - Payload size varint (3 bytes, for sizes 16384-2097151)
// [4-5] - Message type varint (2 bytes, for types 128-32767)
// [6...] - Actual payload data
uint8_t *buf_start = raw_buffer->data();
size_t header_offset = frame_header_padding_ - total_header_len;
// Write the plaintext header
buf_start[header_offset] = 0x00; // indicator
// Encode size varint directly into buffer
ProtoVarInt(payload_len).encode_to_buffer_unchecked(buf_start + header_offset + 1, size_varint_len);
// Encode type varint directly into buffer
ProtoVarInt(type).encode_to_buffer_unchecked(buf_start + header_offset + 1 + size_varint_len, type_varint_len);
struct iovec iov;
// Point iov_base to the beginning of our header (skip unused padding)
// This ensures we only send the actual header and payload, not the empty padding bytes
iov.iov_base = buf_start + header_offset;
iov.iov_len = total_header_len + payload_len;
return write_raw_(&iov, 1);
}
APIError APIPlaintextFrameHelper::try_send_tx_buf_() {
// try send from tx_buf
while (state_ != State::CLOSED && !tx_buf_.empty()) {
ssize_t sent = socket_->write(tx_buf_.data(), tx_buf_.size());
if (is_would_block(sent)) {
break;
} else if (sent == -1) {
state_ = State::FAILED;
HELPER_LOG("Socket write failed with errno %d", errno);
return APIError::SOCKET_WRITE_FAILED;
}
// TODO: inefficient if multiple packets in txbuf
// replace with deque of buffers
tx_buf_.erase(tx_buf_.begin(), tx_buf_.begin() + sent);
}
return APIError::OK;
// Send all packets in one writev call
return write_raw_(this->reusable_iovs_.data(), this->reusable_iovs_.size());
}
APIError APIPlaintextFrameHelper::close() {
state_ = State::CLOSED;
int err = socket_->close();
if (err == -1)
return APIError::CLOSE_FAILED;
return APIError::OK;
}
APIError APIPlaintextFrameHelper::shutdown(int how) {
int err = socket_->shutdown(how);
if (err == -1)
return APIError::SHUTDOWN_FAILED;
if (how == SHUT_RDWR) {
state_ = State::CLOSED;
}
return APIError::OK;
}
// Explicit template instantiation for Plaintext
template APIError APIFrameHelper::write_raw_<APIPlaintextFrameHelper::State>(
const struct iovec *iov, int iovcnt, socket::Socket *socket, std::vector<uint8_t> &tx_buf_, const std::string &info,
APIPlaintextFrameHelper::State &state, APIPlaintextFrameHelper::State failed_state);
#endif // USE_API_PLAINTEXT
} // namespace api

View File

@ -1,6 +1,7 @@
#pragma once
#include <cstdint>
#include <deque>
#include <limits>
#include <utility>
#include <vector>
@ -12,6 +13,7 @@
#include "api_noise_context.h"
#include "esphome/components/socket/socket.h"
#include "esphome/core/application.h"
namespace esphome {
namespace api {
@ -21,15 +23,19 @@ class ProtoWriteBuffer;
struct ReadPacketBuffer {
std::vector<uint8_t> container;
uint16_t type;
size_t data_offset;
size_t data_len;
uint16_t data_offset;
uint16_t data_len;
};
struct PacketBuffer {
const std::vector<uint8_t> container;
uint16_t type;
uint8_t data_offset;
uint8_t data_len;
// Packed packet info structure to minimize memory usage
struct PacketInfo {
uint16_t message_type; // 2 bytes
uint16_t offset; // 2 bytes (sufficient for packet size ~1460 bytes)
uint16_t payload_size; // 2 bytes (up to 65535 bytes)
uint16_t padding; // 2 byte (for alignment)
PacketInfo(uint16_t type, uint16_t off, uint16_t size)
: message_type(type), offset(off), payload_size(size), padding(0) {}
};
enum class APIError : int {
@ -62,38 +68,126 @@ const char *api_error_to_str(APIError err);
class APIFrameHelper {
public:
APIFrameHelper() = default;
explicit APIFrameHelper(std::unique_ptr<socket::Socket> socket) : socket_owned_(std::move(socket)) {
socket_ = socket_owned_.get();
}
virtual ~APIFrameHelper() = default;
virtual APIError init() = 0;
virtual APIError loop() = 0;
virtual APIError read_packet(ReadPacketBuffer *buffer) = 0;
virtual bool can_write_without_blocking() = 0;
virtual APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) = 0;
virtual std::string getpeername() = 0;
virtual int getpeername(struct sockaddr *addr, socklen_t *addrlen) = 0;
virtual APIError close() = 0;
virtual APIError shutdown(int how) = 0;
bool can_write_without_blocking() { return state_ == State::DATA && tx_buf_.empty(); }
std::string getpeername() { return socket_->getpeername(); }
int getpeername(struct sockaddr *addr, socklen_t *addrlen) { return socket_->getpeername(addr, addrlen); }
APIError close() {
state_ = State::CLOSED;
int err = this->socket_->close();
if (err == -1)
return APIError::CLOSE_FAILED;
return APIError::OK;
}
APIError shutdown(int how) {
int err = this->socket_->shutdown(how);
if (err == -1)
return APIError::SHUTDOWN_FAILED;
if (how == SHUT_RDWR) {
state_ = State::CLOSED;
}
return APIError::OK;
}
// Give this helper a name for logging
virtual void set_log_info(std::string info) = 0;
void set_log_info(std::string info) { info_ = std::move(info); }
virtual APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) = 0;
// Write multiple protobuf packets in a single operation
// packets contains (message_type, offset, length) for each message in the buffer
// The buffer contains all messages with appropriate padding before each
virtual APIError write_protobuf_packets(ProtoWriteBuffer buffer, const std::vector<PacketInfo> &packets) = 0;
// Get the frame header padding required by this protocol
virtual uint8_t frame_header_padding() = 0;
// Get the frame footer size required by this protocol
virtual uint8_t frame_footer_size() = 0;
// Check if socket has data ready to read
bool is_socket_ready() const { return socket_ != nullptr && socket_->ready(); }
protected:
// Struct for holding parsed frame data
struct ParsedFrame {
std::vector<uint8_t> msg;
};
// Buffer containing data to be sent
struct SendBuffer {
std::vector<uint8_t> data;
uint16_t offset{0}; // Current offset within the buffer (uint16_t to reduce memory usage)
// Using uint16_t reduces memory usage since ESPHome API messages are limited to UINT16_MAX (65535) bytes
uint16_t remaining() const { return static_cast<uint16_t>(data.size()) - offset; }
const uint8_t *current_data() const { return data.data() + offset; }
};
// Queue of data buffers to be sent
std::deque<SendBuffer> tx_buf_;
// Common state enum for all frame helpers
// Note: Not all states are used by all implementations
// - INITIALIZE: Used by both Noise and Plaintext
// - CLIENT_HELLO, SERVER_HELLO, HANDSHAKE: Only used by Noise protocol
// - DATA: Used by both Noise and Plaintext
// - CLOSED: Used by both Noise and Plaintext
// - FAILED: Used by both Noise and Plaintext
// - EXPLICIT_REJECT: Only used by Noise protocol
enum class State {
INITIALIZE = 1,
CLIENT_HELLO = 2, // Noise only
SERVER_HELLO = 3, // Noise only
HANDSHAKE = 4, // Noise only
DATA = 5,
CLOSED = 6,
FAILED = 7,
EXPLICIT_REJECT = 8, // Noise only
};
// Current state of the frame helper
State state_{State::INITIALIZE};
// Helper name for logging
std::string info_;
// Socket for communication
socket::Socket *socket_{nullptr};
std::unique_ptr<socket::Socket> socket_owned_;
// Common implementation for writing raw data to socket
APIError write_raw_(const struct iovec *iov, int iovcnt);
// Try to send data from the tx buffer
APIError try_send_tx_buf_();
// Helper method to buffer data from IOVs
void buffer_data_from_iov_(const struct iovec *iov, int iovcnt, uint16_t total_write_len);
template<typename StateEnum>
APIError write_raw_(const struct iovec *iov, int iovcnt, socket::Socket *socket, std::vector<uint8_t> &tx_buf,
const std::string &info, StateEnum &state, StateEnum failed_state);
uint8_t frame_header_padding_{0};
uint8_t frame_footer_size_{0};
// Reusable IOV array for write_protobuf_packets to avoid repeated allocations
std::vector<struct iovec> reusable_iovs_;
// Receive buffer for reading frame data
std::vector<uint8_t> rx_buf_;
uint16_t rx_buf_len_ = 0;
// Common initialization for both plaintext and noise protocols
APIError init_common_();
};
#ifdef USE_API_NOISE
class APINoiseFrameHelper : public APIFrameHelper {
public:
APINoiseFrameHelper(std::unique_ptr<socket::Socket> socket, std::shared_ptr<APINoiseContext> ctx)
: socket_(std::move(socket)), ctx_(std::move(ctx)) {
: APIFrameHelper(std::move(socket)), ctx_(std::move(ctx)) {
// Noise header structure:
// Pos 0: indicator (0x01)
// Pos 1-2: encrypted payload size (16-bit big-endian)
@ -105,49 +199,26 @@ class APINoiseFrameHelper : public APIFrameHelper {
APIError init() override;
APIError loop() override;
APIError read_packet(ReadPacketBuffer *buffer) override;
bool can_write_without_blocking() override;
APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) override;
std::string getpeername() override { return this->socket_->getpeername(); }
int getpeername(struct sockaddr *addr, socklen_t *addrlen) override {
return this->socket_->getpeername(addr, addrlen);
}
APIError close() override;
APIError shutdown(int how) override;
// Give this helper a name for logging
void set_log_info(std::string info) override { info_ = std::move(info); }
APIError write_protobuf_packets(ProtoWriteBuffer buffer, const std::vector<PacketInfo> &packets) override;
// Get the frame header padding required by this protocol
uint8_t frame_header_padding() override { return frame_header_padding_; }
// Get the frame footer size required by this protocol
uint8_t frame_footer_size() override { return frame_footer_size_; }
protected:
struct ParsedFrame {
std::vector<uint8_t> msg;
};
APIError state_action_();
APIError try_read_frame_(ParsedFrame *frame);
APIError try_send_tx_buf_();
APIError write_frame_(const uint8_t *data, size_t len);
inline APIError write_raw_(const struct iovec *iov, int iovcnt) {
return APIFrameHelper::write_raw_(iov, iovcnt, socket_.get(), tx_buf_, info_, state_, State::FAILED);
}
APIError write_frame_(const uint8_t *data, uint16_t len);
APIError init_handshake_();
APIError check_handshake_finished_();
void send_explicit_handshake_reject_(const std::string &reason);
std::unique_ptr<socket::Socket> socket_;
std::string info_;
// Fixed-size header buffer for noise protocol:
// 1 byte for indicator + 2 bytes for message size (16-bit value, not varint)
// Note: Maximum message size is 65535, with a limit of 128 bytes during handshake phase
// Note: Maximum message size is UINT16_MAX (65535), with a limit of 128 bytes during handshake phase
uint8_t rx_header_buf_[3];
size_t rx_header_buf_len_ = 0;
std::vector<uint8_t> rx_buf_;
size_t rx_buf_len_ = 0;
uint8_t rx_header_buf_len_ = 0;
std::vector<uint8_t> tx_buf_;
std::vector<uint8_t> prologue_;
std::shared_ptr<APINoiseContext> ctx_;
@ -155,24 +226,13 @@ class APINoiseFrameHelper : public APIFrameHelper {
NoiseCipherState *send_cipher_{nullptr};
NoiseCipherState *recv_cipher_{nullptr};
NoiseProtocolId nid_;
enum class State {
INITIALIZE = 1,
CLIENT_HELLO = 2,
SERVER_HELLO = 3,
HANDSHAKE = 4,
DATA = 5,
CLOSED = 6,
FAILED = 7,
EXPLICIT_REJECT = 8,
} state_ = State::INITIALIZE;
};
#endif // USE_API_NOISE
#ifdef USE_API_PLAINTEXT
class APIPlaintextFrameHelper : public APIFrameHelper {
public:
APIPlaintextFrameHelper(std::unique_ptr<socket::Socket> socket) : socket_(std::move(socket)) {
APIPlaintextFrameHelper(std::unique_ptr<socket::Socket> socket) : APIFrameHelper(std::move(socket)) {
// Plaintext header structure (worst case):
// Pos 0: indicator (0x00)
// Pos 1-3: payload size varint (up to 3 bytes)
@ -184,60 +244,27 @@ class APIPlaintextFrameHelper : public APIFrameHelper {
APIError init() override;
APIError loop() override;
APIError read_packet(ReadPacketBuffer *buffer) override;
bool can_write_without_blocking() override;
APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) override;
std::string getpeername() override { return this->socket_->getpeername(); }
int getpeername(struct sockaddr *addr, socklen_t *addrlen) override {
return this->socket_->getpeername(addr, addrlen);
}
APIError close() override;
APIError shutdown(int how) override;
// Give this helper a name for logging
void set_log_info(std::string info) override { info_ = std::move(info); }
// Get the frame header padding required by this protocol
APIError write_protobuf_packets(ProtoWriteBuffer buffer, const std::vector<PacketInfo> &packets) override;
uint8_t frame_header_padding() override { return frame_header_padding_; }
// Get the frame footer size required by this protocol
uint8_t frame_footer_size() override { return frame_footer_size_; }
protected:
struct ParsedFrame {
std::vector<uint8_t> msg;
};
APIError try_read_frame_(ParsedFrame *frame);
APIError try_send_tx_buf_();
inline APIError write_raw_(const struct iovec *iov, int iovcnt) {
return APIFrameHelper::write_raw_(iov, iovcnt, socket_.get(), tx_buf_, info_, state_, State::FAILED);
}
std::unique_ptr<socket::Socket> socket_;
std::string info_;
// Fixed-size header buffer for plaintext protocol:
// We only need space for the two varints since we validate the indicator byte separately.
// To match noise protocol's maximum message size (65535), we need:
// 3 bytes for message size varint (supports up to 2097151) + 2 bytes for message type varint
// We now store the indicator byte + the two varints.
// To match noise protocol's maximum message size (UINT16_MAX = 65535), we need:
// 1 byte for indicator + 3 bytes for message size varint (supports up to 2097151) + 2 bytes for message type varint
//
// While varints could theoretically be up to 10 bytes each for 64-bit values,
// attempting to process messages with headers that large would likely crash the
// ESP32 due to memory constraints.
uint8_t rx_header_buf_[5]; // 5 bytes for varints (3 for size + 2 for type)
uint8_t rx_header_buf_[6]; // 1 byte indicator + 5 bytes for varints (3 for size + 2 for type)
uint8_t rx_header_buf_pos_ = 0;
bool rx_header_parsed_ = false;
uint32_t rx_header_parsed_type_ = 0;
uint32_t rx_header_parsed_len_ = 0;
std::vector<uint8_t> rx_buf_;
size_t rx_buf_len_ = 0;
std::vector<uint8_t> tx_buf_;
enum class State {
INITIALIZE = 1,
DATA = 2,
CLOSED = 3,
FAILED = 4,
} state_ = State::INITIALIZE;
uint16_t rx_header_parsed_type_ = 0;
uint16_t rx_header_parsed_len_ = 0;
};
#endif

View File

@ -96,6 +96,8 @@ template<> const char *proto_enum_to_string<enums::ColorMode>(enums::ColorMode v
return "COLOR_MODE_UNKNOWN";
case enums::COLOR_MODE_ON_OFF:
return "COLOR_MODE_ON_OFF";
case enums::COLOR_MODE_LEGACY_BRIGHTNESS:
return "COLOR_MODE_LEGACY_BRIGHTNESS";
case enums::COLOR_MODE_BRIGHTNESS:
return "COLOR_MODE_BRIGHTNESS";
case enums::COLOR_MODE_WHITE:

View File

@ -41,7 +41,8 @@ enum FanDirection : uint32_t {
enum ColorMode : uint32_t {
COLOR_MODE_UNKNOWN = 0,
COLOR_MODE_ON_OFF = 1,
COLOR_MODE_BRIGHTNESS = 2,
COLOR_MODE_LEGACY_BRIGHTNESS = 2,
COLOR_MODE_BRIGHTNESS = 3,
COLOR_MODE_WHITE = 7,
COLOR_MODE_COLOR_TEMPERATURE = 11,
COLOR_MODE_COLD_WARM_WHITE = 19,
@ -254,6 +255,11 @@ enum UpdateCommand : uint32_t {
class HelloRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 1;
static constexpr uint16_t ESTIMATED_SIZE = 17;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "hello_request"; }
#endif
std::string client_info{};
uint32_t api_version_major{0};
uint32_t api_version_minor{0};
@ -269,6 +275,11 @@ class HelloRequest : public ProtoMessage {
};
class HelloResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 2;
static constexpr uint16_t ESTIMATED_SIZE = 26;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "hello_response"; }
#endif
uint32_t api_version_major{0};
uint32_t api_version_minor{0};
std::string server_info{};
@ -285,6 +296,11 @@ class HelloResponse : public ProtoMessage {
};
class ConnectRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 3;
static constexpr uint16_t ESTIMATED_SIZE = 9;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "connect_request"; }
#endif
std::string password{};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -297,6 +313,11 @@ class ConnectRequest : public ProtoMessage {
};
class ConnectResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 4;
static constexpr uint16_t ESTIMATED_SIZE = 2;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "connect_response"; }
#endif
bool invalid_password{false};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -309,6 +330,11 @@ class ConnectResponse : public ProtoMessage {
};
class DisconnectRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 5;
static constexpr uint16_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "disconnect_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -319,6 +345,11 @@ class DisconnectRequest : public ProtoMessage {
};
class DisconnectResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 6;
static constexpr uint16_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "disconnect_response"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -329,6 +360,11 @@ class DisconnectResponse : public ProtoMessage {
};
class PingRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 7;
static constexpr uint16_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "ping_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -339,6 +375,11 @@ class PingRequest : public ProtoMessage {
};
class PingResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 8;
static constexpr uint16_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "ping_response"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -349,6 +390,11 @@ class PingResponse : public ProtoMessage {
};
class DeviceInfoRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 9;
static constexpr uint16_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "device_info_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -359,6 +405,11 @@ class DeviceInfoRequest : public ProtoMessage {
};
class DeviceInfoResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 10;
static constexpr uint16_t ESTIMATED_SIZE = 129;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "device_info_response"; }
#endif
bool uses_password{false};
std::string name{};
std::string mac_address{};
@ -390,6 +441,11 @@ class DeviceInfoResponse : public ProtoMessage {
};
class ListEntitiesRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 11;
static constexpr uint16_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -400,6 +456,11 @@ class ListEntitiesRequest : public ProtoMessage {
};
class ListEntitiesDoneResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 19;
static constexpr uint16_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_done_response"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -410,6 +471,11 @@ class ListEntitiesDoneResponse : public ProtoMessage {
};
class SubscribeStatesRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 20;
static constexpr uint16_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "subscribe_states_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -420,6 +486,11 @@ class SubscribeStatesRequest : public ProtoMessage {
};
class ListEntitiesBinarySensorResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 12;
static constexpr uint16_t ESTIMATED_SIZE = 56;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_binary_sensor_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -442,6 +513,11 @@ class ListEntitiesBinarySensorResponse : public ProtoMessage {
};
class BinarySensorStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 21;
static constexpr uint16_t ESTIMATED_SIZE = 9;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "binary_sensor_state_response"; }
#endif
uint32_t key{0};
bool state{false};
bool missing_state{false};
@ -457,6 +533,11 @@ class BinarySensorStateResponse : public ProtoMessage {
};
class ListEntitiesCoverResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 13;
static constexpr uint16_t ESTIMATED_SIZE = 62;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_cover_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -482,6 +563,11 @@ class ListEntitiesCoverResponse : public ProtoMessage {
};
class CoverStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 22;
static constexpr uint16_t ESTIMATED_SIZE = 19;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "cover_state_response"; }
#endif
uint32_t key{0};
enums::LegacyCoverState legacy_state{};
float position{0.0f};
@ -499,6 +585,11 @@ class CoverStateResponse : public ProtoMessage {
};
class CoverCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 30;
static constexpr uint16_t ESTIMATED_SIZE = 25;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "cover_command_request"; }
#endif
uint32_t key{0};
bool has_legacy_command{false};
enums::LegacyCoverCommand legacy_command{};
@ -519,6 +610,11 @@ class CoverCommandRequest : public ProtoMessage {
};
class ListEntitiesFanResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 14;
static constexpr uint16_t ESTIMATED_SIZE = 73;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_fan_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -544,6 +640,11 @@ class ListEntitiesFanResponse : public ProtoMessage {
};
class FanStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 23;
static constexpr uint16_t ESTIMATED_SIZE = 26;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "fan_state_response"; }
#endif
uint32_t key{0};
bool state{false};
bool oscillating{false};
@ -564,6 +665,11 @@ class FanStateResponse : public ProtoMessage {
};
class FanCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 31;
static constexpr uint16_t ESTIMATED_SIZE = 38;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "fan_command_request"; }
#endif
uint32_t key{0};
bool has_state{false};
bool state{false};
@ -590,6 +696,11 @@ class FanCommandRequest : public ProtoMessage {
};
class ListEntitiesLightResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 15;
static constexpr uint16_t ESTIMATED_SIZE = 85;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_light_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -618,6 +729,11 @@ class ListEntitiesLightResponse : public ProtoMessage {
};
class LightStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 24;
static constexpr uint16_t ESTIMATED_SIZE = 63;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "light_state_response"; }
#endif
uint32_t key{0};
bool state{false};
float brightness{0.0f};
@ -644,6 +760,11 @@ class LightStateResponse : public ProtoMessage {
};
class LightCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 32;
static constexpr uint16_t ESTIMATED_SIZE = 107;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "light_command_request"; }
#endif
uint32_t key{0};
bool has_state{false};
bool state{false};
@ -684,6 +805,11 @@ class LightCommandRequest : public ProtoMessage {
};
class ListEntitiesSensorResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 16;
static constexpr uint16_t ESTIMATED_SIZE = 73;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_sensor_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -710,6 +836,11 @@ class ListEntitiesSensorResponse : public ProtoMessage {
};
class SensorStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 25;
static constexpr uint16_t ESTIMATED_SIZE = 12;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "sensor_state_response"; }
#endif
uint32_t key{0};
float state{0.0f};
bool missing_state{false};
@ -725,6 +856,11 @@ class SensorStateResponse : public ProtoMessage {
};
class ListEntitiesSwitchResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 17;
static constexpr uint16_t ESTIMATED_SIZE = 56;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_switch_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -747,6 +883,11 @@ class ListEntitiesSwitchResponse : public ProtoMessage {
};
class SwitchStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 26;
static constexpr uint16_t ESTIMATED_SIZE = 7;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "switch_state_response"; }
#endif
uint32_t key{0};
bool state{false};
void encode(ProtoWriteBuffer buffer) const override;
@ -761,6 +902,11 @@ class SwitchStateResponse : public ProtoMessage {
};
class SwitchCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 33;
static constexpr uint16_t ESTIMATED_SIZE = 7;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "switch_command_request"; }
#endif
uint32_t key{0};
bool state{false};
void encode(ProtoWriteBuffer buffer) const override;
@ -775,6 +921,11 @@ class SwitchCommandRequest : public ProtoMessage {
};
class ListEntitiesTextSensorResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 18;
static constexpr uint16_t ESTIMATED_SIZE = 54;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_text_sensor_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -796,6 +947,11 @@ class ListEntitiesTextSensorResponse : public ProtoMessage {
};
class TextSensorStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 27;
static constexpr uint16_t ESTIMATED_SIZE = 16;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "text_sensor_state_response"; }
#endif
uint32_t key{0};
std::string state{};
bool missing_state{false};
@ -812,6 +968,11 @@ class TextSensorStateResponse : public ProtoMessage {
};
class SubscribeLogsRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 28;
static constexpr uint16_t ESTIMATED_SIZE = 4;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "subscribe_logs_request"; }
#endif
enums::LogLevel level{};
bool dump_config{false};
void encode(ProtoWriteBuffer buffer) const override;
@ -825,6 +986,11 @@ class SubscribeLogsRequest : public ProtoMessage {
};
class SubscribeLogsResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 29;
static constexpr uint16_t ESTIMATED_SIZE = 13;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "subscribe_logs_response"; }
#endif
enums::LogLevel level{};
std::string message{};
bool send_failed{false};
@ -840,6 +1006,11 @@ class SubscribeLogsResponse : public ProtoMessage {
};
class NoiseEncryptionSetKeyRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 124;
static constexpr uint16_t ESTIMATED_SIZE = 9;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "noise_encryption_set_key_request"; }
#endif
std::string key{};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -852,6 +1023,11 @@ class NoiseEncryptionSetKeyRequest : public ProtoMessage {
};
class NoiseEncryptionSetKeyResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 125;
static constexpr uint16_t ESTIMATED_SIZE = 2;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "noise_encryption_set_key_response"; }
#endif
bool success{false};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -864,6 +1040,11 @@ class NoiseEncryptionSetKeyResponse : public ProtoMessage {
};
class SubscribeHomeassistantServicesRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 34;
static constexpr uint16_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "subscribe_homeassistant_services_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -887,6 +1068,11 @@ class HomeassistantServiceMap : public ProtoMessage {
};
class HomeassistantServiceResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 35;
static constexpr uint16_t ESTIMATED_SIZE = 113;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "homeassistant_service_response"; }
#endif
std::string service{};
std::vector<HomeassistantServiceMap> data{};
std::vector<HomeassistantServiceMap> data_template{};
@ -904,6 +1090,11 @@ class HomeassistantServiceResponse : public ProtoMessage {
};
class SubscribeHomeAssistantStatesRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 38;
static constexpr uint16_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "subscribe_home_assistant_states_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -914,6 +1105,11 @@ class SubscribeHomeAssistantStatesRequest : public ProtoMessage {
};
class SubscribeHomeAssistantStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 39;
static constexpr uint16_t ESTIMATED_SIZE = 20;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "subscribe_home_assistant_state_response"; }
#endif
std::string entity_id{};
std::string attribute{};
bool once{false};
@ -929,6 +1125,11 @@ class SubscribeHomeAssistantStateResponse : public ProtoMessage {
};
class HomeAssistantStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 40;
static constexpr uint16_t ESTIMATED_SIZE = 27;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "home_assistant_state_response"; }
#endif
std::string entity_id{};
std::string state{};
std::string attribute{};
@ -943,6 +1144,11 @@ class HomeAssistantStateResponse : public ProtoMessage {
};
class GetTimeRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 36;
static constexpr uint16_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "get_time_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -953,6 +1159,11 @@ class GetTimeRequest : public ProtoMessage {
};
class GetTimeResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 37;
static constexpr uint16_t ESTIMATED_SIZE = 5;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "get_time_response"; }
#endif
uint32_t epoch_seconds{0};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -979,6 +1190,11 @@ class ListEntitiesServicesArgument : public ProtoMessage {
};
class ListEntitiesServicesResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 41;
static constexpr uint16_t ESTIMATED_SIZE = 48;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_services_response"; }
#endif
std::string name{};
uint32_t key{0};
std::vector<ListEntitiesServicesArgument> args{};
@ -1016,6 +1232,11 @@ class ExecuteServiceArgument : public ProtoMessage {
};
class ExecuteServiceRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 42;
static constexpr uint16_t ESTIMATED_SIZE = 39;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "execute_service_request"; }
#endif
uint32_t key{0};
std::vector<ExecuteServiceArgument> args{};
void encode(ProtoWriteBuffer buffer) const override;
@ -1030,6 +1251,11 @@ class ExecuteServiceRequest : public ProtoMessage {
};
class ListEntitiesCameraResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 43;
static constexpr uint16_t ESTIMATED_SIZE = 45;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_camera_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -1050,6 +1276,11 @@ class ListEntitiesCameraResponse : public ProtoMessage {
};
class CameraImageResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 44;
static constexpr uint16_t ESTIMATED_SIZE = 16;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "camera_image_response"; }
#endif
uint32_t key{0};
std::string data{};
bool done{false};
@ -1066,6 +1297,11 @@ class CameraImageResponse : public ProtoMessage {
};
class CameraImageRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 45;
static constexpr uint16_t ESTIMATED_SIZE = 4;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "camera_image_request"; }
#endif
bool single{false};
bool stream{false};
void encode(ProtoWriteBuffer buffer) const override;
@ -1079,6 +1315,11 @@ class CameraImageRequest : public ProtoMessage {
};
class ListEntitiesClimateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 46;
static constexpr uint16_t ESTIMATED_SIZE = 151;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_climate_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -1117,6 +1358,11 @@ class ListEntitiesClimateResponse : public ProtoMessage {
};
class ClimateStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 47;
static constexpr uint16_t ESTIMATED_SIZE = 65;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "climate_state_response"; }
#endif
uint32_t key{0};
enums::ClimateMode mode{};
float current_temperature{0.0f};
@ -1145,6 +1391,11 @@ class ClimateStateResponse : public ProtoMessage {
};
class ClimateCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 48;
static constexpr uint16_t ESTIMATED_SIZE = 83;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "climate_command_request"; }
#endif
uint32_t key{0};
bool has_mode{false};
enums::ClimateMode mode{};
@ -1181,6 +1432,11 @@ class ClimateCommandRequest : public ProtoMessage {
};
class ListEntitiesNumberResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 49;
static constexpr uint16_t ESTIMATED_SIZE = 80;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_number_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -1207,6 +1463,11 @@ class ListEntitiesNumberResponse : public ProtoMessage {
};
class NumberStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 50;
static constexpr uint16_t ESTIMATED_SIZE = 12;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "number_state_response"; }
#endif
uint32_t key{0};
float state{0.0f};
bool missing_state{false};
@ -1222,6 +1483,11 @@ class NumberStateResponse : public ProtoMessage {
};
class NumberCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 51;
static constexpr uint16_t ESTIMATED_SIZE = 10;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "number_command_request"; }
#endif
uint32_t key{0};
float state{0.0f};
void encode(ProtoWriteBuffer buffer) const override;
@ -1235,6 +1501,11 @@ class NumberCommandRequest : public ProtoMessage {
};
class ListEntitiesSelectResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 52;
static constexpr uint16_t ESTIMATED_SIZE = 63;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_select_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -1256,6 +1527,11 @@ class ListEntitiesSelectResponse : public ProtoMessage {
};
class SelectStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 53;
static constexpr uint16_t ESTIMATED_SIZE = 16;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "select_state_response"; }
#endif
uint32_t key{0};
std::string state{};
bool missing_state{false};
@ -1272,6 +1548,11 @@ class SelectStateResponse : public ProtoMessage {
};
class SelectCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 54;
static constexpr uint16_t ESTIMATED_SIZE = 14;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "select_command_request"; }
#endif
uint32_t key{0};
std::string state{};
void encode(ProtoWriteBuffer buffer) const override;
@ -1286,6 +1567,11 @@ class SelectCommandRequest : public ProtoMessage {
};
class ListEntitiesSirenResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 55;
static constexpr uint16_t ESTIMATED_SIZE = 67;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_siren_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -1309,6 +1595,11 @@ class ListEntitiesSirenResponse : public ProtoMessage {
};
class SirenStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 56;
static constexpr uint16_t ESTIMATED_SIZE = 7;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "siren_state_response"; }
#endif
uint32_t key{0};
bool state{false};
void encode(ProtoWriteBuffer buffer) const override;
@ -1323,6 +1614,11 @@ class SirenStateResponse : public ProtoMessage {
};
class SirenCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 57;
static constexpr uint16_t ESTIMATED_SIZE = 33;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "siren_command_request"; }
#endif
uint32_t key{0};
bool has_state{false};
bool state{false};
@ -1345,6 +1641,11 @@ class SirenCommandRequest : public ProtoMessage {
};
class ListEntitiesLockResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 58;
static constexpr uint16_t ESTIMATED_SIZE = 60;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_lock_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -1369,6 +1670,11 @@ class ListEntitiesLockResponse : public ProtoMessage {
};
class LockStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 59;
static constexpr uint16_t ESTIMATED_SIZE = 7;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "lock_state_response"; }
#endif
uint32_t key{0};
enums::LockState state{};
void encode(ProtoWriteBuffer buffer) const override;
@ -1383,6 +1689,11 @@ class LockStateResponse : public ProtoMessage {
};
class LockCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 60;
static constexpr uint16_t ESTIMATED_SIZE = 18;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "lock_command_request"; }
#endif
uint32_t key{0};
enums::LockCommand command{};
bool has_code{false};
@ -1400,6 +1711,11 @@ class LockCommandRequest : public ProtoMessage {
};
class ListEntitiesButtonResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 61;
static constexpr uint16_t ESTIMATED_SIZE = 54;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_button_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -1421,6 +1737,11 @@ class ListEntitiesButtonResponse : public ProtoMessage {
};
class ButtonCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 62;
static constexpr uint16_t ESTIMATED_SIZE = 5;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "button_command_request"; }
#endif
uint32_t key{0};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -1450,6 +1771,11 @@ class MediaPlayerSupportedFormat : public ProtoMessage {
};
class ListEntitiesMediaPlayerResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 63;
static constexpr uint16_t ESTIMATED_SIZE = 81;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_media_player_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -1472,6 +1798,11 @@ class ListEntitiesMediaPlayerResponse : public ProtoMessage {
};
class MediaPlayerStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 64;
static constexpr uint16_t ESTIMATED_SIZE = 14;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "media_player_state_response"; }
#endif
uint32_t key{0};
enums::MediaPlayerState state{};
float volume{0.0f};
@ -1488,6 +1819,11 @@ class MediaPlayerStateResponse : public ProtoMessage {
};
class MediaPlayerCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 65;
static constexpr uint16_t ESTIMATED_SIZE = 31;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "media_player_command_request"; }
#endif
uint32_t key{0};
bool has_command{false};
enums::MediaPlayerCommand command{};
@ -1510,6 +1846,11 @@ class MediaPlayerCommandRequest : public ProtoMessage {
};
class SubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 66;
static constexpr uint16_t ESTIMATED_SIZE = 4;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "subscribe_bluetooth_le_advertisements_request"; }
#endif
uint32_t flags{0};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -1537,6 +1878,11 @@ class BluetoothServiceData : public ProtoMessage {
};
class BluetoothLEAdvertisementResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 67;
static constexpr uint16_t ESTIMATED_SIZE = 107;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_le_advertisement_response"; }
#endif
uint64_t address{0};
std::string name{};
int32_t rssi{0};
@ -1572,6 +1918,11 @@ class BluetoothLERawAdvertisement : public ProtoMessage {
};
class BluetoothLERawAdvertisementsResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 93;
static constexpr uint16_t ESTIMATED_SIZE = 34;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_le_raw_advertisements_response"; }
#endif
std::vector<BluetoothLERawAdvertisement> advertisements{};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -1584,6 +1935,11 @@ class BluetoothLERawAdvertisementsResponse : public ProtoMessage {
};
class BluetoothDeviceRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 68;
static constexpr uint16_t ESTIMATED_SIZE = 12;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_device_request"; }
#endif
uint64_t address{0};
enums::BluetoothDeviceRequestType request_type{};
bool has_address_type{false};
@ -1599,6 +1955,11 @@ class BluetoothDeviceRequest : public ProtoMessage {
};
class BluetoothDeviceConnectionResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 69;
static constexpr uint16_t ESTIMATED_SIZE = 14;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_device_connection_response"; }
#endif
uint64_t address{0};
bool connected{false};
uint32_t mtu{0};
@ -1614,6 +1975,11 @@ class BluetoothDeviceConnectionResponse : public ProtoMessage {
};
class BluetoothGATTGetServicesRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 70;
static constexpr uint16_t ESTIMATED_SIZE = 4;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_gatt_get_services_request"; }
#endif
uint64_t address{0};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -1670,6 +2036,11 @@ class BluetoothGATTService : public ProtoMessage {
};
class BluetoothGATTGetServicesResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 71;
static constexpr uint16_t ESTIMATED_SIZE = 38;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_gatt_get_services_response"; }
#endif
uint64_t address{0};
std::vector<BluetoothGATTService> services{};
void encode(ProtoWriteBuffer buffer) const override;
@ -1684,6 +2055,11 @@ class BluetoothGATTGetServicesResponse : public ProtoMessage {
};
class BluetoothGATTGetServicesDoneResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 72;
static constexpr uint16_t ESTIMATED_SIZE = 4;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_gatt_get_services_done_response"; }
#endif
uint64_t address{0};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -1696,6 +2072,11 @@ class BluetoothGATTGetServicesDoneResponse : public ProtoMessage {
};
class BluetoothGATTReadRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 73;
static constexpr uint16_t ESTIMATED_SIZE = 8;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_gatt_read_request"; }
#endif
uint64_t address{0};
uint32_t handle{0};
void encode(ProtoWriteBuffer buffer) const override;
@ -1709,6 +2090,11 @@ class BluetoothGATTReadRequest : public ProtoMessage {
};
class BluetoothGATTReadResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 74;
static constexpr uint16_t ESTIMATED_SIZE = 17;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_gatt_read_response"; }
#endif
uint64_t address{0};
uint32_t handle{0};
std::string data{};
@ -1724,6 +2110,11 @@ class BluetoothGATTReadResponse : public ProtoMessage {
};
class BluetoothGATTWriteRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 75;
static constexpr uint16_t ESTIMATED_SIZE = 19;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_gatt_write_request"; }
#endif
uint64_t address{0};
uint32_t handle{0};
bool response{false};
@ -1740,6 +2131,11 @@ class BluetoothGATTWriteRequest : public ProtoMessage {
};
class BluetoothGATTReadDescriptorRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 76;
static constexpr uint16_t ESTIMATED_SIZE = 8;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_gatt_read_descriptor_request"; }
#endif
uint64_t address{0};
uint32_t handle{0};
void encode(ProtoWriteBuffer buffer) const override;
@ -1753,6 +2149,11 @@ class BluetoothGATTReadDescriptorRequest : public ProtoMessage {
};
class BluetoothGATTWriteDescriptorRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 77;
static constexpr uint16_t ESTIMATED_SIZE = 17;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_gatt_write_descriptor_request"; }
#endif
uint64_t address{0};
uint32_t handle{0};
std::string data{};
@ -1768,6 +2169,11 @@ class BluetoothGATTWriteDescriptorRequest : public ProtoMessage {
};
class BluetoothGATTNotifyRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 78;
static constexpr uint16_t ESTIMATED_SIZE = 10;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_gatt_notify_request"; }
#endif
uint64_t address{0};
uint32_t handle{0};
bool enable{false};
@ -1782,6 +2188,11 @@ class BluetoothGATTNotifyRequest : public ProtoMessage {
};
class BluetoothGATTNotifyDataResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 79;
static constexpr uint16_t ESTIMATED_SIZE = 17;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_gatt_notify_data_response"; }
#endif
uint64_t address{0};
uint32_t handle{0};
std::string data{};
@ -1797,6 +2208,11 @@ class BluetoothGATTNotifyDataResponse : public ProtoMessage {
};
class SubscribeBluetoothConnectionsFreeRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 80;
static constexpr uint16_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "subscribe_bluetooth_connections_free_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -1807,6 +2223,11 @@ class SubscribeBluetoothConnectionsFreeRequest : public ProtoMessage {
};
class BluetoothConnectionsFreeResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 81;
static constexpr uint16_t ESTIMATED_SIZE = 16;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_connections_free_response"; }
#endif
uint32_t free{0};
uint32_t limit{0};
std::vector<uint64_t> allocated{};
@ -1821,6 +2242,11 @@ class BluetoothConnectionsFreeResponse : public ProtoMessage {
};
class BluetoothGATTErrorResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 82;
static constexpr uint16_t ESTIMATED_SIZE = 12;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_gatt_error_response"; }
#endif
uint64_t address{0};
uint32_t handle{0};
int32_t error{0};
@ -1835,6 +2261,11 @@ class BluetoothGATTErrorResponse : public ProtoMessage {
};
class BluetoothGATTWriteResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 83;
static constexpr uint16_t ESTIMATED_SIZE = 8;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_gatt_write_response"; }
#endif
uint64_t address{0};
uint32_t handle{0};
void encode(ProtoWriteBuffer buffer) const override;
@ -1848,6 +2279,11 @@ class BluetoothGATTWriteResponse : public ProtoMessage {
};
class BluetoothGATTNotifyResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 84;
static constexpr uint16_t ESTIMATED_SIZE = 8;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_gatt_notify_response"; }
#endif
uint64_t address{0};
uint32_t handle{0};
void encode(ProtoWriteBuffer buffer) const override;
@ -1861,6 +2297,11 @@ class BluetoothGATTNotifyResponse : public ProtoMessage {
};
class BluetoothDevicePairingResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 85;
static constexpr uint16_t ESTIMATED_SIZE = 10;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_device_pairing_response"; }
#endif
uint64_t address{0};
bool paired{false};
int32_t error{0};
@ -1875,6 +2316,11 @@ class BluetoothDevicePairingResponse : public ProtoMessage {
};
class BluetoothDeviceUnpairingResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 86;
static constexpr uint16_t ESTIMATED_SIZE = 10;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_device_unpairing_response"; }
#endif
uint64_t address{0};
bool success{false};
int32_t error{0};
@ -1889,6 +2335,11 @@ class BluetoothDeviceUnpairingResponse : public ProtoMessage {
};
class UnsubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 87;
static constexpr uint16_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "unsubscribe_bluetooth_le_advertisements_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -1899,6 +2350,11 @@ class UnsubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage {
};
class BluetoothDeviceClearCacheResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 88;
static constexpr uint16_t ESTIMATED_SIZE = 10;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_device_clear_cache_response"; }
#endif
uint64_t address{0};
bool success{false};
int32_t error{0};
@ -1913,6 +2369,11 @@ class BluetoothDeviceClearCacheResponse : public ProtoMessage {
};
class BluetoothScannerStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 126;
static constexpr uint16_t ESTIMATED_SIZE = 4;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_scanner_state_response"; }
#endif
enums::BluetoothScannerState state{};
enums::BluetoothScannerMode mode{};
void encode(ProtoWriteBuffer buffer) const override;
@ -1926,6 +2387,11 @@ class BluetoothScannerStateResponse : public ProtoMessage {
};
class BluetoothScannerSetModeRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 127;
static constexpr uint16_t ESTIMATED_SIZE = 2;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "bluetooth_scanner_set_mode_request"; }
#endif
enums::BluetoothScannerMode mode{};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -1938,6 +2404,11 @@ class BluetoothScannerSetModeRequest : public ProtoMessage {
};
class SubscribeVoiceAssistantRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 89;
static constexpr uint16_t ESTIMATED_SIZE = 6;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "subscribe_voice_assistant_request"; }
#endif
bool subscribe{false};
uint32_t flags{0};
void encode(ProtoWriteBuffer buffer) const override;
@ -1966,6 +2437,11 @@ class VoiceAssistantAudioSettings : public ProtoMessage {
};
class VoiceAssistantRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 90;
static constexpr uint16_t ESTIMATED_SIZE = 41;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "voice_assistant_request"; }
#endif
bool start{false};
std::string conversation_id{};
uint32_t flags{0};
@ -1983,6 +2459,11 @@ class VoiceAssistantRequest : public ProtoMessage {
};
class VoiceAssistantResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 91;
static constexpr uint16_t ESTIMATED_SIZE = 6;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "voice_assistant_response"; }
#endif
uint32_t port{0};
bool error{false};
void encode(ProtoWriteBuffer buffer) const override;
@ -2009,6 +2490,11 @@ class VoiceAssistantEventData : public ProtoMessage {
};
class VoiceAssistantEventResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 92;
static constexpr uint16_t ESTIMATED_SIZE = 36;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "voice_assistant_event_response"; }
#endif
enums::VoiceAssistantEvent event_type{};
std::vector<VoiceAssistantEventData> data{};
void encode(ProtoWriteBuffer buffer) const override;
@ -2023,6 +2509,11 @@ class VoiceAssistantEventResponse : public ProtoMessage {
};
class VoiceAssistantAudio : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 106;
static constexpr uint16_t ESTIMATED_SIZE = 11;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "voice_assistant_audio"; }
#endif
std::string data{};
bool end{false};
void encode(ProtoWriteBuffer buffer) const override;
@ -2037,6 +2528,11 @@ class VoiceAssistantAudio : public ProtoMessage {
};
class VoiceAssistantTimerEventResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 115;
static constexpr uint16_t ESTIMATED_SIZE = 30;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "voice_assistant_timer_event_response"; }
#endif
enums::VoiceAssistantTimerEvent event_type{};
std::string timer_id{};
std::string name{};
@ -2055,6 +2551,11 @@ class VoiceAssistantTimerEventResponse : public ProtoMessage {
};
class VoiceAssistantAnnounceRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 119;
static constexpr uint16_t ESTIMATED_SIZE = 29;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "voice_assistant_announce_request"; }
#endif
std::string media_id{};
std::string text{};
std::string preannounce_media_id{};
@ -2071,6 +2572,11 @@ class VoiceAssistantAnnounceRequest : public ProtoMessage {
};
class VoiceAssistantAnnounceFinished : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 120;
static constexpr uint16_t ESTIMATED_SIZE = 2;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "voice_assistant_announce_finished"; }
#endif
bool success{false};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -2097,6 +2603,11 @@ class VoiceAssistantWakeWord : public ProtoMessage {
};
class VoiceAssistantConfigurationRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 121;
static constexpr uint16_t ESTIMATED_SIZE = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "voice_assistant_configuration_request"; }
#endif
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -2107,6 +2618,11 @@ class VoiceAssistantConfigurationRequest : public ProtoMessage {
};
class VoiceAssistantConfigurationResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 122;
static constexpr uint16_t ESTIMATED_SIZE = 56;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "voice_assistant_configuration_response"; }
#endif
std::vector<VoiceAssistantWakeWord> available_wake_words{};
std::vector<std::string> active_wake_words{};
uint32_t max_active_wake_words{0};
@ -2122,6 +2638,11 @@ class VoiceAssistantConfigurationResponse : public ProtoMessage {
};
class VoiceAssistantSetConfiguration : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 123;
static constexpr uint16_t ESTIMATED_SIZE = 18;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "voice_assistant_set_configuration"; }
#endif
std::vector<std::string> active_wake_words{};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -2134,6 +2655,11 @@ class VoiceAssistantSetConfiguration : public ProtoMessage {
};
class ListEntitiesAlarmControlPanelResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 94;
static constexpr uint16_t ESTIMATED_SIZE = 53;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_alarm_control_panel_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -2157,6 +2683,11 @@ class ListEntitiesAlarmControlPanelResponse : public ProtoMessage {
};
class AlarmControlPanelStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 95;
static constexpr uint16_t ESTIMATED_SIZE = 7;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "alarm_control_panel_state_response"; }
#endif
uint32_t key{0};
enums::AlarmControlPanelState state{};
void encode(ProtoWriteBuffer buffer) const override;
@ -2171,6 +2702,11 @@ class AlarmControlPanelStateResponse : public ProtoMessage {
};
class AlarmControlPanelCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 96;
static constexpr uint16_t ESTIMATED_SIZE = 16;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "alarm_control_panel_command_request"; }
#endif
uint32_t key{0};
enums::AlarmControlPanelStateCommand command{};
std::string code{};
@ -2187,6 +2723,11 @@ class AlarmControlPanelCommandRequest : public ProtoMessage {
};
class ListEntitiesTextResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 97;
static constexpr uint16_t ESTIMATED_SIZE = 64;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_text_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -2211,6 +2752,11 @@ class ListEntitiesTextResponse : public ProtoMessage {
};
class TextStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 98;
static constexpr uint16_t ESTIMATED_SIZE = 16;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "text_state_response"; }
#endif
uint32_t key{0};
std::string state{};
bool missing_state{false};
@ -2227,6 +2773,11 @@ class TextStateResponse : public ProtoMessage {
};
class TextCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 99;
static constexpr uint16_t ESTIMATED_SIZE = 14;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "text_command_request"; }
#endif
uint32_t key{0};
std::string state{};
void encode(ProtoWriteBuffer buffer) const override;
@ -2241,6 +2792,11 @@ class TextCommandRequest : public ProtoMessage {
};
class ListEntitiesDateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 100;
static constexpr uint16_t ESTIMATED_SIZE = 45;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_date_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -2261,6 +2817,11 @@ class ListEntitiesDateResponse : public ProtoMessage {
};
class DateStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 101;
static constexpr uint16_t ESTIMATED_SIZE = 19;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "date_state_response"; }
#endif
uint32_t key{0};
bool missing_state{false};
uint32_t year{0};
@ -2278,6 +2839,11 @@ class DateStateResponse : public ProtoMessage {
};
class DateCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 102;
static constexpr uint16_t ESTIMATED_SIZE = 17;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "date_command_request"; }
#endif
uint32_t key{0};
uint32_t year{0};
uint32_t month{0};
@ -2294,6 +2860,11 @@ class DateCommandRequest : public ProtoMessage {
};
class ListEntitiesTimeResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 103;
static constexpr uint16_t ESTIMATED_SIZE = 45;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_time_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -2314,6 +2885,11 @@ class ListEntitiesTimeResponse : public ProtoMessage {
};
class TimeStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 104;
static constexpr uint16_t ESTIMATED_SIZE = 19;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "time_state_response"; }
#endif
uint32_t key{0};
bool missing_state{false};
uint32_t hour{0};
@ -2331,6 +2907,11 @@ class TimeStateResponse : public ProtoMessage {
};
class TimeCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 105;
static constexpr uint16_t ESTIMATED_SIZE = 17;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "time_command_request"; }
#endif
uint32_t key{0};
uint32_t hour{0};
uint32_t minute{0};
@ -2347,6 +2928,11 @@ class TimeCommandRequest : public ProtoMessage {
};
class ListEntitiesEventResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 107;
static constexpr uint16_t ESTIMATED_SIZE = 72;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_event_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -2369,6 +2955,11 @@ class ListEntitiesEventResponse : public ProtoMessage {
};
class EventResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 108;
static constexpr uint16_t ESTIMATED_SIZE = 14;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "event_response"; }
#endif
uint32_t key{0};
std::string event_type{};
void encode(ProtoWriteBuffer buffer) const override;
@ -2383,6 +2974,11 @@ class EventResponse : public ProtoMessage {
};
class ListEntitiesValveResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 109;
static constexpr uint16_t ESTIMATED_SIZE = 60;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_valve_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -2407,6 +3003,11 @@ class ListEntitiesValveResponse : public ProtoMessage {
};
class ValveStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 110;
static constexpr uint16_t ESTIMATED_SIZE = 12;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "valve_state_response"; }
#endif
uint32_t key{0};
float position{0.0f};
enums::ValveOperation current_operation{};
@ -2422,6 +3023,11 @@ class ValveStateResponse : public ProtoMessage {
};
class ValveCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 111;
static constexpr uint16_t ESTIMATED_SIZE = 14;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "valve_command_request"; }
#endif
uint32_t key{0};
bool has_position{false};
float position{0.0f};
@ -2438,6 +3044,11 @@ class ValveCommandRequest : public ProtoMessage {
};
class ListEntitiesDateTimeResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 112;
static constexpr uint16_t ESTIMATED_SIZE = 45;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_date_time_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -2458,6 +3069,11 @@ class ListEntitiesDateTimeResponse : public ProtoMessage {
};
class DateTimeStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 113;
static constexpr uint16_t ESTIMATED_SIZE = 12;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "date_time_state_response"; }
#endif
uint32_t key{0};
bool missing_state{false};
uint32_t epoch_seconds{0};
@ -2473,6 +3089,11 @@ class DateTimeStateResponse : public ProtoMessage {
};
class DateTimeCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 114;
static constexpr uint16_t ESTIMATED_SIZE = 10;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "date_time_command_request"; }
#endif
uint32_t key{0};
uint32_t epoch_seconds{0};
void encode(ProtoWriteBuffer buffer) const override;
@ -2486,6 +3107,11 @@ class DateTimeCommandRequest : public ProtoMessage {
};
class ListEntitiesUpdateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 116;
static constexpr uint16_t ESTIMATED_SIZE = 54;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_update_response"; }
#endif
std::string object_id{};
uint32_t key{0};
std::string name{};
@ -2507,6 +3133,11 @@ class ListEntitiesUpdateResponse : public ProtoMessage {
};
class UpdateStateResponse : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 117;
static constexpr uint16_t ESTIMATED_SIZE = 61;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "update_state_response"; }
#endif
uint32_t key{0};
bool missing_state{false};
bool in_progress{false};
@ -2530,6 +3161,11 @@ class UpdateStateResponse : public ProtoMessage {
};
class UpdateCommandRequest : public ProtoMessage {
public:
static constexpr uint16_t MESSAGE_TYPE = 118;
static constexpr uint16_t ESTIMATED_SIZE = 7;
#ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "update_command_request"; }
#endif
uint32_t key{0};
enums::UpdateCommand command{};
void encode(ProtoWriteBuffer buffer) const override;

View File

@ -8,688 +8,12 @@ namespace api {
static const char *const TAG = "api.service";
bool APIServerConnectionBase::send_hello_response(const HelloResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_hello_response: %s", msg.dump().c_str());
#endif
return this->send_message_<HelloResponse>(msg, 2);
}
bool APIServerConnectionBase::send_connect_response(const ConnectResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_connect_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ConnectResponse>(msg, 4);
}
bool APIServerConnectionBase::send_disconnect_request(const DisconnectRequest &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_disconnect_request: %s", msg.dump().c_str());
#endif
return this->send_message_<DisconnectRequest>(msg, 5);
}
bool APIServerConnectionBase::send_disconnect_response(const DisconnectResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_disconnect_response: %s", msg.dump().c_str());
#endif
return this->send_message_<DisconnectResponse>(msg, 6);
}
bool APIServerConnectionBase::send_ping_request(const PingRequest &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_ping_request: %s", msg.dump().c_str());
#endif
return this->send_message_<PingRequest>(msg, 7);
}
bool APIServerConnectionBase::send_ping_response(const PingResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_ping_response: %s", msg.dump().c_str());
#endif
return this->send_message_<PingResponse>(msg, 8);
}
bool APIServerConnectionBase::send_device_info_response(const DeviceInfoResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_device_info_response: %s", msg.dump().c_str());
#endif
return this->send_message_<DeviceInfoResponse>(msg, 10);
}
bool APIServerConnectionBase::send_list_entities_done_response(const ListEntitiesDoneResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_done_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesDoneResponse>(msg, 19);
}
#ifdef USE_BINARY_SENSOR
bool APIServerConnectionBase::send_list_entities_binary_sensor_response(const ListEntitiesBinarySensorResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_binary_sensor_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesBinarySensorResponse>(msg, 12);
}
#endif
#ifdef USE_BINARY_SENSOR
bool APIServerConnectionBase::send_binary_sensor_state_response(const BinarySensorStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_binary_sensor_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BinarySensorStateResponse>(msg, 21);
}
#endif
#ifdef USE_COVER
bool APIServerConnectionBase::send_list_entities_cover_response(const ListEntitiesCoverResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_cover_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesCoverResponse>(msg, 13);
}
#endif
#ifdef USE_COVER
bool APIServerConnectionBase::send_cover_state_response(const CoverStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_cover_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<CoverStateResponse>(msg, 22);
}
#endif
#ifdef USE_COVER
#endif
#ifdef USE_FAN
bool APIServerConnectionBase::send_list_entities_fan_response(const ListEntitiesFanResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_fan_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesFanResponse>(msg, 14);
}
#endif
#ifdef USE_FAN
bool APIServerConnectionBase::send_fan_state_response(const FanStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_fan_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<FanStateResponse>(msg, 23);
}
#endif
#ifdef USE_FAN
#endif
#ifdef USE_LIGHT
bool APIServerConnectionBase::send_list_entities_light_response(const ListEntitiesLightResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_light_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesLightResponse>(msg, 15);
}
#endif
#ifdef USE_LIGHT
bool APIServerConnectionBase::send_light_state_response(const LightStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_light_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<LightStateResponse>(msg, 24);
}
#endif
#ifdef USE_LIGHT
#endif
#ifdef USE_SENSOR
bool APIServerConnectionBase::send_list_entities_sensor_response(const ListEntitiesSensorResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_sensor_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesSensorResponse>(msg, 16);
}
#endif
#ifdef USE_SENSOR
bool APIServerConnectionBase::send_sensor_state_response(const SensorStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_sensor_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<SensorStateResponse>(msg, 25);
}
#endif
#ifdef USE_SWITCH
bool APIServerConnectionBase::send_list_entities_switch_response(const ListEntitiesSwitchResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_switch_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesSwitchResponse>(msg, 17);
}
#endif
#ifdef USE_SWITCH
bool APIServerConnectionBase::send_switch_state_response(const SwitchStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_switch_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<SwitchStateResponse>(msg, 26);
}
#endif
#ifdef USE_SWITCH
#endif
#ifdef USE_TEXT_SENSOR
bool APIServerConnectionBase::send_list_entities_text_sensor_response(const ListEntitiesTextSensorResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_text_sensor_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesTextSensorResponse>(msg, 18);
}
#endif
#ifdef USE_TEXT_SENSOR
bool APIServerConnectionBase::send_text_sensor_state_response(const TextSensorStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_text_sensor_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<TextSensorStateResponse>(msg, 27);
}
#endif
bool APIServerConnectionBase::send_subscribe_logs_response(const SubscribeLogsResponse &msg) {
return this->send_message_<SubscribeLogsResponse>(msg, 29);
}
#ifdef USE_API_NOISE
#endif
#ifdef USE_API_NOISE
bool APIServerConnectionBase::send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_noise_encryption_set_key_response: %s", msg.dump().c_str());
#endif
return this->send_message_<NoiseEncryptionSetKeyResponse>(msg, 125);
}
#endif
bool APIServerConnectionBase::send_homeassistant_service_response(const HomeassistantServiceResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_homeassistant_service_response: %s", msg.dump().c_str());
#endif
return this->send_message_<HomeassistantServiceResponse>(msg, 35);
}
bool APIServerConnectionBase::send_subscribe_home_assistant_state_response(
const SubscribeHomeAssistantStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_subscribe_home_assistant_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<SubscribeHomeAssistantStateResponse>(msg, 39);
}
bool APIServerConnectionBase::send_get_time_request(const GetTimeRequest &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_get_time_request: %s", msg.dump().c_str());
#endif
return this->send_message_<GetTimeRequest>(msg, 36);
}
bool APIServerConnectionBase::send_get_time_response(const GetTimeResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_get_time_response: %s", msg.dump().c_str());
#endif
return this->send_message_<GetTimeResponse>(msg, 37);
}
bool APIServerConnectionBase::send_list_entities_services_response(const ListEntitiesServicesResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_services_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesServicesResponse>(msg, 41);
}
#ifdef USE_ESP32_CAMERA
bool APIServerConnectionBase::send_list_entities_camera_response(const ListEntitiesCameraResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_camera_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesCameraResponse>(msg, 43);
}
#endif
#ifdef USE_ESP32_CAMERA
bool APIServerConnectionBase::send_camera_image_response(const CameraImageResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_camera_image_response: %s", msg.dump().c_str());
#endif
return this->send_message_<CameraImageResponse>(msg, 44);
}
#endif
#ifdef USE_ESP32_CAMERA
#endif
#ifdef USE_CLIMATE
bool APIServerConnectionBase::send_list_entities_climate_response(const ListEntitiesClimateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_climate_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesClimateResponse>(msg, 46);
}
#endif
#ifdef USE_CLIMATE
bool APIServerConnectionBase::send_climate_state_response(const ClimateStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_climate_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ClimateStateResponse>(msg, 47);
}
#endif
#ifdef USE_CLIMATE
#endif
#ifdef USE_NUMBER
bool APIServerConnectionBase::send_list_entities_number_response(const ListEntitiesNumberResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_number_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesNumberResponse>(msg, 49);
}
#endif
#ifdef USE_NUMBER
bool APIServerConnectionBase::send_number_state_response(const NumberStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_number_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<NumberStateResponse>(msg, 50);
}
#endif
#ifdef USE_NUMBER
#endif
#ifdef USE_SELECT
bool APIServerConnectionBase::send_list_entities_select_response(const ListEntitiesSelectResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_select_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesSelectResponse>(msg, 52);
}
#endif
#ifdef USE_SELECT
bool APIServerConnectionBase::send_select_state_response(const SelectStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_select_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<SelectStateResponse>(msg, 53);
}
#endif
#ifdef USE_SELECT
#endif
#ifdef USE_SIREN
bool APIServerConnectionBase::send_list_entities_siren_response(const ListEntitiesSirenResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_siren_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesSirenResponse>(msg, 55);
}
#endif
#ifdef USE_SIREN
bool APIServerConnectionBase::send_siren_state_response(const SirenStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_siren_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<SirenStateResponse>(msg, 56);
}
#endif
#ifdef USE_SIREN
#endif
#ifdef USE_LOCK
bool APIServerConnectionBase::send_list_entities_lock_response(const ListEntitiesLockResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_lock_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesLockResponse>(msg, 58);
void APIServerConnectionBase::log_send_message_(const char *name, const std::string &dump) {
ESP_LOGVV(TAG, "send_message %s: %s", name, dump.c_str());
}
#endif
#ifdef USE_LOCK
bool APIServerConnectionBase::send_lock_state_response(const LockStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_lock_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<LockStateResponse>(msg, 59);
}
#endif
#ifdef USE_LOCK
#endif
#ifdef USE_BUTTON
bool APIServerConnectionBase::send_list_entities_button_response(const ListEntitiesButtonResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_button_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesButtonResponse>(msg, 61);
}
#endif
#ifdef USE_BUTTON
#endif
#ifdef USE_MEDIA_PLAYER
bool APIServerConnectionBase::send_list_entities_media_player_response(const ListEntitiesMediaPlayerResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_media_player_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesMediaPlayerResponse>(msg, 63);
}
#endif
#ifdef USE_MEDIA_PLAYER
bool APIServerConnectionBase::send_media_player_state_response(const MediaPlayerStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_media_player_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<MediaPlayerStateResponse>(msg, 64);
}
#endif
#ifdef USE_MEDIA_PLAYER
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_le_advertisement_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothLEAdvertisementResponse>(msg, 67);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_le_raw_advertisements_response(
const BluetoothLERawAdvertisementsResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_le_raw_advertisements_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothLERawAdvertisementsResponse>(msg, 93);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_device_connection_response(const BluetoothDeviceConnectionResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_device_connection_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothDeviceConnectionResponse>(msg, 69);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_gatt_get_services_response(const BluetoothGATTGetServicesResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_gatt_get_services_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothGATTGetServicesResponse>(msg, 71);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_gatt_get_services_done_response(
const BluetoothGATTGetServicesDoneResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_gatt_get_services_done_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothGATTGetServicesDoneResponse>(msg, 72);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_gatt_read_response(const BluetoothGATTReadResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_gatt_read_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothGATTReadResponse>(msg, 74);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_gatt_notify_data_response(const BluetoothGATTNotifyDataResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_gatt_notify_data_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothGATTNotifyDataResponse>(msg, 79);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_connections_free_response(const BluetoothConnectionsFreeResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_connections_free_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothConnectionsFreeResponse>(msg, 81);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_gatt_error_response(const BluetoothGATTErrorResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_gatt_error_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothGATTErrorResponse>(msg, 82);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_gatt_write_response(const BluetoothGATTWriteResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_gatt_write_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothGATTWriteResponse>(msg, 83);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_gatt_notify_response(const BluetoothGATTNotifyResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_gatt_notify_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothGATTNotifyResponse>(msg, 84);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_device_pairing_response(const BluetoothDevicePairingResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_device_pairing_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothDevicePairingResponse>(msg, 85);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_device_unpairing_response(const BluetoothDeviceUnpairingResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_device_unpairing_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothDeviceUnpairingResponse>(msg, 86);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_device_clear_cache_response(const BluetoothDeviceClearCacheResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_device_clear_cache_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothDeviceClearCacheResponse>(msg, 88);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_scanner_state_response(const BluetoothScannerStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_scanner_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothScannerStateResponse>(msg, 126);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_VOICE_ASSISTANT
#endif
#ifdef USE_VOICE_ASSISTANT
bool APIServerConnectionBase::send_voice_assistant_request(const VoiceAssistantRequest &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_voice_assistant_request: %s", msg.dump().c_str());
#endif
return this->send_message_<VoiceAssistantRequest>(msg, 90);
}
#endif
#ifdef USE_VOICE_ASSISTANT
#endif
#ifdef USE_VOICE_ASSISTANT
#endif
#ifdef USE_VOICE_ASSISTANT
bool APIServerConnectionBase::send_voice_assistant_audio(const VoiceAssistantAudio &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_voice_assistant_audio: %s", msg.dump().c_str());
#endif
return this->send_message_<VoiceAssistantAudio>(msg, 106);
}
#endif
#ifdef USE_VOICE_ASSISTANT
#endif
#ifdef USE_VOICE_ASSISTANT
#endif
#ifdef USE_VOICE_ASSISTANT
bool APIServerConnectionBase::send_voice_assistant_announce_finished(const VoiceAssistantAnnounceFinished &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_voice_assistant_announce_finished: %s", msg.dump().c_str());
#endif
return this->send_message_<VoiceAssistantAnnounceFinished>(msg, 120);
}
#endif
#ifdef USE_VOICE_ASSISTANT
#endif
#ifdef USE_VOICE_ASSISTANT
bool APIServerConnectionBase::send_voice_assistant_configuration_response(
const VoiceAssistantConfigurationResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_voice_assistant_configuration_response: %s", msg.dump().c_str());
#endif
return this->send_message_<VoiceAssistantConfigurationResponse>(msg, 122);
}
#endif
#ifdef USE_VOICE_ASSISTANT
#endif
#ifdef USE_ALARM_CONTROL_PANEL
bool APIServerConnectionBase::send_list_entities_alarm_control_panel_response(
const ListEntitiesAlarmControlPanelResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_alarm_control_panel_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesAlarmControlPanelResponse>(msg, 94);
}
#endif
#ifdef USE_ALARM_CONTROL_PANEL
bool APIServerConnectionBase::send_alarm_control_panel_state_response(const AlarmControlPanelStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_alarm_control_panel_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<AlarmControlPanelStateResponse>(msg, 95);
}
#endif
#ifdef USE_ALARM_CONTROL_PANEL
#endif
#ifdef USE_TEXT
bool APIServerConnectionBase::send_list_entities_text_response(const ListEntitiesTextResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_text_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesTextResponse>(msg, 97);
}
#endif
#ifdef USE_TEXT
bool APIServerConnectionBase::send_text_state_response(const TextStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_text_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<TextStateResponse>(msg, 98);
}
#endif
#ifdef USE_TEXT
#endif
#ifdef USE_DATETIME_DATE
bool APIServerConnectionBase::send_list_entities_date_response(const ListEntitiesDateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_date_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesDateResponse>(msg, 100);
}
#endif
#ifdef USE_DATETIME_DATE
bool APIServerConnectionBase::send_date_state_response(const DateStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_date_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<DateStateResponse>(msg, 101);
}
#endif
#ifdef USE_DATETIME_DATE
#endif
#ifdef USE_DATETIME_TIME
bool APIServerConnectionBase::send_list_entities_time_response(const ListEntitiesTimeResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_time_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesTimeResponse>(msg, 103);
}
#endif
#ifdef USE_DATETIME_TIME
bool APIServerConnectionBase::send_time_state_response(const TimeStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_time_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<TimeStateResponse>(msg, 104);
}
#endif
#ifdef USE_DATETIME_TIME
#endif
#ifdef USE_EVENT
bool APIServerConnectionBase::send_list_entities_event_response(const ListEntitiesEventResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_event_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesEventResponse>(msg, 107);
}
#endif
#ifdef USE_EVENT
bool APIServerConnectionBase::send_event_response(const EventResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_event_response: %s", msg.dump().c_str());
#endif
return this->send_message_<EventResponse>(msg, 108);
}
#endif
#ifdef USE_VALVE
bool APIServerConnectionBase::send_list_entities_valve_response(const ListEntitiesValveResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_valve_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesValveResponse>(msg, 109);
}
#endif
#ifdef USE_VALVE
bool APIServerConnectionBase::send_valve_state_response(const ValveStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_valve_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ValveStateResponse>(msg, 110);
}
#endif
#ifdef USE_VALVE
#endif
#ifdef USE_DATETIME_DATETIME
bool APIServerConnectionBase::send_list_entities_date_time_response(const ListEntitiesDateTimeResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_date_time_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesDateTimeResponse>(msg, 112);
}
#endif
#ifdef USE_DATETIME_DATETIME
bool APIServerConnectionBase::send_date_time_state_response(const DateTimeStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_date_time_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<DateTimeStateResponse>(msg, 113);
}
#endif
#ifdef USE_DATETIME_DATETIME
#endif
#ifdef USE_UPDATE
bool APIServerConnectionBase::send_list_entities_update_response(const ListEntitiesUpdateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_list_entities_update_response: %s", msg.dump().c_str());
#endif
return this->send_message_<ListEntitiesUpdateResponse>(msg, 116);
}
#endif
#ifdef USE_UPDATE
bool APIServerConnectionBase::send_update_state_response(const UpdateStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_update_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<UpdateStateResponse>(msg, 117);
}
#endif
#ifdef USE_UPDATE
#endif
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
switch (msg_type) {
case 1: {
@ -1273,25 +597,25 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
void APIServerConnection::on_hello_request(const HelloRequest &msg) {
HelloResponse ret = this->hello(msg);
if (!this->send_hello_response(ret)) {
if (!this->send_message(ret)) {
this->on_fatal_error();
}
}
void APIServerConnection::on_connect_request(const ConnectRequest &msg) {
ConnectResponse ret = this->connect(msg);
if (!this->send_connect_response(ret)) {
if (!this->send_message(ret)) {
this->on_fatal_error();
}
}
void APIServerConnection::on_disconnect_request(const DisconnectRequest &msg) {
DisconnectResponse ret = this->disconnect(msg);
if (!this->send_disconnect_response(ret)) {
if (!this->send_message(ret)) {
this->on_fatal_error();
}
}
void APIServerConnection::on_ping_request(const PingRequest &msg) {
PingResponse ret = this->ping(msg);
if (!this->send_ping_response(ret)) {
if (!this->send_message(ret)) {
this->on_fatal_error();
}
}
@ -1301,7 +625,7 @@ void APIServerConnection::on_device_info_request(const DeviceInfoRequest &msg) {
return;
}
DeviceInfoResponse ret = this->device_info(msg);
if (!this->send_device_info_response(ret)) {
if (!this->send_message(ret)) {
this->on_fatal_error();
}
}
@ -1367,7 +691,7 @@ void APIServerConnection::on_get_time_request(const GetTimeRequest &msg) {
return;
}
GetTimeResponse ret = this->get_time(msg);
if (!this->send_get_time_response(ret)) {
if (!this->send_message(ret)) {
this->on_fatal_error();
}
}
@ -1393,7 +717,7 @@ void APIServerConnection::on_noise_encryption_set_key_request(const NoiseEncrypt
return;
}
NoiseEncryptionSetKeyResponse ret = this->noise_encryption_set_key(msg);
if (!this->send_noise_encryption_set_key_response(ret)) {
if (!this->send_message(ret)) {
this->on_fatal_error();
}
}
@ -1749,7 +1073,7 @@ void APIServerConnection::on_subscribe_bluetooth_connections_free_request(
return;
}
BluetoothConnectionsFreeResponse ret = this->subscribe_bluetooth_connections_free(msg);
if (!this->send_bluetooth_connections_free_response(ret)) {
if (!this->send_message(ret)) {
this->on_fatal_error();
}
}
@ -1805,7 +1129,7 @@ void APIServerConnection::on_voice_assistant_configuration_request(const VoiceAs
return;
}
VoiceAssistantConfigurationResponse ret = this->voice_assistant_get_configuration(msg);
if (!this->send_voice_assistant_configuration_response(ret)) {
if (!this->send_message(ret)) {
this->on_fatal_error();
}
}

View File

@ -10,162 +10,94 @@ namespace api {
class APIServerConnectionBase : public ProtoService {
public:
#ifdef HAS_PROTO_MESSAGE_DUMP
protected:
void log_send_message_(const char *name, const std::string &dump);
public:
#endif
template<typename T> bool send_message(const T &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
this->log_send_message_(T::message_name(), msg.dump());
#endif
return this->send_message_(msg, T::MESSAGE_TYPE);
}
virtual void on_hello_request(const HelloRequest &value){};
bool send_hello_response(const HelloResponse &msg);
virtual void on_connect_request(const ConnectRequest &value){};
bool send_connect_response(const ConnectResponse &msg);
bool send_disconnect_request(const DisconnectRequest &msg);
virtual void on_disconnect_request(const DisconnectRequest &value){};
bool send_disconnect_response(const DisconnectResponse &msg);
virtual void on_disconnect_response(const DisconnectResponse &value){};
bool send_ping_request(const PingRequest &msg);
virtual void on_ping_request(const PingRequest &value){};
bool send_ping_response(const PingResponse &msg);
virtual void on_ping_response(const PingResponse &value){};
virtual void on_device_info_request(const DeviceInfoRequest &value){};
bool send_device_info_response(const DeviceInfoResponse &msg);
virtual void on_list_entities_request(const ListEntitiesRequest &value){};
bool send_list_entities_done_response(const ListEntitiesDoneResponse &msg);
virtual void on_subscribe_states_request(const SubscribeStatesRequest &value){};
#ifdef USE_BINARY_SENSOR
bool send_list_entities_binary_sensor_response(const ListEntitiesBinarySensorResponse &msg);
#endif
#ifdef USE_BINARY_SENSOR
bool send_binary_sensor_state_response(const BinarySensorStateResponse &msg);
#endif
#ifdef USE_COVER
bool send_list_entities_cover_response(const ListEntitiesCoverResponse &msg);
#endif
#ifdef USE_COVER
bool send_cover_state_response(const CoverStateResponse &msg);
#endif
#ifdef USE_COVER
virtual void on_cover_command_request(const CoverCommandRequest &value){};
#endif
#ifdef USE_FAN
bool send_list_entities_fan_response(const ListEntitiesFanResponse &msg);
#endif
#ifdef USE_FAN
bool send_fan_state_response(const FanStateResponse &msg);
#endif
#ifdef USE_FAN
virtual void on_fan_command_request(const FanCommandRequest &value){};
#endif
#ifdef USE_LIGHT
bool send_list_entities_light_response(const ListEntitiesLightResponse &msg);
#endif
#ifdef USE_LIGHT
bool send_light_state_response(const LightStateResponse &msg);
#endif
#ifdef USE_LIGHT
virtual void on_light_command_request(const LightCommandRequest &value){};
#endif
#ifdef USE_SENSOR
bool send_list_entities_sensor_response(const ListEntitiesSensorResponse &msg);
#endif
#ifdef USE_SENSOR
bool send_sensor_state_response(const SensorStateResponse &msg);
#endif
#ifdef USE_SWITCH
bool send_list_entities_switch_response(const ListEntitiesSwitchResponse &msg);
#endif
#ifdef USE_SWITCH
bool send_switch_state_response(const SwitchStateResponse &msg);
#endif
#ifdef USE_SWITCH
virtual void on_switch_command_request(const SwitchCommandRequest &value){};
#endif
#ifdef USE_TEXT_SENSOR
bool send_list_entities_text_sensor_response(const ListEntitiesTextSensorResponse &msg);
#endif
#ifdef USE_TEXT_SENSOR
bool send_text_sensor_state_response(const TextSensorStateResponse &msg);
#endif
virtual void on_subscribe_logs_request(const SubscribeLogsRequest &value){};
bool send_subscribe_logs_response(const SubscribeLogsResponse &msg);
#ifdef USE_API_NOISE
virtual void on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &value){};
#endif
#ifdef USE_API_NOISE
bool send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyResponse &msg);
#endif
virtual void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &value){};
bool send_homeassistant_service_response(const HomeassistantServiceResponse &msg);
virtual void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &value){};
bool send_subscribe_home_assistant_state_response(const SubscribeHomeAssistantStateResponse &msg);
virtual void on_home_assistant_state_response(const HomeAssistantStateResponse &value){};
bool send_get_time_request(const GetTimeRequest &msg);
virtual void on_get_time_request(const GetTimeRequest &value){};
bool send_get_time_response(const GetTimeResponse &msg);
virtual void on_get_time_response(const GetTimeResponse &value){};
bool send_list_entities_services_response(const ListEntitiesServicesResponse &msg);
virtual void on_execute_service_request(const ExecuteServiceRequest &value){};
#ifdef USE_ESP32_CAMERA
bool send_list_entities_camera_response(const ListEntitiesCameraResponse &msg);
#endif
#ifdef USE_ESP32_CAMERA
bool send_camera_image_response(const CameraImageResponse &msg);
#endif
#ifdef USE_ESP32_CAMERA
virtual void on_camera_image_request(const CameraImageRequest &value){};
#endif
#ifdef USE_CLIMATE
bool send_list_entities_climate_response(const ListEntitiesClimateResponse &msg);
#endif
#ifdef USE_CLIMATE
bool send_climate_state_response(const ClimateStateResponse &msg);
#endif
#ifdef USE_CLIMATE
virtual void on_climate_command_request(const ClimateCommandRequest &value){};
#endif
#ifdef USE_NUMBER
bool send_list_entities_number_response(const ListEntitiesNumberResponse &msg);
#endif
#ifdef USE_NUMBER
bool send_number_state_response(const NumberStateResponse &msg);
#endif
#ifdef USE_NUMBER
virtual void on_number_command_request(const NumberCommandRequest &value){};
#endif
#ifdef USE_SELECT
bool send_list_entities_select_response(const ListEntitiesSelectResponse &msg);
#endif
#ifdef USE_SELECT
bool send_select_state_response(const SelectStateResponse &msg);
#endif
#ifdef USE_SELECT
virtual void on_select_command_request(const SelectCommandRequest &value){};
#endif
#ifdef USE_SIREN
bool send_list_entities_siren_response(const ListEntitiesSirenResponse &msg);
#endif
#ifdef USE_SIREN
bool send_siren_state_response(const SirenStateResponse &msg);
#endif
#ifdef USE_SIREN
virtual void on_siren_command_request(const SirenCommandRequest &value){};
#endif
#ifdef USE_LOCK
bool send_list_entities_lock_response(const ListEntitiesLockResponse &msg);
#endif
#ifdef USE_LOCK
bool send_lock_state_response(const LockStateResponse &msg);
#endif
#ifdef USE_LOCK
virtual void on_lock_command_request(const LockCommandRequest &value){};
#endif
#ifdef USE_BUTTON
bool send_list_entities_button_response(const ListEntitiesButtonResponse &msg);
#endif
#ifdef USE_BUTTON
virtual void on_button_command_request(const ButtonCommandRequest &value){};
#endif
#ifdef USE_MEDIA_PLAYER
bool send_list_entities_media_player_response(const ListEntitiesMediaPlayerResponse &msg);
#endif
#ifdef USE_MEDIA_PLAYER
bool send_media_player_state_response(const MediaPlayerStateResponse &msg);
#endif
#ifdef USE_MEDIA_PLAYER
virtual void on_media_player_command_request(const MediaPlayerCommandRequest &value){};
#endif
@ -173,33 +105,19 @@ class APIServerConnectionBase : public ProtoService {
virtual void on_subscribe_bluetooth_le_advertisements_request(
const SubscribeBluetoothLEAdvertisementsRequest &value){};
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_le_raw_advertisements_response(const BluetoothLERawAdvertisementsResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void on_bluetooth_device_request(const BluetoothDeviceRequest &value){};
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_device_connection_response(const BluetoothDeviceConnectionResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void on_bluetooth_gatt_get_services_request(const BluetoothGATTGetServicesRequest &value){};
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_gatt_get_services_response(const BluetoothGATTGetServicesResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_gatt_get_services_done_response(const BluetoothGATTGetServicesDoneResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void on_bluetooth_gatt_read_request(const BluetoothGATTReadRequest &value){};
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_gatt_read_response(const BluetoothGATTReadResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void on_bluetooth_gatt_write_request(const BluetoothGATTWriteRequest &value){};
#endif
@ -212,49 +130,23 @@ class APIServerConnectionBase : public ProtoService {
#ifdef USE_BLUETOOTH_PROXY
virtual void on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &value){};
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_gatt_notify_data_response(const BluetoothGATTNotifyDataResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void on_subscribe_bluetooth_connections_free_request(const SubscribeBluetoothConnectionsFreeRequest &value){};
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_connections_free_response(const BluetoothConnectionsFreeResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_gatt_error_response(const BluetoothGATTErrorResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_gatt_write_response(const BluetoothGATTWriteResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_gatt_notify_response(const BluetoothGATTNotifyResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_device_pairing_response(const BluetoothDevicePairingResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_device_unpairing_response(const BluetoothDeviceUnpairingResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void on_unsubscribe_bluetooth_le_advertisements_request(
const UnsubscribeBluetoothLEAdvertisementsRequest &value){};
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_device_clear_cache_response(const BluetoothDeviceClearCacheResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_scanner_state_response(const BluetoothScannerStateResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &value){};
#endif
#ifdef USE_VOICE_ASSISTANT
virtual void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &value){};
#endif
#ifdef USE_VOICE_ASSISTANT
bool send_voice_assistant_request(const VoiceAssistantRequest &msg);
#endif
#ifdef USE_VOICE_ASSISTANT
virtual void on_voice_assistant_response(const VoiceAssistantResponse &value){};
#endif
@ -262,7 +154,6 @@ class APIServerConnectionBase : public ProtoService {
virtual void on_voice_assistant_event_response(const VoiceAssistantEventResponse &value){};
#endif
#ifdef USE_VOICE_ASSISTANT
bool send_voice_assistant_audio(const VoiceAssistantAudio &msg);
virtual void on_voice_assistant_audio(const VoiceAssistantAudio &value){};
#endif
#ifdef USE_VOICE_ASSISTANT
@ -271,84 +162,39 @@ class APIServerConnectionBase : public ProtoService {
#ifdef USE_VOICE_ASSISTANT
virtual void on_voice_assistant_announce_request(const VoiceAssistantAnnounceRequest &value){};
#endif
#ifdef USE_VOICE_ASSISTANT
bool send_voice_assistant_announce_finished(const VoiceAssistantAnnounceFinished &msg);
#endif
#ifdef USE_VOICE_ASSISTANT
virtual void on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &value){};
#endif
#ifdef USE_VOICE_ASSISTANT
bool send_voice_assistant_configuration_response(const VoiceAssistantConfigurationResponse &msg);
#endif
#ifdef USE_VOICE_ASSISTANT
virtual void on_voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &value){};
#endif
#ifdef USE_ALARM_CONTROL_PANEL
bool send_list_entities_alarm_control_panel_response(const ListEntitiesAlarmControlPanelResponse &msg);
#endif
#ifdef USE_ALARM_CONTROL_PANEL
bool send_alarm_control_panel_state_response(const AlarmControlPanelStateResponse &msg);
#endif
#ifdef USE_ALARM_CONTROL_PANEL
virtual void on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &value){};
#endif
#ifdef USE_TEXT
bool send_list_entities_text_response(const ListEntitiesTextResponse &msg);
#endif
#ifdef USE_TEXT
bool send_text_state_response(const TextStateResponse &msg);
#endif
#ifdef USE_TEXT
virtual void on_text_command_request(const TextCommandRequest &value){};
#endif
#ifdef USE_DATETIME_DATE
bool send_list_entities_date_response(const ListEntitiesDateResponse &msg);
#endif
#ifdef USE_DATETIME_DATE
bool send_date_state_response(const DateStateResponse &msg);
#endif
#ifdef USE_DATETIME_DATE
virtual void on_date_command_request(const DateCommandRequest &value){};
#endif
#ifdef USE_DATETIME_TIME
bool send_list_entities_time_response(const ListEntitiesTimeResponse &msg);
#endif
#ifdef USE_DATETIME_TIME
bool send_time_state_response(const TimeStateResponse &msg);
#endif
#ifdef USE_DATETIME_TIME
virtual void on_time_command_request(const TimeCommandRequest &value){};
#endif
#ifdef USE_EVENT
bool send_list_entities_event_response(const ListEntitiesEventResponse &msg);
#endif
#ifdef USE_EVENT
bool send_event_response(const EventResponse &msg);
#endif
#ifdef USE_VALVE
bool send_list_entities_valve_response(const ListEntitiesValveResponse &msg);
#endif
#ifdef USE_VALVE
bool send_valve_state_response(const ValveStateResponse &msg);
#endif
#ifdef USE_VALVE
virtual void on_valve_command_request(const ValveCommandRequest &value){};
#endif
#ifdef USE_DATETIME_DATETIME
bool send_list_entities_date_time_response(const ListEntitiesDateTimeResponse &msg);
#endif
#ifdef USE_DATETIME_DATETIME
bool send_date_time_state_response(const DateTimeStateResponse &msg);
#endif
#ifdef USE_DATETIME_DATETIME
virtual void on_date_time_command_request(const DateTimeCommandRequest &value){};
#endif
#ifdef USE_UPDATE
bool send_list_entities_update_response(const ListEntitiesUpdateResponse &msg);
#endif
#ifdef USE_UPDATE
bool send_update_state_response(const UpdateStateResponse &msg);
#endif
#ifdef USE_UPDATE
virtual void on_update_command_request(const UpdateCommandRequest &value){};
#endif

View File

@ -24,10 +24,14 @@ static const char *const TAG = "api";
// APIServer
APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
APIServer::APIServer() { global_api_server = this; }
APIServer::APIServer() {
global_api_server = this;
// Pre-allocate shared write buffer
shared_write_buffer_.reserve(64);
}
void APIServer::setup() {
ESP_LOGCONFIG(TAG, "Setting up Home Assistant API server...");
ESP_LOGCONFIG(TAG, "Running setup");
this->setup_controller();
#ifdef USE_API_NOISE
@ -43,7 +47,7 @@ void APIServer::setup() {
}
#endif
this->socket_ = socket::socket_ip(SOCK_STREAM, 0);
this->socket_ = socket::socket_ip_loop_monitored(SOCK_STREAM, 0); // monitored for incoming connections
if (this->socket_ == nullptr) {
ESP_LOGW(TAG, "Could not create socket");
this->mark_failed();
@ -88,6 +92,12 @@ void APIServer::setup() {
#ifdef USE_LOGGER
if (logger::global_logger != nullptr) {
logger::global_logger->add_on_log_callback([this](int level, const char *tag, const char *message) {
if (this->shutting_down_) {
// Don't try to send logs during shutdown
// as it could result in a recursion and
// we would be filling a buffer we are trying to clear
return;
}
for (auto &c : this->clients_) {
if (!c->remove_)
c->try_send_log_message(level, tag, message);
@ -112,18 +122,20 @@ void APIServer::setup() {
}
void APIServer::loop() {
// Accept new clients
while (true) {
struct sockaddr_storage source_addr;
socklen_t addr_len = sizeof(source_addr);
auto sock = this->socket_->accept((struct sockaddr *) &source_addr, &addr_len);
if (!sock)
break;
ESP_LOGD(TAG, "Accepted %s", sock->getpeername().c_str());
// Accept new clients only if the socket exists and has incoming connections
if (this->socket_ && this->socket_->ready()) {
while (true) {
struct sockaddr_storage source_addr;
socklen_t addr_len = sizeof(source_addr);
auto sock = this->socket_->accept_loop_monitored((struct sockaddr *) &source_addr, &addr_len);
if (!sock)
break;
ESP_LOGD(TAG, "Accepted %s", sock->getpeername().c_str());
auto *conn = new APIConnection(std::move(sock), this);
this->clients_.emplace_back(conn);
conn->start();
auto *conn = new APIConnection(std::move(sock), this);
this->clients_.emplace_back(conn);
conn->start();
}
}
// Process clients and remove disconnected ones in a single pass
@ -155,7 +167,7 @@ void APIServer::loop() {
const uint32_t now = millis();
if (!this->is_connected()) {
if (now - this->last_connected_ > this->reboot_timeout_) {
ESP_LOGE(TAG, "No client connected to API. Rebooting...");
ESP_LOGE(TAG, "No client connected; rebooting");
App.reboot();
}
this->status_set_warning();
@ -167,8 +179,10 @@ void APIServer::loop() {
}
void APIServer::dump_config() {
ESP_LOGCONFIG(TAG, "API Server:");
ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->port_);
ESP_LOGCONFIG(TAG,
"API Server:\n"
" Address: %s:%u",
network::get_use_address().c_str(), this->port_);
#ifdef USE_API_NOISE
ESP_LOGCONFIG(TAG, " Using noise encryption: %s", YESNO(this->noise_ctx_->has_psk()));
if (!this->noise_ctx_->has_psk()) {
@ -213,11 +227,11 @@ bool APIServer::check_password(const std::string &password) const {
void APIServer::handle_disconnect(APIConnection *conn) {}
#ifdef USE_BINARY_SENSOR
void APIServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) {
void APIServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj) {
if (obj->is_internal())
return;
for (auto &c : this->clients_)
c->send_binary_sensor_state(obj, state);
c->send_binary_sensor_state(obj);
}
#endif
@ -253,7 +267,7 @@ void APIServer::on_sensor_update(sensor::Sensor *obj, float state) {
if (obj->is_internal())
return;
for (auto &c : this->clients_)
c->send_sensor_state(obj, state);
c->send_sensor_state(obj);
}
#endif
@ -262,7 +276,7 @@ void APIServer::on_switch_update(switch_::Switch *obj, bool state) {
if (obj->is_internal())
return;
for (auto &c : this->clients_)
c->send_switch_state(obj, state);
c->send_switch_state(obj);
}
#endif
@ -271,7 +285,7 @@ void APIServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::s
if (obj->is_internal())
return;
for (auto &c : this->clients_)
c->send_text_sensor_state(obj, state);
c->send_text_sensor_state(obj);
}
#endif
@ -289,7 +303,7 @@ void APIServer::on_number_update(number::Number *obj, float state) {
if (obj->is_internal())
return;
for (auto &c : this->clients_)
c->send_number_state(obj, state);
c->send_number_state(obj);
}
#endif
@ -325,7 +339,7 @@ void APIServer::on_text_update(text::Text *obj, const std::string &state) {
if (obj->is_internal())
return;
for (auto &c : this->clients_)
c->send_text_state(obj, state);
c->send_text_state(obj);
}
#endif
@ -334,7 +348,7 @@ void APIServer::on_select_update(select::Select *obj, const std::string &state,
if (obj->is_internal())
return;
for (auto &c : this->clients_)
c->send_select_state(obj, state);
c->send_select_state(obj);
}
#endif
@ -343,7 +357,7 @@ void APIServer::on_lock_update(lock::Lock *obj) {
if (obj->is_internal())
return;
for (auto &c : this->clients_)
c->send_lock_state(obj, obj->state);
c->send_lock_state(obj);
}
#endif
@ -394,6 +408,8 @@ void APIServer::set_port(uint16_t port) { this->port_ = port; }
void APIServer::set_password(const std::string &password) { this->password_ = password; }
void APIServer::set_batch_delay(uint32_t batch_delay) { this->batch_delay_ = batch_delay; }
void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
for (auto &client : this->clients_) {
client->send_homeassistant_service_call(call);
@ -452,7 +468,7 @@ bool APIServer::save_noise_psk(psk_t psk, bool make_active) {
ESP_LOGW(TAG, "Disconnecting all clients to reset connections");
this->set_noise_psk(psk);
for (auto &c : this->clients_) {
c->send_disconnect_request(DisconnectRequest());
c->send_message(DisconnectRequest());
}
});
}
@ -472,10 +488,36 @@ void APIServer::request_time() {
bool APIServer::is_connected() const { return !this->clients_.empty(); }
void APIServer::on_shutdown() {
for (auto &c : this->clients_) {
c->send_disconnect_request(DisconnectRequest());
this->shutting_down_ = true;
// Close the listening socket to prevent new connections
if (this->socket_) {
this->socket_->close();
this->socket_ = nullptr;
}
delay(10);
// Change batch delay to 5ms for quick flushing during shutdown
this->batch_delay_ = 5;
// Send disconnect requests to all connected clients
for (auto &c : this->clients_) {
if (!c->send_message(DisconnectRequest())) {
// If we can't send the disconnect request directly (tx_buffer full),
// schedule it in the batch so it will be sent with the 5ms timer
c->schedule_message_(nullptr, &APIConnection::try_send_disconnect_request, DisconnectRequest::MESSAGE_TYPE);
}
}
}
bool APIServer::teardown() {
// If network is disconnected, no point trying to flush buffers
if (!network::is_connected()) {
return true;
}
this->loop();
// Return true only when all clients have been torn down
return this->clients_.empty();
}
} // namespace api

View File

@ -34,11 +34,17 @@ class APIServer : public Component, public Controller {
void loop() override;
void dump_config() override;
void on_shutdown() override;
bool teardown() override;
bool check_password(const std::string &password) const;
bool uses_password() const;
void set_port(uint16_t port);
void set_password(const std::string &password);
void set_reboot_timeout(uint32_t reboot_timeout);
void set_batch_delay(uint32_t batch_delay);
uint32_t get_batch_delay() const { return batch_delay_; }
// Get reference to shared buffer for API connections
std::vector<uint8_t> &get_shared_buffer_ref() { return shared_write_buffer_; }
#ifdef USE_API_NOISE
bool save_noise_psk(psk_t psk, bool make_active = true);
@ -48,7 +54,7 @@ class APIServer : public Component, public Controller {
void handle_disconnect(APIConnection *conn);
#ifdef USE_BINARY_SENSOR
void on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) override;
void on_binary_sensor_update(binary_sensor::BinarySensor *obj) override;
#endif
#ifdef USE_COVER
void on_cover_update(cover::Cover *obj) override;
@ -136,12 +142,15 @@ class APIServer : public Component, public Controller {
}
protected:
bool shutting_down_ = false;
std::unique_ptr<socket::Socket> socket_ = nullptr;
uint16_t port_{6053};
uint32_t reboot_timeout_{300000};
uint32_t batch_delay_{100};
uint32_t last_connected_{0};
std::vector<std::unique_ptr<APIConnection>> clients_;
std::string password_;
std::vector<uint8_t> shared_write_buffer_; // Shared proto write buffer for all connections
std::vector<HomeAssistantStateSubscription> state_subs_;
std::vector<UserServiceDescriptor *> user_services_;
Trigger<std::string, std::string> *client_connected_trigger_ = new Trigger<std::string, std::string>();

View File

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

View File

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

View File

@ -73,7 +73,7 @@ bool ListEntitiesIterator::on_end() { return this->client_->send_list_info_done(
ListEntitiesIterator::ListEntitiesIterator(APIConnection *client) : client_(client) {}
bool ListEntitiesIterator::on_service(UserServiceDescriptor *service) {
auto resp = service->encode_list_service_response();
return this->client_->send_list_entities_services_response(resp);
return this->client_->send_message(resp);
}
#ifdef USE_ESP32_CAMERA

View File

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

View File

@ -1,8 +1,8 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include <vector>
@ -55,6 +55,7 @@ class ProtoVarInt {
return {}; // Incomplete or invalid varint
}
uint16_t as_uint16() const { return this->value_; }
uint32_t as_uint32() const { return this->value_; }
uint64_t as_uint64() const { return this->value_; }
bool as_bool() const { return this->value_; }
@ -359,11 +360,11 @@ class ProtoService {
* @return A ProtoWriteBuffer object with the reserved size.
*/
virtual ProtoWriteBuffer create_buffer(uint32_t reserve_size) = 0;
virtual bool send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) = 0;
virtual bool send_buffer(ProtoWriteBuffer buffer, uint16_t message_type) = 0;
virtual bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) = 0;
// Optimized method that pre-allocates buffer based on message size
template<class C> bool send_message_(const C &msg, uint32_t message_type) {
bool send_message_(const ProtoMessage &msg, uint16_t message_type) {
uint32_t msg_size = 0;
msg.calculate_size(msg_size);

View File

@ -8,7 +8,7 @@ namespace api {
#ifdef USE_BINARY_SENSOR
bool InitialStateIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) {
return this->client_->send_binary_sensor_state(binary_sensor, binary_sensor->state);
return this->client_->send_binary_sensor_state(binary_sensor);
}
#endif
#ifdef USE_COVER
@ -21,27 +21,21 @@ bool InitialStateIterator::on_fan(fan::Fan *fan) { return this->client_->send_fa
bool InitialStateIterator::on_light(light::LightState *light) { return this->client_->send_light_state(light); }
#endif
#ifdef USE_SENSOR
bool InitialStateIterator::on_sensor(sensor::Sensor *sensor) {
return this->client_->send_sensor_state(sensor, sensor->state);
}
bool InitialStateIterator::on_sensor(sensor::Sensor *sensor) { return this->client_->send_sensor_state(sensor); }
#endif
#ifdef USE_SWITCH
bool InitialStateIterator::on_switch(switch_::Switch *a_switch) {
return this->client_->send_switch_state(a_switch, a_switch->state);
}
bool InitialStateIterator::on_switch(switch_::Switch *a_switch) { return this->client_->send_switch_state(a_switch); }
#endif
#ifdef USE_TEXT_SENSOR
bool InitialStateIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) {
return this->client_->send_text_sensor_state(text_sensor, text_sensor->state);
return this->client_->send_text_sensor_state(text_sensor);
}
#endif
#ifdef USE_CLIMATE
bool InitialStateIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_state(climate); }
#endif
#ifdef USE_NUMBER
bool InitialStateIterator::on_number(number::Number *number) {
return this->client_->send_number_state(number, number->state);
}
bool InitialStateIterator::on_number(number::Number *number) { return this->client_->send_number_state(number); }
#endif
#ifdef USE_DATETIME_DATE
bool InitialStateIterator::on_date(datetime::DateEntity *date) { return this->client_->send_date_state(date); }
@ -55,15 +49,13 @@ bool InitialStateIterator::on_datetime(datetime::DateTimeEntity *datetime) {
}
#endif
#ifdef USE_TEXT
bool InitialStateIterator::on_text(text::Text *text) { return this->client_->send_text_state(text, text->state); }
bool InitialStateIterator::on_text(text::Text *text) { return this->client_->send_text_state(text); }
#endif
#ifdef USE_SELECT
bool InitialStateIterator::on_select(select::Select *select) {
return this->client_->send_select_state(select, select->state);
}
bool InitialStateIterator::on_select(select::Select *select) { return this->client_->send_select_state(select); }
#endif
#ifdef USE_LOCK
bool InitialStateIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_state(a_lock, a_lock->state); }
bool InitialStateIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_state(a_lock); }
#endif
#ifdef USE_VALVE
bool InitialStateIterator::on_valve(valve::Valve *valve) { return this->client_->send_valve_state(valve); }

View File

@ -7,7 +7,7 @@ namespace as3935 {
static const char *const TAG = "as3935";
void AS3935Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up AS3935...");
ESP_LOGCONFIG(TAG, "Running setup");
this->irq_pin_->setup();
LOG_PIN(" IRQ Pin: ", this->irq_pin_);
@ -282,7 +282,7 @@ void AS3935Component::display_oscillator(bool state, uint8_t osc) {
// based on the resonance frequency of the antenna and so it should be trimmed
// before the calibration is done.
bool AS3935Component::calibrate_oscillator() {
ESP_LOGI(TAG, "Starting oscillators calibration...");
ESP_LOGI(TAG, "Starting oscillators calibration");
this->write_register(CALIB_RCO, WIPE_ALL, DIRECT_COMMAND, 0); // Send command to calibrate the oscillators
this->display_oscillator(true, 2);
@ -307,7 +307,7 @@ bool AS3935Component::calibrate_oscillator() {
}
void AS3935Component::tune_antenna() {
ESP_LOGI(TAG, "Starting antenna tuning...");
ESP_LOGI(TAG, "Starting antenna tuning");
uint8_t div_ratio = this->read_div_ratio();
uint8_t tune_val = this->read_capacitance();
ESP_LOGI(TAG, "Division Ratio is set to: %d", div_ratio);

View File

@ -23,7 +23,7 @@ static const uint8_t REGISTER_AGC = 0x1A; // 8 bytes / R
static const uint8_t REGISTER_MAGNITUDE = 0x1B; // 16 bytes / R
void AS5600Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up AS5600...");
ESP_LOGCONFIG(TAG, "Running setup");
if (!this->read_byte(REGISTER_STATUS).has_value()) {
this->mark_failed();
@ -91,15 +91,17 @@ void AS5600Component::dump_config() {
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, "Communication with AS5600 failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
return;
}
ESP_LOGCONFIG(TAG, " Watchdog: %d", this->watchdog_);
ESP_LOGCONFIG(TAG, " Fast Filter: %d", this->fast_filter_);
ESP_LOGCONFIG(TAG, " Slow Filter: %d", this->slow_filter_);
ESP_LOGCONFIG(TAG, " Hysteresis: %d", this->hysteresis_);
ESP_LOGCONFIG(TAG, " Start Position: %d", this->start_position_);
ESP_LOGCONFIG(TAG,
" Watchdog: %d\n"
" Fast Filter: %d\n"
" Slow Filter: %d\n"
" Hysteresis: %d\n"
" Start Position: %d",
this->watchdog_, this->fast_filter_, this->slow_filter_, this->hysteresis_, this->start_position_);
if (this->end_mode_ == END_MODE_POSITION) {
ESP_LOGCONFIG(TAG, " End Position: %d", this->end_position_);
} else {

View File

@ -8,7 +8,7 @@ namespace as7341 {
static const char *const TAG = "as7341";
void AS7341Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up AS7341...");
ESP_LOGCONFIG(TAG, "Running setup");
LOG_I2C_DEVICE(this);
// Verify device ID
@ -38,12 +38,14 @@ void AS7341Component::dump_config() {
ESP_LOGCONFIG(TAG, "AS7341:");
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, "Communication with AS7341 failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
}
LOG_UPDATE_INTERVAL(this);
ESP_LOGCONFIG(TAG, " Gain: %u", get_gain());
ESP_LOGCONFIG(TAG, " ATIME: %u", get_atime());
ESP_LOGCONFIG(TAG, " ASTEP: %u", get_astep());
ESP_LOGCONFIG(TAG,
" Gain: %u\n"
" ATIME: %u\n"
" ASTEP: %u",
get_gain(), get_atime(), get_astep());
LOG_SENSOR(" ", "F1", this->f1_);
LOG_SENSOR(" ", "F2", this->f2_);

View File

@ -71,19 +71,22 @@ bool AT581XComponent::i2c_read_reg(uint8_t addr, uint8_t &data) {
return this->read_register(addr, &data, 1) == esphome::i2c::NO_ERROR;
}
void AT581XComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up AT581X..."); }
void AT581XComponent::setup() { ESP_LOGCONFIG(TAG, "Running setup"); }
void AT581XComponent::dump_config() { LOG_I2C_DEVICE(this); }
#define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0]))
bool AT581XComponent::i2c_write_config() {
ESP_LOGCONFIG(TAG, "Writing new config for AT581X...");
ESP_LOGCONFIG(TAG, "Frequency: %dMHz", this->freq_);
ESP_LOGCONFIG(TAG, "Sensing distance: %d", this->delta_);
ESP_LOGCONFIG(TAG, "Power: %dµA", this->power_);
ESP_LOGCONFIG(TAG, "Gain: %d", this->gain_);
ESP_LOGCONFIG(TAG, "Trigger base time: %dms", this->trigger_base_time_ms_);
ESP_LOGCONFIG(TAG, "Trigger keep time: %dms", this->trigger_keep_time_ms_);
ESP_LOGCONFIG(TAG, "Protect time: %dms", this->protect_time_ms_);
ESP_LOGCONFIG(TAG, "Self check time: %dms", this->self_check_time_ms_);
ESP_LOGCONFIG(TAG,
"Writing new config for AT581X\n"
"Frequency: %dMHz\n"
"Sensing distance: %d\n"
"Power: %dµA\n"
"Gain: %d\n"
"Trigger base time: %dms\n"
"Trigger keep time: %dms\n"
"Protect time: %dms\n"
"Self check time: %dms",
this->freq_, this->delta_, this->power_, this->gain_, this->trigger_base_time_ms_,
this->trigger_keep_time_ms_, this->protect_time_ms_, this->self_check_time_ms_);
// Set frequency point
if (!this->i2c_write_reg(FREQ_ADDR, GAIN61_VALUE)) {

View File

@ -41,7 +41,7 @@ void ATM90E26Component::update() {
}
void ATM90E26Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up ATM90E26 Component...");
ESP_LOGCONFIG(TAG, "Running setup");
this->spi_setup();
uint16_t mmode = 0x422; // default values for everything but L/N line current gains
@ -135,7 +135,7 @@ void ATM90E26Component::dump_config() {
ESP_LOGCONFIG("", "ATM90E26:");
LOG_PIN(" CS Pin: ", this->cs_);
if (this->is_failed()) {
ESP_LOGE(TAG, "Communication with ATM90E26 failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
}
LOG_UPDATE_INTERVAL(this);
LOG_SENSOR(" ", "Voltage A", this->voltage_sensor_);

View File

@ -108,7 +108,7 @@ void ATM90E32Component::update() {
}
void ATM90E32Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up ATM90E32 Component...");
ESP_LOGCONFIG(TAG, "Running setup");
this->spi_setup();
uint16_t mmode0 = 0x87; // 3P4W 50Hz
@ -217,7 +217,7 @@ void ATM90E32Component::dump_config() {
ESP_LOGCONFIG("", "ATM90E32:");
LOG_PIN(" CS Pin: ", this->cs_);
if (this->is_failed()) {
ESP_LOGE(TAG, "Communication with ATM90E32 failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
}
LOG_UPDATE_INTERVAL(this);
LOG_SENSOR(" ", "Voltage A", this->phase_[PHASEA].voltage_sensor_);
@ -686,7 +686,7 @@ void ATM90E32Component::restore_power_offset_calibrations_() {
}
void ATM90E32Component::clear_gain_calibrations() {
ESP_LOGI(TAG, "[CALIBRATION] Clearing stored gain calibrations and restoring config-defined values...");
ESP_LOGI(TAG, "[CALIBRATION] Clearing stored gain calibrations and restoring config-defined values");
for (int phase = 0; phase < 3; phase++) {
gain_phase_[phase].voltage_gain = this->phase_[phase].voltage_gain_;

View File

@ -17,7 +17,7 @@ constexpr static const uint8_t AXS_READ_TOUCHPAD[11] = {0xb5, 0xab, 0xa5, 0x5a,
}
void AXS15231Touchscreen::setup() {
ESP_LOGCONFIG(TAG, "Setting up AXS15231 Touchscreen...");
ESP_LOGCONFIG(TAG, "Running setup");
if (this->reset_pin_ != nullptr) {
this->reset_pin_->setup();
this->reset_pin_->digital_write(false);
@ -60,8 +60,10 @@ void AXS15231Touchscreen::dump_config() {
LOG_I2C_DEVICE(this);
LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
ESP_LOGCONFIG(TAG, " Width: %d", this->x_raw_max_);
ESP_LOGCONFIG(TAG, " Height: %d", this->y_raw_max_);
ESP_LOGCONFIG(TAG,
" Width: %d\n"
" Height: %d",
this->x_raw_max_, this->y_raw_max_);
}
} // namespace axs15231

View File

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

View File

@ -484,9 +484,11 @@ void BedJetHub::loop() {}
void BedJetHub::update() { this->dispatch_status_(); }
void BedJetHub::dump_config() {
ESP_LOGCONFIG(TAG, "BedJet Hub '%s'", this->get_name().c_str());
ESP_LOGCONFIG(TAG, " ble_client.app_id: %d", this->parent()->app_id);
ESP_LOGCONFIG(TAG, " ble_client.conn_id: %d", this->parent()->get_conn_id());
ESP_LOGCONFIG(TAG,
"BedJet Hub '%s'\n"
" ble_client.app_id: %d\n"
" ble_client.conn_id: %d",
this->get_name().c_str(), this->parent()->app_id, this->parent()->get_conn_id());
LOG_UPDATE_INTERVAL(this)
ESP_LOGCONFIG(TAG, " Child components (%d):", this->children_.size());
for (auto *child : this->children_) {
@ -527,7 +529,7 @@ void BedJetHub::dispatch_status_() {
}
if (this->timeout_ > 0 && diff > this->timeout_ && this->parent()->enabled) {
ESP_LOGW(TAG, "[%s] Timed out after %" PRId32 " sec. Retrying...", this->get_name().c_str(), this->timeout_);
ESP_LOGW(TAG, "[%s] Timed out after %" PRId32 " sec. Retrying", this->get_name().c_str(), this->timeout_);
// set_enabled(false) will only close the connection if state != IDLE.
this->parent()->set_state(espbt::ClientState::CONNECTING);
this->parent()->set_enabled(false);

View File

@ -119,7 +119,7 @@ void spi_dma_tx_finish_callback(unsigned int param) {
}
void BekenSPILEDStripLightOutput::setup() {
ESP_LOGCONFIG(TAG, "Setting up Beken SPI LED Strip...");
ESP_LOGCONFIG(TAG, "Running setup");
size_t buffer_size = this->get_buffer_size_();
size_t dma_buffer_size = (buffer_size * 8) + (2 * 64);
@ -256,7 +256,7 @@ void BekenSPILEDStripLightOutput::write_state(light::LightState *state) {
this->last_refresh_ = now;
this->mark_shown_();
ESP_LOGVV(TAG, "Writing RGB values to bus...");
ESP_LOGVV(TAG, "Writing RGB values to bus");
if (spi_data == nullptr) {
ESP_LOGE(TAG, "SPI not initialized");
@ -345,8 +345,10 @@ light::ESPColorView BekenSPILEDStripLightOutput::get_view_internal(int32_t index
}
void BekenSPILEDStripLightOutput::dump_config() {
ESP_LOGCONFIG(TAG, "Beken SPI LED Strip:");
ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_);
ESP_LOGCONFIG(TAG,
"Beken SPI LED Strip:\n"
" Pin: %u",
this->pin_);
const char *rgb_order;
switch (this->rgb_order_) {
case ORDER_RGB:
@ -371,9 +373,11 @@ void BekenSPILEDStripLightOutput::dump_config() {
rgb_order = "UNKNOWN";
break;
}
ESP_LOGCONFIG(TAG, " RGB Order: %s", rgb_order);
ESP_LOGCONFIG(TAG, " Max refresh rate: %" PRIu32, *this->max_refresh_rate_);
ESP_LOGCONFIG(TAG, " Number of LEDs: %u", this->num_leds_);
ESP_LOGCONFIG(TAG,
" RGB Order: %s\n"
" Max refresh rate: %" PRIu32 "\n"
" Number of LEDs: %u",
rgb_order, *this->max_refresh_rate_, this->num_leds_);
}
float BekenSPILEDStripLightOutput::get_setup_priority() const { return setup_priority::HARDWARE; }

View File

@ -38,7 +38,7 @@ MTreg:
*/
void BH1750Sensor::setup() {
ESP_LOGCONFIG(TAG, "Setting up BH1750 '%s'...", this->name_.c_str());
ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->name_.c_str());
uint8_t turn_on = BH1750_COMMAND_POWER_ON;
if (this->write(&turn_on, 1) != i2c::ERROR_OK) {
this->mark_failed();
@ -118,7 +118,7 @@ void BH1750Sensor::dump_config() {
LOG_SENSOR("", "BH1750", this);
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, "Communication with BH1750 failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL_FOR, this->get_name().c_str());
}
LOG_UPDATE_INTERVAL(this);

View File

@ -1,7 +1,10 @@
from logging import getLogger
from esphome import automation, core
from esphome.automation import Condition, maybe_simple_id
import esphome.codegen as cg
from esphome.components import mqtt, web_server
from esphome.components.const import CONF_ON_STATE_CHANGE
import esphome.config_validation as cv
from esphome.const import (
CONF_DELAY,
@ -98,6 +101,7 @@ IS_PLATFORM_COMPONENT = True
CONF_TIME_OFF = "time_off"
CONF_TIME_ON = "time_on"
CONF_TRIGGER_ON_INITIAL_STATE = "trigger_on_initial_state"
DEFAULT_DELAY = "1s"
DEFAULT_TIME_OFF = "100ms"
@ -127,9 +131,17 @@ MultiClickTriggerEvent = binary_sensor_ns.struct("MultiClickTriggerEvent")
StateTrigger = binary_sensor_ns.class_(
"StateTrigger", automation.Trigger.template(bool)
)
StateChangeTrigger = binary_sensor_ns.class_(
"StateChangeTrigger",
automation.Trigger.template(cg.optional.template(bool), cg.optional.template(bool)),
)
BinarySensorPublishAction = binary_sensor_ns.class_(
"BinarySensorPublishAction", automation.Action
)
BinarySensorInvalidateAction = binary_sensor_ns.class_(
"BinarySensorInvalidateAction", automation.Action
)
# Condition
BinarySensorCondition = binary_sensor_ns.class_("BinarySensorCondition", Condition)
@ -144,6 +156,8 @@ AutorepeatFilter = binary_sensor_ns.class_("AutorepeatFilter", Filter, cg.Compon
LambdaFilter = binary_sensor_ns.class_("LambdaFilter", Filter)
SettleFilter = binary_sensor_ns.class_("SettleFilter", Filter, cg.Component)
_LOGGER = getLogger(__name__)
FILTER_REGISTRY = Registry()
validate_filters = cv.validate_registry("filter", FILTER_REGISTRY)
@ -386,6 +400,14 @@ def validate_click_timing(value):
return value
def validate_publish_initial_state(value):
value = cv.boolean(value)
_LOGGER.warning(
"The 'publish_initial_state' option has been replaced by 'trigger_on_initial_state' and will be removed in a future release"
)
return value
_BINARY_SENSOR_SCHEMA = (
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
.extend(cv.MQTT_COMPONENT_SCHEMA)
@ -395,7 +417,12 @@ _BINARY_SENSOR_SCHEMA = (
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
mqtt.MQTTBinarySensorComponent
),
cv.Optional(CONF_PUBLISH_INITIAL_STATE): cv.boolean,
cv.Exclusive(
CONF_PUBLISH_INITIAL_STATE, CONF_TRIGGER_ON_INITIAL_STATE
): validate_publish_initial_state,
cv.Exclusive(
CONF_TRIGGER_ON_INITIAL_STATE, CONF_TRIGGER_ON_INITIAL_STATE
): cv.boolean,
cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
cv.Optional(CONF_FILTERS): validate_filters,
cv.Optional(CONF_ON_PRESS): automation.validate_automation(
@ -454,6 +481,11 @@ _BINARY_SENSOR_SCHEMA = (
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger),
}
),
cv.Optional(CONF_ON_STATE_CHANGE): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateChangeTrigger),
}
),
}
)
)
@ -493,8 +525,10 @@ async def setup_binary_sensor_core_(var, config):
if (device_class := config.get(CONF_DEVICE_CLASS)) is not None:
cg.add(var.set_device_class(device_class))
if publish_initial_state := config.get(CONF_PUBLISH_INITIAL_STATE):
cg.add(var.set_publish_initial_state(publish_initial_state))
trigger = config.get(CONF_TRIGGER_ON_INITIAL_STATE, False) or config.get(
CONF_PUBLISH_INITIAL_STATE, False
)
cg.add(var.set_trigger_on_initial_state(trigger))
if inverted := config.get(CONF_INVERTED):
cg.add(var.set_inverted(inverted))
if filters_config := config.get(CONF_FILTERS):
@ -542,6 +576,17 @@ async def setup_binary_sensor_core_(var, config):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [(bool, "x")], conf)
for conf in config.get(CONF_ON_STATE_CHANGE, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(
trigger,
[
(cg.optional.template(bool), "x_previous"),
(cg.optional.template(bool), "x"),
],
conf,
)
if mqtt_id := config.get(CONF_MQTT_ID):
mqtt_ = cg.new_Pvariable(mqtt_id, var)
await mqtt.register_mqtt_component(mqtt_, config)
@ -554,6 +599,7 @@ async def register_binary_sensor(var, config):
if not CORE.has_id(config[CONF_ID]):
var = cg.Pvariable(config[CONF_ID], var)
cg.add(cg.App.register_binary_sensor(var))
CORE.register_platform_component("binary_sensor", var)
await setup_binary_sensor_core_(var, config)
@ -590,3 +636,18 @@ async def binary_sensor_is_off_to_code(config, condition_id, template_arg, args)
async def to_code(config):
cg.add_define("USE_BINARY_SENSOR")
cg.add_global(binary_sensor_ns.using)
@automation.register_action(
"binary_sensor.invalidate_state",
BinarySensorInvalidateAction,
cv.maybe_simple_value(
{
cv.Required(CONF_ID): cv.use_id(BinarySensor),
},
key=CONF_ID,
),
)
async def binary_sensor_invalidate_state_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, paren)

View File

@ -68,8 +68,7 @@ void binary_sensor::MultiClickTrigger::on_state_(bool state) {
*this->at_index_ = *this->at_index_ + 1;
}
void binary_sensor::MultiClickTrigger::schedule_cooldown_() {
ESP_LOGV(TAG, "Multi Click: Invalid length of press, starting cooldown of %" PRIu32 " ms...",
this->invalid_cooldown_);
ESP_LOGV(TAG, "Multi Click: Invalid length of press, starting cooldown of %" PRIu32 " ms", this->invalid_cooldown_);
this->is_in_cooldown_ = true;
this->set_timeout("cooldown", this->invalid_cooldown_, [this]() {
ESP_LOGV(TAG, "Multi Click: Cooldown ended, matching is now enabled again.");

View File

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

View File

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

View File

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

View File

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

View File

@ -14,11 +14,11 @@ class BinarySensor;
class Filter {
public:
virtual optional<bool> new_value(bool value, bool is_initial) = 0;
virtual optional<bool> new_value(bool value) = 0;
void input(bool value, bool is_initial);
void input(bool value);
void output(bool value, bool is_initial);
void output(bool value);
protected:
friend BinarySensor;
@ -30,7 +30,7 @@ class Filter {
class DelayedOnOffFilter : public Filter, public Component {
public:
optional<bool> new_value(bool value, bool is_initial) override;
optional<bool> new_value(bool value) override;
float get_setup_priority() const override;
@ -44,7 +44,7 @@ class DelayedOnOffFilter : public Filter, public Component {
class DelayedOnFilter : public Filter, public Component {
public:
optional<bool> new_value(bool value, bool is_initial) override;
optional<bool> new_value(bool value) override;
float get_setup_priority() const override;
@ -56,7 +56,7 @@ class DelayedOnFilter : public Filter, public Component {
class DelayedOffFilter : public Filter, public Component {
public:
optional<bool> new_value(bool value, bool is_initial) override;
optional<bool> new_value(bool value) override;
float get_setup_priority() const override;
@ -68,7 +68,7 @@ class DelayedOffFilter : public Filter, public Component {
class InvertFilter : public Filter {
public:
optional<bool> new_value(bool value, bool is_initial) override;
optional<bool> new_value(bool value) override;
};
struct AutorepeatFilterTiming {
@ -86,7 +86,7 @@ class AutorepeatFilter : public Filter, public Component {
public:
explicit AutorepeatFilter(std::vector<AutorepeatFilterTiming> timings);
optional<bool> new_value(bool value, bool is_initial) override;
optional<bool> new_value(bool value) override;
float get_setup_priority() const override;
@ -102,7 +102,7 @@ class LambdaFilter : public Filter {
public:
explicit LambdaFilter(std::function<optional<bool>(bool)> f);
optional<bool> new_value(bool value, bool is_initial) override;
optional<bool> new_value(bool value) override;
protected:
std::function<optional<bool>(bool)> f_;
@ -110,7 +110,7 @@ class LambdaFilter : public Filter {
class SettleFilter : public Filter, public Component {
public:
optional<bool> new_value(bool value, bool is_initial) override;
optional<bool> new_value(bool value) override;
float get_setup_priority() const override;

View File

@ -100,7 +100,7 @@ void BL0906::handle_actions_() {
for (int i = 0; i < this->action_queue_.size(); i++) {
ptr_func = this->action_queue_[i];
if (ptr_func) {
ESP_LOGI(TAG, "HandleActionCallback[%d]...", i);
ESP_LOGI(TAG, "HandleActionCallback[%d]", i);
(this->*ptr_func)();
}
}

View File

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

View File

@ -9,6 +9,7 @@ from esphome.const import (
CONF_ID,
CONF_LINE_FREQUENCY,
CONF_POWER,
CONF_RESET,
CONF_VOLTAGE,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_ENERGY,
@ -27,7 +28,6 @@ from esphome.const import (
CONF_CURRENT_REFERENCE = "current_reference"
CONF_ENERGY_REFERENCE = "energy_reference"
CONF_POWER_REFERENCE = "power_reference"
CONF_RESET = "reset"
CONF_VOLTAGE_REFERENCE = "voltage_reference"
DEPENDENCIES = ["uart"]

View File

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

View File

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

View File

@ -1,7 +1,7 @@
#include "ble_sensor.h"
#include "esphome/core/log.h"
#include "esphome/core/application.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#ifdef USE_ESP32
@ -15,11 +15,14 @@ void BLESensor::loop() {}
void BLESensor::dump_config() {
LOG_SENSOR("", "BLE Sensor", this);
ESP_LOGCONFIG(TAG, " MAC address : %s", this->parent()->address_str().c_str());
ESP_LOGCONFIG(TAG, " Service UUID : %s", this->service_uuid_.to_string().c_str());
ESP_LOGCONFIG(TAG, " Characteristic UUID: %s", this->char_uuid_.to_string().c_str());
ESP_LOGCONFIG(TAG, " Descriptor UUID : %s", this->descr_uuid_.to_string().c_str());
ESP_LOGCONFIG(TAG, " Notifications : %s", YESNO(this->notify_));
ESP_LOGCONFIG(TAG,
" MAC address : %s\n"
" Service UUID : %s\n"
" Characteristic UUID: %s\n"
" Descriptor UUID : %s\n"
" Notifications : %s",
this->parent()->address_str().c_str(), this->service_uuid_.to_string().c_str(),
this->char_uuid_.to_string().c_str(), this->descr_uuid_.to_string().c_str(), YESNO(this->notify_));
LOG_UPDATE_INTERVAL(this);
}

View File

@ -18,11 +18,14 @@ void BLETextSensor::loop() {}
void BLETextSensor::dump_config() {
LOG_TEXT_SENSOR("", "BLE Text Sensor", this);
ESP_LOGCONFIG(TAG, " MAC address : %s", this->parent()->address_str().c_str());
ESP_LOGCONFIG(TAG, " Service UUID : %s", this->service_uuid_.to_string().c_str());
ESP_LOGCONFIG(TAG, " Characteristic UUID: %s", this->char_uuid_.to_string().c_str());
ESP_LOGCONFIG(TAG, " Descriptor UUID : %s", this->descr_uuid_.to_string().c_str());
ESP_LOGCONFIG(TAG, " Notifications : %s", YESNO(this->notify_));
ESP_LOGCONFIG(TAG,
" MAC address : %s\n"
" Service UUID : %s\n"
" Characteristic UUID: %s\n"
" Descriptor UUID : %s\n"
" Notifications : %s",
this->parent()->address_str().c_str(), this->service_uuid_.to_string().c_str(),
this->char_uuid_.to_string().c_str(), this->descr_uuid_.to_string().c_str(), YESNO(this->notify_));
LOG_UPDATE_INTERVAL(this);
}

View File

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

View File

@ -75,7 +75,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
resp.data.reserve(param->read.value_len);
// Use bulk insert instead of individual push_backs
resp.data.insert(resp.data.end(), param->read.value, param->read.value + param->read.value_len);
this->proxy_->get_api_connection()->send_bluetooth_gatt_read_response(resp);
this->proxy_->get_api_connection()->send_message(resp);
break;
}
case ESP_GATTC_WRITE_CHAR_EVT:
@ -89,7 +89,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
api::BluetoothGATTWriteResponse resp;
resp.address = this->address_;
resp.handle = param->write.handle;
this->proxy_->get_api_connection()->send_bluetooth_gatt_write_response(resp);
this->proxy_->get_api_connection()->send_message(resp);
break;
}
case ESP_GATTC_UNREG_FOR_NOTIFY_EVT: {
@ -103,7 +103,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
api::BluetoothGATTNotifyResponse resp;
resp.address = this->address_;
resp.handle = param->unreg_for_notify.handle;
this->proxy_->get_api_connection()->send_bluetooth_gatt_notify_response(resp);
this->proxy_->get_api_connection()->send_message(resp);
break;
}
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
@ -116,7 +116,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
api::BluetoothGATTNotifyResponse resp;
resp.address = this->address_;
resp.handle = param->reg_for_notify.handle;
this->proxy_->get_api_connection()->send_bluetooth_gatt_notify_response(resp);
this->proxy_->get_api_connection()->send_message(resp);
break;
}
case ESP_GATTC_NOTIFY_EVT: {
@ -128,7 +128,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
resp.data.reserve(param->notify.value_len);
// Use bulk insert instead of individual push_backs
resp.data.insert(resp.data.end(), param->notify.value, param->notify.value + param->notify.value_len);
this->proxy_->get_api_connection()->send_bluetooth_gatt_notify_data_response(resp);
this->proxy_->get_api_connection()->send_message(resp);
break;
}
default:

View File

@ -39,7 +39,7 @@ void BluetoothProxy::send_bluetooth_scanner_state_(esp32_ble_tracker::ScannerSta
resp.state = static_cast<api::enums::BluetoothScannerState>(state);
resp.mode = this->parent_->get_scan_active() ? api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_ACTIVE
: api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_PASSIVE;
this->api_connection_->send_bluetooth_scanner_state_response(resp);
this->api_connection_->send_message(resp);
}
bool BluetoothProxy::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
@ -58,7 +58,7 @@ static std::vector<api::BluetoothLERawAdvertisement> &get_batch_buffer() {
return batch_buffer;
}
bool BluetoothProxy::parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) {
bool BluetoothProxy::parse_devices(const esp32_ble::BLEScanResult *scan_results, size_t count) {
if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr || !this->raw_advertisements_)
return false;
@ -73,7 +73,7 @@ bool BluetoothProxy::parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_p
// Add new advertisements to the batch buffer
for (size_t i = 0; i < count; i++) {
auto &result = advertisements[i];
auto &result = scan_results[i];
uint8_t length = result.adv_data_len + result.scan_rsp_len;
batch_buffer.emplace_back();
@ -103,7 +103,7 @@ void BluetoothProxy::flush_pending_advertisements() {
api::BluetoothLERawAdvertisementsResponse resp;
resp.advertisements.swap(batch_buffer);
this->api_connection_->send_bluetooth_le_raw_advertisements_response(resp);
this->api_connection_->send_message(resp);
}
void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device) {
@ -141,14 +141,16 @@ void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &devi
manufacturer_data.data.assign(data.data.begin(), data.data.end());
}
this->api_connection_->send_bluetooth_le_advertisement(resp);
this->api_connection_->send_message(resp);
}
void BluetoothProxy::dump_config() {
ESP_LOGCONFIG(TAG, "Bluetooth Proxy:");
ESP_LOGCONFIG(TAG, " Active: %s", YESNO(this->active_));
ESP_LOGCONFIG(TAG, " Connections: %d", this->connections_.size());
ESP_LOGCONFIG(TAG, " Raw advertisements: %s", YESNO(this->raw_advertisements_));
ESP_LOGCONFIG(TAG,
" Active: %s\n"
" Connections: %d\n"
" Raw advertisements: %s",
YESNO(this->active_), this->connections_.size(), YESNO(this->raw_advertisements_));
}
int BluetoothProxy::get_bluetooth_connections_free() {
@ -300,7 +302,7 @@ void BluetoothProxy::loop() {
service_resp.characteristics.push_back(std::move(characteristic_resp));
}
resp.services.push_back(std::move(service_resp));
this->api_connection_->send_bluetooth_gatt_get_services_response(resp);
this->api_connection_->send_message(resp);
}
}
}
@ -453,7 +455,7 @@ void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest
call.success = ret == ESP_OK;
call.error = ret;
this->api_connection_->send_bluetooth_device_clear_cache_response(call);
this->api_connection_->send_message(call);
break;
}
@ -577,7 +579,7 @@ void BluetoothProxy::send_device_connection(uint64_t address, bool connected, ui
call.connected = connected;
call.mtu = mtu;
call.error = error;
this->api_connection_->send_bluetooth_device_connection_response(call);
this->api_connection_->send_message(call);
}
void BluetoothProxy::send_connections_free() {
if (this->api_connection_ == nullptr)
@ -590,7 +592,7 @@ void BluetoothProxy::send_connections_free() {
call.allocated.push_back(connection->address_);
}
}
this->api_connection_->send_bluetooth_connections_free_response(call);
this->api_connection_->send_message(call);
}
void BluetoothProxy::send_gatt_services_done(uint64_t address) {
@ -598,7 +600,7 @@ void BluetoothProxy::send_gatt_services_done(uint64_t address) {
return;
api::BluetoothGATTGetServicesDoneResponse call;
call.address = address;
this->api_connection_->send_bluetooth_gatt_get_services_done_response(call);
this->api_connection_->send_message(call);
}
void BluetoothProxy::send_gatt_error(uint64_t address, uint16_t handle, esp_err_t error) {
@ -608,7 +610,7 @@ void BluetoothProxy::send_gatt_error(uint64_t address, uint16_t handle, esp_err_
call.address = address;
call.handle = handle;
call.error = error;
this->api_connection_->send_bluetooth_gatt_error_response(call);
this->api_connection_->send_message(call);
}
void BluetoothProxy::send_device_pairing(uint64_t address, bool paired, esp_err_t error) {
@ -617,7 +619,7 @@ void BluetoothProxy::send_device_pairing(uint64_t address, bool paired, esp_err_
call.paired = paired;
call.error = error;
this->api_connection_->send_bluetooth_device_pairing_response(call);
this->api_connection_->send_message(call);
}
void BluetoothProxy::send_device_unpairing(uint64_t address, bool success, esp_err_t error) {
@ -626,7 +628,7 @@ void BluetoothProxy::send_device_unpairing(uint64_t address, bool success, esp_e
call.success = success;
call.error = error;
this->api_connection_->send_bluetooth_device_unpairing_response(call);
this->api_connection_->send_message(call);
}
void BluetoothProxy::bluetooth_scanner_set_mode(bool active) {

View File

@ -52,7 +52,7 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com
public:
BluetoothProxy();
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
bool parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) override;
bool parse_devices(const esp32_ble::BLEScanResult *scan_results, size_t count) override;
void dump_config() override;
void setup() override;
void loop() override;

View File

@ -88,14 +88,13 @@ const char *oversampling_to_str(BME280Oversampling oversampling) { // NOLINT
}
void BME280Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up BME280...");
ESP_LOGCONFIG(TAG, "Running setup");
uint8_t chip_id = 0;
// Mark as not failed before initializing. Some devices will turn off sensors to save on batteries
// and when they come back on, the COMPONENT_STATE_FAILED bit must be unset on the component.
if ((this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED) {
this->component_state_ &= ~COMPONENT_STATE_MASK;
this->component_state_ |= COMPONENT_STATE_CONSTRUCTION;
if (this->is_failed()) {
this->reset_to_construction_state();
}
if (!this->read_byte(BME280_REGISTER_CHIPID, &chip_id)) {
@ -182,7 +181,7 @@ void BME280Component::dump_config() {
ESP_LOGCONFIG(TAG, "BME280:");
switch (this->error_code_) {
case COMMUNICATION_FAILED:
ESP_LOGE(TAG, "Communication with BME280 failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
break;
case WRONG_CHIP_ID:
ESP_LOGE(TAG, "BME280 has wrong chip ID! Is it a BME280?");
@ -207,7 +206,7 @@ inline uint8_t oversampling_to_time(BME280Oversampling over_sampling) { return (
void BME280Component::update() {
// Enable sensor
ESP_LOGV(TAG, "Sending conversion request...");
ESP_LOGV(TAG, "Sending conversion request");
uint8_t meas_value = 0;
meas_value |= (this->temperature_oversampling_ & 0b111) << 5;
meas_value |= (this->pressure_oversampling_ & 0b111) << 2;

View File

@ -71,7 +71,7 @@ static const char *iir_filter_to_str(BME680IIRFilter filter) {
}
void BME680Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up BME680...");
ESP_LOGCONFIG(TAG, "Running setup");
uint8_t chip_id;
if (!this->read_byte(BME680_REGISTER_CHIPID, &chip_id) || chip_id != 0x61) {
this->mark_failed();
@ -215,7 +215,7 @@ void BME680Component::dump_config() {
ESP_LOGCONFIG(TAG, "BME680:");
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, "Communication with BME680 failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
}
ESP_LOGCONFIG(TAG, " IIR Filter: %s", iir_filter_to_str(this->iir_filter_));
LOG_UPDATE_INTERVAL(this);
@ -307,7 +307,7 @@ void BME680Component::read_data_() {
this->humidity_sensor_->publish_state(NAN);
if (this->gas_resistance_sensor_ != nullptr)
this->gas_resistance_sensor_->publish_state(NAN);
ESP_LOGW(TAG, "Communication with BME680 failed!");
ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL);
this->status_set_warning();
return;
}

View File

@ -13,7 +13,7 @@ from esphome.const import (
CONF_PRESSURE,
CONF_TEMPERATURE,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
DEVICE_CLASS_TEMPERATURE,
ICON_GAS_CYLINDER,
STATE_CLASS_MEASUREMENT,
@ -71,7 +71,7 @@ CONFIG_SCHEMA = (
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
unit_of_measurement=UNIT_HECTOPASCAL,
accuracy_decimals=1,
device_class=DEVICE_CLASS_PRESSURE,
device_class=DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
{

View File

@ -1,6 +1,6 @@
#include "bme680_bsec.h"
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include <string>
namespace esphome {
@ -15,7 +15,7 @@ std::vector<BME680BSECComponent *>
uint8_t BME680BSECComponent::work_buffer_[BSEC_MAX_WORKBUFFER_SIZE] = {0};
void BME680BSECComponent::setup() {
ESP_LOGCONFIG(TAG, "Setting up BME680(%s) via BSEC...", this->device_id_.c_str());
ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->device_id_.c_str());
uint8_t new_idx = BME680BSECComponent::instances.size();
BME680BSECComponent::instances.push_back(this);
@ -159,11 +159,15 @@ void BME680BSECComponent::dump_config() {
this->bme680_status_);
}
ESP_LOGCONFIG(TAG, " Temperature Offset: %.2f", this->temperature_offset_);
ESP_LOGCONFIG(TAG, " IAQ Mode: %s", this->iaq_mode_ == IAQ_MODE_STATIC ? "Static" : "Mobile");
ESP_LOGCONFIG(TAG, " Supply Voltage: %sV", this->supply_voltage_ == SUPPLY_VOLTAGE_3V3 ? "3.3" : "1.8");
ESP_LOGCONFIG(TAG, " Sample Rate: %s", BME680_BSEC_SAMPLE_RATE_LOG(this->sample_rate_));
ESP_LOGCONFIG(TAG, " State Save Interval: %ims", this->state_save_interval_ms_);
ESP_LOGCONFIG(TAG,
" Temperature Offset: %.2f\n"
" IAQ Mode: %s\n"
" Supply Voltage: %sV\n"
" Sample Rate: %s\n"
" State Save Interval: %ims",
this->temperature_offset_, this->iaq_mode_ == IAQ_MODE_STATIC ? "Static" : "Mobile",
this->supply_voltage_ == SUPPLY_VOLTAGE_3V3 ? "3.3" : "1.8",
BME680_BSEC_SAMPLE_RATE_LOG(this->sample_rate_), this->state_save_interval_ms_);
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
ESP_LOGCONFIG(TAG, " Sample Rate: %s", BME680_BSEC_SAMPLE_RATE_LOG(this->temperature_sample_rate_));

View File

@ -15,6 +15,8 @@ from esphome.const import (
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
ICON_GAS_CYLINDER,
ICON_GAUGE,
ICON_THERMOMETER,
ICON_WATER_PERCENT,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
UNIT_HECTOPASCAL,
@ -27,11 +29,11 @@ from . import CONF_BME680_BSEC_ID, SAMPLE_RATE_OPTIONS, BME680BSECComponent
DEPENDENCIES = ["bme680_bsec"]
CONF_IAQ = "iaq"
CONF_CO2_EQUIVALENT = "co2_equivalent"
CONF_BREATH_VOC_EQUIVALENT = "breath_voc_equivalent"
UNIT_IAQ = "IAQ"
CONF_CO2_EQUIVALENT = "co2_equivalent"
CONF_IAQ = "iaq"
ICON_ACCURACY = "mdi:checkbox-marked-circle-outline"
UNIT_IAQ = "IAQ"
TYPES = [
CONF_TEMPERATURE,
@ -49,6 +51,7 @@ CONFIG_SCHEMA = cv.Schema(
cv.GenerateID(CONF_BME680_BSEC_ID): cv.use_id(BME680BSECComponent),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
unit_of_measurement=UNIT_CELSIUS,
icon=ICON_THERMOMETER,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
@ -65,6 +68,7 @@ CONFIG_SCHEMA = cv.Schema(
),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
unit_of_measurement=UNIT_PERCENT,
icon=ICON_WATER_PERCENT,
accuracy_decimals=1,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,

View File

@ -16,7 +16,7 @@ CODEOWNERS = ["@neffs", "@kbx81"]
DOMAIN = "bme68x_bsec2"
BSEC2_LIBRARY_VERSION = "v1.8.2610"
BSEC2_LIBRARY_VERSION = "1.10.2610"
CONF_ALGORITHM_OUTPUT = "algorithm_output"
CONF_BME68X_BSEC2_ID = "bme68x_bsec2_id"
@ -145,7 +145,6 @@ CONFIG_SCHEMA_BASE = (
): cv.positive_time_period_minutes,
},
)
.add_extra(cv.only_with_arduino)
.add_extra(validate_bme68x)
.add_extra(download_bme68x_blob)
)
@ -179,11 +178,13 @@ async def to_code_base(config):
bsec2_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
cg.add(var.set_bsec2_configuration(bsec2_arr, len(rhs)))
# Although this component does not use SPI, the BSEC2 library requires the SPI library
cg.add_library("SPI", None)
# Although this component does not use SPI, the BSEC2 Arduino library requires the SPI library
if core.CORE.using_arduino:
cg.add_library("SPI", None)
cg.add_library(
"BME68x Sensor library",
"1.1.40407",
"1.3.40408",
"https://github.com/boschsensortec/Bosch-BME68x-Library",
)
cg.add_library(
"BSEC2 Software Library",

View File

@ -1,4 +1,5 @@
#include "esphome/core/defines.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
@ -20,7 +21,7 @@ static const char *const TAG = "bme68x_bsec2.sensor";
static const std::string IAQ_ACCURACY_STATES[4] = {"Stabilizing", "Uncertain", "Calibrating", "Calibrated"};
void BME68xBSEC2Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up BME68X via BSEC2...");
ESP_LOGCONFIG(TAG, "Running setup");
this->bsec_status_ = bsec_init_m(&this->bsec_instance_);
if (this->bsec_status_ != BSEC_OK) {
@ -57,13 +58,13 @@ void BME68xBSEC2Component::setup() {
}
void BME68xBSEC2Component::dump_config() {
ESP_LOGCONFIG(TAG, "BME68X via BSEC2:");
ESP_LOGCONFIG(TAG, " BSEC2 version: %d.%d.%d.%d", this->version_.major, this->version_.minor,
this->version_.major_bugfix, this->version_.minor_bugfix);
ESP_LOGCONFIG(TAG, " BSEC2 configuration blob:");
ESP_LOGCONFIG(TAG, " Configured: %s", YESNO(this->bsec2_blob_configured_));
ESP_LOGCONFIG(TAG,
"BME68X via BSEC2:\n"
" BSEC2 version: %d.%d.%d.%d\n"
" BSEC2 configuration blob:\n"
" Configured: %s",
this->version_.major, this->version_.minor, this->version_.major_bugfix, this->version_.minor_bugfix,
YESNO(this->bsec2_blob_configured_));
if (this->bsec2_configuration_ != nullptr && this->bsec2_configuration_length_) {
ESP_LOGCONFIG(TAG, " Size: %" PRIu32, this->bsec2_configuration_length_);
}
@ -76,11 +77,14 @@ void BME68xBSEC2Component::dump_config() {
if (this->algorithm_output_ != ALGORITHM_OUTPUT_IAQ) {
ESP_LOGCONFIG(TAG, " Algorithm output: %s", BME68X_BSEC2_ALGORITHM_OUTPUT_LOG(this->algorithm_output_));
}
ESP_LOGCONFIG(TAG, " Operating age: %s", BME68X_BSEC2_OPERATING_AGE_LOG(this->operating_age_));
ESP_LOGCONFIG(TAG, " Sample rate: %s", BME68X_BSEC2_SAMPLE_RATE_LOG(this->sample_rate_));
ESP_LOGCONFIG(TAG, " Voltage: %s", BME68X_BSEC2_VOLTAGE_LOG(this->voltage_));
ESP_LOGCONFIG(TAG, " State save interval: %ims", this->state_save_interval_ms_);
ESP_LOGCONFIG(TAG, " Temperature offset: %.2f", this->temperature_offset_);
ESP_LOGCONFIG(TAG,
" Operating age: %s\n"
" Sample rate: %s\n"
" Voltage: %s\n"
" State save interval: %ims\n"
" Temperature offset: %.2f",
BME68X_BSEC2_OPERATING_AGE_LOG(this->operating_age_), BME68X_BSEC2_SAMPLE_RATE_LOG(this->sample_rate_),
BME68X_BSEC2_VOLTAGE_LOG(this->voltage_), this->state_save_interval_ms_, this->temperature_offset_);
#ifdef USE_SENSOR
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);

View File

@ -9,8 +9,10 @@ from esphome.const import (
CONF_SAMPLE_RATE,
CONF_TEMPERATURE,
DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
DEVICE_CLASS_CARBON_DIOXIDE,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
ICON_GAS_CYLINDER,
ICON_GAUGE,
ICON_THERMOMETER,
@ -32,7 +34,6 @@ CONF_CO2_EQUIVALENT = "co2_equivalent"
CONF_IAQ = "iaq"
CONF_IAQ_STATIC = "iaq_static"
ICON_ACCURACY = "mdi:checkbox-marked-circle-outline"
ICON_TEST_TUBE = "mdi:test-tube"
UNIT_IAQ = "IAQ"
TYPES = [
@ -61,7 +62,6 @@ CONFIG_SCHEMA = cv.Schema(
),
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
unit_of_measurement=UNIT_HECTOPASCAL,
icon=ICON_GAUGE,
accuracy_decimals=1,
device_class=DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
state_class=STATE_CLASS_MEASUREMENT,
@ -102,14 +102,14 @@ CONFIG_SCHEMA = cv.Schema(
),
cv.Optional(CONF_CO2_EQUIVALENT): sensor.sensor_schema(
unit_of_measurement=UNIT_PARTS_PER_MILLION,
icon=ICON_TEST_TUBE,
accuracy_decimals=1,
device_class=DEVICE_CLASS_CARBON_DIOXIDE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_BREATH_VOC_EQUIVALENT): sensor.sensor_schema(
unit_of_measurement=UNIT_PARTS_PER_MILLION,
icon=ICON_TEST_TUBE,
accuracy_decimals=1,
device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
state_class=STATE_CLASS_MEASUREMENT,
),
}

View File

@ -1,4 +1,5 @@
#include "esphome/core/defines.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"

View File

@ -119,44 +119,44 @@ const float GRAVITY_EARTH = 9.80665f;
void BMI160Component::internal_setup_(int stage) {
switch (stage) {
case 0:
ESP_LOGCONFIG(TAG, "Setting up BMI160...");
ESP_LOGCONFIG(TAG, "Running setup");
uint8_t chipid;
if (!this->read_byte(BMI160_REGISTER_CHIPID, &chipid) || (chipid != 0b11010001)) {
this->mark_failed();
return;
}
ESP_LOGV(TAG, " Bringing accelerometer out of sleep...");
ESP_LOGV(TAG, " Bringing accelerometer out of sleep");
if (!this->write_byte(BMI160_REGISTER_CMD, (uint8_t) Cmd::ACCL_SET_PMU_MODE | (uint8_t) AcclPmuMode::NORMAL)) {
this->mark_failed();
return;
}
ESP_LOGV(TAG, " Waiting for accelerometer to wake up...");
ESP_LOGV(TAG, " Waiting for accelerometer to wake up");
// need to wait (max delay in datasheet) because we can't send commands while another is in progress
// min 5ms, 10ms
this->set_timeout(10, [this]() { this->internal_setup_(1); });
break;
case 1:
ESP_LOGV(TAG, " Bringing gyroscope out of sleep...");
ESP_LOGV(TAG, " Bringing gyroscope out of sleep");
if (!this->write_byte(BMI160_REGISTER_CMD, (uint8_t) Cmd::GYRO_SET_PMU_MODE | (uint8_t) GyroPmuMode::NORMAL)) {
this->mark_failed();
return;
}
ESP_LOGV(TAG, " Waiting for gyroscope to wake up...");
ESP_LOGV(TAG, " Waiting for gyroscope to wake up");
// wait between 51 & 81ms, doing 100 to be safe
this->set_timeout(10, [this]() { this->internal_setup_(2); });
break;
case 2:
ESP_LOGV(TAG, " Setting up Gyro Config...");
ESP_LOGV(TAG, " Setting up Gyro Config");
uint8_t gyro_config = (uint8_t) GyroBandwidth::OSR4 | (uint8_t) GyroOuputDataRate::HZ_25;
ESP_LOGV(TAG, " Output gyro_config: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(gyro_config));
if (!this->write_byte(BMI160_REGISTER_GYRO_CONFIG, gyro_config)) {
this->mark_failed();
return;
}
ESP_LOGV(TAG, " Setting up Gyro Range...");
ESP_LOGV(TAG, " Setting up Gyro Range");
uint8_t gyro_range = (uint8_t) GyroRange::RANGE_2000_DPS;
ESP_LOGV(TAG, " Output gyro_range: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(gyro_range));
if (!this->write_byte(BMI160_REGISTER_GYRO_RANGE, gyro_range)) {
@ -164,7 +164,7 @@ void BMI160Component::internal_setup_(int stage) {
return;
}
ESP_LOGV(TAG, " Setting up Accel Config...");
ESP_LOGV(TAG, " Setting up Accel Config");
uint8_t accel_config =
(uint8_t) AcclFilterMode::PERF | (uint8_t) AcclBandwidth::RES_AVG16 | (uint8_t) AccelOutputDataRate::HZ_25;
ESP_LOGV(TAG, " Output accel_config: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(accel_config));
@ -172,7 +172,7 @@ void BMI160Component::internal_setup_(int stage) {
this->mark_failed();
return;
}
ESP_LOGV(TAG, " Setting up Accel Range...");
ESP_LOGV(TAG, " Setting up Accel Range");
uint8_t accel_range = (uint8_t) AccelRange::RANGE_16G;
ESP_LOGV(TAG, " Output accel_range: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(accel_range));
if (!this->write_byte(BMI160_REGISTER_ACCEL_RANGE, accel_range)) {
@ -189,7 +189,7 @@ void BMI160Component::dump_config() {
ESP_LOGCONFIG(TAG, "BMI160:");
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, "Communication with BMI160 failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
}
LOG_UPDATE_INTERVAL(this);
LOG_SENSOR(" ", "Acceleration X", this->accel_x_sensor_);
@ -219,7 +219,7 @@ void BMI160Component::update() {
return;
}
ESP_LOGV(TAG, " Updating BMI160...");
ESP_LOGV(TAG, " Updating BMI160");
int16_t data[6];
if (this->read_le_int16_(BMI160_REGISTER_DATA_GYRO_X_LSB, data, 6) != i2c::ERROR_OK) {
this->status_set_warning();

View File

@ -20,7 +20,7 @@ void BMP085Component::update() {
this->set_timeout("temperature", 5, [this]() { this->read_temperature_(); });
}
void BMP085Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up BMP085...");
ESP_LOGCONFIG(TAG, "Running setup");
uint8_t data[22];
if (!this->read_bytes(BMP085_REGISTER_AC1_H, data, 22)) {
this->mark_failed();
@ -129,7 +129,7 @@ void BMP085Component::read_pressure_() {
this->status_clear_warning();
}
bool BMP085Component::set_mode_(uint8_t mode) {
ESP_LOGV(TAG, "Setting mode to 0x%02X...", mode);
ESP_LOGV(TAG, "Setting mode to 0x%02X", mode);
return this->write_byte(BMP085_REGISTER_CONTROL, mode);
}
float BMP085Component::get_setup_priority() const { return setup_priority::DATA; }

View File

@ -57,7 +57,7 @@ static const char *iir_filter_to_str(BMP280IIRFilter filter) {
}
void BMP280Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up BMP280...");
ESP_LOGCONFIG(TAG, "Running setup");
uint8_t chip_id = 0;
// Read the chip id twice, to work around a bug where the first read is 0.
@ -132,7 +132,7 @@ void BMP280Component::dump_config() {
ESP_LOGCONFIG(TAG, "BMP280:");
switch (this->error_code_) {
case COMMUNICATION_FAILED:
ESP_LOGE(TAG, "Communication with BMP280 failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
break;
case WRONG_CHIP_ID:
ESP_LOGE(TAG, "BMP280 has wrong chip ID! Is it a BME280?");
@ -155,7 +155,7 @@ inline uint8_t oversampling_to_time(BMP280Oversampling over_sampling) { return (
void BMP280Component::update() {
// Enable sensor
ESP_LOGV(TAG, "Sending conversion request...");
ESP_LOGV(TAG, "Sending conversion request");
uint8_t meas_value = 0;
meas_value |= (this->temperature_oversampling_ & 0b111) << 5;
meas_value |= (this->pressure_oversampling_ & 0b111) << 2;

View File

@ -70,10 +70,10 @@ static const LogString *iir_filter_to_str(IIRFilter filter) {
void BMP3XXComponent::setup() {
this->error_code_ = NONE;
ESP_LOGCONFIG(TAG, "Setting up BMP3XX...");
ESP_LOGCONFIG(TAG, "Running setup");
// Call the Device base class "initialise" function
if (!reset()) {
ESP_LOGE(TAG, "Failed to reset BMP3XX...");
ESP_LOGE(TAG, "Failed to reset");
this->error_code_ = ERROR_SENSOR_RESET;
this->mark_failed();
}
@ -148,25 +148,25 @@ void BMP3XXComponent::setup() {
}
void BMP3XXComponent::dump_config() {
ESP_LOGCONFIG(TAG, "BMP3XX:");
ESP_LOGCONFIG(TAG, " Type: %s (0x%X)", LOG_STR_ARG(chip_type_to_str(this->chip_id_.reg)), this->chip_id_.reg);
ESP_LOGCONFIG(TAG,
"BMP3XX:\n"
" Type: %s (0x%X)",
LOG_STR_ARG(chip_type_to_str(this->chip_id_.reg)), this->chip_id_.reg);
switch (this->error_code_) {
case NONE:
break;
case ERROR_COMMUNICATION_FAILED:
ESP_LOGE(TAG, "Communication with BMP3XX failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
break;
case ERROR_WRONG_CHIP_ID:
ESP_LOGE(
TAG,
"BMP3XX has wrong chip ID (reported id: 0x%X) - please check if you are really using a BMP 388 or BMP 390",
this->chip_id_.reg);
ESP_LOGE(TAG, "Wrong chip ID (reported id: 0x%X) - please check if you are really using a BMP 388 or BMP 390",
this->chip_id_.reg);
break;
case ERROR_SENSOR_RESET:
ESP_LOGE(TAG, "BMP3XX failed to reset");
ESP_LOGE(TAG, "Failed to reset");
break;
default:
ESP_LOGE(TAG, "BMP3XX error code %d", (int) this->error_code_);
ESP_LOGE(TAG, "Error code %d", (int) this->error_code_);
break;
}
ESP_LOGCONFIG(TAG, " IIR Filter: %s", LOG_STR_ARG(iir_filter_to_str(this->iir_filter_)));
@ -186,7 +186,7 @@ inline uint8_t oversampling_to_time(Oversampling over_sampling) { return (1 << u
void BMP3XXComponent::update() {
// Enable sensor
ESP_LOGV(TAG, "Sending conversion request...");
ESP_LOGV(TAG, "Sending conversion request");
float meas_time = 1.0f;
// Ref: https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp390-ds002.pdf 3.9.2
meas_time += 2.02f * oversampling_to_time(this->temperature_oversampling_) + 0.163f;
@ -296,7 +296,7 @@ bool BMP3XXComponent::get_pressure(float &pressure) {
bool BMP3XXComponent::get_measurements(float &temperature, float &pressure) {
// Check if a measurement is ready
if (!data_ready()) {
ESP_LOGD(TAG, "BMP3XX Get measurement - data not ready skipping update");
ESP_LOGD(TAG, "Get measurement - data not ready skipping update");
return false;
}

View File

@ -72,22 +72,22 @@ void BMP581Component::dump_config() {
case NONE:
break;
case ERROR_COMMUNICATION_FAILED:
ESP_LOGE(TAG, " Communication with BMP581 failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
break;
case ERROR_WRONG_CHIP_ID:
ESP_LOGE(TAG, " BMP581 has wrong chip ID - please verify you are using a BMP 581");
ESP_LOGE(TAG, "Unknown chip ID");
break;
case ERROR_SENSOR_RESET:
ESP_LOGE(TAG, " BMP581 failed to reset");
ESP_LOGE(TAG, "Reset failed");
break;
case ERROR_SENSOR_STATUS:
ESP_LOGE(TAG, " BMP581 sensor status failed, there were NVM problems");
ESP_LOGE(TAG, "Get status failed");
break;
case ERROR_PRIME_IIR_FAILED:
ESP_LOGE(TAG, " BMP581's IIR Filter failed to prime with an initial measurement");
ESP_LOGE(TAG, "IIR Filter failed to prime with initial measurement");
break;
default:
ESP_LOGE(TAG, " BMP581 error code %d", (int) this->error_code_);
ESP_LOGE(TAG, "Error %d", (int) this->error_code_);
break;
}
@ -98,14 +98,20 @@ void BMP581Component::dump_config() {
if (this->temperature_sensor_) {
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
ESP_LOGCONFIG(TAG, " IIR Filter: %s", LOG_STR_ARG(iir_filter_to_str(this->iir_temperature_level_)));
ESP_LOGCONFIG(TAG, " Oversampling: %s", LOG_STR_ARG(oversampling_to_str(this->temperature_oversampling_)));
ESP_LOGCONFIG(TAG,
" IIR Filter: %s\n"
" Oversampling: %s",
LOG_STR_ARG(iir_filter_to_str(this->iir_temperature_level_)),
LOG_STR_ARG(oversampling_to_str(this->temperature_oversampling_)));
}
if (this->pressure_sensor_) {
LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
ESP_LOGCONFIG(TAG, " IIR Filter: %s", LOG_STR_ARG(iir_filter_to_str(this->iir_pressure_level_)));
ESP_LOGCONFIG(TAG, " Oversampling: %s", LOG_STR_ARG(oversampling_to_str(this->pressure_oversampling_)));
ESP_LOGCONFIG(TAG,
" IIR Filter: %s\n"
" Oversampling: %s",
LOG_STR_ARG(iir_filter_to_str(this->iir_pressure_level_)),
LOG_STR_ARG(oversampling_to_str(this->pressure_oversampling_)));
}
}
@ -122,7 +128,7 @@ void BMP581Component::setup() {
*/
this->error_code_ = NONE;
ESP_LOGCONFIG(TAG, "Setting up BMP581...");
ESP_LOGCONFIG(TAG, "Running setup");
////////////////////
// 1) Soft reboot //
@ -130,7 +136,7 @@ void BMP581Component::setup() {
// Power-On-Reboot bit is asserted if sensor successfully reset
if (!this->reset_()) {
ESP_LOGE(TAG, "BMP581 failed to reset");
ESP_LOGE(TAG, "Reset failed");
this->error_code_ = ERROR_SENSOR_RESET;
this->mark_failed();
@ -146,7 +152,7 @@ void BMP581Component::setup() {
// read chip id from sensor
if (!this->read_byte(BMP581_CHIP_ID, &chip_id)) {
ESP_LOGE(TAG, "Failed to read chip id");
ESP_LOGE(TAG, "Read chip ID failed");
this->error_code_ = ERROR_COMMUNICATION_FAILED;
this->mark_failed();
@ -156,7 +162,7 @@ void BMP581Component::setup() {
// verify id
if (chip_id != BMP581_ASIC_ID) {
ESP_LOGE(TAG, "Unknown chip ID, is this a BMP581?");
ESP_LOGE(TAG, "Unknown chip ID");
this->error_code_ = ERROR_WRONG_CHIP_ID;
this->mark_failed();
@ -179,7 +185,7 @@ void BMP581Component::setup() {
// verify status_nvm_rdy bit (it is asserted if boot was successful)
if (!(this->status_.bit.status_nvm_rdy)) {
ESP_LOGE(TAG, "NVM not ready after boot");
ESP_LOGE(TAG, "NVM not ready");
this->error_code_ = ERROR_SENSOR_STATUS;
this->mark_failed();
@ -189,7 +195,7 @@ void BMP581Component::setup() {
// verify status_nvm_err bit (it is asserted if an error is detected)
if (this->status_.bit.status_nvm_err) {
ESP_LOGE(TAG, "NVM error detected on boot");
ESP_LOGE(TAG, "NVM error detected");
this->error_code_ = ERROR_SENSOR_STATUS;
this->mark_failed();
@ -254,7 +260,7 @@ void BMP581Component::setup() {
}
if (!this->prime_iir_filter_()) {
ESP_LOGE(TAG, "Failed to prime the IIR filter with an intiial measurement");
ESP_LOGE(TAG, "Failed to prime the IIR filter with an initial measurement");
this->error_code_ = ERROR_PRIME_IIR_FAILED;
this->mark_failed();
@ -286,10 +292,10 @@ void BMP581Component::update() {
// 1) Request a measurement //
//////////////////////////////
ESP_LOGVV(TAG, "Requesting a measurement from sensor");
ESP_LOGVV(TAG, "Requesting measurement");
if (!this->start_measurement_()) {
ESP_LOGW(TAG, "Failed to request forced measurement of sensor");
ESP_LOGW(TAG, "Requesting forced measurement failed");
this->status_set_warning();
return;
@ -299,7 +305,7 @@ void BMP581Component::update() {
// 2) Wait for measurement to finish (based on oversampling rates) //
//////////////////////////////////////////////////////////////////////
ESP_LOGVV(TAG, "Measurement is expected to take %d ms to complete", this->conversion_time_);
ESP_LOGVV(TAG, "Measurement should take %d ms", this->conversion_time_);
this->set_timeout("measurement", this->conversion_time_, [this]() {
float temperature = 0.0;
@ -311,14 +317,14 @@ void BMP581Component::update() {
if (this->pressure_sensor_) {
if (!this->read_temperature_and_pressure_(temperature, pressure)) {
ESP_LOGW(TAG, "Failed to read temperature and pressure measurements, skipping update");
ESP_LOGW(TAG, "Failed to read temperature and pressure; skipping update");
this->status_set_warning();
return;
}
} else {
if (!this->read_temperature_(temperature)) {
ESP_LOGW(TAG, "Failed to read temperature measurement, skipping update");
ESP_LOGW(TAG, "Failed to read temperature; skipping update");
this->status_set_warning();
return;
@ -349,7 +355,7 @@ bool BMP581Component::check_data_readiness_() {
// - returns data readiness state
if (this->odr_config_.bit.pwr_mode == STANDBY_MODE) {
ESP_LOGD(TAG, "Data is not ready, sensor is in standby mode");
ESP_LOGD(TAG, "Data not ready, sensor is in standby mode");
return false;
}
@ -443,7 +449,7 @@ bool BMP581Component::read_temperature_(float &temperature) {
// - the measured temperature (in degrees Celsius)
if (!this->check_data_readiness_()) {
ESP_LOGW(TAG, "Data from sensor isn't ready, skipping this update");
ESP_LOGW(TAG, "Data not ready, skipping this update");
this->status_set_warning();
return false;
@ -451,7 +457,7 @@ bool BMP581Component::read_temperature_(float &temperature) {
uint8_t data[3];
if (!this->read_bytes(BMP581_MEASUREMENT_DATA, &data[0], 3)) {
ESP_LOGW(TAG, "Failed to read sensor's measurement data");
ESP_LOGW(TAG, "Failed to read measurement");
this->status_set_warning();
return false;
@ -472,7 +478,7 @@ bool BMP581Component::read_temperature_and_pressure_(float &temperature, float &
// - the measured pressure (in Pa)
if (!this->check_data_readiness_()) {
ESP_LOGW(TAG, "Data from sensor isn't ready, skipping this update");
ESP_LOGW(TAG, "Data not ready, skipping this update");
this->status_set_warning();
return false;
@ -480,7 +486,7 @@ bool BMP581Component::read_temperature_and_pressure_(float &temperature, float &
uint8_t data[6];
if (!this->read_bytes(BMP581_MEASUREMENT_DATA, &data[0], 6)) {
ESP_LOGW(TAG, "Failed to read sensor's measurement data");
ESP_LOGW(TAG, "Failed to read measurement");
this->status_set_warning();
return false;

View File

@ -15,7 +15,7 @@ static const uint8_t BP1658CJ_ADDR_START_5CH = 0x30;
static const uint8_t BP1658CJ_DELAY = 2;
void BP1658CJ::setup() {
ESP_LOGCONFIG(TAG, "Setting up BP1658CJ Output Component...");
ESP_LOGCONFIG(TAG, "Running setup");
this->data_pin_->setup();
this->data_pin_->digital_write(false);
this->clock_pin_->setup();
@ -26,8 +26,10 @@ void BP1658CJ::dump_config() {
ESP_LOGCONFIG(TAG, "BP1658CJ:");
LOG_PIN(" Data Pin: ", this->data_pin_);
LOG_PIN(" Clock Pin: ", this->clock_pin_);
ESP_LOGCONFIG(TAG, " Color Channels Max Power: %u", this->max_power_color_channels_);
ESP_LOGCONFIG(TAG, " White Channels Max Power: %u", this->max_power_white_channels_);
ESP_LOGCONFIG(TAG,
" Color Channels Max Power: %u\n"
" White Channels Max Power: %u",
this->max_power_color_channels_, this->max_power_white_channels_);
}
void BP1658CJ::loop() {

View File

@ -20,7 +20,7 @@ static const uint8_t BP5758D_ALL_DATA_CHANNEL_ENABLEMENT = 0b00011111;
static const uint8_t BP5758D_DELAY = 2;
void BP5758D::setup() {
ESP_LOGCONFIG(TAG, "Setting up BP5758D Output Component...");
ESP_LOGCONFIG(TAG, "Running setup");
this->data_pin_->setup();
this->data_pin_->digital_write(false);
delayMicroseconds(BP5758D_DELAY);

View File

@ -108,6 +108,7 @@ async def register_button(var, config):
if not CORE.has_id(config[CONF_ID]):
var = cg.Pvariable(config[CONF_ID], var)
cg.add(cg.App.register_button(var))
CORE.register_platform_component("button", var)
await setup_button_core_(var, config)

View File

@ -7,7 +7,7 @@ namespace canbus {
static const char *const TAG = "canbus";
void Canbus::setup() {
ESP_LOGCONFIG(TAG, "Setting up Canbus...");
ESP_LOGCONFIG(TAG, "Running setup");
if (!this->setup_internal()) {
ESP_LOGE(TAG, "setup error!");
this->mark_failed();

View File

@ -8,7 +8,7 @@ namespace cap1188 {
static const char *const TAG = "cap1188";
void CAP1188Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up CAP1188...");
ESP_LOGCONFIG(TAG, "Running setup");
// Reset device using the reset pin
if (this->reset_pin_ != nullptr) {
@ -52,9 +52,11 @@ void CAP1188Component::dump_config() {
ESP_LOGCONFIG(TAG, "CAP1188:");
LOG_I2C_DEVICE(this);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
ESP_LOGCONFIG(TAG, " Product ID: 0x%x", this->cap1188_product_id_);
ESP_LOGCONFIG(TAG, " Manufacture ID: 0x%x", this->cap1188_manufacture_id_);
ESP_LOGCONFIG(TAG, " Revision ID: 0x%x", this->cap1188_revision_);
ESP_LOGCONFIG(TAG,
" Product ID: 0x%x\n"
" Manufacture ID: 0x%x\n"
" Revision ID: 0x%x",
this->cap1188_product_id_, this->cap1188_manufacture_id_, this->cap1188_revision_);
switch (this->error_code_) {
case COMMUNICATION_FAILED:

View File

@ -29,7 +29,7 @@ void CaptivePortal::handle_config(AsyncWebServerRequest *request) {
void CaptivePortal::handle_wifisave(AsyncWebServerRequest *request) {
std::string ssid = request->arg("ssid").c_str();
std::string psk = request->arg("psk").c_str();
ESP_LOGI(TAG, "Captive Portal Requested WiFi Settings Change:");
ESP_LOGI(TAG, "Requested WiFi Settings Change:");
ESP_LOGI(TAG, " SSID='%s'", ssid.c_str());
ESP_LOGI(TAG, " Password=" LOG_SECRET("'%s'"), psk.c_str());
wifi::global_wifi_component->save_wifi_sta(ssid, psk);

View File

@ -1,6 +1,7 @@
#include "ccs811.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace ccs811 {
@ -163,7 +164,7 @@ void CCS811Component::dump_config() {
if (this->is_failed()) {
switch (this->error_code_) {
case COMMUNICATION_FAILED:
ESP_LOGW(TAG, "Communication failed! Is the sensor connected?");
ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL);
break;
case INVALID_ID:
ESP_LOGW(TAG, "Sensor reported an invalid ID. Is this a CCS811?");

View File

@ -10,7 +10,7 @@ static const char *const TAG = "cd74hc4067";
float CD74HC4067Component::get_setup_priority() const { return setup_priority::DATA; }
void CD74HC4067Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up CD74HC4067...");
ESP_LOGCONFIG(TAG, "Running setup");
this->pin_s0_->setup();
this->pin_s1_->setup();

View File

@ -14,7 +14,7 @@ static const uint8_t CH422G_REG_OUT_UPPER = 0x23; // write reg for output bit
static const char *const TAG = "ch422g";
void CH422GComponent::setup() {
ESP_LOGCONFIG(TAG, "Setting up CH422G...");
ESP_LOGCONFIG(TAG, "Running setup");
// set outputs before mode
this->write_outputs_();
// Set mode and check for errors
@ -37,7 +37,7 @@ void CH422GComponent::dump_config() {
ESP_LOGCONFIG(TAG, "CH422G:");
LOG_I2C_DEVICE(this)
if (this->is_failed()) {
ESP_LOGE(TAG, "Communication with CH422G failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
}
}

View File

@ -4,7 +4,7 @@ namespace esphome {
namespace chsc6x {
void CHSC6XTouchscreen::setup() {
ESP_LOGCONFIG(TAG, "Setting up CHSC6X Touchscreen...");
ESP_LOGCONFIG(TAG, "Running setup");
if (this->interrupt_pin_ != nullptr) {
this->interrupt_pin_->setup();
this->attach_interrupt_(this->interrupt_pin_, gpio::INTERRUPT_FALLING_EDGE);
@ -38,9 +38,11 @@ void CHSC6XTouchscreen::dump_config() {
ESP_LOGCONFIG(TAG, "CHSC6X Touchscreen:");
LOG_I2C_DEVICE(this);
LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
ESP_LOGCONFIG(TAG, " Touch timeout: %d", this->touch_timeout_);
ESP_LOGCONFIG(TAG, " x_raw_max_: %d", this->x_raw_max_);
ESP_LOGCONFIG(TAG, " y_raw_max_: %d", this->y_raw_max_);
ESP_LOGCONFIG(TAG,
" Touch timeout: %d\n"
" x_raw_max_: %d\n"
" y_raw_max_: %d",
this->touch_timeout_, this->x_raw_max_, this->y_raw_max_);
}
} // namespace chsc6x

View File

@ -443,6 +443,7 @@ async def register_climate(var, config):
if not CORE.has_id(config[CONF_ID]):
var = cg.Pvariable(config[CONF_ID], var)
cg.add(cg.App.register_climate(var))
CORE.register_platform_component("climate", var)
await setup_climate_core_(var, config)

View File

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

View File

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

View File

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

View File

@ -0,0 +1 @@
"""CM1106 component for ESPHome."""

View File

@ -0,0 +1,112 @@
#include "cm1106.h"
#include "esphome/core/log.h"
#include <cinttypes>
namespace esphome {
namespace cm1106 {
static const char *const TAG = "cm1106";
static const uint8_t C_M1106_CMD_GET_CO2[4] = {0x11, 0x01, 0x01, 0xED};
static const uint8_t C_M1106_CMD_SET_CO2_CALIB[6] = {0x11, 0x03, 0x03, 0x00, 0x00, 0x00};
static const uint8_t C_M1106_CMD_SET_CO2_CALIB_RESPONSE[4] = {0x16, 0x01, 0x03, 0xE6};
uint8_t cm1106_checksum(const uint8_t *response, size_t len) {
uint8_t crc = 0;
for (int i = 0; i < len - 1; i++) {
crc -= response[i];
}
return crc;
}
void CM1106Component::setup() {
ESP_LOGCONFIG(TAG, "Running setup");
uint8_t response[8] = {0};
if (!this->cm1106_write_command_(C_M1106_CMD_GET_CO2, sizeof(C_M1106_CMD_GET_CO2), response, sizeof(response))) {
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
this->mark_failed();
return;
}
}
void CM1106Component::update() {
uint8_t response[8] = {0};
if (!this->cm1106_write_command_(C_M1106_CMD_GET_CO2, sizeof(C_M1106_CMD_GET_CO2), response, sizeof(response))) {
ESP_LOGW(TAG, "Reading data from CM1106 failed!");
this->status_set_warning();
return;
}
if (response[0] != 0x16 || response[1] != 0x05 || response[2] != 0x01) {
ESP_LOGW(TAG, "Got wrong UART response from CM1106: %02X %02X %02X %02X", response[0], response[1], response[2],
response[3]);
this->status_set_warning();
return;
}
uint8_t checksum = cm1106_checksum(response, sizeof(response));
if (response[7] != checksum) {
ESP_LOGW(TAG, "CM1106 Checksum doesn't match: 0x%02X!=0x%02X", response[7], checksum);
this->status_set_warning();
return;
}
this->status_clear_warning();
uint16_t ppm = response[3] << 8 | response[4];
ESP_LOGD(TAG, "CM1106 Received CO₂=%uppm DF3=%02X DF4=%02X", ppm, response[5], response[6]);
if (this->co2_sensor_ != nullptr)
this->co2_sensor_->publish_state(ppm);
}
void CM1106Component::calibrate_zero(uint16_t ppm) {
uint8_t cmd[6];
memcpy(cmd, C_M1106_CMD_SET_CO2_CALIB, sizeof(cmd));
cmd[3] = ppm >> 8;
cmd[4] = ppm & 0xFF;
uint8_t response[4] = {0};
if (!this->cm1106_write_command_(cmd, sizeof(cmd), response, sizeof(response))) {
ESP_LOGW(TAG, "Reading data from CM1106 failed!");
this->status_set_warning();
return;
}
// check if correct response received
if (memcmp(response, C_M1106_CMD_SET_CO2_CALIB_RESPONSE, sizeof(response)) != 0) {
ESP_LOGW(TAG, "Got wrong UART response from CM1106: %02X %02X %02X %02X", response[0], response[1], response[2],
response[3]);
this->status_set_warning();
return;
}
this->status_clear_warning();
ESP_LOGD(TAG, "CM1106 Successfully calibrated sensor to %uppm", ppm);
}
bool CM1106Component::cm1106_write_command_(const uint8_t *command, size_t command_len, uint8_t *response,
size_t response_len) {
// Empty RX Buffer
while (this->available())
this->read();
this->write_array(command, command_len - 1);
this->write_byte(cm1106_checksum(command, command_len));
this->flush();
if (response == nullptr)
return true;
return this->read_array(response, response_len);
}
void CM1106Component::dump_config() {
ESP_LOGCONFIG(TAG, "CM1106:");
LOG_SENSOR(" ", "CO2", this->co2_sensor_);
this->check_uart_settings(9600);
if (this->is_failed()) {
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
}
}
} // namespace cm1106
} // namespace esphome

View File

@ -0,0 +1,40 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/automation.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/uart/uart.h"
namespace esphome {
namespace cm1106 {
class CM1106Component : public PollingComponent, public uart::UARTDevice {
public:
float get_setup_priority() const override { return esphome::setup_priority::DATA; }
void setup() override;
void update() override;
void dump_config() override;
void calibrate_zero(uint16_t ppm);
void set_co2_sensor(sensor::Sensor *co2_sensor) { this->co2_sensor_ = co2_sensor; }
protected:
sensor::Sensor *co2_sensor_{nullptr};
bool cm1106_write_command_(const uint8_t *command, size_t command_len, uint8_t *response, size_t response_len);
};
template<typename... Ts> class CM1106CalibrateZeroAction : public Action<Ts...> {
public:
CM1106CalibrateZeroAction(CM1106Component *cm1106) : cm1106_(cm1106) {}
void play(Ts... x) override { this->cm1106_->calibrate_zero(400); }
protected:
CM1106Component *cm1106_;
};
} // namespace cm1106
} // namespace esphome

View File

@ -0,0 +1,72 @@
"""CM1106 Sensor component for ESPHome."""
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.automation import maybe_simple_id
from esphome.components import sensor, uart
from esphome.const import (
CONF_CO2,
CONF_ID,
DEVICE_CLASS_CARBON_DIOXIDE,
ICON_MOLECULE_CO2,
STATE_CLASS_MEASUREMENT,
UNIT_PARTS_PER_MILLION,
)
DEPENDENCIES = ["uart"]
CODEOWNERS = ["@andrewjswan"]
cm1106_ns = cg.esphome_ns.namespace("cm1106")
CM1106Component = cm1106_ns.class_(
"CM1106Component", cg.PollingComponent, uart.UARTDevice
)
CM1106CalibrateZeroAction = cm1106_ns.class_(
"CM1106CalibrateZeroAction",
automation.Action,
)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(CM1106Component),
cv.Optional(CONF_CO2): sensor.sensor_schema(
unit_of_measurement=UNIT_PARTS_PER_MILLION,
icon=ICON_MOLECULE_CO2,
accuracy_decimals=0,
device_class=DEVICE_CLASS_CARBON_DIOXIDE,
state_class=STATE_CLASS_MEASUREMENT,
),
},
)
.extend(cv.polling_component_schema("60s"))
.extend(uart.UART_DEVICE_SCHEMA)
)
async def to_code(config) -> None:
"""Code generation entry point."""
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await uart.register_uart_device(var, config)
if co2_config := config.get(CONF_CO2):
sens = await sensor.new_sensor(co2_config)
cg.add(var.set_co2_sensor(sens))
CALIBRATION_ACTION_SCHEMA = maybe_simple_id(
{
cv.GenerateID(): cv.use_id(CM1106Component),
},
)
@automation.register_action(
"cm1106.calibrate_zero",
CM1106CalibrateZeroAction,
CALIBRATION_ACTION_SCHEMA,
)
async def cm1106_calibration_to_code(config, action_id, template_arg, args) -> None:
"""Service code generation entry point."""
paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, paren)

View File

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

View File

@ -189,6 +189,7 @@ async def register_cover(var, config):
if not CORE.has_id(config[CONF_ID]):
var = cg.Pvariable(config[CONF_ID], var)
cg.add(cg.App.register_cover(var))
CORE.register_platform_component("cover", var)
await setup_cover_core_(var, config)

View File

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

View File

@ -42,7 +42,7 @@ static const uint8_t CSE7761_CMD_ENABLE_WRITE = 0xE5; // Enable write operation
enum CSE7761 { RMS_IAC, RMS_IBC, RMS_UC, POWER_PAC, POWER_PBC, POWER_SC, ENERGY_AC, ENERGY_BC };
void CSE7761Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up CSE7761...");
ESP_LOGCONFIG(TAG, "Running setup");
this->write_(CSE7761_SPECIAL_COMMAND, CSE7761_CMD_RESET);
uint16_t syscon = this->read_(0x00, 2); // Default 0x0A04
if ((0x0A04 == syscon) && this->chip_init_()) {
@ -57,7 +57,7 @@ void CSE7761Component::setup() {
void CSE7761Component::dump_config() {
ESP_LOGCONFIG(TAG, "CSE7761:");
if (this->is_failed()) {
ESP_LOGE(TAG, "Communication with CSE7761 failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
}
LOG_UPDATE_INTERVAL(this);
this->check_uart_settings(38400, 1, uart::UART_CONFIG_PARITY_EVEN, 8);

View File

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

View File

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

View File

@ -6,7 +6,7 @@ namespace cst226 {
static const char *const TAG = "cst226.touchscreen";
void CST226Touchscreen::setup() {
ESP_LOGCONFIG(TAG, "Setting up CST226 Touchscreen...");
ESP_LOGCONFIG(TAG, "Running setup");
if (this->reset_pin_ != nullptr) {
this->reset_pin_->setup();
this->reset_pin_->digital_write(true);

View File

@ -1,4 +1,5 @@
#include "cst816_touchscreen.h"
#include "esphome/core/helpers.h"
namespace esphome {
namespace cst816 {
@ -38,7 +39,7 @@ void CST816Touchscreen::continue_setup_() {
}
void CST816Touchscreen::setup() {
ESP_LOGCONFIG(TAG, "Setting up CST816 Touchscreen...");
ESP_LOGCONFIG(TAG, "Running setup");
if (this->reset_pin_ != nullptr) {
this->reset_pin_->setup();
this->reset_pin_->digital_write(true);
@ -74,8 +75,10 @@ void CST816Touchscreen::dump_config() {
LOG_I2C_DEVICE(this);
LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
ESP_LOGCONFIG(TAG, " X Raw Min: %d, X Raw Max: %d", this->x_raw_min_, this->x_raw_max_);
ESP_LOGCONFIG(TAG, " Y Raw Min: %d, Y Raw Max: %d", this->y_raw_min_, this->y_raw_max_);
ESP_LOGCONFIG(TAG,
" X Raw Min: %d, X Raw Max: %d\n"
" Y Raw Min: %d, Y Raw Max: %d",
this->x_raw_min_, this->x_raw_max_, this->y_raw_min_, this->y_raw_max_);
const char *name;
switch (this->chip_id_) {
case CST820_CHIP_ID:

View File

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

View File

@ -1,7 +1,7 @@
#include "dac7678_output.h"
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace dac7678 {
@ -20,9 +20,9 @@ static const uint8_t DAC7678_REG_INTERNAL_REF_0 = 0x80;
static const uint8_t DAC7678_REG_INTERNAL_REF_1 = 0x90;
void DAC7678Output::setup() {
ESP_LOGCONFIG(TAG, "Setting up DAC7678OutputComponent...");
ESP_LOGCONFIG(TAG, "Running setup");
ESP_LOGV(TAG, "Resetting device...");
ESP_LOGV(TAG, "Resetting device");
// Reset device
if (!this->write_byte_16(DAC7678_REG_SOFTWARE_RESET, 0x0000)) {

View File

@ -70,7 +70,7 @@ bool DallasTemperatureSensor::read_scratch_pad_() {
}
void DallasTemperatureSensor::setup() {
ESP_LOGCONFIG(TAG, "setting up Dallas temperature sensor...");
ESP_LOGCONFIG(TAG, "Running setup");
if (!this->check_address_())
return;
if (!this->read_scratch_pad_())
@ -80,7 +80,7 @@ void DallasTemperatureSensor::setup() {
if ((this->address_ & 0xff) == DALLAS_MODEL_DS18S20) {
// DS18S20 doesn't support resolution.
ESP_LOGW(TAG, "DS18S20 doesn't support setting resolution.");
ESP_LOGW(TAG, "DS18S20 doesn't support setting resolution");
return;
}
@ -125,7 +125,6 @@ bool DallasTemperatureSensor::check_scratch_pad_() {
crc8(this->scratch_pad_, 8));
#endif
if (!chksum_validity) {
ESP_LOGW(TAG, "'%s' - Scratch pad checksum invalid!", this->get_name().c_str());
this->status_set_warning("scratch pad checksum invalid");
ESP_LOGD(TAG, "Scratch pad: %02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X.%02X (%02X)", this->scratch_pad_[0],
this->scratch_pad_[1], this->scratch_pad_[2], this->scratch_pad_[3], this->scratch_pad_[4],

View File

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

View File

@ -2,7 +2,6 @@ import base64
from pathlib import Path
import re
import secrets
from typing import Optional
import requests
from ruamel.yaml import YAML
@ -84,7 +83,7 @@ async def to_code(config):
def import_config(
path: str,
name: str,
friendly_name: Optional[str],
friendly_name: str | None,
project_name: str,
import_url: str,
network: str = CONF_WIFI,

View File

@ -158,7 +158,9 @@ async def setup_datetime_core_(var, config):
async def register_datetime(var, config):
if not CORE.has_id(config[CONF_ID]):
var = cg.Pvariable(config[CONF_ID], var)
cg.add(getattr(cg.App, f"register_{config[CONF_TYPE].lower()}")(var))
entity_type = config[CONF_TYPE].lower()
cg.add(getattr(cg.App, f"register_{entity_type}")(var))
CORE.register_platform_component(entity_type, var)
await setup_datetime_core_(var, config)
cg.add_define(f"USE_DATETIME_{config[CONF_TYPE]}")

View File

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

View File

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

View File

@ -11,40 +11,40 @@ static const char *const TAG = "datetime.datetime_entity";
void DateTimeEntity::publish_state() {
if (this->year_ == 0 || this->month_ == 0 || this->day_ == 0) {
this->has_state_ = false;
this->set_has_state(false);
return;
}
if (this->year_ < 1970 || this->year_ > 3000) {
this->has_state_ = false;
this->set_has_state(false);
ESP_LOGE(TAG, "Year must be between 1970 and 3000");
return;
}
if (this->month_ < 1 || this->month_ > 12) {
this->has_state_ = false;
this->set_has_state(false);
ESP_LOGE(TAG, "Month must be between 1 and 12");
return;
}
if (this->day_ > days_in_month(this->month_, this->year_)) {
this->has_state_ = false;
this->set_has_state(false);
ESP_LOGE(TAG, "Day must be between 1 and %d for month %d", days_in_month(this->month_, this->year_), this->month_);
return;
}
if (this->hour_ > 23) {
this->has_state_ = false;
this->set_has_state(false);
ESP_LOGE(TAG, "Hour must be between 0 and 23");
return;
}
if (this->minute_ > 59) {
this->has_state_ = false;
this->set_has_state(false);
ESP_LOGE(TAG, "Minute must be between 0 and 59");
return;
}
if (this->second_ > 59) {
this->has_state_ = false;
this->set_has_state(false);
ESP_LOGE(TAG, "Second must be between 0 and 59");
return;
}
this->has_state_ = true;
this->set_has_state(true);
ESP_LOGD(TAG, "'%s': Sending datetime %04u-%02u-%02u %02d:%02d:%02d", this->get_name().c_str(), this->year_,
this->month_, this->day_, this->hour_, this->minute_, this->second_);
this->state_callback_.call();

View File

@ -11,21 +11,21 @@ static const char *const TAG = "datetime.time_entity";
void TimeEntity::publish_state() {
if (this->hour_ > 23) {
this->has_state_ = false;
this->set_has_state(false);
ESP_LOGE(TAG, "Hour must be between 0 and 23");
return;
}
if (this->minute_ > 59) {
this->has_state_ = false;
this->set_has_state(false);
ESP_LOGE(TAG, "Minute must be between 0 and 59");
return;
}
if (this->second_ > 59) {
this->has_state_ = false;
this->set_has_state(false);
ESP_LOGE(TAG, "Second must be between 0 and 59");
return;
}
this->has_state_ = true;
this->set_has_state(true);
ESP_LOGD(TAG, "'%s': Sending time %02d:%02d:%02d", this->get_name().c_str(), this->hour_, this->minute_,
this->second_);
this->state_callback_.call();

View File

@ -2,9 +2,9 @@
#include <algorithm>
#include "esphome/core/application.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include "esphome/core/version.h"
#include <cinttypes>
#include <climits>
@ -15,10 +15,6 @@ namespace debug {
static const char *const TAG = "debug";
void DebugComponent::dump_config() {
#ifndef ESPHOME_LOG_HAS_DEBUG
return; // Can't log below if debug logging is disabled
#endif
ESP_LOGCONFIG(TAG, "Debug component:");
#ifdef USE_TEXT_SENSOR
LOG_TEXT_SENSOR(" ", "Device info", this->device_info_);

View File

@ -2,8 +2,8 @@
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/macros.h"
#include "esphome/core/helpers.h"
#include "esphome/core/macros.h"
#ifdef USE_SENSOR
#include "esphome/components/sensor/sensor.h"

View File

@ -107,8 +107,10 @@ std::string DebugComponent::get_wakeup_cause_() {
}
void DebugComponent::log_partition_info_() {
ESP_LOGCONFIG(TAG, "Partition table:");
ESP_LOGCONFIG(TAG, " %-12s %-4s %-8s %-10s %-10s", "Name", "Type", "Subtype", "Address", "Size");
ESP_LOGCONFIG(TAG,
"Partition table:\n"
" %-12s %-4s %-8s %-10s %-10s",
"Name", "Type", "Subtype", "Address", "Size");
esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL);
while (it != NULL) {
const esp_partition_t *partition = esp_partition_get(it);

View File

@ -6,24 +6,26 @@ namespace esphome {
namespace deep_sleep {
static const char *const TAG = "deep_sleep";
// 5 seconds for deep sleep to ensure clean disconnect from Home Assistant
static const uint32_t TEARDOWN_TIMEOUT_DEEP_SLEEP_MS = 5000;
bool global_has_deep_sleep = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
void DeepSleepComponent::setup() {
ESP_LOGCONFIG(TAG, "Setting up Deep Sleep...");
ESP_LOGCONFIG(TAG, "Running setup");
global_has_deep_sleep = true;
const optional<uint32_t> run_duration = get_run_duration_();
if (run_duration.has_value()) {
ESP_LOGI(TAG, "Scheduling Deep Sleep to start in %" PRIu32 " ms", *run_duration);
ESP_LOGI(TAG, "Scheduling in %" PRIu32 " ms", *run_duration);
this->set_timeout(*run_duration, [this]() { this->begin_sleep(); });
} else {
ESP_LOGD(TAG, "Not scheduling Deep Sleep, as no run duration is configured.");
ESP_LOGD(TAG, "Not scheduling; no run duration configured");
}
}
void DeepSleepComponent::dump_config() {
ESP_LOGCONFIG(TAG, "Setting up Deep Sleep...");
ESP_LOGCONFIG(TAG, "Deep sleep:");
if (this->sleep_duration_.has_value()) {
uint32_t duration = *this->sleep_duration_ / 1000;
ESP_LOGCONFIG(TAG, " Sleep Duration: %" PRIu32 " ms", duration);
@ -57,11 +59,15 @@ void DeepSleepComponent::begin_sleep(bool manual) {
return;
}
ESP_LOGI(TAG, "Beginning Deep Sleep");
ESP_LOGI(TAG, "Beginning sleep");
if (this->sleep_duration_.has_value()) {
ESP_LOGI(TAG, "Sleeping for %" PRId64 "us", *this->sleep_duration_);
}
App.run_safe_shutdown_hooks();
// It's critical to teardown components cleanly for deep sleep to ensure
// Home Assistant sees a clean disconnect instead of marking the device unavailable
App.teardown_components(TEARDOWN_TIMEOUT_DEEP_SLEEP_MS);
App.run_powerdown_hooks();
this->deep_sleep_();
}

View File

@ -46,10 +46,12 @@ void DeepSleepComponent::dump_config_platform_() {
LOG_PIN(" Wakeup Pin: ", this->wakeup_pin_);
}
if (this->wakeup_cause_to_run_duration_.has_value()) {
ESP_LOGCONFIG(TAG, " Default Wakeup Run Duration: %" PRIu32 " ms",
this->wakeup_cause_to_run_duration_->default_cause);
ESP_LOGCONFIG(TAG, " Touch Wakeup Run Duration: %" PRIu32 " ms", this->wakeup_cause_to_run_duration_->touch_cause);
ESP_LOGCONFIG(TAG, " GPIO Wakeup Run Duration: %" PRIu32 " ms", this->wakeup_cause_to_run_duration_->gpio_cause);
ESP_LOGCONFIG(TAG,
" Default Wakeup Run Duration: %" PRIu32 " ms\n"
" Touch Wakeup Run Duration: %" PRIu32 " ms\n"
" GPIO Wakeup Run Duration: %" PRIu32 " ms",
this->wakeup_cause_to_run_duration_->default_cause, this->wakeup_cause_to_run_duration_->touch_cause,
this->wakeup_cause_to_run_duration_->gpio_cause);
}
}
@ -59,7 +61,7 @@ bool DeepSleepComponent::prepare_to_sleep_() {
// Defer deep sleep until inactive
if (!this->next_enter_deep_sleep_) {
this->status_set_warning();
ESP_LOGW(TAG, "Waiting wakeup pin state change to enter deep sleep...");
ESP_LOGW(TAG, "Waiting for wakeup pin state change");
}
this->next_enter_deep_sleep_ = true;
return false;

View File

@ -1,14 +1,22 @@
import esphome.codegen as cg
from esphome.components import (
alarm_control_panel,
binary_sensor,
button,
climate,
cover,
datetime,
event,
fan,
light,
lock,
number,
select,
sensor,
switch,
text,
text_sensor,
valve,
)
import esphome.config_validation as cv
from esphome.const import (
@ -20,7 +28,9 @@ from esphome.const import (
CONF_INVERTED,
CONF_MAX_VALUE,
CONF_MIN_VALUE,
CONF_MODE,
CONF_NAME,
CONF_OPTIONS,
CONF_OUTPUT_ID,
CONF_SENSORS,
CONF_STATE_CLASS,
@ -31,9 +41,11 @@ from esphome.const import (
CONF_UNIT_OF_MEASUREMENT,
DEVICE_CLASS_ENERGY,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_IDENTIFY,
DEVICE_CLASS_MOISTURE,
DEVICE_CLASS_MOTION,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_UPDATE,
ICON_BLUETOOTH,
ICON_BLUR,
ICON_THERMOMETER,
@ -45,38 +57,68 @@ from esphome.const import (
)
AUTO_LOAD = [
"alarm_control_panel",
"binary_sensor",
"button",
"climate",
"cover",
"datetime",
"event",
"fan",
"light",
"lock",
"number",
"select",
"sensor",
"switch",
"text",
"text_sensor",
"valve",
]
demo_ns = cg.esphome_ns.namespace("demo")
DemoAlarmControlPanel = demo_ns.class_(
"DemoAlarmControlPanel", alarm_control_panel.AlarmControlPanel, cg.Component
)
DemoAlarmControlPanelType = demo_ns.enum("DemoAlarmControlPanelType", is_class=True)
DemoBinarySensor = demo_ns.class_(
"DemoBinarySensor", binary_sensor.BinarySensor, cg.PollingComponent
)
DemoButton = demo_ns.class_("DemoButton", button.Button)
DemoClimate = demo_ns.class_("DemoClimate", climate.Climate, cg.Component)
DemoClimateType = demo_ns.enum("DemoClimateType", is_class=True)
DemoCover = demo_ns.class_("DemoCover", cover.Cover, cg.Component)
DemoCoverType = demo_ns.enum("DemoCoverType", is_class=True)
DemoDate = demo_ns.class_("DemoDate", datetime.DateEntity, cg.Component)
DemoDateTime = demo_ns.class_("DemoDateTime", datetime.DateTimeEntity, cg.Component)
DemoTime = demo_ns.class_("DemoTime", datetime.TimeEntity, cg.Component)
DemoEvent = demo_ns.class_("DemoEvent", event.Event, cg.Component)
DemoFan = demo_ns.class_("DemoFan", fan.Fan, cg.Component)
DemoFanType = demo_ns.enum("DemoFanType", is_class=True)
DemoLight = demo_ns.class_("DemoLight", light.LightOutput, cg.Component)
DemoLightType = demo_ns.enum("DemoLightType", is_class=True)
DemoLock = demo_ns.class_("DemoLock", lock.Lock, cg.Component)
DemoLockType = demo_ns.enum("DemoLockType", is_class=True)
DemoNumber = demo_ns.class_("DemoNumber", number.Number, cg.Component)
DemoNumberType = demo_ns.enum("DemoNumberType", is_class=True)
DemoSelect = demo_ns.class_("DemoSelect", select.Select, cg.Component)
DemoSelectType = demo_ns.enum("DemoSelectType", is_class=True)
DemoSensor = demo_ns.class_("DemoSensor", sensor.Sensor, cg.PollingComponent)
DemoSwitch = demo_ns.class_("DemoSwitch", switch.Switch, cg.Component)
DemoText = demo_ns.class_("DemoText", text.Text, cg.Component)
DemoTextType = demo_ns.enum("DemoTextType", is_class=True)
DemoTextSensor = demo_ns.class_(
"DemoTextSensor", text_sensor.TextSensor, cg.PollingComponent
)
DemoValve = demo_ns.class_("DemoValve", valve.Valve)
DemoValveType = demo_ns.enum("DemoValveType", is_class=True)
ALARM_CONTROL_PANEL_TYPES = {
1: DemoAlarmControlPanelType.TYPE_1,
2: DemoAlarmControlPanelType.TYPE_2,
3: DemoAlarmControlPanelType.TYPE_3,
}
CLIMATE_TYPES = {
1: DemoClimateType.TYPE_1,
2: DemoClimateType.TYPE_2,
@ -103,21 +145,67 @@ LIGHT_TYPES = {
6: DemoLightType.TYPE_6,
7: DemoLightType.TYPE_7,
}
LOCK_TYPES = {
1: DemoLockType.TYPE_1,
2: DemoLockType.TYPE_2,
}
NUMBER_TYPES = {
1: DemoNumberType.TYPE_1,
2: DemoNumberType.TYPE_2,
3: DemoNumberType.TYPE_3,
}
TEXT_TYPES = {
1: DemoTextType.TYPE_1,
2: DemoTextType.TYPE_2,
}
VALVE_TYPES = {
1: DemoValveType.TYPE_1,
2: DemoValveType.TYPE_2,
}
CONF_ALARM_CONTROL_PANELS = "alarm_control_panels"
CONF_BUTTONS = "buttons"
CONF_CLIMATES = "climates"
CONF_COVERS = "covers"
CONF_DATETIMES = "datetimes"
CONF_FANS = "fans"
CONF_LIGHTS = "lights"
CONF_LOCKS = "locks"
CONF_NUMBERS = "numbers"
CONF_SELECTS = "selects"
CONF_TEXTS = "texts"
CONF_VALVES = "valves"
CONFIG_SCHEMA = cv.Schema(
{
cv.Optional(
CONF_ALARM_CONTROL_PANELS,
default=[
{
CONF_NAME: "Demo Alarm Control Panel",
CONF_TYPE: 1,
},
{
CONF_NAME: "Demo Alarm Control Panel Code",
CONF_TYPE: 2,
},
{
CONF_NAME: "Demo Alarm Control Panel Code to Arm",
CONF_TYPE: 3,
},
],
): [
alarm_control_panel.alarm_control_panel_schema(
DemoAlarmControlPanel
).extend(
{
cv.Required(CONF_TYPE): cv.enum(
ALARM_CONTROL_PANEL_TYPES, int=True
),
}
)
],
cv.Optional(
CONF_BINARY_SENSORS,
default=[
@ -135,6 +223,21 @@ CONFIG_SCHEMA = cv.Schema(
cv.polling_component_schema("60s")
)
],
cv.Optional(
CONF_BUTTONS,
default=[
{
CONF_NAME: "Demo Update Button",
CONF_DEVICE_CLASS: DEVICE_CLASS_UPDATE,
},
{
CONF_NAME: "Demo Button Identify",
CONF_DEVICE_CLASS: DEVICE_CLASS_IDENTIFY,
},
],
): [
button.button_schema(DemoButton),
],
cv.Optional(
CONF_CLIMATES,
default=[
@ -191,6 +294,20 @@ CONFIG_SCHEMA = cv.Schema(
}
)
],
cv.Optional(
CONF_DATETIMES,
default=[
{CONF_NAME: "Demo DateTime", CONF_TYPE: "DATETIME"},
{CONF_NAME: "Demo Date", CONF_TYPE: "DATE"},
{CONF_NAME: "Demo Time", CONF_TYPE: "TIME"},
],
): [
cv.Any(
datetime.date_schema(DemoDate),
datetime.datetime_schema(DemoDateTime),
datetime.time_schema(DemoTime),
)
],
cv.Optional(
CONF_FANS,
default=[
@ -262,6 +379,19 @@ CONFIG_SCHEMA = cv.Schema(
}
)
],
cv.Optional(
CONF_LOCKS,
default=[
{CONF_NAME: "Demo Lock", CONF_TYPE: 1},
{CONF_NAME: "Demo Lock and Open", CONF_TYPE: 2},
],
): [
lock.lock_schema(DemoLock).extend(
{
cv.Required(CONF_TYPE): cv.enum(LOCK_TYPES, int=True),
}
)
],
cv.Optional(
CONF_NUMBERS,
default=[
@ -299,6 +429,25 @@ CONFIG_SCHEMA = cv.Schema(
}
)
],
cv.Optional(
CONF_SELECTS,
default=[
{
CONF_NAME: "Demo Select 1",
CONF_OPTIONS: ["Option 1", "Option 2", "Option 3"],
},
{
CONF_NAME: "Demo Select 2",
CONF_OPTIONS: ["Option A", "Option B", "Option C"],
},
],
): [
select.select_schema(DemoSelect).extend(
{
cv.Required(CONF_OPTIONS): cv.ensure_list(cv.string_strict),
}
)
],
cv.Optional(
CONF_SENSORS,
default=[
@ -355,6 +504,19 @@ CONFIG_SCHEMA = cv.Schema(
},
],
): [switch.switch_schema(DemoSwitch).extend(cv.COMPONENT_SCHEMA)],
cv.Optional(
CONF_TEXTS,
default=[
{CONF_NAME: "Demo Text 1", CONF_MODE: "TEXT", CONF_TYPE: 1},
{CONF_NAME: "Demo Text 2", CONF_MODE: "PASSWORD", CONF_TYPE: 2},
],
): [
text.text_schema(DemoText).extend(
{
cv.Required(CONF_TYPE): cv.enum(TEXT_TYPES, int=True),
}
)
],
cv.Optional(
CONF_TEXT_SENSORS,
default=[
@ -371,15 +533,36 @@ CONFIG_SCHEMA = cv.Schema(
cv.polling_component_schema("60s")
)
],
cv.Optional(
CONF_VALVES,
default=[
{CONF_NAME: "Demo Valve 1", CONF_TYPE: 1},
{CONF_NAME: "Demo Valve 2", CONF_TYPE: 2},
],
): [
valve.valve_schema(DemoValve).extend(
{
cv.Required(CONF_TYPE): cv.enum(VALVE_TYPES, int=True),
}
)
],
}
)
async def to_code(config):
for conf in config[CONF_ALARM_CONTROL_PANELS]:
var = await alarm_control_panel.new_alarm_control_panel(conf)
cg.add(var.set_type(conf[CONF_TYPE]))
await cg.register_component(var, conf)
for conf in config[CONF_BINARY_SENSORS]:
var = await binary_sensor.new_binary_sensor(conf)
await cg.register_component(var, conf)
for conf in config[CONF_BUTTONS]:
await button.new_button(conf)
for conf in config[CONF_CLIMATES]:
var = await climate.new_climate(conf)
await cg.register_component(var, conf)
@ -390,6 +573,10 @@ async def to_code(config):
await cg.register_component(var, conf)
cg.add(var.set_type(conf[CONF_TYPE]))
for conf in config[CONF_DATETIMES]:
var = await datetime.new_datetime(conf)
await cg.register_component(var, conf)
for conf in config[CONF_FANS]:
var = await fan.new_fan(conf)
await cg.register_component(var, conf)
@ -400,6 +587,11 @@ async def to_code(config):
await cg.register_component(var, conf)
cg.add(var.set_type(conf[CONF_TYPE]))
for conf in config[CONF_LOCKS]:
var = await lock.new_lock(conf)
if conf[CONF_TYPE] == 2:
cg.add(var.traits.set_supports_open(True))
for conf in config[CONF_NUMBERS]:
var = await number.new_number(
conf,
@ -410,6 +602,10 @@ async def to_code(config):
await cg.register_component(var, conf)
cg.add(var.set_type(conf[CONF_TYPE]))
for conf in config[CONF_SELECTS]:
var = await select.new_select(conf, options=conf[CONF_OPTIONS])
await cg.register_component(var, conf)
for conf in config[CONF_SENSORS]:
var = await sensor.new_sensor(conf)
await cg.register_component(var, conf)
@ -418,6 +614,16 @@ async def to_code(config):
var = await switch.new_switch(conf)
await cg.register_component(var, conf)
for conf in config[CONF_TEXTS]:
var = await text.new_text(conf)
await cg.register_component(var, conf)
if conf[CONF_TYPE] == 2:
cg.add(var.traits.set_mode(text.TextMode.TEXT_MODE_PASSWORD))
for conf in config[CONF_TEXT_SENSORS]:
var = await text_sensor.new_text_sensor(conf)
await cg.register_component(var, conf)
for conf in config[CONF_VALVES]:
var = await valve.new_valve(conf)
cg.add(var.set_type(conf[CONF_TYPE]))

View File

@ -0,0 +1,65 @@
#pragma once
#include "esphome/components/alarm_control_panel/alarm_control_panel.h"
#include "esphome/core/component.h"
namespace esphome {
namespace demo {
using namespace alarm_control_panel;
enum class DemoAlarmControlPanelType {
TYPE_1,
TYPE_2,
TYPE_3,
};
class DemoAlarmControlPanel : public AlarmControlPanel, public Component {
public:
void setup() override {}
uint32_t get_supported_features() const override { return ACP_FEAT_ARM_AWAY | ACP_FEAT_TRIGGER; }
bool get_requires_code() const override { return this->type_ != DemoAlarmControlPanelType::TYPE_1; }
bool get_requires_code_to_arm() const override { return this->type_ == DemoAlarmControlPanelType::TYPE_3; }
void set_type(DemoAlarmControlPanelType type) { this->type_ = type; }
protected:
void control(const AlarmControlPanelCall &call) override {
auto state = call.get_state().value_or(ACP_STATE_DISARMED);
switch (state) {
case ACP_STATE_ARMED_AWAY:
if (this->get_requires_code_to_arm() && call.get_code().has_value()) {
if (call.get_code().value() != "1234") {
this->status_momentary_error("Invalid code", 5000);
return;
}
}
this->publish_state(ACP_STATE_ARMED_AWAY);
break;
case ACP_STATE_DISARMED:
if (this->get_requires_code() && call.get_code().has_value()) {
if (call.get_code().value() != "1234") {
this->status_momentary_error("Invalid code", 5000);
return;
}
}
this->publish_state(ACP_STATE_DISARMED);
return;
case ACP_STATE_TRIGGERED:
this->publish_state(ACP_STATE_TRIGGERED);
return;
case ACP_STATE_PENDING:
this->publish_state(ACP_STATE_PENDING);
return;
default:
break;
}
}
DemoAlarmControlPanelType type_{};
};
} // namespace demo
} // namespace esphome

View File

@ -0,0 +1,15 @@
#pragma once
#include "esphome/components/button/button.h"
#include "esphome/core/log.h"
namespace esphome {
namespace demo {
class DemoButton : public button::Button {
protected:
void press_action() override {}
};
} // namespace demo
} // namespace esphome

View File

@ -0,0 +1,34 @@
#pragma once
#include "esphome/core/defines.h"
#ifdef USE_DATETIME_DATE
#include "esphome/components/datetime/date_entity.h"
#include "esphome/core/component.h"
namespace esphome {
namespace demo {
class DemoDate : public datetime::DateEntity, public Component {
public:
void setup() override {
this->year_ = 2038;
this->month_ = 01;
this->day_ = 19;
this->publish_state();
}
protected:
void control(const datetime::DateCall &call) override {
this->year_ = call.get_year().value_or(this->year_);
this->month_ = call.get_month().value_or(this->month_);
this->day_ = call.get_day().value_or(this->day_);
this->publish_state();
}
};
} // namespace demo
} // namespace esphome
#endif

View File

@ -0,0 +1,40 @@
#pragma once
#include "esphome/core/defines.h"
#ifdef USE_DATETIME_DATETIME
#include "esphome/components/datetime/datetime_entity.h"
#include "esphome/core/component.h"
namespace esphome {
namespace demo {
class DemoDateTime : public datetime::DateTimeEntity, public Component {
public:
void setup() override {
this->year_ = 2038;
this->month_ = 01;
this->day_ = 19;
this->hour_ = 3;
this->minute_ = 14;
this->second_ = 8;
this->publish_state();
}
protected:
void control(const datetime::DateTimeCall &call) override {
this->year_ = call.get_year().value_or(this->year_);
this->month_ = call.get_month().value_or(this->month_);
this->day_ = call.get_day().value_or(this->day_);
this->hour_ = call.get_hour().value_or(this->hour_);
this->minute_ = call.get_minute().value_or(this->minute_);
this->second_ = call.get_second().value_or(this->second_);
this->publish_state();
}
};
} // namespace demo
} // namespace esphome
#endif

View File

@ -0,0 +1,17 @@
#pragma once
#include "esphome/components/lock/lock.h"
namespace esphome {
namespace demo {
class DemoLock : public lock::Lock {
protected:
void control(const lock::LockCall &call) override {
auto state = *call.get_state();
this->publish_state(state);
}
};
} // namespace demo
} // namespace esphome

View File

@ -0,0 +1,15 @@
#pragma once
#include "esphome/components/select/select.h"
#include "esphome/core/component.h"
namespace esphome {
namespace demo {
class DemoSelect : public select::Select, public Component {
protected:
void control(const std::string &value) override { this->publish_state(value); }
};
} // namespace demo
} // namespace esphome

View File

@ -0,0 +1,18 @@
#pragma once
#include "esphome/components/text/text.h"
#include "esphome/core/component.h"
namespace esphome {
namespace demo {
class DemoText : public text::Text, public Component {
public:
void setup() override { this->publish_state("I am a text entity"); }
protected:
void control(const std::string &value) override { this->publish_state(value); }
};
} // namespace demo
} // namespace esphome

View File

@ -0,0 +1,34 @@
#pragma once
#include "esphome/core/defines.h"
#ifdef USE_DATETIME_TIME
#include "esphome/components/datetime/time_entity.h"
#include "esphome/core/component.h"
namespace esphome {
namespace demo {
class DemoTime : public datetime::TimeEntity, public Component {
public:
void setup() override {
this->hour_ = 3;
this->minute_ = 14;
this->second_ = 8;
this->publish_state();
}
protected:
void control(const datetime::TimeCall &call) override {
this->hour_ = call.get_hour().value_or(this->hour_);
this->minute_ = call.get_minute().value_or(this->minute_);
this->second_ = call.get_second().value_or(this->second_);
this->publish_state();
}
};
} // namespace demo
} // namespace esphome
#endif

View File

@ -0,0 +1,54 @@
#pragma once
#include "esphome/components/valve/valve.h"
namespace esphome {
namespace demo {
enum class DemoValveType {
TYPE_1,
TYPE_2,
};
class DemoValve : public valve::Valve {
public:
valve::ValveTraits get_traits() override {
valve::ValveTraits traits;
if (this->type_ == DemoValveType::TYPE_2) {
traits.set_supports_position(true);
traits.set_supports_toggle(true);
traits.set_supports_stop(true);
}
return traits;
}
void set_type(DemoValveType type) { this->type_ = type; }
protected:
void control(const valve::ValveCall &call) override {
if (call.get_position().has_value()) {
this->position = *call.get_position();
this->publish_state();
return;
} else if (call.get_toggle().has_value()) {
if (call.get_toggle().value()) {
if (this->position == valve::VALVE_OPEN) {
this->position = valve::VALVE_CLOSED;
this->publish_state();
} else {
this->position = valve::VALVE_OPEN;
this->publish_state();
}
}
return;
} else if (call.get_stop()) {
this->current_operation = valve::VALVE_OPERATION_IDLE;
this->publish_state(); // Keep the current position
return;
}
}
DemoValveType type_{};
};
} // namespace demo
} // namespace esphome

View File

@ -21,7 +21,7 @@ uint8_t Command::execute(DfrobotSen0395Component *parent) {
if (this->retries_left_ > 0) {
this->retries_left_ -= 1;
this->cmd_sent_ = false;
ESP_LOGD(TAG, "Retrying...");
ESP_LOGD(TAG, "Retrying");
return 0;
} else {
this->parent_->find_prompt_();
@ -33,7 +33,7 @@ uint8_t Command::execute(DfrobotSen0395Component *parent) {
if (this->retries_left_ > 0) {
this->retries_left_ -= 1;
this->cmd_sent_ = false;
ESP_LOGD(TAG, "Retrying...");
ESP_LOGD(TAG, "Retrying");
return 0;
} else {
this->parent_->find_prompt_();
@ -51,7 +51,7 @@ uint8_t Command::execute(DfrobotSen0395Component *parent) {
if (this->retries_left_ > 0) {
this->retries_left_ -= 1;
this->cmd_sent_ = false;
ESP_LOGD(TAG, "Retrying...");
ESP_LOGD(TAG, "Retrying");
} else {
return 1; // Command done
}

View File

@ -1,6 +1,6 @@
#include "dht.h"
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace dht {
@ -8,25 +8,19 @@ namespace dht {
static const char *const TAG = "dht";
void DHT::setup() {
ESP_LOGCONFIG(TAG, "Setting up DHT...");
ESP_LOGCONFIG(TAG, "Running setup");
this->pin_->digital_write(true);
this->pin_->setup();
this->pin_->digital_write(true);
}
void DHT::dump_config() {
ESP_LOGCONFIG(TAG, "DHT:");
LOG_PIN(" Pin: ", this->pin_);
if (this->is_auto_detect_) {
ESP_LOGCONFIG(TAG, " Auto-detected model: %s", this->model_ == DHT_MODEL_DHT11 ? "DHT11" : "DHT22");
} else if (this->model_ == DHT_MODEL_DHT11) {
ESP_LOGCONFIG(TAG, " Model: DHT11");
} else {
ESP_LOGCONFIG(TAG, " Model: DHT22 (or equivalent)");
}
ESP_LOGCONFIG(TAG, " Internal Pull-up: %s", ONOFF(this->pin_->get_flags() & gpio::FLAG_PULLUP));
ESP_LOGCONFIG(TAG, " %sModel: %s", this->is_auto_detect_ ? "Auto-detected " : "",
this->model_ == DHT_MODEL_DHT11 ? "DHT11" : "DHT22 or equivalent");
ESP_LOGCONFIG(TAG, " Internal pull-up: %s", ONOFF(this->pin_->get_flags() & gpio::FLAG_PULLUP));
LOG_UPDATE_INTERVAL(this);
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
}
@ -46,7 +40,7 @@ void DHT::update() {
}
if (success) {
ESP_LOGD(TAG, "Got Temperature=%.1f°C Humidity=%.1f%%", temperature, humidity);
ESP_LOGD(TAG, "Temperature %.1f°C Humidity %.1f%%", temperature, humidity);
if (this->temperature_sensor_ != nullptr)
this->temperature_sensor_->publish_state(temperature);
@ -54,11 +48,8 @@ void DHT::update() {
this->humidity_sensor_->publish_state(humidity);
this->status_clear_warning();
} else {
const char *str = "";
if (this->is_auto_detect_) {
str = " and consider manually specifying the DHT model using the model option";
}
ESP_LOGW(TAG, "Invalid readings! Please check your wiring (pull-up resistor, pin number)%s.", str);
ESP_LOGW(TAG, "Invalid readings! Check pin number and pull-up resistor%s.",
this->is_auto_detect_ ? " and try manually specifying the model" : "");
if (this->temperature_sensor_ != nullptr)
this->temperature_sensor_->publish_state(NAN);
if (this->humidity_sensor_ != nullptr)
@ -68,10 +59,12 @@ void DHT::update() {
}
float DHT::get_setup_priority() const { return setup_priority::DATA; }
void DHT::set_dht_model(DHTModel model) {
this->model_ = model;
this->is_auto_detect_ = model == DHT_MODEL_AUTO_DETECT;
}
bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool report_errors) {
*humidity = NAN;
*temperature = NAN;
@ -121,9 +114,9 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r
while (!this->pin_->digital_read()) {
if (micros() - start_time > 90) {
if (i < 0) {
error_code = 1;
error_code = 1; // line didn't clear
} else {
error_code = 2;
error_code = 2; // rising edge for bit i timeout
}
break;
}
@ -139,9 +132,9 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r
end_time = micros();
if (end_time - start_time > 90) {
if (i < 0) {
error_code = 3;
error_code = 3; // requesting data failed
} else {
error_code = 4;
error_code = 4; // falling edge for bit i timeout
}
break;
}
@ -166,22 +159,9 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r
if (!report_errors && error_code != 0)
return false;
switch (error_code) {
case 1:
ESP_LOGW(TAG, "Waiting for DHT communication to clear failed!");
return false;
case 2:
ESP_LOGW(TAG, "Rising edge for bit %d failed!", i);
return false;
case 3:
ESP_LOGW(TAG, "Requesting data from DHT failed!");
return false;
case 4:
ESP_LOGW(TAG, "Falling edge for bit %d failed!", i);
return false;
case 0:
default:
break;
if (error_code) {
ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL);
return false;
}
ESP_LOGVV(TAG,
@ -206,15 +186,15 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r
if (checksum_a == data[4]) {
// Data format: 8bit integral RH data + 8bit decimal RH data + 8bit integral T data + 8bit decimal T data + 8bit
// check sum - some models always have 0 in the decimal part
const uint16_t raw_temperature = uint16_t(data[2]) * 10 + (data[3] & 0x7F);
*temperature = raw_temperature / 10.0f;
const uint16_t raw_temperature = static_cast<uint16_t>(data[2]) * 10 + (data[3] & 0x7F);
*temperature = static_cast<float>(raw_temperature) / 10.0f;
if ((data[3] & 0x80) != 0) {
// negative
*temperature *= -1;
}
const uint16_t raw_humidity = uint16_t(data[0]) * 10 + data[1];
*humidity = raw_humidity / 10.0f;
const uint16_t raw_humidity = static_cast<uint16_t>(data[0]) * 10 + data[1];
*humidity = static_cast<float>(raw_humidity) / 10.0f;
} else {
// For compatibility with DHT11 models which might only use 2 bytes checksums, only use the data from these two
// bytes
@ -222,8 +202,8 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r
*humidity = data[0];
}
} else {
uint16_t raw_humidity = (uint16_t(data[0] & 0xFF) << 8) | (data[1] & 0xFF);
uint16_t raw_temperature = (uint16_t(data[2] & 0xFF) << 8) | (data[3] & 0xFF);
uint16_t raw_humidity = encode_uint16(data[0], data[1]);
uint16_t raw_temperature = encode_uint16(data[2], data[3]);
if (raw_temperature & 0x8000) {
if (!(raw_temperature & 0x4000))
@ -234,24 +214,23 @@ bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool r
if (raw_temperature == 1 && raw_humidity == 10) {
if (report_errors) {
ESP_LOGW(TAG, "Invalid temperature+humidity! Sensor reported 1°C and 1%% Hum");
ESP_LOGW(TAG, "Invalid data");
}
return false;
}
*humidity = raw_humidity * 0.1f;
if (*humidity > 100)
*humidity = static_cast<float>(raw_humidity) * 0.1f;
if (*humidity > 100.0f)
*humidity = NAN;
*temperature = int16_t(raw_temperature) * 0.1f;
*temperature = static_cast<int16_t>(raw_temperature) * 0.1f;
}
if (*temperature == 0.0f && (*humidity == 1.0f || *humidity == 2.0f)) {
if (report_errors) {
ESP_LOGW(TAG, "DHT reports invalid data. Is the update interval too high or the sensor damaged?");
ESP_LOGW(TAG, "Invalid data");
}
return false;
}
return true;
}

View File

@ -34,7 +34,7 @@ void DHT12Component::update() {
this->status_clear_warning();
}
void DHT12Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up DHT12...");
ESP_LOGCONFIG(TAG, "Running setup");
uint8_t data[5];
if (!this->read_data_(data)) {
this->mark_failed();
@ -45,7 +45,7 @@ void DHT12Component::dump_config() {
ESP_LOGD(TAG, "DHT12:");
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, "Communication with DHT12 failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
}
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);

View File

@ -182,9 +182,11 @@ using display_writer_t = std::function<void(Display &)>;
#define LOG_DISPLAY(prefix, type, obj) \
if ((obj) != nullptr) { \
ESP_LOGCONFIG(TAG, prefix type); \
ESP_LOGCONFIG(TAG, "%s Rotations: %d °", prefix, (obj)->rotation_); \
ESP_LOGCONFIG(TAG, "%s Dimensions: %dpx x %dpx", prefix, (obj)->get_width(), (obj)->get_height()); \
ESP_LOGCONFIG(TAG, \
prefix type "\n" \
"%s Rotations: %d °\n" \
"%s Dimensions: %dpx x %dpx", \
prefix, (obj)->rotation_, prefix, (obj)->get_width(), (obj)->get_height()); \
}
/// Turn the pixel OFF.

View File

@ -12,7 +12,7 @@ void DPS310Component::setup() {
auto timer = DPS310_INIT_TIMEOUT;
uint8_t reg = 0;
ESP_LOGCONFIG(TAG, "Setting up DPS310...");
ESP_LOGCONFIG(TAG, "Running setup");
// first, reset the sensor
if (!this->write_byte(DPS310_REG_RESET, DPS310_CMD_RESET)) {
this->mark_failed();
@ -86,12 +86,14 @@ void DPS310Component::setup() {
}
void DPS310Component::dump_config() {
ESP_LOGCONFIG(TAG, "DPS310:");
ESP_LOGCONFIG(TAG, " Product ID: %u", this->prod_rev_id_ & 0x0F);
ESP_LOGCONFIG(TAG, " Revision ID: %u", (this->prod_rev_id_ >> 4) & 0x0F);
ESP_LOGCONFIG(TAG,
"DPS310:\n"
" Product ID: %u\n"
" Revision ID: %u",
this->prod_rev_id_ & 0x0F, (this->prod_rev_id_ >> 4) & 0x0F);
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, "Communication with DPS310 failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
}
LOG_UPDATE_INTERVAL(this);
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);

View File

@ -10,7 +10,7 @@ namespace ds1307 {
static const char *const TAG = "ds1307";
void DS1307Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up DS1307...");
ESP_LOGCONFIG(TAG, "Running setup");
if (!this->read_rtc_()) {
this->mark_failed();
}
@ -22,7 +22,7 @@ void DS1307Component::dump_config() {
ESP_LOGCONFIG(TAG, "DS1307:");
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, "Communication with DS1307 failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
}
ESP_LOGCONFIG(TAG, " Timezone: '%s'", this->timezone_.c_str());
}

View File

@ -278,9 +278,11 @@ bool Dsmr::parse_telegram() {
}
void Dsmr::dump_config() {
ESP_LOGCONFIG(TAG, "DSMR:");
ESP_LOGCONFIG(TAG, " Max telegram length: %d", this->max_telegram_len_);
ESP_LOGCONFIG(TAG, " Receive timeout: %.1fs", this->receive_timeout_ / 1e3f);
ESP_LOGCONFIG(TAG,
"DSMR:\n"
" Max telegram length: %d\n"
" Receive timeout: %.1fs",
this->max_telegram_len_, this->receive_timeout_ / 1e3f);
if (this->request_pin_ != nullptr) {
LOG_PIN(" Request Pin: ", this->request_pin_);
}

View File

@ -1,6 +1,6 @@
#include "duty_cycle_sensor.h"
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace duty_cycle {
@ -8,7 +8,7 @@ namespace duty_cycle {
static const char *const TAG = "duty_cycle";
void DutyCycleSensor::setup() {
ESP_LOGCONFIG(TAG, "Setting up Duty Cycle Sensor '%s'...", this->get_name().c_str());
ESP_LOGCONFIG(TAG, "Running setup for '%s'", this->get_name().c_str());
this->pin_->setup();
this->store_.pin = this->pin_->to_isr();
this->store_.last_level = this->pin_->digital_read();

View File

@ -94,9 +94,11 @@ void DutyTimeSensor::publish_and_save_(const uint32_t sec, const uint32_t ms) {
}
void DutyTimeSensor::dump_config() {
ESP_LOGCONFIG(TAG, "Duty Time:");
ESP_LOGCONFIG(TAG, " Update Interval: %" PRId32 "ms", this->get_update_interval());
ESP_LOGCONFIG(TAG, " Restore: %s", ONOFF(this->restore_));
ESP_LOGCONFIG(TAG,
"Duty Time:\n"
" Update Interval: %" PRId32 "ms\n"
" Restore: %s",
this->get_update_interval(), ONOFF(this->restore_));
LOG_SENSOR(" ", "Duty Time Sensor:", this);
LOG_SENSOR(" ", "Last Duty Time Sensor:", this->last_duty_time_sensor_);
}

View File

@ -1,6 +1,6 @@
#include "ee895.h"
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome {
namespace ee895 {
@ -16,7 +16,7 @@ static const uint16_t PRESSURE_ADDRESS = 0x04B0;
void EE895Component::setup() {
uint16_t crc16_check = 0;
ESP_LOGCONFIG(TAG, "Setting up EE895...");
ESP_LOGCONFIG(TAG, "Running setup");
write_command_(SERIAL_NUMBER, 8);
uint8_t serial_number[20];
this->read(serial_number, 20);
@ -35,7 +35,7 @@ void EE895Component::dump_config() {
LOG_I2C_DEVICE(this);
switch (this->error_code_) {
case COMMUNICATION_FAILED:
ESP_LOGE(TAG, "Communication with EE895 failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
break;
case CRC_CHECK_FAILED:
ESP_LOGE(TAG, "The crc check failed");

View File

@ -16,7 +16,7 @@ static const uint8_t GET_Y_RES[4] = {0x53, 0x63, 0x00, 0x00};
static const uint8_t GET_POWER_STATE_CMD[4] = {0x53, 0x50, 0x00, 0x01};
void EKTF2232Touchscreen::setup() {
ESP_LOGCONFIG(TAG, "Setting up EKT2232 Touchscreen...");
ESP_LOGCONFIG(TAG, "Running setup");
this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT | gpio::FLAG_PULLUP);
this->interrupt_pin_->setup();

View File

@ -57,7 +57,7 @@ static const uint8_t EMC2101_POLARITY_BIT = 1 << 4;
float Emc2101Component::get_setup_priority() const { return setup_priority::HARDWARE; }
void Emc2101Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up Emc2101 sensor...");
ESP_LOGCONFIG(TAG, "Running setup");
// make sure we're talking to the right chip
uint8_t chip_id = reg(EMC2101_REGISTER_WHOAMI).get();
@ -94,14 +94,16 @@ void Emc2101Component::dump_config() {
ESP_LOGCONFIG(TAG, "Emc2101 component:");
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, "Communication with EMC2101 failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
}
ESP_LOGCONFIG(TAG, " Mode: %s", this->dac_mode_ ? "DAC" : "PWM");
if (this->dac_mode_) {
ESP_LOGCONFIG(TAG, " DAC Conversion Rate: %X", this->dac_conversion_rate_);
} else {
ESP_LOGCONFIG(TAG, " PWM Resolution: %02X", this->pwm_resolution_);
ESP_LOGCONFIG(TAG, " PWM Divider: %02X", this->pwm_divider_);
ESP_LOGCONFIG(TAG,
" PWM Resolution: %02X\n"
" PWM Divider: %02X",
this->pwm_resolution_, this->pwm_divider_);
}
ESP_LOGCONFIG(TAG, " Inverted: %s", YESNO(this->inverted_));
}
@ -110,7 +112,7 @@ void Emc2101Component::set_duty_cycle(float value) {
uint8_t duty_cycle = remap(value, 0.0f, 1.0f, (uint8_t) 0, this->max_output_value_);
ESP_LOGD(TAG, "Setting duty fan setting to %02X", duty_cycle);
if (!this->write_byte(EMC2101_REGISTER_FAN_SETTING, duty_cycle)) {
ESP_LOGE(TAG, "Communication with EMC2101 failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
this->status_set_warning();
return;
}
@ -119,7 +121,7 @@ void Emc2101Component::set_duty_cycle(float value) {
float Emc2101Component::get_duty_cycle() {
uint8_t duty_cycle;
if (!this->read_byte(EMC2101_REGISTER_FAN_SETTING, &duty_cycle)) {
ESP_LOGE(TAG, "Communication with EMC2101 failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
this->status_set_warning();
return NAN;
}
@ -129,7 +131,7 @@ float Emc2101Component::get_duty_cycle() {
float Emc2101Component::get_internal_temperature() {
uint8_t temperature;
if (!this->read_byte(EMC2101_REGISTER_INTERNAL_TEMP, &temperature)) {
ESP_LOGE(TAG, "Communication with EMC2101 failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
this->status_set_warning();
return NAN;
}
@ -141,7 +143,7 @@ float Emc2101Component::get_external_temperature() {
uint8_t lsb, msb;
if (!this->read_byte(EMC2101_REGISTER_EXTERNAL_TEMP_MSB, &msb) ||
!this->read_byte(EMC2101_REGISTER_EXTERNAL_TEMP_LSB, &lsb)) {
ESP_LOGE(TAG, "Communication with EMC2101 failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
this->status_set_warning();
return NAN;
}
@ -155,7 +157,7 @@ float Emc2101Component::get_speed() {
// Read **LSB** first to match 'Data Read Interlock' behavior from 6.1 of datasheet
uint8_t lsb, msb;
if (!this->read_byte(EMC2101_REGISTER_TACH_LSB, &lsb) || !this->read_byte(EMC2101_REGISTER_TACH_MSB, &msb)) {
ESP_LOGE(TAG, "Communication with EMC2101 failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
this->status_set_warning();
return NAN;
}

View File

@ -49,7 +49,7 @@ static const uint8_t ENS160_DATA_STATUS_NEWGPR = 0x01;
static const uint8_t ENS160_DATA_AQI = 0x07;
void ENS160Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up ENS160...");
ESP_LOGCONFIG(TAG, "Running setup");
// check part_id
uint16_t part_id;
@ -279,7 +279,7 @@ void ENS160Component::dump_config() {
switch (this->error_code_) {
case COMMUNICATION_FAILED:
ESP_LOGE(TAG, "Communication failed! Is the sensor connected?");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
break;
case READ_FAILED:
ESP_LOGE(TAG, "Error reading from register");

View File

@ -87,7 +87,7 @@ static uint32_t crc7(uint32_t value) {
}
void ENS210Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up ENS210...");
ESP_LOGCONFIG(TAG, "Running setup");
uint8_t data[2];
uint16_t part_id = 0;
// Reset
@ -163,7 +163,7 @@ void ENS210Component::update() {
// Read T_VAL and H_VAL
if (!this->read_bytes(ENS210_REGISTER_T_VAL, data, 6)) {
ESP_LOGE(TAG, "Communication with ENS210 failed!");
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
this->status_set_warning();
return;
}

View File

@ -25,9 +25,11 @@ static const size_t MCLK_DIV_FRE = 256;
}
void ES7210::dump_config() {
ESP_LOGCONFIG(TAG, "ES7210 audio ADC:");
ESP_LOGCONFIG(TAG, " Bits Per Sample: %" PRIu8, this->bits_per_sample_);
ESP_LOGCONFIG(TAG, " Sample Rate: %" PRIu32, this->sample_rate_);
ESP_LOGCONFIG(TAG,
"ES7210 audio ADC:\n"
" Bits Per Sample: %" PRIu8 "\n"
" Sample Rate: %" PRIu32,
this->bits_per_sample_, this->sample_rate_);
if (this->is_failed()) {
ESP_LOGE(TAG, " Failed to initialize");
@ -36,7 +38,7 @@ void ES7210::dump_config() {
}
void ES7210::setup() {
ESP_LOGCONFIG(TAG, "Setting up ES7210...");
ESP_LOGCONFIG(TAG, "Running setup");
// Software reset
ES7210_ERROR_FAILED(this->write_byte(ES7210_RESET_REG00, 0xff));

View File

@ -34,7 +34,7 @@ void ES7243E::dump_config() {
}
void ES7243E::setup() {
ESP_LOGCONFIG(TAG, "Setting up ES7243E...");
ESP_LOGCONFIG(TAG, "Running setup");
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG01, 0x3A));
ES7243E_ERROR_FAILED(this->write_byte(ES7243E_RESET_REG00, 0x80));

View File

@ -17,7 +17,7 @@ static const char *const TAG = "es8156";
}
void ES8156::setup() {
ESP_LOGCONFIG(TAG, "Setting up ES8156...");
ESP_LOGCONFIG(TAG, "Running setup");
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG02_SCLK_MODE, 0x04));
ES8156_ERROR_FAILED(this->write_byte(ES8156_REG20_ANALOG_SYS1, 0x2A));

View File

@ -22,7 +22,7 @@ static const char *const TAG = "es8311";
}
void ES8311::setup() {
ESP_LOGCONFIG(TAG, "Setting up ES8311...");
ESP_LOGCONFIG(TAG, "Running setup");
// Reset
ES8311_ERROR_FAILED(this->write_byte(ES8311_REG00_RESET, 0x1F));
@ -52,11 +52,13 @@ void ES8311::setup() {
}
void ES8311::dump_config() {
ESP_LOGCONFIG(TAG, "ES8311 Audio Codec:");
ESP_LOGCONFIG(TAG, " Use MCLK: %s", YESNO(this->use_mclk_));
ESP_LOGCONFIG(TAG, " Use Microphone: %s", YESNO(this->use_mic_));
ESP_LOGCONFIG(TAG, " DAC Bits per Sample: %" PRIu8, this->resolution_out_);
ESP_LOGCONFIG(TAG, " Sample Rate: %" PRIu32, this->sample_frequency_);
ESP_LOGCONFIG(TAG,
"ES8311 Audio Codec:\n"
" Use MCLK: %s\n"
" Use Microphone: %s\n"
" DAC Bits per Sample: %" PRIu8 "\n"
" Sample Rate: %" PRIu32,
YESNO(this->use_mclk_), YESNO(this->use_mic_), this->resolution_out_, this->sample_frequency_);
if (this->is_failed()) {
ESP_LOGCONFIG(TAG, " Failed to initialize!");

View File

View File

@ -0,0 +1,26 @@
import esphome.codegen as cg
from esphome.components import i2c
from esphome.components.audio_dac import AudioDac
import esphome.config_validation as cv
from esphome.const import CONF_ID
CODEOWNERS = ["@P4uLT"]
CONF_ES8388_ID = "es8388_id"
es8388_ns = cg.esphome_ns.namespace("es8388")
ES8388 = es8388_ns.class_("ES8388", AudioDac, cg.Component, i2c.I2CDevice)
DEPENDENCIES = ["i2c"]
CONFIG_SCHEMA = (
cv.Schema({cv.GenerateID(): cv.declare_id(ES8388)})
.extend(cv.COMPONENT_SCHEMA)
.extend(i2c.i2c_device_schema(0x10))
)
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)

View File

@ -0,0 +1,289 @@
#include "es8388.h"
#include <cinttypes>
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
namespace esphome {
namespace es8388 {
static const char *const TAG = "es8388";
// Mark the component as failed; use only in setup
#define ES8388_ERROR_FAILED(func) \
if (!(func)) { \
this->mark_failed(); \
return; \
}
// Return false; use outside of setup
#define ES8388_ERROR_CHECK(func) \
if (!(func)) { \
return false; \
}
void ES8388::setup() {
ESP_LOGCONFIG(TAG, "Running setup");
// mute DAC
this->set_mute_state_(true);
// I2S worker mode
ES8388_ERROR_FAILED(this->write_byte(ES8388_MASTERMODE, 0x00));
/* Chip Control and Power Management */
ES8388_ERROR_FAILED(this->write_byte(ES8388_CONTROL2, 0x50));
// normal all and power up all
ES8388_ERROR_FAILED(this->write_byte(ES8388_CHIPPOWER, 0x00));
// vmidsel/500k
// EnRef=0,Play&Record Mode,(0x17-both of mic&play)
ES8388_ERROR_FAILED(this->write_byte(ES8388_CONTROL1, 0x12));
// i2s 16 bits
ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL1, 0x18));
// sample freq 256
// DACFsMode,SINGLE SPEED; DACFsRatio,256
ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL2, 0x02));
// 0x00 audio on LIN1&RIN1, 0x09 LIN2&RIN2
ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL16, 0x00));
// only left DAC to left mixer enable 0db
ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL17, 0x90));
// only right DAC to right mixer enable 0db
ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL20, 0x90));
// set internal ADC and DAC use the same LRCK clock, ADC LRCK as internal LRCK
ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL21, 0x80));
// vroi=0 - 1.5k VREF to analog output resistance (default)
ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL23, 0x00));
// power down adc and line in
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCPOWER, 0xFF));
//@nightdav
ES8388_ERROR_FAILED(
this->write_byte(ES8388_ADCCONTROL1, 0x00)); // +21dB : recommended value for ALC & voice recording
// set to Mono Right
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL3, 0x02));
// I2S 16 Bits length and I2S serial audio data format
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL4, 0x0d));
// ADCFsMode,singel SPEED,RATIO=256
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL5, 0x02));
// ADC Volume
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL8, 0x00));
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL9, 0x00));
//@nightDav
// ALC Config (as recommended by ES8388 user guide for voice recording)
// Reg 0x12 = 0xe2 (ALC enable, PGA Max. Gain=23.5dB, Min. Gain=0dB)
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL10, 0xe2));
// Reg 0x13 = 0xa0 (ALC Target=-1.5dB, ALC Hold time =0 mS)
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL11, 0xa0));
// Reg 0x14 = 0x12(Decay time =820uS , Attack time = 416 uS)
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL12, 0x12));
// Reg 0x15 = 0x06(ALC mode)
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL13, 0x06));
// Reg 0x16 = 0xc3(nose gate = -40.5dB, NGG = 0x01(mute ADC))
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL14, 0xc3));
// Power on ADC
ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL21, 0x80));
// Start state machine
ES8388_ERROR_FAILED(this->write_byte(ES8388_CHIPPOWER, 0xF0));
delay(1);
ES8388_ERROR_FAILED(this->write_byte(ES8388_CHIPPOWER, 0x00));
// DAC volume max
// Set initial volume
// this->set_volume(0.75); // 0.75 = 0xBF = 0dB
this->set_mute_state_(false);
// unmute ADC with fade in
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL7, 0x60));
// unmute DAC with fade in
ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL3, 0x20));
// Power on ADC, Enable LIN&RIN, Power off MICBIAS, set int1lp to low power mode
ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCPOWER, 0x09));
#ifdef USE_SELECT
if (this->dac_output_select_ != nullptr) {
auto dac_power = this->get_dac_power();
if (dac_power.has_value()) {
auto dac_power_str = this->dac_output_select_->at(dac_power.value());
if (dac_power_str.has_value()) {
this->dac_output_select_->publish_state(dac_power_str.value());
} else {
ESP_LOGW(TAG, "Unknown DAC output power value: %d", dac_power.value());
}
}
}
if (this->adc_input_mic_select_ != nullptr) {
auto mic_input = this->get_mic_input();
if (mic_input.has_value()) {
auto mic_input_str = this->adc_input_mic_select_->at(mic_input.value());
if (mic_input_str.has_value()) {
this->adc_input_mic_select_->publish_state(mic_input_str.value());
} else {
ESP_LOGW(TAG, "Unknown ADC input mic value: %d", mic_input.value());
}
}
}
#endif
}
void ES8388::dump_config() {
ESP_LOGCONFIG(TAG, "ES8388 Audio Codec:");
LOG_I2C_DEVICE(this);
#ifdef USE_SELECT
LOG_SELECT(" ", "DacOutputSelect", this->dac_output_select_);
LOG_SELECT(" ", "ADCInputMicSelect", this->adc_input_mic_select_);
#endif
if (this->is_failed()) {
ESP_LOGCONFIG(TAG, " Failed to initialize");
return;
}
}
bool ES8388::set_volume(float volume) {
volume = clamp(volume, 0.0f, 1.0f);
uint8_t value = remap<uint8_t, float>(volume, 0.0f, 1.0f, -96, 0);
ESP_LOGD(TAG, "Setting ES8388_DACCONTROL4 / ES8388_DACCONTROL5 to 0x%02X (volume: %f)", value, volume);
ES8388_ERROR_CHECK(this->write_byte(ES8388_DACCONTROL4, value));
ES8388_ERROR_CHECK(this->write_byte(ES8388_DACCONTROL5, value));
return true;
}
float ES8388::volume() {
uint8_t value;
ES8388_ERROR_CHECK(this->read_byte(ES8388_DACCONTROL4, &value));
return remap<float, uint8_t>(value, -96, 0, 0.0f, 1.0f);
}
bool ES8388::set_mute_state_(bool mute_state) {
uint8_t value = 0;
this->is_muted_ = mute_state;
ES8388_ERROR_CHECK(this->read_byte(ES8388_DACCONTROL3, &value));
ESP_LOGV(TAG, "Read ES8388_DACCONTROL3: 0x%02X", value);
if (mute_state) {
value = 0x3C;
}
ESP_LOGV(TAG, "Setting ES8388_DACCONTROL3 to 0x%02X (muted: %s)", value, YESNO(mute_state));
return this->write_byte(ES8388_DACCONTROL3, value);
}
// Set dac power output
bool ES8388::set_dac_output(DacOutputLine line) {
uint8_t reg_out1 = 0;
uint8_t reg_out2 = 0;
uint8_t dac_power = 0;
// 0x00: -30dB , 0x1E: 0dB
switch (line) {
case DAC_OUTPUT_LINE1:
reg_out1 = 0x1E;
dac_power = ES8388_DAC_OUTPUT_LOUT1_ROUT1;
break;
case DAC_OUTPUT_LINE2:
reg_out2 = 0x1E;
dac_power = ES8388_DAC_OUTPUT_LOUT2_ROUT2;
break;
case DAC_OUTPUT_BOTH:
reg_out1 = 0x1E;
reg_out2 = 0x1E;
dac_power = ES8388_DAC_OUTPUT_BOTH;
break;
default:
ESP_LOGE(TAG, "Unknown DAC output line: %d", line);
return false;
};
ESP_LOGV(TAG, "Setting ES8388_DACPOWER to 0x%02X", dac_power);
ESP_LOGV(TAG, "Setting ES8388_DACCONTROL24 / ES8388_DACCONTROL25 to 0x%02X", reg_out1);
ESP_LOGV(TAG, "Setting ES8388_DACCONTROL26 / ES8388_DACCONTROL27 to 0x%02X", reg_out2);
ES8388_ERROR_CHECK(this->write_byte(ES8388_DACCONTROL24, reg_out1)); // LOUT1VOL
ES8388_ERROR_CHECK(this->write_byte(ES8388_DACCONTROL25, reg_out1)); // ROUT1VOL
ES8388_ERROR_CHECK(this->write_byte(ES8388_DACCONTROL26, reg_out2)); // LOUT2VOL
ES8388_ERROR_CHECK(this->write_byte(ES8388_DACCONTROL27, reg_out2)); // ROUT1VOL
return this->write_byte(ES8388_DACPOWER, dac_power);
}
optional<DacOutputLine> ES8388::get_dac_power() {
uint8_t dac_power;
if (!this->read_byte(ES8388_DACPOWER, &dac_power)) {
this->status_momentary_warning("Failed to read ES8388_DACPOWER");
return {};
}
switch (dac_power) {
case ES8388_DAC_OUTPUT_LOUT1_ROUT1:
return DAC_OUTPUT_LINE1;
case ES8388_DAC_OUTPUT_LOUT2_ROUT2:
return DAC_OUTPUT_LINE2;
case ES8388_DAC_OUTPUT_BOTH:
return DAC_OUTPUT_BOTH;
default:
return {};
}
}
// Set ADC input MIC
bool ES8388::set_adc_input_mic(AdcInputMicLine line) {
uint8_t mic_input = 0;
switch (line) {
case ADC_INPUT_MIC_LINE1:
mic_input = ES8388_ADC_INPUT_LINPUT1_RINPUT1;
break;
case ADC_INPUT_MIC_LINE2:
mic_input = ES8388_ADC_INPUT_LINPUT2_RINPUT2;
break;
case ADC_INPUT_MIC_DIFFERENCE:
mic_input = ES8388_ADC_INPUT_DIFFERENCE;
break;
default:
ESP_LOGE(TAG, "Unknown ADC input mic line: %d", line);
return false;
}
ESP_LOGV(TAG, "Setting ES8388_ADCCONTROL2 to 0x%02X", mic_input);
ES8388_ERROR_CHECK(this->write_byte(ES8388_ADCCONTROL2, mic_input));
return true;
}
optional<AdcInputMicLine> ES8388::get_mic_input() {
uint8_t mic_input;
if (!this->read_byte(ES8388_ADCCONTROL2, &mic_input)) {
this->status_momentary_warning("Failed to read ES8388_ADCCONTROL2");
return {};
}
switch (mic_input) {
case ES8388_ADC_INPUT_LINPUT1_RINPUT1:
return ADC_INPUT_MIC_LINE1;
case ES8388_ADC_INPUT_LINPUT2_RINPUT2:
return ADC_INPUT_MIC_LINE2;
case ES8388_ADC_INPUT_DIFFERENCE:
return ADC_INPUT_MIC_DIFFERENCE;
default:
return {};
};
}
} // namespace es8388
} // namespace esphome

View File

@ -0,0 +1,81 @@
#pragma once
#include <map>
#include "esphome/components/audio_dac/audio_dac.h"
#include "esphome/components/i2c/i2c.h"
#include "esphome/core/component.h"
#ifdef USE_SELECT
#include "esphome/components/select/select.h"
#endif
#include "es8388_const.h"
namespace esphome {
namespace es8388 {
enum DacOutputLine : uint8_t {
DAC_OUTPUT_LINE1,
DAC_OUTPUT_LINE2,
DAC_OUTPUT_BOTH,
};
enum AdcInputMicLine : uint8_t {
ADC_INPUT_MIC_LINE1,
ADC_INPUT_MIC_LINE2,
ADC_INPUT_MIC_DIFFERENCE,
};
class ES8388 : public audio_dac::AudioDac, public Component, public i2c::I2CDevice {
#ifdef USE_SELECT
SUB_SELECT(dac_output)
SUB_SELECT(adc_input_mic)
#endif
public:
/////////////////////////
// Component overrides //
/////////////////////////
void setup() override;
float get_setup_priority() const override { return setup_priority::DATA; }
void dump_config() override;
////////////////////////
// AudioDac overrides //
////////////////////////
/// @brief Writes the volume out to the DAC
/// @param volume floating point between 0.0 and 1.0
/// @return True if successful and false otherwise
bool set_volume(float volume) override;
/// @brief Gets the current volume out from the DAC
/// @return floating point between 0.0 and 1.0
float volume() override;
/// @brief Disables mute for audio out
/// @return True if successful and false otherwise
bool set_mute_off() override { return this->set_mute_state_(false); }
/// @brief Enables mute for audio out
/// @return True if successful and false otherwise
bool set_mute_on() override { return this->set_mute_state_(true); }
bool is_muted() override { return this->is_muted_; }
optional<DacOutputLine> get_dac_power();
optional<AdcInputMicLine> get_mic_input();
bool set_dac_output(DacOutputLine line);
bool set_adc_input_mic(AdcInputMicLine line);
protected:
/// @brief Mutes or unmutes the DAC audio out
/// @param mute_state True to mute, false to unmute
/// @return True if successful and false otherwise
bool set_mute_state_(bool mute_state);
};
} // namespace es8388
} // namespace esphome

View File

@ -0,0 +1,83 @@
#pragma once
#include <cstdint>
namespace esphome {
namespace es8388 {
/* ES8388 register */
static const uint8_t ES8388_CONTROL1 = 0x00;
static const uint8_t ES8388_CONTROL2 = 0x01;
static const uint8_t ES8388_CHIPPOWER = 0x02;
static const uint8_t ES8388_ADCPOWER = 0x03;
static const uint8_t ES8388_DACPOWER = 0x04;
static const uint8_t ES8388_CHIPLOPOW1 = 0x05;
static const uint8_t ES8388_CHIPLOPOW2 = 0x06;
static const uint8_t ES8388_ANAVOLMANAG = 0x07;
static const uint8_t ES8388_MASTERMODE = 0x08;
/* ADC */
static const uint8_t ES8388_ADCCONTROL1 = 0x09;
static const uint8_t ES8388_ADCCONTROL2 = 0x0a;
static const uint8_t ES8388_ADCCONTROL3 = 0x0b;
static const uint8_t ES8388_ADCCONTROL4 = 0x0c;
static const uint8_t ES8388_ADCCONTROL5 = 0x0d;
static const uint8_t ES8388_ADCCONTROL6 = 0x0e;
static const uint8_t ES8388_ADCCONTROL7 = 0x0f;
static const uint8_t ES8388_ADCCONTROL8 = 0x10;
static const uint8_t ES8388_ADCCONTROL9 = 0x11;
static const uint8_t ES8388_ADCCONTROL10 = 0x12;
static const uint8_t ES8388_ADCCONTROL11 = 0x13;
static const uint8_t ES8388_ADCCONTROL12 = 0x14;
static const uint8_t ES8388_ADCCONTROL13 = 0x15;
static const uint8_t ES8388_ADCCONTROL14 = 0x16;
/* DAC */
static const uint8_t ES8388_DACCONTROL1 = 0x17;
static const uint8_t ES8388_DACCONTROL2 = 0x18;
static const uint8_t ES8388_DACCONTROL3 = 0x19;
static const uint8_t ES8388_DACCONTROL4 = 0x1a;
static const uint8_t ES8388_DACCONTROL5 = 0x1b;
static const uint8_t ES8388_DACCONTROL6 = 0x1c;
static const uint8_t ES8388_DACCONTROL7 = 0x1d;
static const uint8_t ES8388_DACCONTROL8 = 0x1e;
static const uint8_t ES8388_DACCONTROL9 = 0x1f;
static const uint8_t ES8388_DACCONTROL10 = 0x20;
static const uint8_t ES8388_DACCONTROL11 = 0x21;
static const uint8_t ES8388_DACCONTROL12 = 0x22;
static const uint8_t ES8388_DACCONTROL13 = 0x23;
static const uint8_t ES8388_DACCONTROL14 = 0x24;
static const uint8_t ES8388_DACCONTROL15 = 0x25;
static const uint8_t ES8388_DACCONTROL16 = 0x26;
static const uint8_t ES8388_DACCONTROL17 = 0x27;
static const uint8_t ES8388_DACCONTROL18 = 0x28;
static const uint8_t ES8388_DACCONTROL19 = 0x29;
static const uint8_t ES8388_DACCONTROL20 = 0x2a;
static const uint8_t ES8388_DACCONTROL21 = 0x2b;
static const uint8_t ES8388_DACCONTROL22 = 0x2c;
static const uint8_t ES8388_DACCONTROL23 = 0x2d;
static const uint8_t ES8388_DACCONTROL24 = 0x2e;
static const uint8_t ES8388_DACCONTROL25 = 0x2f;
static const uint8_t ES8388_DACCONTROL26 = 0x30;
static const uint8_t ES8388_DACCONTROL27 = 0x31;
static const uint8_t ES8388_DACCONTROL28 = 0x32;
static const uint8_t ES8388_DACCONTROL29 = 0x33;
static const uint8_t ES8388_DACCONTROL30 = 0x34;
static const uint8_t ES8388_DAC_OUTPUT_NONE = 0xC0; // ALL DAC DOWN
static const uint8_t ES8388_DAC_OUTPUT_LOUT1_ROUT1 = 0x30;
static const uint8_t ES8388_DAC_OUTPUT_LOUT2_ROUT2 = 0x0C;
static const uint8_t ES8388_DAC_OUTPUT_BOTH = 0x3C;
static const uint8_t ES8388_ADC_INPUT_LINPUT1_RINPUT1 = 0x00;
static const uint8_t ES8388_ADC_INPUT_MIC1 = 0x05;
static const uint8_t ES8388_ADC_INPUT_MIC2 = 0x06;
static const uint8_t ES8388_ADC_INPUT_LINPUT2_RINPUT2 = 0x50;
static const uint8_t ES8388_ADC_INPUT_DIFFERENCE = 0xf0;
} // namespace es8388
} // namespace esphome

View File

@ -0,0 +1,47 @@
import esphome.codegen as cg
from esphome.components import select
import esphome.config_validation as cv
from esphome.const import ENTITY_CATEGORY_CONFIG, ICON_CHIP # noqa: F401
from ..audio_dac import CONF_ES8388_ID, ES8388, es8388_ns
CONF_DAC_OUTPUT = "dac_output"
CONF_ADC_INPUT_MIC = "adc_input_mic"
DacOutputSelect = es8388_ns.class_("DacOutputSelect", select.Select)
ADCInputMicSelect = es8388_ns.class_("ADCInputMicSelect", select.Select)
CONFIG_SCHEMA = cv.All(
{
cv.GenerateID(CONF_ES8388_ID): cv.use_id(ES8388),
cv.Optional(CONF_DAC_OUTPUT): select.select_schema(
DacOutputSelect,
entity_category=ENTITY_CATEGORY_CONFIG,
icon=ICON_CHIP,
),
cv.Optional(CONF_ADC_INPUT_MIC): select.select_schema(
ADCInputMicSelect,
entity_category=ENTITY_CATEGORY_CONFIG,
icon=ICON_CHIP,
),
}
)
async def to_code(config):
parent = await cg.get_variable(config[CONF_ES8388_ID])
if dac_output_config := config.get(CONF_DAC_OUTPUT):
s = await select.new_select(
dac_output_config,
options=["LINE1", "LINE2", "BOTH"],
)
await cg.register_parented(s, parent)
cg.add(parent.set_dac_output_select(s))
if adc_input_mic_config := config.get(CONF_ADC_INPUT_MIC):
s = await select.new_select(
adc_input_mic_config,
options=["LINE1", "LINE2", "DIFFERENCE"],
)
await cg.register_parented(s, parent)
cg.add(parent.set_adc_input_mic_select(s))

View File

@ -0,0 +1,12 @@
#include "adc_input_mic_select.h"
namespace esphome {
namespace es8388 {
void ADCInputMicSelect::control(const std::string &value) {
this->publish_state(value);
this->parent_->set_adc_input_mic(static_cast<AdcInputMicLine>(this->index_of(value).value()));
}
} // namespace es8388
} // namespace esphome

View File

@ -0,0 +1,15 @@
#pragma once
#include "esphome/components/es8388/es8388.h"
#include "esphome/components/select/select.h"
namespace esphome {
namespace es8388 {
class ADCInputMicSelect : public select::Select, public Parented<ES8388> {
protected:
void control(const std::string &value) override;
};
} // namespace es8388
} // namespace esphome

View File

@ -0,0 +1,12 @@
#include "dac_output_select.h"
namespace esphome {
namespace es8388 {
void DacOutputSelect::control(const std::string &value) {
this->publish_state(value);
this->parent_->set_dac_output(static_cast<DacOutputLine>(this->index_of(value).value()));
}
} // namespace es8388
} // namespace esphome

View File

@ -0,0 +1,15 @@
#pragma once
#include "esphome/components/es8388/es8388.h"
#include "esphome/components/select/select.h"
namespace esphome {
namespace es8388 {
class DacOutputSelect : public select::Select, public Parented<ES8388> {
protected:
void control(const std::string &value) override;
};
} // namespace es8388
} // namespace esphome

View File

@ -3,7 +3,6 @@ import itertools
import logging
import os
from pathlib import Path
from typing import Optional, Union
from esphome import git
import esphome.codegen as cg
@ -58,8 +57,10 @@ from .const import ( # noqa
VARIANT_ESP32,
VARIANT_ESP32C2,
VARIANT_ESP32C3,
VARIANT_ESP32C5,
VARIANT_ESP32C6,
VARIANT_ESP32H2,
VARIANT_ESP32P4,
VARIANT_ESP32S2,
VARIANT_ESP32S3,
VARIANT_FRIENDLY,
@ -70,12 +71,35 @@ from .const import ( # noqa
from .gpio import esp32_pin_to_code # noqa
_LOGGER = logging.getLogger(__name__)
CODEOWNERS = ["@esphome/core"]
AUTO_LOAD = ["preferences"]
CODEOWNERS = ["@esphome/core"]
IS_TARGET_PLATFORM = True
CONF_RELEASE = "release"
CONF_ASSERTION_LEVEL = "assertion_level"
CONF_COMPILER_OPTIMIZATION = "compiler_optimization"
CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES = "enable_idf_experimental_features"
CONF_ENABLE_LWIP_ASSERT = "enable_lwip_assert"
CONF_RELEASE = "release"
ASSERTION_LEVELS = {
"DISABLE": "CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE",
"ENABLE": "CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE",
"SILENT": "CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT",
}
COMPILER_OPTIMIZATIONS = {
"DEBUG": "CONFIG_COMPILER_OPTIMIZATION_DEBUG",
"NONE": "CONFIG_COMPILER_OPTIMIZATION_NONE",
"PERF": "CONFIG_COMPILER_OPTIMIZATION_PERF",
"SIZE": "CONFIG_COMPILER_OPTIMIZATION_SIZE",
}
ARDUINO_ALLOWED_VARIANTS = [
VARIANT_ESP32,
VARIANT_ESP32C3,
VARIANT_ESP32S2,
VARIANT_ESP32S3,
]
def get_cpu_frequencies(*frequencies):
@ -88,8 +112,10 @@ CPU_FREQUENCIES = {
VARIANT_ESP32S3: get_cpu_frequencies(80, 160, 240),
VARIANT_ESP32C2: get_cpu_frequencies(80, 120),
VARIANT_ESP32C3: get_cpu_frequencies(80, 160),
VARIANT_ESP32C5: get_cpu_frequencies(80, 160, 240),
VARIANT_ESP32C6: get_cpu_frequencies(80, 120, 160),
VARIANT_ESP32H2: get_cpu_frequencies(16, 32, 48, 64, 96),
VARIANT_ESP32P4: get_cpu_frequencies(40, 360, 400),
}
# Make sure not missed here if a new variant added.
@ -124,12 +150,17 @@ def set_core_data(config):
CORE.data[KEY_ESP32][KEY_COMPONENTS] = {}
elif conf[CONF_TYPE] == FRAMEWORK_ARDUINO:
CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "arduino"
if variant not in ARDUINO_ALLOWED_VARIANTS:
raise cv.Invalid(
f"ESPHome does not support using the Arduino framework for the {variant}. Please use the ESP-IDF framework instead.",
path=[CONF_FRAMEWORK, CONF_TYPE],
)
CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version.parse(
config[CONF_FRAMEWORK][CONF_VERSION]
)
CORE.data[KEY_ESP32][KEY_BOARD] = config[CONF_BOARD]
CORE.data[KEY_ESP32][KEY_VARIANT] = config[CONF_VARIANT]
CORE.data[KEY_ESP32][KEY_VARIANT] = variant
CORE.data[KEY_ESP32][KEY_EXTRA_BUILD_FILES] = {}
return config
@ -189,7 +220,7 @@ class RawSdkconfigValue:
value: str
SdkconfigValueType = Union[bool, int, HexInt, str, RawSdkconfigValue]
SdkconfigValueType = bool | int | HexInt | str | RawSdkconfigValue
def add_idf_sdkconfig_option(name: str, value: SdkconfigValueType):
@ -206,8 +237,8 @@ def add_idf_component(
ref: str = None,
path: str = None,
refresh: TimePeriod = None,
components: Optional[list[str]] = None,
submodules: Optional[list[str]] = None,
components: list[str] | None = None,
submodules: list[str] | None = None,
):
"""Add an esp-idf component to the project."""
if not CORE.using_esp_idf:
@ -296,11 +327,11 @@ ARDUINO_PLATFORM_VERSION = cv.Version(5, 4, 0)
# The default/recommended esp-idf framework version
# - https://github.com/espressif/esp-idf/releases
# - https://api.registry.platformio.org/v3/packages/platformio/tool/framework-espidf
RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION = cv.Version(5, 1, 6)
RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION = cv.Version(5, 3, 2)
# The platformio/espressif32 version to use for esp-idf frameworks
# - https://github.com/platformio/platform-espressif32/releases
# - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32
ESP_IDF_PLATFORM_VERSION = cv.Version(51, 3, 7)
ESP_IDF_PLATFORM_VERSION = cv.Version(53, 3, 13)
# List based on https://registry.platformio.org/tools/platformio/framework-espidf/versions
SUPPORTED_PLATFORMIO_ESP_IDF_5X = [
@ -369,8 +400,8 @@ def _arduino_check_versions(value):
def _esp_idf_check_versions(value):
value = value.copy()
lookups = {
"dev": (cv.Version(5, 1, 6), "https://github.com/espressif/esp-idf.git"),
"latest": (cv.Version(5, 1, 6), None),
"dev": (cv.Version(5, 3, 2), "https://github.com/espressif/esp-idf.git"),
"latest": (cv.Version(5, 3, 2), None),
"recommended": (RECOMMENDED_ESP_IDF_FRAMEWORK_VERSION, None),
}
@ -448,8 +479,8 @@ def _parse_platform_version(value):
if ver.major >= 50: # a pioarduino version
if "-" in value:
# maybe a release candidate?...definitely not our default, just use it as-is...
return f"https://github.com/pioarduino/platform-espressif32.git#{value}"
return f"https://github.com/pioarduino/platform-espressif32.git#{ver.major}.{ver.minor:02d}.{ver.patch:02d}"
return f"https://github.com/pioarduino/platform-espressif32/releases/download/{value}/platform-espressif32.zip"
return f"https://github.com/pioarduino/platform-espressif32/releases/download/{ver.major}.{ver.minor:02d}.{ver.patch:02d}/platform-espressif32.zip"
# if platform version is a valid version constraint, prefix the default package
cv.platformio_version_constraint(value)
return f"platformio/espressif32@{value}"
@ -539,6 +570,10 @@ ARDUINO_FRAMEWORK_SCHEMA = cv.All(
)
CONF_SDKCONFIG_OPTIONS = "sdkconfig_options"
CONF_ENABLE_LWIP_DHCP_SERVER = "enable_lwip_dhcp_server"
CONF_ENABLE_LWIP_MDNS_QUERIES = "enable_lwip_mdns_queries"
CONF_ENABLE_LWIP_BRIDGE_INTERFACE = "enable_lwip_bridge_interface"
ESP_IDF_FRAMEWORK_SCHEMA = cv.All(
cv.Schema(
{
@ -551,11 +586,30 @@ ESP_IDF_FRAMEWORK_SCHEMA = cv.All(
},
cv.Optional(CONF_ADVANCED, default={}): cv.Schema(
{
cv.Optional(CONF_ASSERTION_LEVEL): cv.one_of(
*ASSERTION_LEVELS, upper=True
),
cv.Optional(CONF_COMPILER_OPTIMIZATION, default="SIZE"): cv.one_of(
*COMPILER_OPTIMIZATIONS, upper=True
),
cv.Optional(CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES): cv.boolean,
cv.Optional(CONF_ENABLE_LWIP_ASSERT, default=True): cv.boolean,
cv.Optional(
CONF_IGNORE_EFUSE_CUSTOM_MAC, default=False
): cv.boolean,
cv.Optional(CONF_IGNORE_EFUSE_MAC_CRC): cv.boolean,
cv.Optional(CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES): cv.boolean,
# DHCP server is needed for WiFi AP mode. When WiFi component is used,
# it will handle disabling DHCP server when AP is not configured.
# Default to false (disabled) when WiFi is not used.
cv.OnlyWithout(
CONF_ENABLE_LWIP_DHCP_SERVER, "wifi", default=False
): cv.boolean,
cv.Optional(
CONF_ENABLE_LWIP_MDNS_QUERIES, default=False
): cv.boolean,
cv.Optional(
CONF_ENABLE_LWIP_BRIDGE_INTERFACE, default=False
): cv.boolean,
}
),
cv.Optional(CONF_COMPONENTS, default=[]): cv.ensure_list(
@ -576,6 +630,21 @@ ESP_IDF_FRAMEWORK_SCHEMA = cv.All(
)
def _set_default_framework(config):
if CONF_FRAMEWORK not in config:
config = config.copy()
variant = config[CONF_VARIANT]
if variant in ARDUINO_ALLOWED_VARIANTS:
config[CONF_FRAMEWORK] = ARDUINO_FRAMEWORK_SCHEMA({})
config[CONF_FRAMEWORK][CONF_TYPE] = FRAMEWORK_ARDUINO
else:
config[CONF_FRAMEWORK] = ESP_IDF_FRAMEWORK_SCHEMA({})
config[CONF_FRAMEWORK][CONF_TYPE] = FRAMEWORK_ESP_IDF
return config
FRAMEWORK_ESP_IDF = "esp-idf"
FRAMEWORK_ARDUINO = "arduino"
FRAMEWORK_SCHEMA = cv.typed_schema(
@ -585,7 +654,6 @@ FRAMEWORK_SCHEMA = cv.typed_schema(
},
lower=True,
space="-",
default_type=FRAMEWORK_ARDUINO,
)
@ -612,10 +680,11 @@ CONFIG_SCHEMA = cv.All(
),
cv.Optional(CONF_PARTITIONS): cv.file_,
cv.Optional(CONF_VARIANT): cv.one_of(*VARIANTS, upper=True),
cv.Optional(CONF_FRAMEWORK, default={}): FRAMEWORK_SCHEMA,
cv.Optional(CONF_FRAMEWORK): FRAMEWORK_SCHEMA,
}
),
_detect_variant,
_set_default_framework,
set_core_data,
)
@ -626,6 +695,7 @@ FINAL_VALIDATE_SCHEMA = cv.Schema(final_validate)
async def to_code(config):
cg.add_platformio_option("board", config[CONF_BOARD])
cg.add_platformio_option("board_upload.flash_size", config[CONF_FLASH_SIZE])
cg.set_cpp_standard("gnu++17")
cg.add_build_flag("-DUSE_ESP32")
cg.add_define("ESPHOME_BOARD", config[CONF_BOARD])
cg.add_build_flag(f"-DUSE_ESP32_VARIANT_{config[CONF_VARIANT]}")
@ -638,7 +708,7 @@ async def to_code(config):
conf = config[CONF_FRAMEWORK]
cg.add_platformio_option("platform", conf[CONF_PLATFORM_VERSION])
if CONF_ADVANCED in conf and conf[CONF_ADVANCED][CONF_IGNORE_EFUSE_CUSTOM_MAC]:
if conf[CONF_ADVANCED][CONF_IGNORE_EFUSE_CUSTOM_MAC]:
cg.add_define("USE_ESP32_IGNORE_EFUSE_CUSTOM_MAC")
add_extra_script(
@ -669,8 +739,6 @@ async def to_code(config):
add_idf_sdkconfig_option(
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME", "partitions.csv"
)
add_idf_sdkconfig_option("CONFIG_COMPILER_OPTIMIZATION_DEFAULT", False)
add_idf_sdkconfig_option("CONFIG_COMPILER_OPTIMIZATION_SIZE", True)
# Increase freertos tick speed from 100Hz to 1kHz so that delay() resolution is 1ms
add_idf_sdkconfig_option("CONFIG_FREERTOS_HZ", 1000)
@ -684,16 +752,41 @@ async def to_code(config):
# Set default CPU frequency
add_idf_sdkconfig_option(f"CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_{freq}", True)
# Apply LWIP optimization settings
advanced = conf[CONF_ADVANCED]
# DHCP server: only disable if explicitly set to false
# WiFi component handles its own optimization when AP mode is not used
if (
CONF_ENABLE_LWIP_DHCP_SERVER in advanced
and not advanced[CONF_ENABLE_LWIP_DHCP_SERVER]
):
add_idf_sdkconfig_option("CONFIG_LWIP_DHCPS", False)
if not advanced.get(CONF_ENABLE_LWIP_MDNS_QUERIES, False):
add_idf_sdkconfig_option("CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES", False)
if not advanced.get(CONF_ENABLE_LWIP_BRIDGE_INTERFACE, False):
add_idf_sdkconfig_option("CONFIG_LWIP_BRIDGEIF_MAX_PORTS", 0)
cg.add_platformio_option("board_build.partitions", "partitions.csv")
if CONF_PARTITIONS in config:
add_extra_build_file(
"partitions.csv", CORE.relative_config_path(config[CONF_PARTITIONS])
)
for name, value in conf[CONF_SDKCONFIG_OPTIONS].items():
add_idf_sdkconfig_option(name, RawSdkconfigValue(value))
if assertion_level := advanced.get(CONF_ASSERTION_LEVEL):
for key, flag in ASSERTION_LEVELS.items():
add_idf_sdkconfig_option(flag, assertion_level == key)
if conf[CONF_ADVANCED].get(CONF_IGNORE_EFUSE_MAC_CRC):
add_idf_sdkconfig_option("CONFIG_COMPILER_OPTIMIZATION_DEFAULT", False)
compiler_optimization = advanced.get(CONF_COMPILER_OPTIMIZATION)
for key, flag in COMPILER_OPTIMIZATIONS.items():
add_idf_sdkconfig_option(flag, compiler_optimization == key)
add_idf_sdkconfig_option(
"CONFIG_LWIP_ESP_LWIP_ASSERT",
conf[CONF_ADVANCED][CONF_ENABLE_LWIP_ASSERT],
)
if advanced.get(CONF_IGNORE_EFUSE_MAC_CRC):
add_idf_sdkconfig_option("CONFIG_ESP_MAC_IGNORE_MAC_CRC_ERROR", True)
if (framework_ver.major, framework_ver.minor) >= (4, 4):
add_idf_sdkconfig_option(
@ -703,7 +796,7 @@ async def to_code(config):
add_idf_sdkconfig_option(
"CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE", False
)
if conf[CONF_ADVANCED].get(CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES):
if advanced.get(CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES):
_LOGGER.warning(
"Using experimental features in ESP-IDF may result in unexpected failures."
)
@ -716,6 +809,9 @@ async def to_code(config):
),
)
for name, value in conf[CONF_SDKCONFIG_OPTIONS].items():
add_idf_sdkconfig_option(name, RawSdkconfigValue(value))
for component in conf[CONF_COMPONENTS]:
source = component[CONF_SOURCE]
if source[CONF_TYPE] == TYPE_GIT:

View File

@ -4,6 +4,7 @@ from .const import (
VARIANT_ESP32C3,
VARIANT_ESP32C6,
VARIANT_ESP32H2,
VARIANT_ESP32P4,
VARIANT_ESP32S2,
VARIANT_ESP32S3,
)
@ -1338,17 +1339,7 @@ ESP32_BOARD_PINS = {
}
"""
BOARDS generated with:
git clone https://github.com/platformio/platform-espressif32
for x in platform-espressif32/boards/*.json; do
mcu=$(jq -r .build.mcu <"$x");
name=$(jq -r .name <"$x");
fname=$(basename "$x")
board="${fname%.*}"
variant=$(echo "$mcu" | tr '[:lower:]' '[:upper:]')
echo " \"$board\": {\"name\": \"$name\", \"variant\": VARIANT_${variant},},"
done | sort
BOARDS generated with script/generate-esp32-boards.py
"""
BOARDS = {
@ -1360,6 +1351,10 @@ BOARDS = {
"name": "Adafruit pyCamera S3",
"variant": VARIANT_ESP32S3,
},
"adafruit_feather_esp32_v2": {
"name": "Adafruit Feather ESP32 V2",
"variant": VARIANT_ESP32,
},
"adafruit_feather_esp32c6": {
"name": "Adafruit Feather ESP32-C6",
"variant": VARIANT_ESP32C6,
@ -1392,10 +1387,6 @@ BOARDS = {
"name": "Adafruit Feather ESP32-S3 TFT",
"variant": VARIANT_ESP32S3,
},
"adafruit_feather_esp32_v2": {
"name": "Adafruit Feather ESP32 V2",
"variant": VARIANT_ESP32,
},
"adafruit_funhouse_esp32s2": {
"name": "Adafruit FunHouse",
"variant": VARIANT_ESP32S2,
@ -1420,14 +1411,14 @@ BOARDS = {
"name": "Adafruit Metro ESP32-S3",
"variant": VARIANT_ESP32S3,
},
"adafruit_qtpy_esp32c3": {
"name": "Adafruit QT Py ESP32-C3",
"variant": VARIANT_ESP32C3,
},
"adafruit_qtpy_esp32": {
"name": "Adafruit QT Py ESP32",
"variant": VARIANT_ESP32,
},
"adafruit_qtpy_esp32c3": {
"name": "Adafruit QT Py ESP32-C3",
"variant": VARIANT_ESP32C3,
},
"adafruit_qtpy_esp32s2": {
"name": "Adafruit QT Py ESP32-S2",
"variant": VARIANT_ESP32S2,
@ -1476,14 +1467,14 @@ BOARDS = {
"name": "Smart Bee Data Logger",
"variant": VARIANT_ESP32S3,
},
"bee_motion_mini": {
"name": "Smart Bee Motion Mini",
"variant": VARIANT_ESP32C3,
},
"bee_motion": {
"name": "Smart Bee Motion",
"variant": VARIANT_ESP32S2,
},
"bee_motion_mini": {
"name": "Smart Bee Motion Mini",
"variant": VARIANT_ESP32C3,
},
"bee_motion_s3": {
"name": "Smart Bee Motion S3",
"variant": VARIANT_ESP32S3,
@ -1516,6 +1507,10 @@ BOARDS = {
"name": "D-duino-32",
"variant": VARIANT_ESP32,
},
"deneyapkart": {
"name": "Deneyap Kart",
"variant": VARIANT_ESP32,
},
"deneyapkart1A": {
"name": "Deneyap Kart 1A",
"variant": VARIANT_ESP32,
@ -1528,10 +1523,6 @@ BOARDS = {
"name": "Deneyap Kart G",
"variant": VARIANT_ESP32C3,
},
"deneyapkart": {
"name": "Deneyap Kart",
"variant": VARIANT_ESP32,
},
"deneyapmini": {
"name": "Deneyap Mini",
"variant": VARIANT_ESP32S2,
@ -1572,8 +1563,8 @@ BOARDS = {
"name": "Seeed Studio Edgebox-ESP-100",
"variant": VARIANT_ESP32S3,
},
"esp320": {
"name": "Electronic SweetPeas ESP320",
"esp-wrover-kit": {
"name": "Espressif ESP-WROVER-KIT",
"variant": VARIANT_ESP32,
},
"esp32-c2-devkitm-1": {
@ -1600,26 +1591,10 @@ BOARDS = {
"name": "Espressif ESP32-C6-DevKitM-1",
"variant": VARIANT_ESP32C6,
},
"esp32cam": {
"name": "AI Thinker ESP32-CAM",
"variant": VARIANT_ESP32,
},
"esp32-devkitlipo": {
"name": "OLIMEX ESP32-DevKit-LiPo",
"variant": VARIANT_ESP32,
},
"esp32dev": {
"name": "Espressif ESP32 Dev Module",
"variant": VARIANT_ESP32,
},
"esp32doit-devkit-v1": {
"name": "DOIT ESP32 DEVKIT V1",
"variant": VARIANT_ESP32,
},
"esp32doit-espduino": {
"name": "DOIT ESPduino32",
"variant": VARIANT_ESP32,
},
"esp32-evb": {
"name": "OLIMEX ESP32-EVB",
"variant": VARIANT_ESP32,
@ -1632,18 +1607,26 @@ BOARDS = {
"name": "Espressif ESP32-H2-DevKit",
"variant": VARIANT_ESP32H2,
},
"esp32-p4": {
"name": "Espressif ESP32-P4 generic",
"variant": VARIANT_ESP32P4,
},
"esp32-p4-evboard": {
"name": "Espressif ESP32-P4 Function EV Board",
"variant": VARIANT_ESP32P4,
},
"esp32-pico-devkitm-2": {
"name": "Espressif ESP32-PICO-DevKitM-2",
"variant": VARIANT_ESP32,
},
"esp32-poe-iso": {
"name": "OLIMEX ESP32-PoE-ISO",
"variant": VARIANT_ESP32,
},
"esp32-poe": {
"name": "OLIMEX ESP32-PoE",
"variant": VARIANT_ESP32,
},
"esp32-poe-iso": {
"name": "OLIMEX ESP32-PoE-ISO",
"variant": VARIANT_ESP32,
},
"esp32-pro": {
"name": "OLIMEX ESP32-PRO",
"variant": VARIANT_ESP32,
@ -1660,14 +1643,6 @@ BOARDS = {
"name": "Espressif ESP32-S2-Saola-1",
"variant": VARIANT_ESP32S2,
},
"esp32s3box": {
"name": "Espressif ESP32-S3-Box",
"variant": VARIANT_ESP32S3,
},
"esp32s3camlcd": {
"name": "ESP32S3 CAM LCD",
"variant": VARIANT_ESP32S3,
},
"esp32-s3-devkitc-1": {
"name": "Espressif ESP32-S3-DevKitC-1-N8 (8 MB QD, No PSRAM)",
"variant": VARIANT_ESP32S3,
@ -1676,18 +1651,50 @@ BOARDS = {
"name": "Espressif ESP32-S3-DevKitM-1",
"variant": VARIANT_ESP32S3,
},
"esp32-solo1": {
"name": "Espressif Generic ESP32-solo1 4M Flash",
"variant": VARIANT_ESP32,
},
"esp320": {
"name": "Electronic SweetPeas ESP320",
"variant": VARIANT_ESP32,
},
"esp32cam": {
"name": "AI Thinker ESP32-CAM",
"variant": VARIANT_ESP32,
},
"esp32dev": {
"name": "Espressif ESP32 Dev Module",
"variant": VARIANT_ESP32,
},
"esp32doit-devkit-v1": {
"name": "DOIT ESP32 DEVKIT V1",
"variant": VARIANT_ESP32,
},
"esp32doit-espduino": {
"name": "DOIT ESPduino32",
"variant": VARIANT_ESP32,
},
"esp32s3_120_16_8-qio_opi": {
"name": "ESP32-S3 16MB QIO, 8MB OPI PSRAM",
"variant": VARIANT_ESP32S3,
},
"esp32s3_powerfeather": {
"name": "ESP32-S3 PowerFeather",
"variant": VARIANT_ESP32S3,
},
"esp32s3box": {
"name": "Espressif ESP32-S3-Box",
"variant": VARIANT_ESP32S3,
},
"esp32s3camlcd": {
"name": "ESP32S3 CAM LCD",
"variant": VARIANT_ESP32S3,
},
"esp32s3usbotg": {
"name": "Espressif ESP32-S3-USB-OTG",
"variant": VARIANT_ESP32S3,
},
"esp32-solo1": {
"name": "Espressif Generic ESP32-solo1 4M Flash",
"variant": VARIANT_ESP32,
},
"esp32thing": {
"name": "SparkFun ESP32 Thing",
"variant": VARIANT_ESP32,
@ -1712,10 +1719,6 @@ BOARDS = {
"name": "ESPino32",
"variant": VARIANT_ESP32,
},
"esp-wrover-kit": {
"name": "Espressif ESP-WROVER-KIT",
"variant": VARIANT_ESP32,
},
"etboard": {
"name": "ETBoard",
"variant": VARIANT_ESP32,
@ -1744,6 +1747,14 @@ BOARDS = {
"name": "Franzininho WiFi MSC",
"variant": VARIANT_ESP32S2,
},
"freenove_esp32_s3_wroom": {
"name": "Freenove ESP32-S3 WROOM N8R8 (8MB Flash / 8MB PSRAM)",
"variant": VARIANT_ESP32S3,
},
"freenove_esp32_wrover": {
"name": "Freenove ESP32-Wrover",
"variant": VARIANT_ESP32,
},
"frogboard": {
"name": "Frog Board ESP32",
"variant": VARIANT_ESP32,
@ -1760,6 +1771,10 @@ BOARDS = {
"name": "Heltec WiFi Kit 32 (V3)",
"variant": VARIANT_ESP32S3,
},
"heltec_wifi_kit_32_v2": {
"name": "Heltec WiFi Kit 32 (V2)",
"variant": VARIANT_ESP32,
},
"heltec_wifi_lora_32": {
"name": "Heltec WiFi LoRa 32",
"variant": VARIANT_ESP32,
@ -1772,14 +1787,14 @@ BOARDS = {
"name": "Heltec WiFi LoRa 32 (V3)",
"variant": VARIANT_ESP32S3,
},
"heltec_wireless_stick_lite": {
"name": "Heltec Wireless Stick Lite",
"variant": VARIANT_ESP32,
},
"heltec_wireless_stick": {
"name": "Heltec Wireless Stick",
"variant": VARIANT_ESP32,
},
"heltec_wireless_stick_lite": {
"name": "Heltec Wireless Stick Lite",
"variant": VARIANT_ESP32,
},
"honeylemon": {
"name": "HONEYLemon",
"variant": VARIANT_ESP32,
@ -1792,6 +1807,14 @@ BOARDS = {
"name": "Hornbill ESP32 Minima",
"variant": VARIANT_ESP32,
},
"huidu_hd_wf2": {
"name": "Huidu HD-WF2",
"variant": VARIANT_ESP32S3,
},
"huidu_hd_wf4": {
"name": "Huidu HD-WF4",
"variant": VARIANT_ESP32S3,
},
"imbrios-logsens-v1p1": {
"name": "Imbrios LogSens V1P1",
"variant": VARIANT_ESP32,
@ -1824,6 +1847,10 @@ BOARDS = {
"name": "ArtronShop IOXESP32PS",
"variant": VARIANT_ESP32,
},
"jczn_2432s028r": {
"name": "ESP32-2432S028R CYD",
"variant": VARIANT_ESP32,
},
"kb32-ft": {
"name": "MakerAsia KB32-FT",
"variant": VARIANT_ESP32,
@ -1848,6 +1875,10 @@ BOARDS = {
"name": "LilyGo T-Display-S3",
"variant": VARIANT_ESP32S3,
},
"lilygo-t3-s3": {
"name": "LilyGo T3-S3",
"variant": VARIANT_ESP32S3,
},
"lionbit": {
"name": "Lion:Bit Dev Board",
"variant": VARIANT_ESP32,
@ -1856,14 +1887,14 @@ BOARDS = {
"name": "Lion:Bit S3 STEM Dev Board",
"variant": VARIANT_ESP32S3,
},
"lolin32_lite": {
"name": "WEMOS LOLIN32 Lite",
"variant": VARIANT_ESP32,
},
"lolin32": {
"name": "WEMOS LOLIN32",
"variant": VARIANT_ESP32,
},
"lolin32_lite": {
"name": "WEMOS LOLIN32 Lite",
"variant": VARIANT_ESP32,
},
"lolin_c3_mini": {
"name": "WEMOS LOLIN C3 Mini",
"variant": VARIANT_ESP32C3,
@ -1884,26 +1915,30 @@ BOARDS = {
"name": "WEMOS LOLIN S2 PICO",
"variant": VARIANT_ESP32S2,
},
"lolin_s3": {
"name": "WEMOS LOLIN S3",
"variant": VARIANT_ESP32S3,
},
"lolin_s3_mini": {
"name": "WEMOS LOLIN S3 Mini",
"variant": VARIANT_ESP32S3,
},
"lolin_s3": {
"name": "WEMOS LOLIN S3",
"lolin_s3_mini_pro": {
"name": "WEMOS LOLIN S3 Mini Pro",
"variant": VARIANT_ESP32S3,
},
"lolin_s3_pro": {
"name": "WEMOS LOLIN S3 PRO",
"variant": VARIANT_ESP32S3,
},
"lopy4": {
"name": "Pycom LoPy4",
"variant": VARIANT_ESP32,
},
"lopy": {
"name": "Pycom LoPy",
"variant": VARIANT_ESP32,
},
"lopy4": {
"name": "Pycom LoPy4",
"variant": VARIANT_ESP32,
},
"m5stack-atom": {
"name": "M5Stack-ATOM",
"variant": VARIANT_ESP32,
@ -1912,16 +1947,16 @@ BOARDS = {
"name": "M5Stack AtomS3",
"variant": VARIANT_ESP32S3,
},
"m5stack-core2": {
"name": "M5Stack Core2",
"m5stack-core-esp32": {
"name": "M5Stack Core ESP32",
"variant": VARIANT_ESP32,
},
"m5stack-core-esp32-16M": {
"name": "M5Stack Core ESP32 16M",
"variant": VARIANT_ESP32,
},
"m5stack-core-esp32": {
"name": "M5Stack Core ESP32",
"m5stack-core2": {
"name": "M5Stack Core2",
"variant": VARIANT_ESP32,
},
"m5stack-coreink": {
@ -1940,10 +1975,6 @@ BOARDS = {
"name": "M5Stack GREY ESP32",
"variant": VARIANT_ESP32,
},
"m5stack_paper": {
"name": "M5Stack Paper",
"variant": VARIANT_ESP32,
},
"m5stack-stamps3": {
"name": "M5Stack StampS3",
"variant": VARIANT_ESP32S3,
@ -1956,6 +1987,10 @@ BOARDS = {
"name": "M5Stack Timer CAM",
"variant": VARIANT_ESP32,
},
"m5stack_paper": {
"name": "M5Stack Paper",
"variant": VARIANT_ESP32,
},
"m5stamp-pico": {
"name": "M5Stamp-Pico",
"variant": VARIANT_ESP32,
@ -2024,14 +2059,14 @@ BOARDS = {
"name": "Node32s",
"variant": VARIANT_ESP32,
},
"nodemcu-32s2": {
"name": "Ai-Thinker NodeMCU-32S2 (ESP-12K)",
"variant": VARIANT_ESP32S2,
},
"nodemcu-32s": {
"name": "NodeMCU-32S",
"variant": VARIANT_ESP32,
},
"nodemcu-32s2": {
"name": "Ai-Thinker NodeMCU-32S2 (ESP-12K)",
"variant": VARIANT_ESP32S2,
},
"nscreen-32": {
"name": "YeaCreate NSCREEN-32",
"variant": VARIANT_ESP32,
@ -2080,10 +2115,22 @@ BOARDS = {
"name": "RoboHeart Hercules",
"variant": VARIANT_ESP32,
},
"rymcu-esp32-s3-devkitc-1": {
"name": "RYMCU ESP32-S3-DevKitC-1-N8R2 (8 MB QD, 2 MB PSRAM)",
"variant": VARIANT_ESP32S3,
},
"s_odi_ultra": {
"name": "S.ODI Ultra v1",
"variant": VARIANT_ESP32,
},
"seeed_xiao_esp32c3": {
"name": "Seeed Studio XIAO ESP32C3",
"variant": VARIANT_ESP32C3,
},
"seeed_xiao_esp32c6": {
"name": "Seeed Studio XIAO ESP32C6",
"variant": VARIANT_ESP32C6,
},
"seeed_xiao_esp32s3": {
"name": "Seeed Studio XIAO ESP32S3",
"variant": VARIANT_ESP32S3,
@ -2100,34 +2147,38 @@ BOARDS = {
"name": "SG-O AirMon",
"variant": VARIANT_ESP32,
},
"s_odi_ultra": {
"name": "S.ODI Ultra v1",
"sparkfun_esp32_iot_redboard": {
"name": "SparkFun ESP32 IoT RedBoard",
"variant": VARIANT_ESP32,
},
"sparkfun_esp32c6_thing_plus": {
"name": "Sparkfun ESP32-C6 Thing Plus",
"variant": VARIANT_ESP32C6,
},
"sparkfun_esp32_iot_redboard": {
"name": "SparkFun ESP32 IoT RedBoard",
"variant": VARIANT_ESP32,
},
"sparkfun_esp32micromod": {
"name": "SparkFun ESP32 MicroMod",
"variant": VARIANT_ESP32,
},
"sparkfun_esp32s2_thing_plus_c": {
"name": "SparkFun ESP32 Thing Plus C",
"variant": VARIANT_ESP32,
},
"sparkfun_esp32s2_thing_plus": {
"name": "SparkFun ESP32-S2 Thing Plus",
"variant": VARIANT_ESP32S2,
},
"sparkfun_esp32s2_thing_plus_c": {
"name": "SparkFun ESP32 Thing Plus C",
"variant": VARIANT_ESP32,
},
"sparkfun_esp32s3_thing_plus": {
"name": "SPARKFUN_ESP32S3_THING_PLUS",
"variant": VARIANT_ESP32S3,
},
"sparkfun_lora_gateway_1-channel": {
"name": "SparkFun LoRa Gateway 1-Channel",
"variant": VARIANT_ESP32,
},
"sparkfun_qwiic_pocket_esp32c6": {
"name": "SparkFun ESP32-C6 Qwiic Pocket",
"variant": VARIANT_ESP32C6,
},
"tamc_termod_s3": {
"name": "TAMC Termod S3",
"variant": VARIANT_ESP32S3,
@ -2136,6 +2187,10 @@ BOARDS = {
"name": "Unexpected Maker TinyPICO",
"variant": VARIANT_ESP32,
},
"trueverit-iot-driver": {
"name": "Trueverit ESP32 Universal IoT Driver",
"variant": VARIANT_ESP32,
},
"trueverit-iot-driver-mk2": {
"name": "Trueverit ESP32 Universal IoT Driver MK II",
"variant": VARIANT_ESP32,
@ -2144,32 +2199,16 @@ BOARDS = {
"name": "Trueverit ESP32 Universal IoT Driver MK III",
"variant": VARIANT_ESP32,
},
"trueverit-iot-driver": {
"name": "Trueverit ESP32 Universal IoT Driver",
"variant": VARIANT_ESP32,
},
"ttgo-lora32-v1": {
"name": "TTGO LoRa32-OLED V1",
"variant": VARIANT_ESP32,
},
"ttgo-lora32-v21": {
"name": "TTGO LoRa32-OLED v2.1.6",
"variant": VARIANT_ESP32,
},
"ttgo-lora32-v2": {
"name": "TTGO LoRa32-OLED V2",
"variant": VARIANT_ESP32,
},
"ttgo-t1": {
"name": "TTGO T1",
"variant": VARIANT_ESP32,
},
"ttgo-t7-v13-mini32": {
"name": "TTGO T7 V1.3 Mini32",
"variant": VARIANT_ESP32,
},
"ttgo-t7-v14-mini32": {
"name": "TTGO T7 V1.4 Mini32",
"ttgo-lora32-v21": {
"name": "TTGO LoRa32-OLED v2.1.6",
"variant": VARIANT_ESP32,
},
"ttgo-t-beam": {
@ -2184,6 +2223,18 @@ BOARDS = {
"name": "TTGO T-Watch",
"variant": VARIANT_ESP32,
},
"ttgo-t1": {
"name": "TTGO T1",
"variant": VARIANT_ESP32,
},
"ttgo-t7-v13-mini32": {
"name": "TTGO T7 V1.3 Mini32",
"variant": VARIANT_ESP32,
},
"ttgo-t7-v14-mini32": {
"name": "TTGO T7 V1.4 Mini32",
"variant": VARIANT_ESP32,
},
"turta_iot_node": {
"name": "Turta IoT Node",
"variant": VARIANT_ESP32,
@ -2256,9 +2307,17 @@ BOARDS = {
"name": "SQFMI Watchy v2.0",
"variant": VARIANT_ESP32,
},
"wemosbat": {
"name": "WeMos WiFi and Bluetooth Battery",
"variant": VARIANT_ESP32,
"waveshare_esp32_s3_zero": {
"name": "Waveshare ESP32-S3-Zero",
"variant": VARIANT_ESP32S3,
},
"waveshare_esp32s3_touch_lcd_128": {
"name": "Waveshare ESP32-S3-Touch-LCD-1.28 (16 MB QD, 2MB PSRAM)",
"variant": VARIANT_ESP32S3,
},
"weactstudio_esp32c3coreboard": {
"name": "WeAct Studio ESP32C3CoreBoard",
"variant": VARIANT_ESP32C3,
},
"wemos_d1_mini32": {
"name": "WEMOS D1 MINI ESP32",
@ -2268,6 +2327,10 @@ BOARDS = {
"name": "WEMOS D1 R32",
"variant": VARIANT_ESP32,
},
"wemosbat": {
"name": "WeMos WiFi and Bluetooth Battery",
"variant": VARIANT_ESP32,
},
"wesp32": {
"name": "Silicognition wESP32",
"variant": VARIANT_ESP32,
@ -2276,14 +2339,14 @@ BOARDS = {
"name": "Widora AIR",
"variant": VARIANT_ESP32,
},
"wifiduino32c3": {
"name": "Blinker WiFiduinoV2 (ESP32-C3)",
"variant": VARIANT_ESP32C3,
},
"wifiduino32": {
"name": "Blinker WiFiduino32",
"variant": VARIANT_ESP32,
},
"wifiduino32c3": {
"name": "Blinker WiFiduinoV2 (ESP32-C3)",
"variant": VARIANT_ESP32C3,
},
"wifiduino32s3": {
"name": "Blinker WiFiduino32S3",
"variant": VARIANT_ESP32S3,
@ -2292,12 +2355,32 @@ BOARDS = {
"name": "Pycom WiPy3",
"variant": VARIANT_ESP32,
},
"ws_esp32_s3_matrix": {
"name": "Waveshare ESP32-S3-Matrix",
"variant": VARIANT_ESP32S3,
},
"wt32-eth01": {
"name": "Wireless-Tag WT32-ETH01 Ethernet Module",
"variant": VARIANT_ESP32,
},
"wt32-sc01-plus": {
"name": "wt32-sc01-plus",
"variant": VARIANT_ESP32S3,
},
"xinabox_cw02": {
"name": "XinaBox CW02",
"variant": VARIANT_ESP32,
},
"yb_esp32s3_amp_v2": {
"name": "YelloByte YB-ESP32-S3-AMP (Rev.2)",
"variant": VARIANT_ESP32S3,
},
"yb_esp32s3_amp_v3": {
"name": "YelloByte YB-ESP32-S3-AMP (Rev.3)",
"variant": VARIANT_ESP32S3,
},
"yb_esp32s3_eth": {
"name": "YelloByte YB-ESP32-S3-ETH",
"variant": VARIANT_ESP32S3,
},
}

View File

@ -17,16 +17,20 @@ VARIANT_ESP32S2 = "ESP32S2"
VARIANT_ESP32S3 = "ESP32S3"
VARIANT_ESP32C2 = "ESP32C2"
VARIANT_ESP32C3 = "ESP32C3"
VARIANT_ESP32C5 = "ESP32C5"
VARIANT_ESP32C6 = "ESP32C6"
VARIANT_ESP32H2 = "ESP32H2"
VARIANT_ESP32P4 = "ESP32P4"
VARIANTS = [
VARIANT_ESP32,
VARIANT_ESP32S2,
VARIANT_ESP32S3,
VARIANT_ESP32C2,
VARIANT_ESP32C3,
VARIANT_ESP32C5,
VARIANT_ESP32C6,
VARIANT_ESP32H2,
VARIANT_ESP32P4,
]
VARIANT_FRIENDLY = {
@ -35,8 +39,10 @@ VARIANT_FRIENDLY = {
VARIANT_ESP32S3: "ESP32-S3",
VARIANT_ESP32C2: "ESP32-C2",
VARIANT_ESP32C3: "ESP32-C3",
VARIANT_ESP32C5: "ESP32-C5",
VARIANT_ESP32C6: "ESP32-C6",
VARIANT_ESP32H2: "ESP32-H2",
VARIANT_ESP32P4: "ESP32-P4",
}
esp32_ns = cg.esphome_ns.namespace("esp32")

View File

@ -15,8 +15,9 @@
#ifdef USE_ARDUINO
#include <Esp.h>
#else
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
#include <esp_clk_tree.h>
#endif
void setup();
void loop();
#endif
@ -63,7 +64,13 @@ uint32_t arch_get_cpu_cycle_count() { return cpu_hal_get_cycle_count(); }
uint32_t arch_get_cpu_freq_hz() {
uint32_t freq = 0;
#ifdef USE_ESP_IDF
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
esp_clk_tree_src_get_freq_hz(SOC_MOD_CLK_CPU, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &freq);
#else
rtc_cpu_freq_config_t config;
rtc_clk_cpu_freq_get_config(&config);
freq = config.freq_mhz * 1000000U;
#endif
#elif defined(USE_ARDUINO)
freq = ESP.getCpuFreqMHz() * 1000000;
#endif

View File

@ -1,6 +1,7 @@
from collections.abc import Callable
from dataclasses import dataclass
import logging
from typing import Any, Callable
from typing import Any
from esphome import pins
import esphome.codegen as cg
@ -26,8 +27,10 @@ from .const import (
VARIANT_ESP32,
VARIANT_ESP32C2,
VARIANT_ESP32C3,
VARIANT_ESP32C5,
VARIANT_ESP32C6,
VARIANT_ESP32H2,
VARIANT_ESP32P4,
VARIANT_ESP32S2,
VARIANT_ESP32S3,
esp32_ns,
@ -35,8 +38,10 @@ from .const import (
from .gpio_esp32 import esp32_validate_gpio_pin, esp32_validate_supports
from .gpio_esp32_c2 import esp32_c2_validate_gpio_pin, esp32_c2_validate_supports
from .gpio_esp32_c3 import esp32_c3_validate_gpio_pin, esp32_c3_validate_supports
from .gpio_esp32_c5 import esp32_c5_validate_gpio_pin, esp32_c5_validate_supports
from .gpio_esp32_c6 import esp32_c6_validate_gpio_pin, esp32_c6_validate_supports
from .gpio_esp32_h2 import esp32_h2_validate_gpio_pin, esp32_h2_validate_supports
from .gpio_esp32_p4 import esp32_p4_validate_gpio_pin, esp32_p4_validate_supports
from .gpio_esp32_s2 import esp32_s2_validate_gpio_pin, esp32_s2_validate_supports
from .gpio_esp32_s3 import esp32_s3_validate_gpio_pin, esp32_s3_validate_supports
@ -97,6 +102,10 @@ _esp32_validations = {
pin_validation=esp32_c3_validate_gpio_pin,
usage_validation=esp32_c3_validate_supports,
),
VARIANT_ESP32C5: ESP32ValidationFunctions(
pin_validation=esp32_c5_validate_gpio_pin,
usage_validation=esp32_c5_validate_supports,
),
VARIANT_ESP32C6: ESP32ValidationFunctions(
pin_validation=esp32_c6_validate_gpio_pin,
usage_validation=esp32_c6_validate_supports,
@ -105,6 +114,10 @@ _esp32_validations = {
pin_validation=esp32_h2_validate_gpio_pin,
usage_validation=esp32_h2_validate_supports,
),
VARIANT_ESP32P4: ESP32ValidationFunctions(
pin_validation=esp32_p4_validate_gpio_pin,
usage_validation=esp32_p4_validate_supports,
),
VARIANT_ESP32S2: ESP32ValidationFunctions(
pin_validation=esp32_s2_validate_gpio_pin,
usage_validation=esp32_s2_validate_supports,

View File

@ -0,0 +1,45 @@
import logging
import esphome.config_validation as cv
from esphome.const import CONF_INPUT, CONF_MODE, CONF_NUMBER
from esphome.pins import check_strapping_pin
_ESP32C5_SPI_PSRAM_PINS = {
16: "SPICS0",
17: "SPIQ",
18: "SPIWP",
19: "VDD_SPI",
20: "SPIHD",
21: "SPICLK",
22: "SPID",
}
_ESP32C5_STRAPPING_PINS = {2, 7, 27, 28}
_LOGGER = logging.getLogger(__name__)
def esp32_c5_validate_gpio_pin(value):
if value < 0 or value > 28:
raise cv.Invalid(f"Invalid pin number: {value} (must be 0-28)")
if value in _ESP32C5_SPI_PSRAM_PINS:
raise cv.Invalid(
f"This pin cannot be used on ESP32-C5s and is already used by the SPI/PSRAM interface (function: {_ESP32C5_SPI_PSRAM_PINS[value]})"
)
return value
def esp32_c5_validate_supports(value):
num = value[CONF_NUMBER]
mode = value[CONF_MODE]
is_input = mode[CONF_INPUT]
if num < 0 or num > 28:
raise cv.Invalid(f"Invalid pin number: {num} (must be 0-28)")
if is_input:
# All ESP32 pins support input mode
pass
check_strapping_pin(value, _ESP32C5_STRAPPING_PINS, _LOGGER)
return value

View File

@ -0,0 +1,43 @@
import logging
import esphome.config_validation as cv
from esphome.const import CONF_INPUT, CONF_MODE, CONF_NUMBER
_ESP32P4_USB_JTAG_PINS = {24, 25}
_ESP32P4_STRAPPING_PINS = {34, 35, 36, 37, 38}
_LOGGER = logging.getLogger(__name__)
def esp32_p4_validate_gpio_pin(value):
if value < 0 or value > 54:
raise cv.Invalid(f"Invalid pin number: {value} (must be 0-54)")
if value in _ESP32P4_STRAPPING_PINS:
_LOGGER.warning(
"GPIO%d is a Strapping PIN and should be avoided.\n"
"Attaching external pullup/down resistors to strapping pins can cause unexpected failures.\n"
"See https://esphome.io/guides/faq.html#why-am-i-getting-a-warning-about-strapping-pins",
value,
)
if value in _ESP32P4_USB_JTAG_PINS:
_LOGGER.warning(
"GPIO%d is reserved for the USB-Serial-JTAG interface.\n"
"To use this pin as GPIO, USB-Serial-JTAG will be disabled.",
value,
)
return value
def esp32_p4_validate_supports(value):
num = value[CONF_NUMBER]
mode = value[CONF_MODE]
is_input = mode[CONF_INPUT]
if num < 0 or num > 54:
raise cv.Invalid(f"Invalid pin number: {value} (must be 0-54)")
if is_input:
# All ESP32 pins support input mode
pass
return value

View File

@ -1,8 +1,8 @@
#ifdef USE_ESP32
#include "esphome/core/preferences.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include "esphome/core/preferences.h"
#include <nvs_flash.h>
#include <cstring>
#include <cinttypes>
@ -84,7 +84,7 @@ class ESP32Preferences : public ESPPreferences {
if (err == 0)
return;
ESP_LOGW(TAG, "nvs_open failed: %s - erasing NVS...", esp_err_to_name(err));
ESP_LOGW(TAG, "nvs_open failed: %s - erasing NVS", esp_err_to_name(err));
nvs_flash_deinit();
nvs_flash_erase();
nvs_flash_init();
@ -111,7 +111,7 @@ class ESP32Preferences : public ESPPreferences {
if (s_pending_save.empty())
return true;
ESP_LOGD(TAG, "Saving %d preferences to flash...", s_pending_save.size());
ESP_LOGV(TAG, "Saving %d items...", s_pending_save.size());
// goal try write all pending saves even if one fails
int cached = 0, written = 0, failed = 0;
esp_err_t last_err = ESP_OK;
@ -139,10 +139,10 @@ class ESP32Preferences : public ESPPreferences {
}
s_pending_save.erase(s_pending_save.begin() + i);
}
ESP_LOGD(TAG, "Saving %d preferences to flash: %d cached, %d written, %d failed", cached + written + failed, cached,
written, failed);
ESP_LOGD(TAG, "Writing %d items: %d cached, %d written, %d failed", cached + written + failed, cached, written,
failed);
if (failed > 0) {
ESP_LOGE(TAG, "Error saving %d preferences to flash. Last error=%s for key=%s", failed, esp_err_to_name(last_err),
ESP_LOGE(TAG, "Writing %d items failed. Last error=%s for key=%s", failed, esp_err_to_name(last_err),
last_key.c_str());
}
@ -173,7 +173,7 @@ class ESP32Preferences : public ESPPreferences {
}
bool reset() override {
ESP_LOGD(TAG, "Cleaning up preferences in flash...");
ESP_LOGD(TAG, "Erasing storage");
s_pending_save.clear();
nvs_flash_deinit();

View File

@ -1,3 +1,4 @@
from enum import Enum
import re
from esphome import automation
@ -12,9 +13,110 @@ import esphome.final_validate as fv
DEPENDENCIES = ["esp32"]
CODEOWNERS = ["@jesserockz", "@Rapsssito"]
class BTLoggers(Enum):
"""Bluetooth logger categories available in ESP-IDF.
Each logger controls debug output for a specific Bluetooth subsystem.
The value is the ESP-IDF sdkconfig option name for controlling the log level.
"""
# Core Stack Layers
HCI = "CONFIG_BT_LOG_HCI_TRACE_LEVEL"
"""Host Controller Interface - Low-level interface between host and controller"""
BTM = "CONFIG_BT_LOG_BTM_TRACE_LEVEL"
"""Bluetooth Manager - Core device control, connections, and security"""
L2CAP = "CONFIG_BT_LOG_L2CAP_TRACE_LEVEL"
"""Logical Link Control and Adaptation Protocol - Connection multiplexing"""
RFCOMM = "CONFIG_BT_LOG_RFCOMM_TRACE_LEVEL"
"""Serial port emulation over Bluetooth (Classic only)"""
SDP = "CONFIG_BT_LOG_SDP_TRACE_LEVEL"
"""Service Discovery Protocol - Service discovery (Classic only)"""
GAP = "CONFIG_BT_LOG_GAP_TRACE_LEVEL"
"""Generic Access Profile - Device discovery and connections"""
# Network Protocols
BNEP = "CONFIG_BT_LOG_BNEP_TRACE_LEVEL"
"""Bluetooth Network Encapsulation Protocol - IP over Bluetooth"""
PAN = "CONFIG_BT_LOG_PAN_TRACE_LEVEL"
"""Personal Area Networking - Ethernet over Bluetooth"""
# Audio/Video Profiles (Classic Bluetooth)
A2D = "CONFIG_BT_LOG_A2D_TRACE_LEVEL"
"""Advanced Audio Distribution - A2DP audio streaming"""
AVDT = "CONFIG_BT_LOG_AVDT_TRACE_LEVEL"
"""Audio/Video Distribution Transport - A2DP transport protocol"""
AVCT = "CONFIG_BT_LOG_AVCT_TRACE_LEVEL"
"""Audio/Video Control Transport - AVRCP transport protocol"""
AVRC = "CONFIG_BT_LOG_AVRC_TRACE_LEVEL"
"""Audio/Video Remote Control - Media playback control"""
# Security
SMP = "CONFIG_BT_LOG_SMP_TRACE_LEVEL"
"""Security Manager Protocol - BLE pairing and encryption"""
# Application Layer
BTIF = "CONFIG_BT_LOG_BTIF_TRACE_LEVEL"
"""Bluetooth Interface - Application interface layer"""
BTC = "CONFIG_BT_LOG_BTC_TRACE_LEVEL"
"""Bluetooth Common - Task handling and coordination"""
# BLE Specific
BLE_SCAN = "CONFIG_BT_LOG_BLE_SCAN_TRACE_LEVEL"
"""BLE scanning operations"""
GATT = "CONFIG_BT_LOG_GATT_TRACE_LEVEL"
"""Generic Attribute Profile - BLE data exchange protocol"""
# Other Profiles
MCA = "CONFIG_BT_LOG_MCA_TRACE_LEVEL"
"""Multi-Channel Adaptation - Health device profile"""
HID = "CONFIG_BT_LOG_HID_TRACE_LEVEL"
"""Human Interface Device - Keyboards, mice, controllers"""
APPL = "CONFIG_BT_LOG_APPL_TRACE_LEVEL"
"""Application layer logging"""
OSI = "CONFIG_BT_LOG_OSI_TRACE_LEVEL"
"""OS abstraction layer - Threading, memory, timers"""
BLUFI = "CONFIG_BT_LOG_BLUFI_TRACE_LEVEL"
"""ESP32 WiFi provisioning over Bluetooth"""
# Set to track which loggers are needed by components
_required_loggers: set[BTLoggers] = set()
def register_bt_logger(*loggers: BTLoggers) -> None:
"""Register Bluetooth logger categories that a component needs.
Args:
*loggers: One or more BTLoggers enum members
"""
for logger in loggers:
if not isinstance(logger, BTLoggers):
raise TypeError(
f"Logger must be a BTLoggers enum member, got {type(logger)}"
)
_required_loggers.add(logger)
CONF_BLE_ID = "ble_id"
CONF_IO_CAPABILITY = "io_capability"
CONF_ADVERTISING_CYCLE_TIME = "advertising_cycle_time"
CONF_DISABLE_BT_LOGS = "disable_bt_logs"
NO_BLUETOOTH_VARIANTS = [const.VARIANT_ESP32S2]
@ -62,6 +164,9 @@ CONFIG_SCHEMA = cv.Schema(
cv.Optional(
CONF_ADVERTISING_CYCLE_TIME, default="10s"
): cv.positive_time_period_milliseconds,
cv.SplitDefault(CONF_DISABLE_BT_LOGS, esp32_idf=True): cv.All(
cv.only_with_esp_idf, cv.boolean
),
}
).extend(cv.COMPONENT_SCHEMA)
@ -140,6 +245,16 @@ async def to_code(config):
add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True)
add_idf_sdkconfig_option("CONFIG_BT_BLE_42_FEATURES_SUPPORTED", True)
# Register the core BLE loggers that are always needed
register_bt_logger(BTLoggers.GAP, BTLoggers.BTM, BTLoggers.HCI)
# Apply logger settings if log disabling is enabled
if config.get(CONF_DISABLE_BT_LOGS, False):
# Disable all Bluetooth loggers that are not required
for logger in BTLoggers:
if logger not in _required_loggers:
add_idf_sdkconfig_option(f"{logger.value}_NONE", True)
cg.add_define("USE_ESP32_BLE")

View File

@ -23,12 +23,15 @@ namespace esp32_ble {
static const char *const TAG = "esp32_ble";
// Maximum size of the BLE event queue
static constexpr size_t MAX_BLE_QUEUE_SIZE = SCAN_RESULT_BUFFER_SIZE * 2;
static RAMAllocator<BLEEvent> EVENT_ALLOCATOR( // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
RAMAllocator<BLEEvent>::ALLOW_FAILURE | RAMAllocator<BLEEvent>::ALLOC_INTERNAL);
void ESP32BLE::setup() {
global_ble = this;
ESP_LOGCONFIG(TAG, "Setting up BLE...");
ESP_LOGCONFIG(TAG, "Running setup");
if (!ble_pre_setup_()) {
ESP_LOGE(TAG, "BLE could not be prepared for configuration");
@ -270,14 +273,14 @@ void ESP32BLE::loop() {
case BLE_COMPONENT_STATE_DISABLED:
return;
case BLE_COMPONENT_STATE_DISABLE: {
ESP_LOGD(TAG, "Disabling BLE...");
ESP_LOGD(TAG, "Disabling");
for (auto *ble_event_handler : this->ble_status_event_handlers_) {
ble_event_handler->ble_before_disabled_event_handler();
}
if (!ble_dismantle_()) {
ESP_LOGE(TAG, "BLE could not be dismantled");
ESP_LOGE(TAG, "Could not be dismantled");
this->mark_failed();
return;
}
@ -285,11 +288,11 @@ void ESP32BLE::loop() {
return;
}
case BLE_COMPONENT_STATE_ENABLE: {
ESP_LOGD(TAG, "Enabling BLE...");
ESP_LOGD(TAG, "Enabling");
this->state_ = BLE_COMPONENT_STATE_OFF;
if (!ble_setup_()) {
ESP_LOGE(TAG, "BLE could not be set up");
ESP_LOGE(TAG, "Could not be set up");
this->mark_failed();
return;
}
@ -304,20 +307,52 @@ void ESP32BLE::loop() {
BLEEvent *ble_event = this->ble_events_.pop();
while (ble_event != nullptr) {
switch (ble_event->type_) {
case BLEEvent::GATTS:
this->real_gatts_event_handler_(ble_event->event_.gatts.gatts_event, ble_event->event_.gatts.gatts_if,
&ble_event->event_.gatts.gatts_param);
case BLEEvent::GATTS: {
esp_gatts_cb_event_t event = ble_event->event_.gatts.gatts_event;
esp_gatt_if_t gatts_if = ble_event->event_.gatts.gatts_if;
esp_ble_gatts_cb_param_t *param = ble_event->event_.gatts.gatts_param;
ESP_LOGV(TAG, "gatts_event [esp_gatt_if: %d] - %d", gatts_if, event);
for (auto *gatts_handler : this->gatts_event_handlers_) {
gatts_handler->gatts_event_handler(event, gatts_if, param);
}
break;
case BLEEvent::GATTC:
this->real_gattc_event_handler_(ble_event->event_.gattc.gattc_event, ble_event->event_.gattc.gattc_if,
&ble_event->event_.gattc.gattc_param);
}
case BLEEvent::GATTC: {
esp_gattc_cb_event_t event = ble_event->event_.gattc.gattc_event;
esp_gatt_if_t gattc_if = ble_event->event_.gattc.gattc_if;
esp_ble_gattc_cb_param_t *param = ble_event->event_.gattc.gattc_param;
ESP_LOGV(TAG, "gattc_event [esp_gatt_if: %d] - %d", gattc_if, event);
for (auto *gattc_handler : this->gattc_event_handlers_) {
gattc_handler->gattc_event_handler(event, gattc_if, param);
}
break;
case BLEEvent::GAP:
this->real_gap_event_handler_(ble_event->event_.gap.gap_event, &ble_event->event_.gap.gap_param);
}
case BLEEvent::GAP: {
esp_gap_ble_cb_event_t gap_event = ble_event->event_.gap.gap_event;
if (gap_event == ESP_GAP_BLE_SCAN_RESULT_EVT) {
// Use the new scan event handler - no memcpy!
for (auto *scan_handler : this->gap_scan_event_handlers_) {
scan_handler->gap_scan_event_handler(ble_event->scan_result());
}
} else if (gap_event == ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT ||
gap_event == ESP_GAP_BLE_SCAN_START_COMPLETE_EVT ||
gap_event == 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);
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));
}
}
break;
}
default:
break;
}
// Destructor will clean up external allocations for GATTC/GATTS
ble_event->~BLEEvent();
EVENT_ALLOCATOR.deallocate(ble_event, 1);
ble_event = this->ble_events_.pop();
@ -327,59 +362,55 @@ void ESP32BLE::loop() {
}
}
void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
template<typename... Args> void enqueue_ble_event(Args... args) {
if (global_ble->ble_events_.size() >= MAX_BLE_QUEUE_SIZE) {
ESP_LOGD(TAG, "Event queue full (%zu), dropping event", MAX_BLE_QUEUE_SIZE);
return;
}
BLEEvent *new_event = EVENT_ALLOCATOR.allocate(1);
if (new_event == nullptr) {
// Memory too fragmented to allocate new event. Can only drop it until memory comes back
return;
}
new (new_event) BLEEvent(event, param);
new (new_event) BLEEvent(args...);
global_ble->ble_events_.push(new_event);
} // NOLINT(clang-analyzer-unix.Malloc)
void ESP32BLE::real_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
ESP_LOGV(TAG, "(BLE) gap_event_handler - %d", event);
for (auto *gap_handler : this->gap_event_handlers_) {
gap_handler->gap_event_handler(event, param);
// Explicit template instantiations for the friend function
template void enqueue_ble_event(esp_gap_ble_cb_event_t, esp_ble_gap_cb_param_t *);
template void enqueue_ble_event(esp_gatts_cb_event_t, esp_gatt_if_t, esp_ble_gatts_cb_param_t *);
template void enqueue_ble_event(esp_gattc_cb_event_t, esp_gatt_if_t, esp_ble_gattc_cb_param_t *);
void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
switch (event) {
// Only queue the 4 GAP events we actually handle
case ESP_GAP_BLE_SCAN_RESULT_EVT:
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:
enqueue_ble_event(event, param);
return;
// Ignore these GAP events as they are not relevant for our use case
case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT:
case ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT:
return;
default:
break;
}
ESP_LOGW(TAG, "Ignoring unexpected GAP event type: %d", event);
}
void ESP32BLE::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t *param) {
BLEEvent *new_event = EVENT_ALLOCATOR.allocate(1);
if (new_event == nullptr) {
// Memory too fragmented to allocate new event. Can only drop it until memory comes back
return;
}
new (new_event) BLEEvent(event, gatts_if, param);
global_ble->ble_events_.push(new_event);
} // NOLINT(clang-analyzer-unix.Malloc)
void ESP32BLE::real_gatts_event_handler_(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t *param) {
ESP_LOGV(TAG, "(BLE) gatts_event [esp_gatt_if: %d] - %d", gatts_if, event);
for (auto *gatts_handler : this->gatts_event_handlers_) {
gatts_handler->gatts_event_handler(event, gatts_if, param);
}
enqueue_ble_event(event, gatts_if, param);
}
void ESP32BLE::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) {
BLEEvent *new_event = EVENT_ALLOCATOR.allocate(1);
if (new_event == nullptr) {
// Memory too fragmented to allocate new event. Can only drop it until memory comes back
return;
}
new (new_event) BLEEvent(event, gattc_if, param);
global_ble->ble_events_.push(new_event);
} // NOLINT(clang-analyzer-unix.Malloc)
void ESP32BLE::real_gattc_event_handler_(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) {
ESP_LOGV(TAG, "(BLE) gattc_event [esp_gatt_if: %d] - %d", gattc_if, event);
for (auto *gattc_handler : this->gattc_event_handlers_) {
gattc_handler->gattc_event_handler(event, gattc_if, param);
}
enqueue_ble_event(event, gattc_if, param);
}
float ESP32BLE::get_setup_priority() const { return setup_priority::BLUETOOTH; }
@ -408,10 +439,12 @@ void ESP32BLE::dump_config() {
io_capability_s = "invalid";
break;
}
ESP_LOGCONFIG(TAG, "ESP32 BLE:");
ESP_LOGCONFIG(TAG, " MAC address: %02X:%02X:%02X:%02X:%02X:%02X", mac_address[0], mac_address[1], mac_address[2],
mac_address[3], mac_address[4], mac_address[5]);
ESP_LOGCONFIG(TAG, " IO Capability: %s", io_capability_s);
ESP_LOGCONFIG(TAG,
"ESP32 BLE:\n"
" MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n"
" IO Capability: %s",
mac_address[0], mac_address[1], mac_address[2], mac_address[3], mac_address[4], mac_address[5],
io_capability_s);
} else {
ESP_LOGCONFIG(TAG, "ESP32 BLE: bluetooth stack is not enabled");
}

View File

@ -2,6 +2,7 @@
#include "ble_advertising.h"
#include "ble_uuid.h"
#include "ble_scan_result.h"
#include <functional>
@ -22,6 +23,13 @@
namespace esphome {
namespace esp32_ble {
// Maximum number of BLE scan results to buffer
#ifdef USE_PSRAM
static constexpr uint8_t SCAN_RESULT_BUFFER_SIZE = 32;
#else
static constexpr uint8_t SCAN_RESULT_BUFFER_SIZE = 20;
#endif
uint64_t ble_addr_to_uint64(const esp_bd_addr_t address);
// NOLINTNEXTLINE(modernize-use-using)
@ -57,6 +65,11 @@ class GAPEventHandler {
virtual void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) = 0;
};
class GAPScanEventHandler {
public:
virtual void gap_scan_event_handler(const BLEScanResult &scan_result) = 0;
};
class GATTcEventHandler {
public:
virtual void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
@ -101,6 +114,9 @@ class ESP32BLE : public Component {
void advertising_register_raw_advertisement_callback(std::function<void(bool)> &&callback);
void register_gap_event_handler(GAPEventHandler *handler) { this->gap_event_handlers_.push_back(handler); }
void register_gap_scan_event_handler(GAPScanEventHandler *handler) {
this->gap_scan_event_handlers_.push_back(handler);
}
void register_gattc_event_handler(GATTcEventHandler *handler) { this->gattc_event_handlers_.push_back(handler); }
void register_gatts_event_handler(GATTsEventHandler *handler) { this->gatts_event_handlers_.push_back(handler); }
void register_ble_status_event_handler(BLEStatusEventHandler *handler) {
@ -113,16 +129,16 @@ class ESP32BLE : public Component {
static void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);
static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);
void real_gatts_event_handler_(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
void real_gattc_event_handler_(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);
void real_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);
bool ble_setup_();
bool ble_dismantle_();
bool ble_pre_setup_();
void advertising_init_();
private:
template<typename... Args> friend void enqueue_ble_event(Args... args);
std::vector<GAPEventHandler *> gap_event_handlers_;
std::vector<GAPScanEventHandler *> gap_scan_event_handlers_;
std::vector<GATTcEventHandler *> gattc_event_handlers_;
std::vector<GATTsEventHandler *> gatts_event_handlers_;
std::vector<BLEStatusEventHandler *> ble_status_event_handlers_;

View File

@ -2,92 +2,232 @@
#ifdef USE_ESP32
#include <cstddef> // for offsetof
#include <vector>
#include <esp_gap_ble_api.h>
#include <esp_gattc_api.h>
#include <esp_gatts_api.h>
#include "ble_scan_result.h"
namespace esphome {
namespace esp32_ble {
// Compile-time verification that ESP-IDF scan complete events only contain a status field
// This ensures our reinterpret_cast in ble.cpp is safe
static_assert(sizeof(esp_ble_gap_cb_param_t::ble_scan_param_cmpl_evt_param) == sizeof(esp_bt_status_t),
"ESP-IDF scan_param_cmpl structure has unexpected size");
static_assert(sizeof(esp_ble_gap_cb_param_t::ble_scan_start_cmpl_evt_param) == sizeof(esp_bt_status_t),
"ESP-IDF scan_start_cmpl structure has unexpected size");
static_assert(sizeof(esp_ble_gap_cb_param_t::ble_scan_stop_cmpl_evt_param) == sizeof(esp_bt_status_t),
"ESP-IDF scan_stop_cmpl structure has unexpected size");
// Verify the status field is at offset 0 (first member)
static_assert(offsetof(esp_ble_gap_cb_param_t, scan_param_cmpl.status) ==
offsetof(esp_ble_gap_cb_param_t, scan_param_cmpl),
"status must be first member of scan_param_cmpl");
static_assert(offsetof(esp_ble_gap_cb_param_t, scan_start_cmpl.status) ==
offsetof(esp_ble_gap_cb_param_t, scan_start_cmpl),
"status must be first member of scan_start_cmpl");
static_assert(offsetof(esp_ble_gap_cb_param_t, scan_stop_cmpl.status) ==
offsetof(esp_ble_gap_cb_param_t, scan_stop_cmpl),
"status must be first member of scan_stop_cmpl");
// Received GAP, GATTC and GATTS events are only queued, and get processed in the main loop().
// This class stores each event in a single type.
// This class stores each event with minimal memory usage.
// GAP events (99% of traffic) don't have the vector overhead.
// GATTC/GATTS events use heap allocation for their param and data.
//
// Event flow:
// 1. ESP-IDF BLE stack calls our static handlers in the BLE task context
// 2. The handlers create a BLEEvent instance, copying only the data we need
// 3. The event is pushed to a thread-safe queue
// 4. In the main loop(), events are popped from the queue and processed
// 5. The event destructor cleans up any external allocations
//
// Thread safety:
// - GAP events: We copy only the fields we need directly into the union
// - GATTC/GATTS events: We heap-allocate and copy the entire param struct, ensuring
// the data remains valid even after the BLE callback returns. The original
// param pointer from ESP-IDF is only valid during the callback.
class BLEEvent {
public:
BLEEvent(esp_gap_ble_cb_event_t e, esp_ble_gap_cb_param_t *p) {
this->event_.gap.gap_event = e;
memcpy(&this->event_.gap.gap_param, p, sizeof(esp_ble_gap_cb_param_t));
this->type_ = GAP;
};
BLEEvent(esp_gattc_cb_event_t e, esp_gatt_if_t i, esp_ble_gattc_cb_param_t *p) {
this->event_.gattc.gattc_event = e;
this->event_.gattc.gattc_if = i;
memcpy(&this->event_.gattc.gattc_param, p, sizeof(esp_ble_gattc_cb_param_t));
// Need to also make a copy of relevant event data.
switch (e) {
case ESP_GATTC_NOTIFY_EVT:
this->data.assign(p->notify.value, p->notify.value + p->notify.value_len);
this->event_.gattc.gattc_param.notify.value = this->data.data();
break;
case ESP_GATTC_READ_CHAR_EVT:
case ESP_GATTC_READ_DESCR_EVT:
this->data.assign(p->read.value, p->read.value + p->read.value_len);
this->event_.gattc.gattc_param.read.value = this->data.data();
break;
default:
break;
}
this->type_ = GATTC;
};
BLEEvent(esp_gatts_cb_event_t e, esp_gatt_if_t i, esp_ble_gatts_cb_param_t *p) {
this->event_.gatts.gatts_event = e;
this->event_.gatts.gatts_if = i;
memcpy(&this->event_.gatts.gatts_param, p, sizeof(esp_ble_gatts_cb_param_t));
// Need to also make a copy of relevant event data.
switch (e) {
case ESP_GATTS_WRITE_EVT:
this->data.assign(p->write.value, p->write.value + p->write.len);
this->event_.gatts.gatts_param.write.value = this->data.data();
break;
default:
break;
}
this->type_ = GATTS;
};
union {
// NOLINTNEXTLINE(readability-identifier-naming)
struct gap_event {
esp_gap_ble_cb_event_t gap_event;
esp_ble_gap_cb_param_t gap_param;
} gap;
// NOLINTNEXTLINE(readability-identifier-naming)
struct gattc_event {
esp_gattc_cb_event_t gattc_event;
esp_gatt_if_t gattc_if;
esp_ble_gattc_cb_param_t gattc_param;
} gattc;
// NOLINTNEXTLINE(readability-identifier-naming)
struct gatts_event {
esp_gatts_cb_event_t gatts_event;
esp_gatt_if_t gatts_if;
esp_ble_gatts_cb_param_t gatts_param;
} gatts;
} event_;
std::vector<uint8_t> data{};
// NOLINTNEXTLINE(readability-identifier-naming)
enum ble_event_t : uint8_t {
GAP,
GATTC,
GATTS,
} type_;
};
// Constructor for GAP events - no external allocations needed
BLEEvent(esp_gap_ble_cb_event_t e, esp_ble_gap_cb_param_t *p) {
this->type_ = GAP;
this->event_.gap.gap_event = e;
if (p == nullptr) {
return; // Invalid event, but we can't log in header file
}
// Only copy the data we actually use for each GAP event type
switch (e) {
case ESP_GAP_BLE_SCAN_RESULT_EVT:
// Copy only the fields we use from scan results
memcpy(this->event_.gap.scan_result.bda, p->scan_rst.bda, sizeof(esp_bd_addr_t));
this->event_.gap.scan_result.ble_addr_type = p->scan_rst.ble_addr_type;
this->event_.gap.scan_result.rssi = p->scan_rst.rssi;
this->event_.gap.scan_result.adv_data_len = p->scan_rst.adv_data_len;
this->event_.gap.scan_result.scan_rsp_len = p->scan_rst.scan_rsp_len;
this->event_.gap.scan_result.search_evt = p->scan_rst.search_evt;
memcpy(this->event_.gap.scan_result.ble_adv, p->scan_rst.ble_adv,
ESP_BLE_ADV_DATA_LEN_MAX + ESP_BLE_SCAN_RSP_DATA_LEN_MAX);
break;
case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT:
this->event_.gap.scan_complete.status = p->scan_param_cmpl.status;
break;
case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT:
this->event_.gap.scan_complete.status = p->scan_start_cmpl.status;
break;
case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT:
this->event_.gap.scan_complete.status = p->scan_stop_cmpl.status;
break;
default:
// We only handle 4 GAP event types, others are dropped
break;
}
}
// Constructor for GATTC events - uses heap allocation
// Creates a copy of the param struct since the original is only valid during the callback
BLEEvent(esp_gattc_cb_event_t e, esp_gatt_if_t i, esp_ble_gattc_cb_param_t *p) {
this->type_ = GATTC;
this->event_.gattc.gattc_event = e;
this->event_.gattc.gattc_if = i;
if (p == nullptr) {
this->event_.gattc.gattc_param = nullptr;
this->event_.gattc.data = nullptr;
return; // Invalid event, but we can't log in header file
}
// Heap-allocate param and data
// Heap allocation is used because GATTC/GATTS events are rare (<1% of events)
// while GAP events (99%) are stored inline to minimize memory usage
this->event_.gattc.gattc_param = new esp_ble_gattc_cb_param_t(*p);
// Copy data for events that need it
switch (e) {
case ESP_GATTC_NOTIFY_EVT:
this->event_.gattc.data = new std::vector<uint8_t>(p->notify.value, p->notify.value + p->notify.value_len);
this->event_.gattc.gattc_param->notify.value = this->event_.gattc.data->data();
break;
case ESP_GATTC_READ_CHAR_EVT:
case ESP_GATTC_READ_DESCR_EVT:
this->event_.gattc.data = new std::vector<uint8_t>(p->read.value, p->read.value + p->read.value_len);
this->event_.gattc.gattc_param->read.value = this->event_.gattc.data->data();
break;
default:
this->event_.gattc.data = nullptr;
break;
}
}
// Constructor for GATTS events - uses heap allocation
// Creates a copy of the param struct since the original is only valid during the callback
BLEEvent(esp_gatts_cb_event_t e, esp_gatt_if_t i, esp_ble_gatts_cb_param_t *p) {
this->type_ = GATTS;
this->event_.gatts.gatts_event = e;
this->event_.gatts.gatts_if = i;
if (p == nullptr) {
this->event_.gatts.gatts_param = nullptr;
this->event_.gatts.data = nullptr;
return; // Invalid event, but we can't log in header file
}
// Heap-allocate param and data
// Heap allocation is used because GATTC/GATTS events are rare (<1% of events)
// while GAP events (99%) are stored inline to minimize memory usage
this->event_.gatts.gatts_param = new esp_ble_gatts_cb_param_t(*p);
// Copy data for events that need it
switch (e) {
case ESP_GATTS_WRITE_EVT:
this->event_.gatts.data = new std::vector<uint8_t>(p->write.value, p->write.value + p->write.len);
this->event_.gatts.gatts_param->write.value = this->event_.gatts.data->data();
break;
default:
this->event_.gatts.data = nullptr;
break;
}
}
// Destructor to clean up heap allocations
~BLEEvent() {
switch (this->type_) {
case GATTC:
delete this->event_.gattc.gattc_param;
delete this->event_.gattc.data;
break;
case GATTS:
delete this->event_.gatts.gatts_param;
delete this->event_.gatts.data;
break;
default:
break;
}
}
// Disable copy to prevent double-delete
BLEEvent(const BLEEvent &) = delete;
BLEEvent &operator=(const BLEEvent &) = delete;
union {
// NOLINTNEXTLINE(readability-identifier-naming)
struct gap_event {
esp_gap_ble_cb_event_t gap_event;
union {
BLEScanResult scan_result; // 73 bytes
// This matches ESP-IDF's scan complete event structures
// All three (scan_param_cmpl, scan_start_cmpl, scan_stop_cmpl) have identical layout
struct {
esp_bt_status_t status;
} scan_complete; // 1 byte
};
} gap; // 80 bytes total
// NOLINTNEXTLINE(readability-identifier-naming)
struct gattc_event {
esp_gattc_cb_event_t gattc_event;
esp_gatt_if_t gattc_if;
esp_ble_gattc_cb_param_t *gattc_param; // Heap-allocated
std::vector<uint8_t> *data; // Heap-allocated
} gattc; // 16 bytes (pointers only)
// NOLINTNEXTLINE(readability-identifier-naming)
struct gatts_event {
esp_gatts_cb_event_t gatts_event;
esp_gatt_if_t gatts_if;
esp_ble_gatts_cb_param_t *gatts_param; // Heap-allocated
std::vector<uint8_t> *data; // Heap-allocated
} gatts; // 16 bytes (pointers only)
} event_; // 80 bytes
ble_event_t type_;
// Helper methods to access event data
ble_event_t type() const { return type_; }
esp_gap_ble_cb_event_t gap_event_type() const { return event_.gap.gap_event; }
const BLEScanResult &scan_result() const { return event_.gap.scan_result; }
esp_bt_status_t scan_complete_status() const { return event_.gap.scan_complete.status; }
};
// BLEEvent total size: 84 bytes (80 byte union + 1 byte type + 3 bytes padding)
} // namespace esp32_ble
} // namespace esphome

View File

@ -0,0 +1,24 @@
#pragma once
#ifdef USE_ESP32
#include <esp_gap_ble_api.h>
namespace esphome {
namespace esp32_ble {
// Structure for BLE scan results - only fields we actually use
struct __attribute__((packed)) BLEScanResult {
esp_bd_addr_t bda;
uint8_t ble_addr_type;
int8_t rssi;
uint8_t ble_adv[ESP_BLE_ADV_DATA_LEN_MAX + ESP_BLE_SCAN_RSP_DATA_LEN_MAX];
uint8_t adv_data_len;
uint8_t scan_rsp_len;
uint8_t search_evt;
}; // ~73 bytes vs ~400 bytes for full esp_ble_gap_cb_param_t
} // namespace esp32_ble
} // namespace esphome
#endif

View File

@ -1,7 +1,7 @@
#pragma once
#include "esphome/core/helpers.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#ifdef USE_ESP32

View File

@ -45,6 +45,17 @@ template<class T> class Queue {
return element;
}
size_t size() const {
// Lock-free size check. While std::queue::size() is not thread-safe, we intentionally
// avoid locking here to prevent blocking the BLE callback thread. The size is only
// used to decide whether to drop incoming events when the queue is near capacity.
// With a queue limit of 40-64 events and normal processing, dropping events should
// be extremely rare. When it does approach capacity, being off by 1-2 events is
// acceptable to avoid blocking the BLE stack's time-sensitive callbacks.
// Trade-off: We prefer occasional dropped events over potential BLE stack delays.
return q_.size();
}
protected:
std::queue<T *> q_;
SemaphoreHandle_t m_;

View File

@ -45,8 +45,10 @@ void BLEClientBase::loop() {
float BLEClientBase::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH; }
void BLEClientBase::dump_config() {
ESP_LOGCONFIG(TAG, " Address: %s", this->address_str().c_str());
ESP_LOGCONFIG(TAG, " Auto-Connect: %s", TRUEFALSE(this->auto_connect_));
ESP_LOGCONFIG(TAG,
" Address: %s\n"
" Auto-Connect: %s",
this->address_str().c_str(), TRUEFALSE(this->auto_connect_));
std::string state_name;
switch (this->state()) {
case espbt::ClientState::INIT:

View File

@ -4,7 +4,7 @@ from esphome import automation
import esphome.codegen as cg
from esphome.components import esp32_ble
from esphome.components.esp32 import add_idf_sdkconfig_option
from esphome.components.esp32_ble import bt_uuid
from esphome.components.esp32_ble import BTLoggers, bt_uuid
import esphome.config_validation as cv
from esphome.config_validation import UNDEFINED
from esphome.const import (
@ -525,6 +525,9 @@ async def to_code_characteristic(service_var, char_conf):
async def to_code(config):
# Register the loggers this component needs
esp32_ble.register_bt_logger(BTLoggers.GATT, BTLoggers.SMP)
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)

View File

@ -2,6 +2,7 @@
#include "ble_server.h"
#include "ble_service.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#ifdef USE_ESP32

View File

@ -43,7 +43,6 @@ class BLEServer : public Component,
float get_setup_priority() const override;
bool can_proceed() override;
void teardown();
bool is_running();
void set_manufacturer_data(const std::vector<uint8_t> &data) {

View File

@ -1,14 +1,15 @@
from __future__ import annotations
from collections.abc import MutableMapping
from collections.abc import Callable, MutableMapping
import logging
from typing import Any, Callable
from typing import Any
from esphome import automation
import esphome.codegen as cg
from esphome.components import esp32_ble
from esphome.components.esp32 import add_idf_sdkconfig_option
from esphome.components.esp32_ble import (
BTLoggers,
bt_uuid,
bt_uuid16_format,
bt_uuid32_format,
@ -259,11 +260,15 @@ ESP_BLE_DEVICE_SCHEMA = cv.Schema(
async def to_code(config):
# Register the loggers this component needs
esp32_ble.register_bt_logger(BTLoggers.BLE_SCAN)
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
parent = await cg.get_variable(config[esp32_ble.CONF_BLE_ID])
cg.add(parent.register_gap_event_handler(var))
cg.add(parent.register_gap_scan_event_handler(var))
cg.add(parent.register_gattc_event_handler(var))
cg.add(parent.register_ble_status_event_handler(var))
cg.add(var.set_parent(parent))

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