1
0
mirror of https://github.com/esphome/esphome.git synced 2025-06-16 15:26:58 +02:00

Compare commits

...

308 Commits

Author SHA1 Message Date
Keith Burzinski
68ef9cb3dc
[i2s_audio] Add `dump_config` methods, shorten log messages (#9099) 2025-06-16 07:36:49 +00:00
Jesse Hills
8e176b9c61
Merge branch 'beta' into dev 2025-06-16 17:07:31 +12:00
Jesse Hills
426be153db
Merge pull request #9094 from esphome/bump-2025.6.0b2
2025.6.0b2
2025-06-16 17:06:59 +12:00
Jesse Hills
c4f7c2d259
[ruff] Apply various ruff suggestions (#8947) 2025-06-15 22:13:14 -05:00
Jesse Hills
2a81efda0b
Remove `std::` prefix as not all platforms have access yet. (#9095) 2025-06-16 12:55:51 +12:00
Jesse Hills
882bfc79c7
Remove `std::` prefix as not all platforms have access yet. (#9095) 2025-06-16 12:55:23 +12:00
dependabot[bot]
6bad276589
Bump aioesphomeapi from 32.2.1 to 32.2.3 (#9091)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-16 11:45:58 +12:00
Jesse Hills
47d8048a62
Bump version to 2025.6.0b2 2025-06-16 10:07:07 +12:00
J. Nick Koston
20d7ba5d7c
Reduce Component blocking threshold memory usage by 2 bytes per component (#9081) 2025-06-16 10:07:07 +12:00
J. Nick Koston
e435e72654
Add common base classes for entity protobuf messages to reduce duplicate code (#9090) 2025-06-16 10:07:07 +12:00
J. Nick Koston
497d66f7ec
Ensure we can send batches where the first message exceeds MAX_PACKET_SIZE (#9068) 2025-06-16 10:07:07 +12:00
Kevin Ahrendt
242b02a416
[i2s_audio] Check for a nullptr before disabling and deleting channel (#9062) 2025-06-16 10:07:07 +12:00
J. Nick Koston
9644a6bb9c
Fix protobuf encoding size mismatch by passing force parameter in encode_string (#9074) 2025-06-16 10:07:07 +12:00
J. Nick Koston
70d66062d6
Make BLE queue lock free (#9088) 2025-06-16 10:07:07 +12:00
J. Nick Koston
39f6f9b0dc
Implement a lock free ring buffer for BLEScanResult to avoid drops (#9087) 2025-06-16 10:07:07 +12:00
dhewg
0454dd4e07
[fan] fix initial FanCall to properly set speed (#8277) 2025-06-16 10:07:07 +12:00
J. Nick Koston
6f4e76c8f3
Fix unbound BLE event queue growth and reduce memory usage (#9052) 2025-06-16 10:07:07 +12:00
J. Nick Koston
5cdcf2415d
Optimize Application area_ from std::string to const char* (#9085) 2025-06-16 10:07:07 +12:00
J. Nick Koston
1719a2e08b
Fix API message encoding to return actual size instead of calculated size (#9073) 2025-06-16 10:07:07 +12:00
J. Nick Koston
5640a9fe73
Optimize memory usage by lazy-allocating raw callbacks in sensors (#9077) 2025-06-16 10:07:07 +12:00
J. Nick Koston
4787e22f61
Reduce entity memory usage by eliminating field shadowing and bit-packing (#9076) 2025-06-16 10:07:01 +12:00
J. Nick Koston
fb12e4e66a
Small optimizations to api buffer helper (#9071) 2025-06-16 09:49:45 +12:00
J. Nick Koston
77740a1044
Optimize Component and Application state storage from uint32_t to uint8_t (#9082) 2025-06-16 09:49:45 +12:00
J. Nick Koston
1fdfe7578f
Make ParseOnOffState enum uint8_t (#9083) 2025-06-16 09:49:45 +12:00
J. Nick Koston
ebecf7047e
Fix captive_portal loading entire web_server (#9066) 2025-06-16 09:49:45 +12:00
Jesse Hills
00e8332bf5
[esp32] Dynamically set default framework based on variant (#9060) 2025-06-16 09:49:45 +12:00
Jesse Hills
5fc1f90822
[prometheus] Remove `cv.only_with_arduino` (#9061) 2025-06-16 09:49:45 +12:00
J. Nick Koston
0a1be3d19c
Fix misleading comment in API (#9069) 2025-06-16 09:49:45 +12:00
Nate Clark
40db3146b9
Fix BYPASS_AUTO feature to work with or without an arming delay (#9051) 2025-06-16 09:49:45 +12:00
Edward Firmo
535c495b33
[nextion] Remove upload flags reset from success path to prevent TFT corruption (#9064) 2025-06-16 09:49:45 +12:00
J. Nick Koston
592446e430
Always perform select() when loop duration exceeds interval (#9058) 2025-06-16 09:49:45 +12:00
J. Nick Koston
7a5c9a821a
Fix dashboard logging being escaped before parser (#9054) 2025-06-16 09:49:45 +12:00
J. Nick Koston
c17a3b6fcc
Reduce Component memory usage by 20 bytes per component (#9080)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-06-16 09:34:37 +12:00
J. Nick Koston
28d11553e0
Reduce Component blocking threshold memory usage by 2 bytes per component (#9081) 2025-06-16 09:33:38 +12:00
J. Nick Koston
1dbebe90ba
Add common base classes for entity protobuf messages to reduce duplicate code (#9090) 2025-06-16 09:29:25 +12:00
J. Nick Koston
06810e8e6a
Ensure we can send batches where the first message exceeds MAX_PACKET_SIZE (#9068) 2025-06-16 09:22:14 +12:00
Kevin Ahrendt
bd85ba9b6a
[i2s_audio] Check for a nullptr before disabling and deleting channel (#9062) 2025-06-16 09:19:50 +12:00
J. Nick Koston
be58cdda3b
Fix protobuf encoding size mismatch by passing force parameter in encode_string (#9074) 2025-06-16 09:19:04 +12:00
J. Nick Koston
fcce4a8be6
Make BLE queue lock free (#9088) 2025-06-16 09:16:46 +12:00
J. Nick Koston
61a558a062
Implement a lock free ring buffer for BLEScanResult to avoid drops (#9087) 2025-06-16 08:53:45 +12:00
dhewg
59f69ac5ca
[fan] fix initial FanCall to properly set speed (#8277) 2025-06-15 13:16:33 -05:00
dependabot[bot]
f82ac34784
Bump aioesphomeapi from 32.2.1 to 32.2.3 (#9091)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-15 10:42:01 -05:00
J. Nick Koston
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
a6fa963605
[core] Add some missing includes (#8864) 2025-05-21 20:02:14 +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
923 changed files with 18274 additions and 7178 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.0
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

@ -266,6 +266,7 @@ enum EntityCategory {
// ==================== BINARY SENSOR ====================
message ListEntitiesBinarySensorResponse {
option (id) = 12;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_BINARY_SENSOR";
@ -282,6 +283,7 @@ message ListEntitiesBinarySensorResponse {
}
message BinarySensorStateResponse {
option (id) = 21;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_BINARY_SENSOR";
option (no_delay) = true;
@ -296,6 +298,7 @@ message BinarySensorStateResponse {
// ==================== COVER ====================
message ListEntitiesCoverResponse {
option (id) = 13;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_COVER";
@ -325,6 +328,7 @@ enum CoverOperation {
}
message CoverStateResponse {
option (id) = 22;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_COVER";
option (no_delay) = true;
@ -367,6 +371,7 @@ message CoverCommandRequest {
// ==================== FAN ====================
message ListEntitiesFanResponse {
option (id) = 14;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_FAN";
@ -395,6 +400,7 @@ enum FanDirection {
}
message FanStateResponse {
option (id) = 23;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_FAN";
option (no_delay) = true;
@ -432,7 +438,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;
@ -443,6 +450,7 @@ enum ColorMode {
}
message ListEntitiesLightResponse {
option (id) = 15;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_LIGHT";
@ -466,6 +474,7 @@ message ListEntitiesLightResponse {
}
message LightStateResponse {
option (id) = 24;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_LIGHT";
option (no_delay) = true;
@ -535,6 +544,7 @@ enum SensorLastResetType {
message ListEntitiesSensorResponse {
option (id) = 16;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_SENSOR";
@ -556,6 +566,7 @@ message ListEntitiesSensorResponse {
}
message SensorStateResponse {
option (id) = 25;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_SENSOR";
option (no_delay) = true;
@ -570,6 +581,7 @@ message SensorStateResponse {
// ==================== SWITCH ====================
message ListEntitiesSwitchResponse {
option (id) = 17;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_SWITCH";
@ -586,6 +598,7 @@ message ListEntitiesSwitchResponse {
}
message SwitchStateResponse {
option (id) = 26;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_SWITCH";
option (no_delay) = true;
@ -606,6 +619,7 @@ message SwitchCommandRequest {
// ==================== TEXT SENSOR ====================
message ListEntitiesTextSensorResponse {
option (id) = 18;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_TEXT_SENSOR";
@ -621,6 +635,7 @@ message ListEntitiesTextSensorResponse {
}
message TextSensorStateResponse {
option (id) = 27;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_TEXT_SENSOR";
option (no_delay) = true;
@ -788,6 +803,7 @@ message ExecuteServiceRequest {
// ==================== CAMERA ====================
message ListEntitiesCameraResponse {
option (id) = 43;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_ESP32_CAMERA";
@ -868,6 +884,7 @@ enum ClimatePreset {
}
message ListEntitiesClimateResponse {
option (id) = 46;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_CLIMATE";
@ -902,6 +919,7 @@ message ListEntitiesClimateResponse {
}
message ClimateStateResponse {
option (id) = 47;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_CLIMATE";
option (no_delay) = true;
@ -963,6 +981,7 @@ enum NumberMode {
}
message ListEntitiesNumberResponse {
option (id) = 49;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_NUMBER";
@ -983,6 +1002,7 @@ message ListEntitiesNumberResponse {
}
message NumberStateResponse {
option (id) = 50;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_NUMBER";
option (no_delay) = true;
@ -1006,6 +1026,7 @@ message NumberCommandRequest {
// ==================== SELECT ====================
message ListEntitiesSelectResponse {
option (id) = 52;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_SELECT";
@ -1021,6 +1042,7 @@ message ListEntitiesSelectResponse {
}
message SelectStateResponse {
option (id) = 53;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_SELECT";
option (no_delay) = true;
@ -1044,6 +1066,7 @@ message SelectCommandRequest {
// ==================== SIREN ====================
message ListEntitiesSirenResponse {
option (id) = 55;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_SIREN";
@ -1061,6 +1084,7 @@ message ListEntitiesSirenResponse {
}
message SirenStateResponse {
option (id) = 56;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_SIREN";
option (no_delay) = true;
@ -1101,6 +1125,7 @@ enum LockCommand {
}
message ListEntitiesLockResponse {
option (id) = 58;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_LOCK";
@ -1122,6 +1147,7 @@ message ListEntitiesLockResponse {
}
message LockStateResponse {
option (id) = 59;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_LOCK";
option (no_delay) = true;
@ -1144,6 +1170,7 @@ message LockCommandRequest {
// ==================== BUTTON ====================
message ListEntitiesButtonResponse {
option (id) = 61;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_BUTTON";
@ -1195,6 +1222,7 @@ message MediaPlayerSupportedFormat {
}
message ListEntitiesMediaPlayerResponse {
option (id) = 63;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_MEDIA_PLAYER";
@ -1213,6 +1241,7 @@ message ListEntitiesMediaPlayerResponse {
}
message MediaPlayerStateResponse {
option (id) = 64;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_MEDIA_PLAYER";
option (no_delay) = true;
@ -1734,6 +1763,7 @@ enum AlarmControlPanelStateCommand {
message ListEntitiesAlarmControlPanelResponse {
option (id) = 94;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_ALARM_CONTROL_PANEL";
@ -1751,6 +1781,7 @@ message ListEntitiesAlarmControlPanelResponse {
message AlarmControlPanelStateResponse {
option (id) = 95;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_ALARM_CONTROL_PANEL";
option (no_delay) = true;
@ -1775,6 +1806,7 @@ enum TextMode {
}
message ListEntitiesTextResponse {
option (id) = 97;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_TEXT";
@ -1793,6 +1825,7 @@ message ListEntitiesTextResponse {
}
message TextStateResponse {
option (id) = 98;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_TEXT";
option (no_delay) = true;
@ -1817,6 +1850,7 @@ message TextCommandRequest {
// ==================== DATETIME DATE ====================
message ListEntitiesDateResponse {
option (id) = 100;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_DATETIME_DATE";
@ -1831,6 +1865,7 @@ message ListEntitiesDateResponse {
}
message DateStateResponse {
option (id) = 101;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_DATETIME_DATE";
option (no_delay) = true;
@ -1858,6 +1893,7 @@ message DateCommandRequest {
// ==================== DATETIME TIME ====================
message ListEntitiesTimeResponse {
option (id) = 103;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_DATETIME_TIME";
@ -1872,6 +1908,7 @@ message ListEntitiesTimeResponse {
}
message TimeStateResponse {
option (id) = 104;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_DATETIME_TIME";
option (no_delay) = true;
@ -1899,6 +1936,7 @@ message TimeCommandRequest {
// ==================== EVENT ====================
message ListEntitiesEventResponse {
option (id) = 107;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_EVENT";
@ -1916,6 +1954,7 @@ message ListEntitiesEventResponse {
}
message EventResponse {
option (id) = 108;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_EVENT";
@ -1926,6 +1965,7 @@ message EventResponse {
// ==================== VALVE ====================
message ListEntitiesValveResponse {
option (id) = 109;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_VALVE";
@ -1951,6 +1991,7 @@ enum ValveOperation {
}
message ValveStateResponse {
option (id) = 110;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_VALVE";
option (no_delay) = true;
@ -1975,6 +2016,7 @@ message ValveCommandRequest {
// ==================== DATETIME DATETIME ====================
message ListEntitiesDateTimeResponse {
option (id) = 112;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_DATETIME_DATETIME";
@ -1989,6 +2031,7 @@ message ListEntitiesDateTimeResponse {
}
message DateTimeStateResponse {
option (id) = 113;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_DATETIME_DATETIME";
option (no_delay) = true;
@ -2012,6 +2055,7 @@ message DateTimeCommandRequest {
// ==================== UPDATE ====================
message ListEntitiesUpdateResponse {
option (id) = 116;
option (base_class) = "InfoResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_UPDATE";
@ -2027,6 +2071,7 @@ message ListEntitiesUpdateResponse {
}
message UpdateStateResponse {
option (id) = 117;
option (base_class) = "StateResponseProtoMessage";
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_UPDATE";
option (no_delay) = true;

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();
fill_entity_state_base(binary_sensor, resp);
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 =
@ -318,10 +335,12 @@ bool APIConnection::try_send_cover_state_(cover::Cover *cover) {
if (traits.get_supports_tilt())
msg.tilt = cover->tilt;
msg.current_operation = static_cast<enums::CoverOperation>(cover->current_operation);
msg.key = cover->get_object_id_hash();
return this->send_cover_state_response(msg);
fill_entity_state_base(cover, 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;
@ -384,10 +403,12 @@ bool APIConnection::try_send_fan_state_(fan::Fan *fan) {
msg.direction = static_cast<enums::FanDirection>(fan->direction);
if (traits.supports_preset_modes())
msg.preset_mode = fan->preset_mode;
msg.key = fan->get_object_id_hash();
return this->send_fan_state_response(msg);
fill_entity_state_base(fan, 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;
@ -449,10 +470,12 @@ bool APIConnection::try_send_light_state_(light::LightState *light) {
resp.warm_white = values.get_warm_white();
if (light->supports_effects())
resp.effect = light->get_effect_name();
resp.key = light->get_object_id_hash();
return this->send_light_state_response(resp);
fill_entity_state_base(light, 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();
resp.key = sensor->get_object_id_hash();
return this->send_sensor_state_response(resp);
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();
fill_entity_state_base(sensor, 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;
resp.key = a_switch->get_object_id_hash();
return this->send_switch_state_response(resp);
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;
fill_entity_state_base(a_switch, 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,48 +614,46 @@ 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();
resp.key = text_sensor->get_object_id_hash();
return this->send_text_sensor_state_response(resp);
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();
fill_entity_state_base(text_sensor, 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();
fill_entity_state_base(climate, resp);
auto traits = climate->get_traits();
resp.mode = static_cast<enums::ClimateMode>(climate->mode);
resp.action = static_cast<enums::ClimateAction>(climate->action);
@ -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();
resp.key = number->get_object_id_hash();
return this->send_number_state_response(resp);
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();
fill_entity_state_base(number, 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);
fill_entity_state_base(date, 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);
fill_entity_state_base(time, 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);
fill_entity_state_base(datetime, 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();
resp.key = text->get_object_id_hash();
return this->send_text_state_response(resp);
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();
fill_entity_state_base(text, 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();
resp.key = select->get_object_id_hash();
return this->send_select_state_response(resp);
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();
fill_entity_state_base(select, 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);
resp.key = a_lock->get_object_id_hash();
return this->send_lock_state_response(resp);
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);
fill_entity_state_base(a_lock, 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);
fill_entity_state_base(valve, 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);
fill_entity_state_base(media_player, 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);
fill_entity_state_base(a_alarm_control_panel, 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.key = event->get_object_id_hash();
return this->send_event_response(resp);
resp.event_type = event_type;
fill_entity_state_base(event, resp);
return encode_message_to_buffer(resp, EventResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
}
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);
fill_entity_state_base(update, 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);
@ -1497,7 +1538,7 @@ bool APIConnection::try_send_log_message(int level, const char *tag, const char
buffer.encode_string(3, line, line_length); // string message = 3
// SubscribeLogsResponse - 29
return this->send_buffer(buffer, 29);
return this->send_buffer(buffer, SubscribeLogsResponse::MESSAGE_TYPE);
}
HelloResponse APIConnection::hello(const HelloRequest &msg) {
@ -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,8 +1684,8 @@ bool APIConnection::try_to_clear_buffer(bool log_out_of_space) {
}
return false;
}
bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) {
if (!this->try_to_clear_buffer(message_type != 29)) { // SubscribeLogsResponse
bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint16_t message_type) {
if (!this->try_to_clear_buffer(message_type != SubscribeLogsResponse::MESSAGE_TYPE)) { // SubscribeLogsResponse
return false;
}
@ -1666,17 +1707,354 @@ 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;
uint16_t remaining_size = std::numeric_limits<uint16_t>::max();
// Track where each message's header padding begins in the buffer
// For plaintext: this is where the 6-byte header padding starts
// 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
items_processed++;
// After first message, set remaining size to MAX_PACKET_SIZE to avoid fragmentation
if (items_processed == 1) {
remaining_size = MAX_PACKET_SIZE;
}
remaining_size -= payload_size;
// Calculate where the next message's header padding will start
// Current buffer size + footer space (that prepare_message_buffer will add for this message)
current_offset = this->parent_->get_shared_buffer_ref().size() + footer_size;
}
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 info fields
static void fill_entity_info_base(esphome::EntityBase *entity, InfoResponseProtoMessage &response) {
// Set common fields that are shared by all entity types
response.key = entity->get_object_id_hash();
response.object_id = entity->get_object_id();
@ -514,12 +295,142 @@ 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);
// Helper function to fill common entity state fields
static void fill_entity_state_base(esphome::EntityBase *entity, StateResponseProtoMessage &response) {
response.key = entity->get_object_id_hash();
}
// Non-template helper to encode any ProtoMessage
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 +440,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 +460,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

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

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:
@ -626,6 +628,7 @@ template<> const char *proto_enum_to_string<enums::UpdateCommand>(enums::UpdateC
}
}
#endif
bool HelloRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 2: {

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,
@ -252,8 +253,34 @@ enum UpdateCommand : uint32_t {
} // namespace enums
class InfoResponseProtoMessage : public ProtoMessage {
public:
~InfoResponseProtoMessage() override = default;
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
bool disabled_by_default{false};
std::string icon{};
enums::EntityCategory entity_category{};
protected:
};
class StateResponseProtoMessage : public ProtoMessage {
public:
~StateResponseProtoMessage() override = default;
uint32_t key{0};
protected:
};
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 +296,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 +317,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 +334,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 +351,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 +366,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 +381,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 +396,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 +411,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 +426,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 +462,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 +477,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 +492,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
@ -418,17 +505,15 @@ class SubscribeStatesRequest : public ProtoMessage {
protected:
};
class ListEntitiesBinarySensorResponse : public ProtoMessage {
class ListEntitiesBinarySensorResponse : public InfoResponseProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
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 device_class{};
bool is_status_binary_sensor{false};
bool disabled_by_default{false};
std::string icon{};
enums::EntityCategory entity_category{};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -440,9 +525,13 @@ class ListEntitiesBinarySensorResponse : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BinarySensorStateResponse : public ProtoMessage {
class BinarySensorStateResponse : public StateResponseProtoMessage {
public:
uint32_t key{0};
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
bool state{false};
bool missing_state{false};
void encode(ProtoWriteBuffer buffer) const override;
@ -455,19 +544,17 @@ class BinarySensorStateResponse : public ProtoMessage {
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class ListEntitiesCoverResponse : public ProtoMessage {
class ListEntitiesCoverResponse : public InfoResponseProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
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
bool assumed_state{false};
bool supports_position{false};
bool supports_tilt{false};
std::string device_class{};
bool disabled_by_default{false};
std::string icon{};
enums::EntityCategory entity_category{};
bool supports_stop{false};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -480,9 +567,13 @@ class ListEntitiesCoverResponse : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class CoverStateResponse : public ProtoMessage {
class CoverStateResponse : public StateResponseProtoMessage {
public:
uint32_t key{0};
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
enums::LegacyCoverState legacy_state{};
float position{0.0f};
float tilt{0.0f};
@ -499,6 +590,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{};
@ -517,19 +613,17 @@ class CoverCommandRequest : public ProtoMessage {
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class ListEntitiesFanResponse : public ProtoMessage {
class ListEntitiesFanResponse : public InfoResponseProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
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
bool supports_oscillation{false};
bool supports_speed{false};
bool supports_direction{false};
int32_t supported_speed_count{0};
bool disabled_by_default{false};
std::string icon{};
enums::EntityCategory entity_category{};
std::vector<std::string> supported_preset_modes{};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -542,9 +636,13 @@ class ListEntitiesFanResponse : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class FanStateResponse : public ProtoMessage {
class FanStateResponse : public StateResponseProtoMessage {
public:
uint32_t key{0};
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
bool state{false};
bool oscillating{false};
enums::FanSpeed speed{};
@ -564,6 +662,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};
@ -588,12 +691,13 @@ class FanCommandRequest : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class ListEntitiesLightResponse : public ProtoMessage {
class ListEntitiesLightResponse : public InfoResponseProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
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::vector<enums::ColorMode> supported_color_modes{};
bool legacy_supports_brightness{false};
bool legacy_supports_rgb{false};
@ -602,9 +706,6 @@ class ListEntitiesLightResponse : public ProtoMessage {
float min_mireds{0.0f};
float max_mireds{0.0f};
std::vector<std::string> effects{};
bool disabled_by_default{false};
std::string icon{};
enums::EntityCategory entity_category{};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -616,9 +717,13 @@ class ListEntitiesLightResponse : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class LightStateResponse : public ProtoMessage {
class LightStateResponse : public StateResponseProtoMessage {
public:
uint32_t key{0};
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
bool state{false};
float brightness{0.0f};
enums::ColorMode color_mode{};
@ -644,6 +749,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};
@ -682,21 +792,19 @@ class LightCommandRequest : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class ListEntitiesSensorResponse : public ProtoMessage {
class ListEntitiesSensorResponse : public InfoResponseProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
std::string icon{};
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 unit_of_measurement{};
int32_t accuracy_decimals{0};
bool force_update{false};
std::string device_class{};
enums::SensorStateClass state_class{};
enums::SensorLastResetType legacy_last_reset_type{};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -708,9 +816,13 @@ class ListEntitiesSensorResponse : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class SensorStateResponse : public ProtoMessage {
class SensorStateResponse : public StateResponseProtoMessage {
public:
uint32_t key{0};
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
float state{0.0f};
bool missing_state{false};
void encode(ProtoWriteBuffer buffer) const override;
@ -723,16 +835,14 @@ class SensorStateResponse : public ProtoMessage {
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class ListEntitiesSwitchResponse : public ProtoMessage {
class ListEntitiesSwitchResponse : public InfoResponseProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
std::string icon{};
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
bool assumed_state{false};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
std::string device_class{};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -745,9 +855,13 @@ class ListEntitiesSwitchResponse : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class SwitchStateResponse : public ProtoMessage {
class SwitchStateResponse : public StateResponseProtoMessage {
public:
uint32_t key{0};
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
bool state{false};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -761,6 +875,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;
@ -773,15 +892,13 @@ class SwitchCommandRequest : public ProtoMessage {
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class ListEntitiesTextSensorResponse : public ProtoMessage {
class ListEntitiesTextSensorResponse : public InfoResponseProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
std::string icon{};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
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 device_class{};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -794,9 +911,13 @@ class ListEntitiesTextSensorResponse : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class TextSensorStateResponse : public ProtoMessage {
class TextSensorStateResponse : public StateResponseProtoMessage {
public:
uint32_t key{0};
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
std::string state{};
bool missing_state{false};
void encode(ProtoWriteBuffer buffer) const override;
@ -812,6 +933,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 +951,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 +971,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 +988,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 +1005,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 +1033,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 +1055,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 +1070,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 +1090,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 +1109,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 +1124,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 +1155,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 +1197,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;
@ -1028,15 +1214,13 @@ class ExecuteServiceRequest : public ProtoMessage {
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
};
class ListEntitiesCameraResponse : public ProtoMessage {
class ListEntitiesCameraResponse : public InfoResponseProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
bool disabled_by_default{false};
std::string icon{};
enums::EntityCategory entity_category{};
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
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -1050,6 +1234,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 +1255,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;
@ -1077,12 +1271,13 @@ class CameraImageRequest : public ProtoMessage {
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class ListEntitiesClimateResponse : public ProtoMessage {
class ListEntitiesClimateResponse : public InfoResponseProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
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
bool supports_current_temperature{false};
bool supports_two_point_target_temperature{false};
std::vector<enums::ClimateMode> supported_modes{};
@ -1096,9 +1291,6 @@ class ListEntitiesClimateResponse : public ProtoMessage {
std::vector<std::string> supported_custom_fan_modes{};
std::vector<enums::ClimatePreset> supported_presets{};
std::vector<std::string> supported_custom_presets{};
bool disabled_by_default{false};
std::string icon{};
enums::EntityCategory entity_category{};
float visual_current_temperature_step{0.0f};
bool supports_current_humidity{false};
bool supports_target_humidity{false};
@ -1115,9 +1307,13 @@ class ListEntitiesClimateResponse : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class ClimateStateResponse : public ProtoMessage {
class ClimateStateResponse : public StateResponseProtoMessage {
public:
uint32_t key{0};
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
enums::ClimateMode mode{};
float current_temperature{0.0f};
float target_temperature{0.0f};
@ -1145,6 +1341,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{};
@ -1179,18 +1380,16 @@ class ClimateCommandRequest : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class ListEntitiesNumberResponse : public ProtoMessage {
class ListEntitiesNumberResponse : public InfoResponseProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
std::string icon{};
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
float min_value{0.0f};
float max_value{0.0f};
float step{0.0f};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
std::string unit_of_measurement{};
enums::NumberMode mode{};
std::string device_class{};
@ -1205,9 +1404,13 @@ class ListEntitiesNumberResponse : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class NumberStateResponse : public ProtoMessage {
class NumberStateResponse : public StateResponseProtoMessage {
public:
uint32_t key{0};
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
float state{0.0f};
bool missing_state{false};
void encode(ProtoWriteBuffer buffer) const override;
@ -1222,6 +1425,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;
@ -1233,16 +1441,14 @@ class NumberCommandRequest : public ProtoMessage {
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
};
class ListEntitiesSelectResponse : public ProtoMessage {
class ListEntitiesSelectResponse : public InfoResponseProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
std::string icon{};
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::vector<std::string> options{};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -1254,9 +1460,13 @@ class ListEntitiesSelectResponse : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class SelectStateResponse : public ProtoMessage {
class SelectStateResponse : public StateResponseProtoMessage {
public:
uint32_t key{0};
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
std::string state{};
bool missing_state{false};
void encode(ProtoWriteBuffer buffer) const override;
@ -1272,6 +1482,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;
@ -1284,18 +1499,16 @@ class SelectCommandRequest : public ProtoMessage {
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
};
class ListEntitiesSirenResponse : public ProtoMessage {
class ListEntitiesSirenResponse : public InfoResponseProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
std::string icon{};
bool disabled_by_default{false};
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::vector<std::string> tones{};
bool supports_duration{false};
bool supports_volume{false};
enums::EntityCategory entity_category{};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -1307,9 +1520,13 @@ class ListEntitiesSirenResponse : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class SirenStateResponse : public ProtoMessage {
class SirenStateResponse : public StateResponseProtoMessage {
public:
uint32_t key{0};
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
bool state{false};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -1323,6 +1540,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};
@ -1343,15 +1565,13 @@ class SirenCommandRequest : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class ListEntitiesLockResponse : public ProtoMessage {
class ListEntitiesLockResponse : public InfoResponseProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
std::string icon{};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
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
bool assumed_state{false};
bool supports_open{false};
bool requires_code{false};
@ -1367,9 +1587,13 @@ class ListEntitiesLockResponse : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class LockStateResponse : public ProtoMessage {
class LockStateResponse : public StateResponseProtoMessage {
public:
uint32_t key{0};
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
enums::LockState state{};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -1383,6 +1607,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};
@ -1398,15 +1627,13 @@ class LockCommandRequest : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class ListEntitiesButtonResponse : public ProtoMessage {
class ListEntitiesButtonResponse : public InfoResponseProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
std::string icon{};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
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 device_class{};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -1421,6 +1648,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;
@ -1448,15 +1680,13 @@ class MediaPlayerSupportedFormat : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class ListEntitiesMediaPlayerResponse : public ProtoMessage {
class ListEntitiesMediaPlayerResponse : public InfoResponseProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
std::string icon{};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
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
bool supports_pause{false};
std::vector<MediaPlayerSupportedFormat> supported_formats{};
void encode(ProtoWriteBuffer buffer) const override;
@ -1470,9 +1700,13 @@ class ListEntitiesMediaPlayerResponse : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class MediaPlayerStateResponse : public ProtoMessage {
class MediaPlayerStateResponse : public StateResponseProtoMessage {
public:
uint32_t key{0};
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
enums::MediaPlayerState state{};
float volume{0.0f};
bool muted{false};
@ -1488,6 +1722,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 +1749,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 +1781,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 +1821,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 +1838,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 +1858,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 +1878,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 +1939,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 +1958,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 +1975,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 +1993,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 +2013,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 +2034,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 +2052,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 +2072,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 +2091,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 +2111,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 +2126,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 +2145,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 +2164,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 +2182,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 +2200,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 +2219,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 +2238,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 +2253,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 +2272,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 +2290,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 +2307,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 +2340,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 +2362,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 +2393,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 +2412,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 +2431,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 +2454,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 +2475,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 +2506,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 +2521,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 +2541,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;
@ -2132,15 +2556,13 @@ class VoiceAssistantSetConfiguration : public ProtoMessage {
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
};
class ListEntitiesAlarmControlPanelResponse : public ProtoMessage {
class ListEntitiesAlarmControlPanelResponse : public InfoResponseProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
std::string icon{};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
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
uint32_t supported_features{0};
bool requires_code{false};
bool requires_code_to_arm{false};
@ -2155,9 +2577,13 @@ class ListEntitiesAlarmControlPanelResponse : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class AlarmControlPanelStateResponse : public ProtoMessage {
class AlarmControlPanelStateResponse : public StateResponseProtoMessage {
public:
uint32_t key{0};
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
enums::AlarmControlPanelState state{};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -2171,6 +2597,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{};
@ -2185,15 +2616,13 @@ class AlarmControlPanelCommandRequest : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class ListEntitiesTextResponse : public ProtoMessage {
class ListEntitiesTextResponse : public InfoResponseProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
std::string icon{};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
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
uint32_t min_length{0};
uint32_t max_length{0};
std::string pattern{};
@ -2209,9 +2638,13 @@ class ListEntitiesTextResponse : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class TextStateResponse : public ProtoMessage {
class TextStateResponse : public StateResponseProtoMessage {
public:
uint32_t key{0};
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
std::string state{};
bool missing_state{false};
void encode(ProtoWriteBuffer buffer) const override;
@ -2227,6 +2660,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;
@ -2239,15 +2677,13 @@ class TextCommandRequest : public ProtoMessage {
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
};
class ListEntitiesDateResponse : public ProtoMessage {
class ListEntitiesDateResponse : public InfoResponseProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
std::string icon{};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
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
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -2259,9 +2695,13 @@ class ListEntitiesDateResponse : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class DateStateResponse : public ProtoMessage {
class DateStateResponse : public StateResponseProtoMessage {
public:
uint32_t key{0};
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
bool missing_state{false};
uint32_t year{0};
uint32_t month{0};
@ -2278,6 +2718,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};
@ -2292,15 +2737,13 @@ class DateCommandRequest : public ProtoMessage {
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class ListEntitiesTimeResponse : public ProtoMessage {
class ListEntitiesTimeResponse : public InfoResponseProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
std::string icon{};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
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
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -2312,9 +2755,13 @@ class ListEntitiesTimeResponse : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class TimeStateResponse : public ProtoMessage {
class TimeStateResponse : public StateResponseProtoMessage {
public:
uint32_t key{0};
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
bool missing_state{false};
uint32_t hour{0};
uint32_t minute{0};
@ -2331,6 +2778,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};
@ -2345,15 +2797,13 @@ class TimeCommandRequest : public ProtoMessage {
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class ListEntitiesEventResponse : public ProtoMessage {
class ListEntitiesEventResponse : public InfoResponseProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
std::string icon{};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
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 device_class{};
std::vector<std::string> event_types{};
void encode(ProtoWriteBuffer buffer) const override;
@ -2367,9 +2817,13 @@ class ListEntitiesEventResponse : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class EventResponse : public ProtoMessage {
class EventResponse : public StateResponseProtoMessage {
public:
uint32_t key{0};
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
std::string event_type{};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -2381,15 +2835,13 @@ class EventResponse : public ProtoMessage {
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
};
class ListEntitiesValveResponse : public ProtoMessage {
class ListEntitiesValveResponse : public InfoResponseProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
std::string icon{};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
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 device_class{};
bool assumed_state{false};
bool supports_position{false};
@ -2405,9 +2857,13 @@ class ListEntitiesValveResponse : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class ValveStateResponse : public ProtoMessage {
class ValveStateResponse : public StateResponseProtoMessage {
public:
uint32_t key{0};
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
float position{0.0f};
enums::ValveOperation current_operation{};
void encode(ProtoWriteBuffer buffer) const override;
@ -2422,6 +2878,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};
@ -2436,15 +2897,13 @@ class ValveCommandRequest : public ProtoMessage {
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class ListEntitiesDateTimeResponse : public ProtoMessage {
class ListEntitiesDateTimeResponse : public InfoResponseProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
std::string icon{};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
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
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -2456,9 +2915,13 @@ class ListEntitiesDateTimeResponse : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class DateTimeStateResponse : public ProtoMessage {
class DateTimeStateResponse : public StateResponseProtoMessage {
public:
uint32_t key{0};
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
bool missing_state{false};
uint32_t epoch_seconds{0};
void encode(ProtoWriteBuffer buffer) const override;
@ -2473,6 +2936,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;
@ -2484,15 +2952,13 @@ class DateTimeCommandRequest : public ProtoMessage {
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
};
class ListEntitiesUpdateResponse : public ProtoMessage {
class ListEntitiesUpdateResponse : public InfoResponseProtoMessage {
public:
std::string object_id{};
uint32_t key{0};
std::string name{};
std::string unique_id{};
std::string icon{};
bool disabled_by_default{false};
enums::EntityCategory entity_category{};
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 device_class{};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@ -2505,9 +2971,13 @@ class ListEntitiesUpdateResponse : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class UpdateStateResponse : public ProtoMessage {
class UpdateStateResponse : public StateResponseProtoMessage {
public:
uint32_t key{0};
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
bool missing_state{false};
bool in_progress{false};
bool has_progress{false};
@ -2530,6 +3000,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_; }
@ -215,7 +216,7 @@ class ProtoWriteBuffer {
this->buffer_->insert(this->buffer_->end(), data, data + len);
}
void encode_string(uint32_t field_id, const std::string &value, bool force = false) {
this->encode_string(field_id, value.data(), value.size());
this->encode_string(field_id, value.data(), value.size(), force);
}
void encode_bytes(uint32_t field_id, const uint8_t *data, size_t len, bool force = false) {
this->encode_string(field_id, reinterpret_cast<const char *>(data), len, force);
@ -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

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

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."""
from esphome import automation
from esphome.automation import maybe_simple_id
import esphome.codegen as cg
from esphome.components import sensor, uart
import esphome.config_validation as cv
from esphome.const import (
CONF_CO2,
CONF_ID,
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

@ -28,7 +28,7 @@ static RAMAllocator<BLEEvent> EVENT_ALLOCATOR( // NOLINT(cppcoreguidelines-avoi
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 +270,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 +285,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 +304,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();
@ -325,61 +357,74 @@ void ESP32BLE::loop() {
if (this->advertising_ != nullptr) {
this->advertising_->loop();
}
// Log dropped events periodically
size_t dropped = this->ble_events_.get_and_reset_dropped_count();
if (dropped > 0) {
ESP_LOGW(TAG, "Dropped %zu BLE events due to buffer overflow", dropped);
}
}
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) {
// Check if queue is full before allocating
if (global_ble->ble_events_.full()) {
// Queue is full, drop the event
global_ble->ble_events_.increment_dropped_count();
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
global_ble->ble_events_.increment_dropped_count();
return;
}
new (new_event) BLEEvent(event, param);
global_ble->ble_events_.push(new_event);
new (new_event) BLEEvent(args...);
// Push the event - since we're the only producer and we checked full() above,
// this should always succeed unless we have a bug
if (!global_ble->ble_events_.push(new_event)) {
// This should not happen in SPSC queue with single producer
ESP_LOGE(TAG, "BLE queue push failed unexpectedly");
new_event->~BLEEvent();
EVENT_ALLOCATOR.deallocate(new_event, 1);
}
} // 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 +453,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,16 @@
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
// Maximum size of the BLE event queue - must be power of 2 for lock-free queue
static constexpr size_t MAX_BLE_QUEUE_SIZE = 64;
uint64_t ble_addr_to_uint64(const esp_bd_addr_t address);
// NOLINTNEXTLINE(modernize-use-using)
@ -57,6 +68,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 +117,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,22 +132,22 @@ 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_;
BLEComponentState state_{BLE_COMPONENT_STATE_OFF};
Queue<BLEEvent> ble_events_;
LockFreeQueue<BLEEvent, MAX_BLE_QUEUE_SIZE> ble_events_;
BLEAdvertising *advertising_{};
esp_ble_io_cap_t io_cap_{ESP_IO_CAP_NONE};
uint32_t advertising_cycle_time_{};

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

@ -2,52 +2,78 @@
#ifdef USE_ESP32
#include <mutex>
#include <queue>
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <atomic>
#include <cstddef>
/*
* BLE events come in from a separate Task (thread) in the ESP32 stack. Rather
* than trying to deal with various locking strategies, all incoming GAP and GATT
* events will simply be placed on a semaphore guarded queue. The next time the
* component runs loop(), these events are popped off the queue and handed at
* this safer time.
* than using mutex-based locking, this lock-free queue allows the BLE
* task to enqueue events without blocking. The main loop() then processes
* these events at a safer time.
*
* This is a Single-Producer Single-Consumer (SPSC) lock-free ring buffer.
* The BLE task is the only producer, and the main loop() is the only consumer.
*/
namespace esphome {
namespace esp32_ble {
template<class T> class Queue {
template<class T, size_t SIZE> class LockFreeQueue {
public:
Queue() { m_ = xSemaphoreCreateMutex(); }
LockFreeQueue() : head_(0), tail_(0), dropped_count_(0) {}
void push(T *element) {
bool push(T *element) {
if (element == nullptr)
return;
// It is not called from main loop. Thus it won't block main thread.
xSemaphoreTake(m_, portMAX_DELAY);
q_.push(element);
xSemaphoreGive(m_);
return false;
size_t current_tail = tail_.load(std::memory_order_relaxed);
size_t next_tail = (current_tail + 1) % SIZE;
if (next_tail == head_.load(std::memory_order_acquire)) {
// Buffer full
dropped_count_.fetch_add(1, std::memory_order_relaxed);
return false;
}
buffer_[current_tail] = element;
tail_.store(next_tail, std::memory_order_release);
return true;
}
T *pop() {
T *element = nullptr;
size_t current_head = head_.load(std::memory_order_relaxed);
if (xSemaphoreTake(m_, 5L / portTICK_PERIOD_MS)) {
if (!q_.empty()) {
element = q_.front();
q_.pop();
}
xSemaphoreGive(m_);
if (current_head == tail_.load(std::memory_order_acquire)) {
return nullptr; // Empty
}
T *element = buffer_[current_head];
head_.store((current_head + 1) % SIZE, std::memory_order_release);
return element;
}
size_t size() const {
size_t tail = tail_.load(std::memory_order_acquire);
size_t head = head_.load(std::memory_order_acquire);
return (tail - head + SIZE) % SIZE;
}
size_t get_and_reset_dropped_count() { return dropped_count_.exchange(0, std::memory_order_relaxed); }
void increment_dropped_count() { dropped_count_.fetch_add(1, std::memory_order_relaxed); }
bool empty() const { return head_.load(std::memory_order_acquire) == tail_.load(std::memory_order_acquire); }
bool full() const {
size_t next_tail = (tail_.load(std::memory_order_relaxed) + 1) % SIZE;
return next_tail == head_.load(std::memory_order_acquire);
}
protected:
std::queue<T *> q_;
SemaphoreHandle_t m_;
T *buffer_[SIZE];
std::atomic<size_t> head_;
std::atomic<size_t> tail_;
std::atomic<size_t> dropped_count_;
};
} // namespace esp32_ble

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

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