1
0
mirror of https://github.com/esphome/esphome.git synced 2026-02-11 02:02:09 +00:00

Compare commits

..

221 Commits

Author SHA1 Message Date
J. Nick Koston
8151f29163 Merge branch 'dev' into combine_logs 2026-01-04 16:11:31 -10:00
tomaszduda23
12027569d3 [nrf52,zigbee] add support for binary_input (#11535)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick+github@koston.org>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-01-04 21:11:14 -05:00
J. Nick Koston
43d836a7d3 Merge branch 'dev' into combine_logs 2026-01-04 16:11:06 -10:00
J. Nick Koston
ab0e15e4bb [runtime_stats] Combine log statements to reduce loop blocking (#12954) 2026-01-04 16:10:51 -10:00
J. Nick Koston
9f7925c1d5 [safe_mode] Combine log statements to reduce loop blocking (#12955) 2026-01-04 16:10:19 -10:00
J. Nick Koston
7449421cea [shelly_dimmer] Combine log statements to reduce loop blocking (#12956) 2026-01-04 16:10:06 -10:00
J. Nick Koston
4f20c1ceb1 [rp2040_pwm] Combine log statements to reduce loop blocking (#12952) 2026-01-04 16:09:51 -10:00
J. Nick Koston
452fcd56dd [remote_receiver] Combine log statements to reduce loop blocking (#12951) 2026-01-04 16:08:45 -10:00
J. Nick Koston
44fc156ef6 [remote_base] Combine log statements to reduce loop blocking (#12950) 2026-01-04 16:07:50 -10:00
J. Nick Koston
3ec05a5a13 [radon_eye_rd200] Combine log statements to reduce loop blocking (#12949) 2026-01-04 16:06:55 -10:00
J. Nick Koston
e6a630ae64 [qmp6988] Combine log statements to reduce loop blocking (#12947) 2026-01-04 16:06:34 -10:00
J. Nick Koston
3c8fd5c5c0 [pulse_counter] Combine log statements to reduce loop blocking (#12946) 2026-01-04 16:05:05 -10:00
J. Nick Koston
a635c82830 [pid] Combine log statements to reduce loop blocking (#12942) 2026-01-04 16:04:50 -10:00
J. Nick Koston
69f431b053 Merge branch 'dev' into combine_logs 2026-01-04 14:59:30 -10:00
J. Nick Koston
0b9fcf9ed3 [pn532] Combine log statements to reduce loop blocking (#12943) 2026-01-04 14:46:21 -10:00
J. Nick Koston
2d8abbb2ac [pn7150] Combine log statements to reduce loop blocking (#12944) 2026-01-04 14:46:09 -10:00
J. Nick Koston
6d8142c539 [rpi_dpi_rgb] Combine log statements to reduce loop blocking (#12953) 2026-01-04 14:45:52 -10:00
J. Nick Koston
50f27cdd77 [pn7160] Combine log statements to reduce loop blocking (#12945) 2026-01-04 14:45:38 -10:00
J. Nick Koston
6c809583d3 [qspi_dbi] Combine log statements to reduce loop blocking (#12948)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-04 14:45:22 -10:00
J. Nick Koston
f41f0506c1 [pcf8574] Combine log statements to reduce loop blocking (#12941) 2026-01-04 14:05:17 -10:00
J. Nick Koston
850f189225 [api] Fix message batch size mismatch and improve naming consistency (#12940) 2026-01-04 23:44:49 +00:00
J. Nick Koston
d4baaedd07 Merge branch 'dev' into combine_logs 2026-01-04 13:14:18 -10:00
J. Nick Koston
a37d4b17eb [wifi] Combine log statements to reduce loop blocking (#12939) 2026-01-04 23:11:48 +00:00
J. Nick Koston
7309a65167 [tlc5971] Combine log statements to reduce loop blocking (#12922) 2026-01-04 13:11:08 -10:00
J. Nick Koston
f22dec6cf1 Merge branch 'dev' into combine_logs 2026-01-04 13:07:22 -10:00
J. Nick Koston
7fde110ac5 [voice_assistant] Combine log statements to reduce loop blocking (#12930) 2026-01-04 13:04:22 -10:00
J. Nick Koston
9ed107bc33 [xgzp68xx] Combine log statements to reduce loop blocking (#12935) 2026-01-04 13:04:08 -10:00
J. Nick Koston
b291f359ae [x9c] Combine log statements to reduce loop blocking (#12934) 2026-01-04 13:03:56 -10:00
J. Nick Koston
161545584d [wl_134] Combine log statements to reduce loop blocking (#12933) 2026-01-04 13:03:42 -10:00
J. Nick Koston
3ea11d4e59 [xpt2046] Combine log statements to reduce loop blocking (#12937) 2026-01-04 13:03:22 -10:00
J. Nick Koston
5713d69efe [ufire_ec] Combine log statements to reduce loop blocking (#12925) 2026-01-04 13:03:06 -10:00
J. Nick Koston
9d9f9c3c84 [xiaomi_xmwsdj04mmc] Combine log statements to reduce loop blocking (#12936) 2026-01-04 13:02:52 -10:00
J. Nick Koston
29d332af92 [wireguard] Combine log statements to reduce loop blocking (#12932) 2026-01-04 13:02:02 -10:00
J. Nick Koston
c44d095f8a [usb_host] Combine log statements to reduce loop blocking (#12927) 2026-01-04 13:01:49 -10:00
J. Nick Koston
0b996616b8 [waveshare_epaper] Combine log statements to reduce loop blocking (#12931) 2026-01-04 13:01:33 -10:00
J. Nick Koston
557b6a9ef0 [sun] Combine log statements to reduce loop blocking (#12919) 2026-01-04 13:00:59 -10:00
J. Nick Koston
6e633f7f3b [usb_uart] Combine log statements to reduce loop blocking (#12928) 2026-01-04 13:00:03 -10:00
J. Nick Koston
c59455e445 [mqtt] Combine log statements to reduce loop blocking (#12938) 2026-01-04 12:59:47 -10:00
J. Nick Koston
32b3d27c7c [uln2003] Combine log statements to reduce loop blocking (#12926) 2026-01-04 12:58:42 -10:00
J. Nick Koston
88cb5d9671 [tmp1075] Combine log statements to reduce loop blocking (#12923) 2026-01-04 12:58:20 -10:00
J. Nick Koston
56d1d928f9 [tlc5947] Combine log statements to reduce loop blocking (#12921) 2026-01-04 12:57:10 -10:00
J. Nick Koston
e9cab96cb7 [sx1509] Combine log statements to reduce loop blocking (#12920) 2026-01-04 12:56:50 -10:00
J. Nick Koston
022c42f9ca [tuya] Combine log statements to reduce loop blocking (#12924) 2026-01-04 12:53:58 -10:00
J. Nick Koston
25ef9aff04 [vl53l0x] Combine log statements to reduce loop blocking (#12929) 2026-01-04 12:53:46 -10:00
Clyde Stubbs
71940acc49 [esp32_ble] Remove requirement for configured network (#12891) 2026-01-04 22:37:44 +00:00
J. Nick Koston
f537632599 cleanups 2026-01-04 12:04:00 -10:00
J. Nick Koston
8c7dfa0c62 tweaks 2026-01-04 12:01:28 -10:00
J. Nick Koston
0dbf40dcdf Merge branch 'dev' into combine_logs 2026-01-04 11:57:00 -10:00
J. Nick Koston
05695affff [m5stack_8angle] Combine log statements to reduce loop blocking (#12908) 2026-01-04 11:55:31 -10:00
J. Nick Koston
f2308c77c6 [libretiny_pwm] Combine log statements to reduce loop blocking (#12907) 2026-01-04 11:55:18 -10:00
J. Nick Koston
a5368d1d95 [modbus] Combine log statements to reduce loop blocking (#12910) 2026-01-04 11:54:47 -10:00
J. Nick Koston
b8d93f2150 [mopeka_std_check] Combine log statements to reduce loop blocking (#12911) 2026-01-04 11:54:31 -10:00
J. Nick Koston
ca574a1550 [ledc] Combine log statements to reduce loop blocking (#12906) 2026-01-04 11:54:14 -10:00
J. Nick Koston
b0855b4a0e [lc709203f] Combine log statements to reduce loop blocking (#12905) 2026-01-04 11:53:50 -10:00
J. Nick Koston
1fccddf67f [ina2xx_base] Combine log statements to reduce loop blocking (#12904) 2026-01-04 11:52:56 -10:00
J. Nick Koston
548600b47a [ina260] Combine log statements to reduce loop blocking (#12903) 2026-01-04 11:52:34 -10:00
J. Nick Koston
9bbfad4a08 [honeywellabp] Combine log statements to reduce loop blocking (#12902) 2026-01-04 11:52:08 -10:00
J. Nick Koston
8ae1f26b6a [hlw8012] Combine log statements to reduce loop blocking (#12901) 2026-01-04 11:51:45 -10:00
J. Nick Koston
9b2a36a313 [hc8] Combine log statements to reduce loop blocking (#12900) 2026-01-04 11:51:33 -10:00
J. Nick Koston
aa4b274b3c [mcp3204] Combine log statements to reduce loop blocking (#12912) 2026-01-04 11:51:18 -10:00
J. Nick Koston
d1d5c942ec [mcp9600] Combine log statements to reduce loop blocking (#12913) 2026-01-04 11:51:01 -10:00
J. Nick Koston
ccc9d95c9d [mqtt] Combine log statements to reduce loop blocking (#12914) 2026-01-04 11:28:14 -10:00
J. Nick Koston
6d9d593e12 [my9231] Combine log statements to reduce loop blocking (#12915) 2026-01-04 11:27:14 -10:00
J. Nick Koston
fc9683f024 [opentherm] Combine log statements to reduce loop blocking (#12916) 2026-01-04 11:26:13 -10:00
J. Nick Koston
61ecfb5f2b [openthread] Combine log statements to reduce loop blocking (#12917) 2026-01-04 11:25:52 -10:00
J. Nick Koston
7e75826064 [wifi] Fix LibreTiny thread safety with queue-based event handling (#12833) 2026-01-04 11:25:24 -10:00
J. Nick Koston
8287484a36 [gl_r01_i2c] Combine log statements to reduce loop blocking (#12899) 2026-01-04 11:24:51 -10:00
J. Nick Koston
dd8259b2ce [gcja5] Combine log statements to reduce loop blocking (#12898) 2026-01-04 11:24:36 -10:00
Stuart Parmenter
449e478bec [hub75] Bump esp-hub75 version to 0.2.2 (#12674) 2026-01-04 10:50:10 -10:00
Jonathan Swoboda
9ae19d53dc [ultrasonic] Fix timeout issues and deprecate timeout option (#12897)
Co-authored-by: Claude <noreply@anthropic.com>
2026-01-04 13:39:56 -05:00
J. Nick Koston
77b3ffee00 [factory_reset] Combine log statements to reduce loop blocking (#12866) 2026-01-04 08:34:16 -10:00
J. Nick Koston
dff8dc0ed1 [cc1101] Combine log statements to reduce loop blocking (#12869) 2026-01-04 08:34:07 -10:00
J. Nick Koston
5a8b0f59b8 [cd74hc4067] Combine log statements to reduce loop blocking (#12870) 2026-01-04 08:33:58 -10:00
J. Nick Koston
25a325da61 [current_based] Combine log statements to reduce loop blocking (#12873) 2026-01-04 08:33:49 -10:00
J. Nick Koston
a6db5a2ed8 [dfrobot_sen0395] Combine log statements to reduce loop blocking (#12876) 2026-01-04 08:33:38 -10:00
J. Nick Koston
9e5dbb073a [emmeti] Combine log statements to reduce loop blocking (#12878) 2026-01-04 08:31:14 -10:00
J. Nick Koston
cf513975f3 [ens160_base] Combine log statements to reduce loop blocking (#12880) 2026-01-04 08:30:45 -10:00
J. Nick Koston
1e70091a27 [esp32_hosted] Combine log statements to reduce loop blocking (#12884) 2026-01-04 08:28:17 -10:00
J. Nick Koston
766826cc9c [esp32][libretiny] Reuse preference buffer to avoid heap churn (#12890) 2026-01-04 08:28:01 -10:00
J. Nick Koston
8a4ee19c0b [es8388] Combine log statements to reduce loop blocking (#12882) 2026-01-04 08:26:19 -10:00
J. Nick Koston
b1f9c08f51 [esp32_ble_tracker] Make start_scan action idempotent (#12864) 2026-01-04 08:11:36 -10:00
J. Nick Koston
facf4777a4 [ezo_pmp] Combine log statements to reduce loop blocking (#12888) 2026-01-03 18:04:00 -10:00
J. Nick Koston
096de869b6 [esp32_ble_client] Combine log statements to reduce loop blocking (#12883) 2026-01-03 18:01:55 -10:00
J. Nick Koston
c59314ec09 [debug] Combine log statements to reduce loop blocking (#12875) 2026-01-03 18:01:28 -10:00
J. Nick Koston
e94158a12f [fan] Combine log statements to reduce loop blocking (#12889) 2026-01-03 18:00:52 -10:00
J. Nick Koston
cb598c43e8 [endstop] Combine log statements to reduce loop blocking (#12879) 2026-01-03 18:00:31 -10:00
J. Nick Koston
6e8817cbc4 [esp8266_pwm] Combine log statements to reduce loop blocking (#12885) 2026-01-03 18:00:11 -10:00
J. Nick Koston
9f06f046d6 [espnow] Combine log statements to reduce loop blocking (#12887) 2026-01-03 17:59:53 -10:00
J. Nick Koston
44fa6bae95 [dht] Combine log statements to reduce loop blocking (#12877) 2026-01-03 17:57:53 -10:00
J. Nick Koston
bc9093127e [cap1188] Combine log statements to reduce loop blocking (#12868) 2026-01-03 17:00:14 -10:00
J. Nick Koston
cf93b66306 [chsc6x] Combine log statements to reduce loop blocking (#12871) 2026-01-03 16:59:55 -10:00
J. Nick Koston
16ada4d477 [epaper_spi] Combine log statements to reduce loop blocking (#12881) 2026-01-04 02:48:39 +00:00
J. Nick Koston
c96d0015a0 [esp_ldo] Combine log statements to reduce loop blocking (#12886) 2026-01-04 02:48:04 +00:00
J. Nick Koston
12c6f5749e [cst816] Combine log statements to reduce loop blocking (#12872) 2026-01-04 02:46:29 +00:00
Douwe
5f1eacf4ec [water_heater] (4/4) Implement tests for new water_heater component (#12517)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-01-03 16:43:31 -10:00
J. Nick Koston
d23c6a833b Merge branch 'dev' into combine_logs 2026-01-03 16:05:53 -10:00
J. Nick Koston
5d384c77c5 [esp32] Move heap functions to flash, saving ~6KB (#12862) 2026-01-03 16:00:50 -10:00
dependabot[bot]
32562ca991 Bump aioesphomeapi from 43.10.0 to 43.10.1 (#12865)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-04 01:59:03 +00:00
J. Nick Koston
6b4b1272db [binary_sensor] Combine log statements to reduce loop blocking (#12849) 2026-01-04 01:56:52 +00:00
J. Nick Koston
7b74f94360 [wifi] Combine log statements to reduce loop blocking (#12856)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-01-04 01:54:56 +00:00
J. Nick Koston
997ab553c1 [ac_dimmer] Combine log statements to reduce loop blocking (#12840)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-01-04 01:36:08 +00:00
Frederic Meeuwissen
8b80fe9c6b [esp32_rmt_led_strip] Support inverted logic (#12825)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-03 20:32:27 -05:00
J. Nick Koston
ee65f2f0cd [adc] Combine log statements to reduce loop blocking (#12841) 2026-01-03 15:24:41 -10:00
J. Nick Koston
723ccd7547 [ade7880] Combine log statements to reduce loop blocking (#12842) 2026-01-03 15:05:41 -10:00
J. Nick Koston
102862e99d [ads1115] Combine log statements to reduce loop blocking (#12843) 2026-01-03 15:05:29 -10:00
J. Nick Koston
9cb265347c [ads1118] Combine log statements to reduce loop blocking (#12844) 2026-01-03 15:05:15 -10:00
J. Nick Koston
d84562f878 [anova] Combine log statements to reduce loop blocking (#12845) 2026-01-03 15:04:57 -10:00
J. Nick Koston
6bbee3cfc6 [as3935] Combine log statements to reduce loop blocking (#12846) 2026-01-03 15:04:38 -10:00
J. Nick Koston
41e7ecb29f [bedjet] Combine log statements to reduce loop blocking (#12848) 2026-01-03 15:04:21 -10:00
J. Nick Koston
0196d6ee55 [ble_nus] Combine log statements to reduce loop blocking (#12850) 2026-01-03 15:03:44 -10:00
J. Nick Koston
ea848db683 [bp1658cj] Combine log statements to reduce loop blocking (#12851) 2026-01-03 15:03:20 -10:00
J. Nick Koston
41a188ac35 [ac_dimmer] Fix ESP8266 build by requiring waveform support (#12852) 2026-01-03 15:03:01 -10:00
J. Nick Koston
8ddfeb2d38 [captive_portal] Combine log statements to reduce loop blocking (#12853) 2026-01-03 15:02:26 -10:00
J. Nick Koston
d364432e3a [uart] Combine log statements to reduce loop blocking (#12855) 2026-01-03 15:02:12 -10:00
J. Nick Koston
2a6b192af8 [ethernet] Combine log statements to reduce loop blocking (#12854) 2026-01-03 15:01:35 -10:00
J. Nick Koston
07a581e13a [update] Combine log statements to reduce loop blocking (#12857) 2026-01-03 15:01:24 -10:00
J. Nick Koston
5f5edf90e9 [water_heater] Combine log statements to reduce loop blocking (#12858) 2026-01-03 15:01:12 -10:00
J. Nick Koston
5e24469ce3 [http_request] Combine log statements to reduce loop blocking (#12859) 2026-01-03 15:01:01 -10:00
J. Nick Koston
d7a1ac83ca [esp32_ble_tracker] Combine log statements to reduce loop blocking (#12860) 2026-01-03 15:00:51 -10:00
Douwe
f11abc7dbf [water_heater] (2/4) Implement template for new water_heater component (#12516)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-01-03 14:45:49 -10:00
tomaszduda23
ec05692f0d [nrf52] add printk doc (#12839) 2026-01-04 00:12:31 +00:00
J. Nick Koston
2e2e54811a [absolute_humidity] Combine log statements to reduce loop blocking (#12838) 2026-01-03 17:52:23 -06:00
Clyde Stubbs
c29aa61e2a [image] Use alternative version of CairoSVG on Windows (#12811) 2026-01-04 10:08:47 +11:00
J. Nick Koston
48ac8aa75b Merge branch 'dev' into combine_logs 2026-01-03 12:55:16 -10:00
J. Nick Koston
cb3edfc654 [wifi] Use stack-based MAC formatting in ESP8266 and IDF event handlers (#12834) 2026-01-03 12:32:22 -10:00
J. Nick Koston
6685fa1da9 [core] Fix startup delay from setup timing logs when console connected (#12832) 2026-01-03 12:32:10 -10:00
J. Nick Koston
d505f0316b [wifi] Combine scan result log lines to reduce loop blocking with many matching APs (#12830) 2026-01-03 12:31:58 -10:00
J. Nick Koston
9781073f2a [espnow] Use stack-based MAC formatting and remove dead code (#12836) 2026-01-03 12:31:38 -10:00
John Hollowell
0a0501c140 Fix comment typos (#12828) 2026-01-03 17:11:48 -05:00
Jasper van der Neut - Stulen
a6e9aa7876 [mhz19] Refactor Actions to Parented (#12837) 2026-01-03 17:11:02 -05:00
Conrad Juhl Andersen
ede7391582 [wts01] Fix negative values for WTS01 sensor (#12835) 2026-01-03 17:06:33 -05:00
Jasper van der Neut - Stulen
5cfcf8d104 [mhz19] Make detection range configurable (#12677)
Co-authored-by: Fabio Pugliese Ornellas <fabio.ornellas@gmail.com>
2026-01-03 15:51:48 -05:00
J. Nick Koston
c34665f650 [api] Fix KeyError when running logs after password removal (#12831) 2026-01-03 19:13:07 +00:00
Mariusz Kryński
69867bf818 [nrf52, zephyr] move nrf52-specific code to nrf52 component (#12582)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick+github@koston.org>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-01-03 18:58:56 +00:00
J. Nick Koston
1d323c2d71 [api] Remove deprecated password authentication (#12819) 2026-01-03 07:14:48 -10:00
tomaszduda23
95a7356ea0 [uart] make sure that all variables are initialized (#12823) 2026-01-03 03:43:17 -06:00
J. Nick Koston
89b550b74a [tests] Remove reserved / character from entity names in component tests (#12820) 2026-01-03 01:00:46 -06:00
dependabot[bot]
538c6544a0 Bump ruamel-yaml from 0.18.17 to 0.19.1 (#12768)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-02 20:51:56 -10:00
dependabot[bot]
98e3695c89 Bump aioesphomeapi from 43.9.1 to 43.10.0 (#12821)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-03 06:45:17 +00:00
J. Nick Koston
00fd4f2fdd [esp8266] Exclude unused waveform code to save ~596 bytes RAM (#12690) 2026-01-02 19:51:07 -10:00
J. Nick Koston
2a5be725c8 [api] Enable zero-copy bytes SOURCE_BOTH messages (#12816) 2026-01-02 19:50:30 -10:00
Robert Klep
c4d339a4c9 [core] Add CONF_ON_START (#12439) (#12440) 2026-01-02 23:42:18 -05:00
J. Nick Koston
0058779481 Merge remote-tracking branch 'upstream/dev' into combine_logs
# Conflicts:
#	esphome/components/hlw8012/hlw8012.cpp
#	esphome/components/sonoff_d1/sonoff_d1.cpp
#	esphome/components/xiaomi_xmwsdj04mmc/xiaomi_xmwsdj04mmc.cpp
2026-01-02 16:48:10 -10:00
J. Nick Koston
6409970f6e [uponor_smatrix] Use stack-based hex formatting in verbose logging (#12797)
Co-authored-by: Stefan Rado <628587+kroimon@users.noreply.github.com>
2026-01-02 16:41:02 -10:00
J. Nick Koston
bc1af007b4 [vbus] Use stack-based hex formatting in verbose logging (#12796) 2026-01-02 16:40:47 -10:00
Thomas Rupprecht
c3ffc1635d [gps] add icon for HDOP and use correct state_class for longitude and… (#12718) 2026-01-02 21:40:28 -05:00
J. Nick Koston
016eeef04a [tee501] Use stack-based hex formatting in verbose logging (#12795) 2026-01-02 16:40:06 -10:00
J. Nick Koston
ace48464a8 [addressable_light] Use StringRef to avoid allocation when saving effect name (#12759) 2026-01-02 16:39:44 -10:00
J. Nick Koston
64ba376330 [hte501] Use stack-based hex formatting in verbose logging (#12794) 2026-01-02 16:37:38 -10:00
J. Nick Koston
d946ddabfd [xiaomi_ble] Use stack-based hex formatting in verbose logging (#12793) 2026-01-02 16:37:16 -10:00
J. Nick Koston
a57011b50b [kuntze] Use stack buffer for hex formatting in verbose logging (#12775) 2026-01-02 16:36:57 -10:00
J. Nick Koston
1240e7907e [api] Use stack-based format_hex_pretty_to for packet logging macros (#12788) 2026-01-02 16:35:44 -10:00
J. Nick Koston
f0391f0213 [api] Remove object_id from API protocol - clients compute it from name #12698 (#12818) 2026-01-02 16:32:46 -10:00
J. Nick Koston
3cc6810be5 [core] Remove object_id RAM storage - no longer in hot path after #12627 (#12631) 2026-01-02 15:46:01 -10:00
J. Nick Koston
916370a943 [gpio] Avoid heap allocation in dump_summary (#12760) 2026-01-02 15:42:56 -10:00
J. Nick Koston
e2f45c590e [esp32_improv] Use stack buffer for hex formatting in verbose logging (#12737) 2026-01-02 14:28:38 -10:00
J. Nick Koston
7d21411ca4 [epaper_spi] Use stack buffer for hex formatting in command logging (#12734) 2026-01-02 14:27:00 -10:00
J. Nick Koston
56ed5af27d [nextion] Use stack buffers for hex formatting in upload logging (#12733) 2026-01-02 14:26:14 -10:00
J. Nick Koston
c8241b0122 [sonoff_d1] Use stack buffer for hex formatting in logging (#12730) 2026-01-02 14:25:02 -10:00
J. Nick Koston
30efd7fb07 [jsn_sr04t] Use stack buffer for hex formatting in error logging (#12729) 2026-01-02 14:24:47 -10:00
J. Nick Koston
1703343694 [a02yyuw] Use stack buffer for hex formatting in error logging (#12728) 2026-01-02 14:24:30 -10:00
J. Nick Koston
7fa04b6c25 [a01nyub] Use stack buffer for hex formatting in error logging (#12727) 2026-01-02 14:23:33 -10:00
J. Nick Koston
61b6476de4 [opentherm] Replace heap-allocating format calls with printf format specifiers in debug_error (#12726) 2026-01-02 14:23:18 -10:00
J. Nick Koston
b4e5e0bc9b [rc522] Use stack buffers for hex formatting in tag logging (#12725) 2026-01-02 14:22:58 -10:00
J. Nick Koston
f9b4e0e489 [remote_base] Use stack buffer for hex formatting in haier protocol logging (#12723) 2026-01-02 14:22:26 -10:00
J. Nick Koston
9ccb100cca [remote_base] Use stack buffer for hex formatting in mirage protocol logging (#12722) 2026-01-02 14:21:42 -10:00
J. Nick Koston
20b66cba23 [shelly_dimmer] Use stack buffer for hex formatting in command logging (#12721) 2026-01-02 14:21:23 -10:00
J. Nick Koston
b711172b33 [wifi] Use precision format specifier for SSID logging to avoid stack copy (#12704) 2026-01-02 14:21:09 -10:00
J. Nick Koston
0c4184b129 [cse7766] Use stack buffer for hex formatting in debug logging (#12732) 2026-01-02 14:20:17 -10:00
J. Nick Koston
0e108c2178 [esp32] Add minimum_chip_revision setting and log chip revision at startup (#12696) 2026-01-02 14:14:52 -10:00
J. Nick Koston
2230e56347 [wifi] Use stack buffers for IP address logging to avoid heap allocations (#12680) 2026-01-02 14:14:24 -10:00
J. Nick Koston
2ff9535f5f [esp32_improv] Use stack buffer for URL formatting to avoid heap allocation (#12682) 2026-01-02 14:14:12 -10:00
J. Nick Koston
ddb6c6cfd4 [captive_portal] Use stack buffer for IP address logging in DNS server (#12679) 2026-01-02 14:13:59 -10:00
J. Nick Koston
00ab64a3c7 [wifi] Use wifi_ssid_to() to avoid heap allocations in automation and connection checks (#12678) 2026-01-02 14:13:43 -10:00
J. Nick Koston
e732f8469e [udp] Avoid heap allocations when joining multicast groups (#12685) 2026-01-02 14:13:26 -10:00
J. Nick Koston
023be88a87 [tuya] Use stack buffers for hex logging to avoid heap allocations (#12689) 2026-01-02 14:13:08 -10:00
J. Nick Koston
25e60d62cf [mqtt] Avoid heap allocations when logging IP addresses (#12686) 2026-01-02 14:12:04 -10:00
J. Nick Koston
167a42aa27 [api] Use StringRef in send_action_response and send_execute_service_response (#12658) 2026-01-02 14:11:45 -10:00
J. Nick Koston
0ef49a8b73 [ld2410][ld2412][ld2450] Use stack buffers for hex logging (#12688) 2026-01-02 14:11:31 -10:00
J. Nick Koston
51259888bf [voice_assistant] Use zero-copy buffer access for audio data (#12656) 2026-01-02 14:10:21 -10:00
J. Nick Koston
0b7ff09657 [api] Use pointer to FixedVector for siren tones field (#12657) 2026-01-02 14:09:40 -10:00
J. Nick Koston
f394cf3f4d [packet_transport] Use stack-based format_hex_pretty_to for logging (#12791) 2026-01-02 14:06:03 -10:00
J. Nick Koston
4cb066bcbf [api] Use StringRef in handle_action_response to avoid temporary string (#12655) 2026-01-02 14:05:50 -10:00
J. Nick Koston
e7001c5eea [api] Auto-generate zero-copy pointer access for incoming API bytes fields (#12654) 2026-01-02 14:05:37 -10:00
esphomebot
5bb9ffa0cb Update webserver local assets to 20260102-230255 (#12817) 2026-01-02 23:14:11 +00:00
J. Nick Koston
c6713eaccb [web_server] Fix URL collisions with UTF-8 names and sub-devices (#12627) 2026-01-02 13:07:11 -10:00
Jonathan Swoboda
087f521b19 [ultrasonic] Use interrupt-based measurement for reliability (#12617)
Co-authored-by: Claude <noreply@anthropic.com>
2026-01-02 15:58:53 -05:00
Jonathan Swoboda
763515d3a1 [core] Remove unused USE_ESP32_FRAMEWORK_ARDUINO ifdefs (#12813)
Co-authored-by: Claude <noreply@anthropic.com>
2026-01-02 14:47:14 -05:00
J. Nick Koston
6d4f4d8d23 [api] Auto-generate StringRef for incoming API string fields (#12648) 2026-01-02 08:17:05 -10:00
Tobias Stanzel
d7fd85e610 [spi] Allow any achievable data rate (#12753)
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
2026-01-02 18:10:30 +11:00
J. Nick Koston
8acaa16987 [usb_cdc_acm] Use stack-based hex formatting in verbose logging (#12792) 2026-01-02 01:04:11 -06:00
J. Nick Koston
4e8c02b396 [xiaomi_*] Use stack-based hex formatting for bindkey logging (#12798) 2026-01-01 20:25:12 -10:00
J. Nick Koston
a828abf53d [ota] Remove MD5 authentication support (#12707) 2026-01-01 20:24:31 -10:00
J. Nick Koston
ebfa0149cc [light] Use StringRef to avoid allocation in JSON effect name serialization (#12758) 2026-01-01 20:23:37 -10:00
J. Nick Koston
3a4cca0027 [ble_client] Use stack buffer for hex formatting in very verbose logging (#12744) 2026-01-01 20:22:48 -10:00
J. Nick Koston
7702a9ae85 [ethernet] Use stack buffer for hex formatting in very verbose logging (#12742) 2026-01-01 20:22:19 -10:00
J. Nick Koston
2e8baa0493 [esp32_ble_tracker] Use stack buffer for hex formatting in very verbose logging (#12741) 2026-01-01 20:21:33 -10:00
J. Nick Koston
69ec311d21 [hlk_fm22x] Use stack buffer for hex formatting in verbose logging (#12740) 2026-01-01 20:20:58 -10:00
J. Nick Koston
1cc18055ef [i2c] Use stack buffer for hex formatting in verbose logging (#12739) 2026-01-01 20:20:24 -10:00
J. Nick Koston
bcc6bbbf5f [espnow] Use stack buffer for hex formatting in verbose logging (#12738) 2026-01-01 20:19:49 -10:00
J. Nick Koston
71c3d4ca27 [mopeka_std_check] Use stack-based format_hex_pretty_to for very verbose logging (#12790) 2026-01-01 20:19:20 -10:00
J. Nick Koston
c6f3860f90 [ee895] Use stack-based format_hex_to for verbose logging (#12789) 2026-01-01 20:18:23 -10:00
J. Nick Koston
0049c8ad38 [zwave_proxy] Use stack-based format_hex_pretty_to for very verbose logging (#12786) 2026-01-01 20:17:51 -10:00
J. Nick Koston
e1788bba45 [seeed_mr60fda2] Use stack-based format_hex_pretty_to for verbose logging (#12785) 2026-01-01 20:17:22 -10:00
J. Nick Koston
4fcd263ea8 [seeed_mr60bha2] Replace format_hex_pretty with stack-based format_hex_pretty_to (#12784) 2026-01-01 20:16:40 -10:00
J. Nick Koston
c81ce243cc [qspi_dbi] Replace format_hex_pretty with stack-based format_hex_pretty_to (#12783) 2026-01-01 20:13:10 -10:00
J. Nick Koston
7df41124b2 [pn532_spi] Replace format_hex_pretty with stack-based format_hex_pretty_to (#12782) 2026-01-01 20:11:53 -10:00
J. Nick Koston
b5188731f8 [modbus] Use stack buffer for hex formatting in verbose logging (#12780) 2026-01-01 20:10:45 -10:00
J. Nick Koston
0924281545 [mitsubishi] Use stack buffer for hex formatting in verbose logging (#12779) 2026-01-01 20:10:08 -10:00
J. Nick Koston
14e97642f7 [mipi_rgb] Use stack buffer for hex formatting in init sequence logging (#12777) 2026-01-01 20:09:37 -10:00
J. Nick Koston
544aaeaa66 [mipi_dsi] Use stack buffer for hex formatting in very verbose logging (#12776) 2026-01-01 20:08:57 -10:00
Stuart Parmenter
7483bbd6ea [display] Ensure drivers respect clipping during fill() (#12808) 2026-01-02 16:34:39 +11:00
Artur
2841b5fe44 [sn74hc595]: fix 'Attempted read from write-only channel' when using esp-idf framework (#12801) 2026-01-01 23:28:10 -05:00
J. Nick Koston
ed435241b1 [mipi_spi] Use stack buffer for hex formatting in verbose logging (#12778) 2026-01-01 11:48:37 -10:00
H. Árkosi Róbert
9847e51fbc [bthome_mithermometer] Add BTHome parsing for Xiaomi Mijia BLE Sensors (#12635) 2026-01-02 08:40:18 +11:00
dependabot[bot]
dc320f455a Bump bleak from 2.1.0 to 2.1.1 (#12804)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-01 09:16:01 -10:00
Clyde Stubbs
1945e85ddc [core] Make LockFreeQueue more widely available (#12766) 2026-01-01 22:07:35 +11:00
J. Nick Koston
5bb241e42d Combine logs 2025-12-29 23:07:36 -10:00
J. Nick Koston
28d4b6378b Combine logs 2025-12-29 23:03:40 -10:00
J. Nick Koston
eea8cdb543 Combine logs 2025-12-29 22:55:08 -10:00
J. Nick Koston
70496a4c20 Combine logs 2025-12-29 22:51:05 -10:00
336 changed files with 9639 additions and 7605 deletions

View File

@@ -91,6 +91,7 @@ esphome/components/bmp3xx_spi/* @latonita
esphome/components/bmp581/* @kahrendt
esphome/components/bp1658cj/* @Cossid
esphome/components/bp5758d/* @Cossid
esphome/components/bthome_mithermometer/* @nagyrobi
esphome/components/button/* @esphome/core
esphome/components/bytebuffer/* @clydebarrow
esphome/components/camera/* @bdraco @DT-art1
@@ -574,5 +575,6 @@ esphome/components/xpt2046/touchscreen/* @nielsnl68 @numo68
esphome/components/xxtea/* @clydebarrow
esphome/components/zephyr/* @tomaszduda23
esphome/components/zhlt01/* @cfeenstra1024
esphome/components/zigbee/* @tomaszduda23
esphome/components/zio_ultrasonic/* @kahrendt
esphome/components/zwave_proxy/* @kbx81

View File

@@ -431,33 +431,25 @@ def run_miniterm(config: ConfigType, port: str, args) -> int:
while tries < 5:
try:
with ser:
buffer = b""
ser.timeout = 0.1 # 100ms timeout for non-blocking reads
while True:
try:
# Read all available data and timestamp it
chunk = ser.read(ser.in_waiting or 1)
if not chunk:
continue
time_ = datetime.now()
nanoseconds = time_.microsecond // 1000
time_str = f"[{time_.hour:02}:{time_.minute:02}:{time_.second:02}.{nanoseconds:03}]"
# Add to buffer and process complete lines
buffer += chunk
while b"\n" in buffer:
raw_line, buffer = buffer.split(b"\n", 1)
line = raw_line.replace(b"\r", b"").decode(
"utf8", "backslashreplace"
)
safe_print(parser.parse_line(line, time_str))
backtrace_state = platformio_api.process_stacktrace(
config, line, backtrace_state=backtrace_state
)
raw = ser.readline()
except serial.SerialException:
_LOGGER.error("Serial port closed!")
return 0
line = (
raw.replace(b"\r", b"")
.replace(b"\n", b"")
.decode("utf8", "backslashreplace")
)
time_ = datetime.now()
nanoseconds = time_.microsecond // 1000
time_str = f"[{time_.hour:02}:{time_.minute:02}:{time_.second:02}.{nanoseconds:03}]"
safe_print(parser.parse_line(line, time_str))
backtrace_state = platformio_api.process_stacktrace(
config, line, backtrace_state=backtrace_state
)
except serial.SerialException:
tries += 1
time.sleep(1)
@@ -788,13 +780,6 @@ def command_vscode(args: ArgsProtocol) -> int | None:
def command_compile(args: ArgsProtocol, config: ConfigType) -> int | None:
# Set memory analysis options in config
if args.analyze_memory:
config.setdefault(CONF_ESPHOME, {})["analyze_memory"] = True
if args.memory_report:
config.setdefault(CONF_ESPHOME, {})["memory_report_file"] = args.memory_report
exit_code = write_cpp(config)
if exit_code != 0:
return exit_code
@@ -1278,17 +1263,6 @@ def parse_args(argv):
help="Only generate source code, do not compile.",
action="store_true",
)
parser_compile.add_argument(
"--analyze-memory",
help="Analyze and display memory usage by component after compilation.",
action="store_true",
)
parser_compile.add_argument(
"--memory-report",
help="Save memory analysis report to a file (supports .json or .txt).",
type=str,
metavar="FILE",
)
parser_upload = subparsers.add_parser(
"upload",

View File

@@ -1,7 +1,6 @@
"""CLI interface for memory analysis with report generation."""
from collections import defaultdict
import json
import sys
from . import (
@@ -298,28 +297,6 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
return "\n".join(lines)
def to_json(self) -> str:
"""Export analysis results as JSON."""
data = {
"components": {
name: {
"text": mem.text_size,
"rodata": mem.rodata_size,
"data": mem.data_size,
"bss": mem.bss_size,
"flash_total": mem.flash_total,
"ram_total": mem.ram_total,
"symbol_count": mem.symbol_count,
}
for name, mem in self.components.items()
},
"totals": {
"flash": sum(c.flash_total for c in self.components.values()),
"ram": sum(c.ram_total for c in self.components.values()),
},
}
return json.dumps(data, indent=2)
def dump_uncategorized_symbols(self, output_file: str | None = None) -> None:
"""Dump uncategorized symbols for analysis."""
# Sort by size descending

View File

@@ -90,13 +90,16 @@ void AbsoluteHumidityComponent::loop() {
this->status_set_error(LOG_STR("Invalid saturation vapor pressure equation selection!"));
return;
}
ESP_LOGD(TAG, "Saturation vapor pressure %f kPa", es);
// Calculate absolute humidity
const float absolute_humidity = vapor_density(es, hr, temperature_k);
ESP_LOGD(TAG,
"Saturation vapor pressure %f kPa\n"
"Publishing absolute humidity %f g/m³",
es, absolute_humidity);
// Publish absolute humidity
ESP_LOGD(TAG, "Publishing absolute humidity %f g/m³", absolute_humidity);
this->status_clear_warning();
this->publish_state(absolute_humidity);
}

View File

@@ -211,13 +211,13 @@ void AcDimmer::write_state(float state) {
this->store_.value = new_value;
}
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,
"AcDimmer:\n"
" Min Power: %.1f%%\n"
" Init with half cycle: %s",
this->store_.min_power / 10.0f, YESNO(this->init_with_half_cycle_));
LOG_PIN(" Output Pin: ", this->gate_pin_);
LOG_PIN(" Zero-Cross Pin: ", this->zero_cross_pin_);
if (method_ == DIM_METHOD_LEADING_PULSE) {
ESP_LOGCONFIG(TAG, " Method: leading pulse");
} else if (method_ == DIM_METHOD_LEADING) {

View File

@@ -3,6 +3,7 @@ import esphome.codegen as cg
from esphome.components import output
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_METHOD, CONF_MIN_POWER
from esphome.core import CORE
CODEOWNERS = ["@glmnet"]
@@ -36,6 +37,12 @@ CONFIG_SCHEMA = cv.All(
async def to_code(config):
if CORE.is_esp8266:
# ac_dimmer uses setTimer1Callback which requires the waveform generator
from esphome.components.esp8266.const import require_waveform
require_waveform()
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)

View File

@@ -121,23 +121,21 @@ void ADCSensor::setup() {
void ADCSensor::dump_config() {
LOG_SENSOR("", "ADC Sensor", this);
LOG_PIN(" Pin: ", this->pin_);
ESP_LOGCONFIG(TAG,
" Channel: %d\n"
" Unit: %s\n"
" Attenuation: %s\n"
" Samples: %i\n"
" Sampling mode: %s",
this->channel_, LOG_STR_ARG(adc_unit_to_str(this->adc_unit_)),
this->autorange_ ? "Auto" : LOG_STR_ARG(attenuation_to_str(this->attenuation_)), this->sample_count_,
LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
ESP_LOGCONFIG(
TAG,
" Channel: %d\n"
" Unit: %s\n"
" Attenuation: %s\n"
" Samples: %i\n"
" Sampling mode: %s\n"
" Setup Status:\n"
" Handle Init: %s\n"
" Config: %s\n"
" Calibration: %s\n"
" Overall Init: %s",
this->channel_, LOG_STR_ARG(adc_unit_to_str(this->adc_unit_)),
this->autorange_ ? "Auto" : LOG_STR_ARG(attenuation_to_str(this->attenuation_)), this->sample_count_,
LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)),
this->setup_flags_.handle_init_complete ? "OK" : "FAILED", this->setup_flags_.config_complete ? "OK" : "FAILED",
this->setup_flags_.calibration_complete ? "OK" : "FAILED", this->setup_flags_.init_complete ? "OK" : "FAILED");

View File

@@ -162,11 +162,13 @@ void ADE7880::update() {
}
void ADE7880::dump_config() {
ESP_LOGCONFIG(TAG, "ADE7880:");
ESP_LOGCONFIG(TAG,
"ADE7880:\n"
" Frequency: %.0f Hz",
this->frequency_);
LOG_PIN(" IRQ0 Pin: ", this->irq0_pin_);
LOG_PIN(" IRQ1 Pin: ", this->irq1_pin_);
LOG_PIN(" RESET Pin: ", this->reset_pin_);
ESP_LOGCONFIG(TAG, " Frequency: %.0f Hz", this->frequency_);
if (this->channel_a_ != nullptr) {
ESP_LOGCONFIG(TAG, " Phase A:");

View File

@@ -21,10 +21,12 @@ void ADS1115Sensor::update() {
void ADS1115Sensor::dump_config() {
LOG_SENSOR(" ", "ADS1115 Sensor", this);
ESP_LOGCONFIG(TAG, " Multiplexer: %u", this->multiplexer_);
ESP_LOGCONFIG(TAG, " Gain: %u", this->gain_);
ESP_LOGCONFIG(TAG, " Resolution: %u", this->resolution_);
ESP_LOGCONFIG(TAG, " Sample rate: %u", this->samplerate_);
ESP_LOGCONFIG(TAG,
" Multiplexer: %u\n"
" Gain: %u\n"
" Resolution: %u\n"
" Sample rate: %u",
this->multiplexer_, this->gain_, this->resolution_, this->samplerate_);
}
} // namespace ads1115

View File

@@ -9,8 +9,10 @@ static const char *const TAG = "ads1118.sensor";
void ADS1118Sensor::dump_config() {
LOG_SENSOR(" ", "ADS1118 Sensor", this);
ESP_LOGCONFIG(TAG, " Multiplexer: %u", this->multiplexer_);
ESP_LOGCONFIG(TAG, " Gain: %u", this->gain_);
ESP_LOGCONFIG(TAG,
" Multiplexer: %u\n"
" Gain: %u",
this->multiplexer_, this->gain_);
}
float ADS1118Sensor::sample() {

View File

@@ -67,8 +67,10 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
case ESP_GATTC_SEARCH_CMPL_EVT: {
auto *chr = this->parent_->get_characteristic(ANOVA_SERVICE_UUID, ANOVA_CHARACTERISTIC_UUID);
if (chr == nullptr) {
ESP_LOGW(TAG, "[%s] No control service found at device, not an Anova..?", this->get_name().c_str());
ESP_LOGW(TAG, "[%s] Note, this component does not currently support Anova Nano.", this->get_name().c_str());
ESP_LOGW(TAG,
"[%s] No control service found at device, not an Anova..?\n"
"[%s] Note, this component does not currently support Anova Nano.",
this->get_name().c_str(), this->get_name().c_str());
break;
}
this->char_handle_ = chr->handle;

View File

@@ -226,32 +226,6 @@ def _encryption_schema(config):
return ENCRYPTION_SCHEMA(config)
def _validate_api_config(config: ConfigType) -> ConfigType:
"""Validate API configuration with mutual exclusivity check and deprecation warning."""
# Check if both password and encryption are configured
has_password = CONF_PASSWORD in config and config[CONF_PASSWORD]
has_encryption = CONF_ENCRYPTION in config
if has_password and has_encryption:
raise cv.Invalid(
"The 'password' and 'encryption' options are mutually exclusive. "
"The API client only supports one authentication method at a time. "
"Please remove one of them. "
"Note: 'password' authentication is deprecated and will be removed in version 2026.1.0. "
"We strongly recommend using 'encryption' instead for better security."
)
# Warn about password deprecation
if has_password:
_LOGGER.warning(
"API 'password' authentication has been deprecated since May 2022 and will be removed in version 2026.1.0. "
"Please migrate to the 'encryption' configuration. "
"See https://esphome.io/components/api/#configuration-variables"
)
return config
def _consume_api_sockets(config: ConfigType) -> ConfigType:
"""Register socket needs for API component."""
from esphome.components import socket
@@ -268,7 +242,17 @@ CONFIG_SCHEMA = cv.All(
{
cv.GenerateID(): cv.declare_id(APIServer),
cv.Optional(CONF_PORT, default=6053): cv.port,
cv.Optional(CONF_PASSWORD, default=""): cv.string_strict,
# Removed in 2026.1.0 - kept to provide helpful error message
cv.Optional(CONF_PASSWORD): cv.invalid(
"The 'password' option has been removed in ESPHome 2026.1.0.\n"
"Password authentication was deprecated in May 2022.\n"
"Please migrate to encryption for secure API communication:\n\n"
"api:\n"
" encryption:\n"
" key: !secret api_encryption_key\n\n"
"Generate a key with: openssl rand -base64 32\n"
"Or visit https://esphome.io/components/api/#configuration-variables"
),
cv.Optional(
CONF_REBOOT_TIMEOUT, default="15min"
): cv.positive_time_period_milliseconds,
@@ -330,7 +314,6 @@ CONFIG_SCHEMA = cv.All(
}
).extend(cv.COMPONENT_SCHEMA),
cv.rename_key(CONF_SERVICES, CONF_ACTIONS),
_validate_api_config,
_consume_api_sockets,
)
@@ -344,9 +327,6 @@ async def to_code(config: ConfigType) -> None:
CORE.register_controller()
cg.add(var.set_port(config[CONF_PORT]))
if config[CONF_PASSWORD]:
cg.add_define("USE_API_PASSWORD")
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]))
if CONF_LISTEN_BACKLOG in config:

View File

@@ -7,10 +7,7 @@ service APIConnection {
option (needs_setup_connection) = false;
option (needs_authentication) = false;
}
rpc authenticate (AuthenticationRequest) returns (AuthenticationResponse) {
option (needs_setup_connection) = false;
option (needs_authentication) = false;
}
// REMOVED in ESPHome 2026.1.0: rpc authenticate (AuthenticationRequest) returns (AuthenticationResponse)
rpc disconnect (DisconnectRequest) returns (DisconnectResponse) {
option (needs_setup_connection) = false;
option (needs_authentication) = false;
@@ -82,14 +79,13 @@ service APIConnection {
// * VarInt denoting the type of message.
// * The message object encoded as a ProtoBuf message
// The connection is established in 4 steps:
// The connection is established in 2 steps:
// * First, the client connects to the server and sends a "Hello Request" identifying itself
// * The server responds with a "Hello Response" and selects the protocol version
// * After receiving this message, the client attempts to authenticate itself using
// the password and a "Connect Request"
// * The server responds with a "Connect Response" and notifies of invalid password.
// * The server responds with a "Hello Response" and the connection is authenticated
// If anything in this initial process fails, the connection must immediately closed
// by both sides and _no_ disconnection message is to be sent.
// Note: Password authentication via AuthenticationRequest/AuthenticationResponse (message IDs 3, 4)
// was removed in ESPHome 2026.1.0. Those message IDs are reserved and should not be reused.
// Message sent at the beginning of each connection
// Can only be sent by the client and only at the beginning of the connection
@@ -130,25 +126,23 @@ message HelloResponse {
string name = 4;
}
// Message sent at the beginning of each connection to authenticate the client
// Can only be sent by the client and only at the beginning of the connection
// DEPRECATED in ESPHome 2026.1.0 - Password authentication is no longer supported.
// These messages are kept for protocol documentation but are not processed by the server.
// Use noise encryption instead: https://esphome.io/components/api/#configuration-variables
message AuthenticationRequest {
option (id) = 3;
option (source) = SOURCE_CLIENT;
option (no_delay) = true;
option (ifdef) = "USE_API_PASSWORD";
option deprecated = true;
// The password to log in with
string password = 1;
}
// Confirmation of successful connection. After this the connection is available for all traffic.
// Can only be sent by the server and only at the beginning of the connection
message AuthenticationResponse {
option (id) = 4;
option (source) = SOURCE_SERVER;
option (no_delay) = true;
option (ifdef) = "USE_API_PASSWORD";
option deprecated = true;
bool invalid_password = 1;
}
@@ -205,7 +199,9 @@ message DeviceInfoResponse {
option (id) = 10;
option (source) = SOURCE_SERVER;
bool uses_password = 1 [(field_ifdef) = "USE_API_PASSWORD"];
// Deprecated in ESPHome 2026.1.0, but kept for backward compatibility
// with older ESPHome versions that still send this field.
bool uses_password = 1 [deprecated = true];
// The name of the node, given by "App.set_name()"
string name = 2;
@@ -2425,7 +2421,7 @@ message ZWaveProxyFrame {
option (ifdef) = "USE_ZWAVE_PROXY";
option (no_delay) = true;
bytes data = 1 [(pointer_to_buffer) = true];
bytes data = 1;
}
enum ZWaveProxyRequestType {
@@ -2439,5 +2435,5 @@ message ZWaveProxyRequest {
option (ifdef) = "USE_ZWAVE_PROXY";
ZWaveProxyRequestType type = 1;
bytes data = 2 [(pointer_to_buffer) = true];
bytes data = 2;
}

View File

@@ -101,14 +101,16 @@ APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *pa
#if defined(USE_API_PLAINTEXT) && defined(USE_API_NOISE)
auto &noise_ctx = parent->get_noise_ctx();
if (noise_ctx.has_psk()) {
this->helper_ = std::unique_ptr<APIFrameHelper>{new APINoiseFrameHelper(std::move(sock), noise_ctx)};
this->helper_ =
std::unique_ptr<APIFrameHelper>{new APINoiseFrameHelper(std::move(sock), noise_ctx, &this->client_info_)};
} else {
this->helper_ = std::unique_ptr<APIFrameHelper>{new APIPlaintextFrameHelper(std::move(sock))};
this->helper_ = std::unique_ptr<APIFrameHelper>{new APIPlaintextFrameHelper(std::move(sock), &this->client_info_)};
}
#elif defined(USE_API_PLAINTEXT)
this->helper_ = std::unique_ptr<APIFrameHelper>{new APIPlaintextFrameHelper(std::move(sock))};
this->helper_ = std::unique_ptr<APIFrameHelper>{new APIPlaintextFrameHelper(std::move(sock), &this->client_info_)};
#elif defined(USE_API_NOISE)
this->helper_ = std::unique_ptr<APIFrameHelper>{new APINoiseFrameHelper(std::move(sock), parent->get_noise_ctx())};
this->helper_ = std::unique_ptr<APIFrameHelper>{
new APINoiseFrameHelper(std::move(sock), parent->get_noise_ctx(), &this->client_info_)};
#else
#error "No frame helper defined"
#endif
@@ -129,10 +131,8 @@ void APIConnection::start() {
this->fatal_error_with_log_(LOG_STR("Helper init failed"), err);
return;
}
// Initialize client name with peername (IP address) until Hello message provides actual name
char peername[socket::PEERNAME_MAX_LEN];
size_t len = this->helper_->getpeername_to(peername);
this->helper_->set_client_name(peername, len);
this->client_info_.peername = helper_->getpeername();
this->client_info_.name = this->client_info_.peername;
}
APIConnection::~APIConnection() {
@@ -252,7 +252,8 @@ void APIConnection::loop() {
// Disconnect if not responded within 2.5*keepalive
if (now - this->last_traffic_ > KEEPALIVE_DISCONNECT_TIMEOUT) {
on_fatal_error();
this->log_client_(ESPHOME_LOG_LEVEL_WARN, LOG_STR("is unresponsive; disconnecting"));
ESP_LOGW(TAG, "%s (%s) is unresponsive; disconnecting", this->client_info_.name.c_str(),
this->client_info_.peername.c_str());
}
} else if (now - this->last_traffic_ > KEEPALIVE_TIMEOUT_MS && !this->flags_.remove) {
// Only send ping if we're not disconnecting
@@ -286,7 +287,7 @@ bool APIConnection::send_disconnect_response(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
this->log_client_(ESPHOME_LOG_LEVEL_DEBUG, LOG_STR("disconnected"));
ESP_LOGD(TAG, "%s (%s) disconnected", this->client_info_.name.c_str(), this->client_info_.peername.c_str());
this->flags_.next_close = true;
DisconnectResponse resp;
return this->send_message(resp, DisconnectResponse::MESSAGE_TYPE);
@@ -1503,12 +1504,9 @@ void APIConnection::complete_authentication_() {
}
this->flags_.connection_state = static_cast<uint8_t>(ConnectionState::AUTHENTICATED);
this->log_client_(ESPHOME_LOG_LEVEL_DEBUG, LOG_STR("connected"));
ESP_LOGD(TAG, "%s (%s) connected", this->client_info_.name.c_str(), this->client_info_.peername.c_str());
#ifdef USE_API_CLIENT_CONNECTED_TRIGGER
char peername_buf[socket::PEERNAME_MAX_LEN];
this->helper_->getpeername_to(peername_buf);
this->parent_->get_client_connected_trigger()->trigger(std::string(this->helper_->get_client_name()),
std::string(peername_buf));
this->parent_->get_client_connected_trigger()->trigger(this->client_info_.name, this->client_info_.peername);
#endif
#ifdef USE_HOMEASSISTANT_TIME
if (homeassistant::global_homeassistant_time != nullptr) {
@@ -1523,14 +1521,12 @@ void APIConnection::complete_authentication_() {
}
bool APIConnection::send_hello_response(const HelloRequest &msg) {
// Copy client name with truncation if needed (set_client_name handles truncation)
this->helper_->set_client_name(msg.client_info.c_str(), msg.client_info.size());
this->client_info_.name.assign(msg.client_info.c_str(), msg.client_info.size());
this->client_info_.peername = this->helper_->getpeername();
this->client_api_version_major_ = msg.api_version_major;
this->client_api_version_minor_ = msg.api_version_minor;
char peername[socket::PEERNAME_MAX_LEN];
this->helper_->getpeername_to(peername);
ESP_LOGV(TAG, "Hello from client: '%s' | %s | API Version %" PRIu32 ".%" PRIu32, this->helper_->get_client_name(),
peername, this->client_api_version_major_, this->client_api_version_minor_);
ESP_LOGV(TAG, "Hello from client: '%s' | %s | API Version %" PRIu32 ".%" PRIu32, this->client_info_.name.c_str(),
this->client_info_.peername.c_str(), this->client_api_version_major_, this->client_api_version_minor_);
HelloResponse resp;
resp.api_version_major = 1;
@@ -1539,27 +1535,11 @@ bool APIConnection::send_hello_response(const HelloRequest &msg) {
resp.set_server_info(ESPHOME_VERSION_REF);
resp.set_name(StringRef(App.get_name()));
#ifdef USE_API_PASSWORD
// Password required - wait for authentication
this->flags_.connection_state = static_cast<uint8_t>(ConnectionState::CONNECTED);
#else
// No password configured - auto-authenticate
// Auto-authenticate - password auth was removed in ESPHome 2026.1.0
this->complete_authentication_();
#endif
return this->send_message(resp, HelloResponse::MESSAGE_TYPE);
}
#ifdef USE_API_PASSWORD
bool APIConnection::send_authenticate_response(const AuthenticationRequest &msg) {
AuthenticationResponse resp;
// bool invalid_password = 1;
resp.invalid_password = !this->parent_->check_password(msg.password.byte(), msg.password.size());
if (!resp.invalid_password) {
this->complete_authentication_();
}
return this->send_message(resp, AuthenticationResponse::MESSAGE_TYPE);
}
#endif // USE_API_PASSWORD
bool APIConnection::send_ping_response(const PingRequest &msg) {
PingResponse resp;
@@ -1568,9 +1548,6 @@ bool APIConnection::send_ping_response(const PingRequest &msg) {
bool APIConnection::send_device_info_response(const DeviceInfoRequest &msg) {
DeviceInfoResponse resp{};
#ifdef USE_API_PASSWORD
resp.uses_password = true;
#endif
resp.set_name(StringRef(App.get_name()));
resp.set_friendly_name(StringRef(App.get_friendly_name()));
#ifdef USE_AREAS
@@ -1715,18 +1692,10 @@ void APIConnection::on_home_assistant_state_response(const HomeAssistantStateRes
continue;
}
// Create null-terminated state for callback (parse_number needs null-termination)
// HA state max length is 255, so 256 byte buffer covers all cases
char state_buf[256];
size_t copy_len = msg.state_len;
if (copy_len >= sizeof(state_buf)) {
copy_len = sizeof(state_buf) - 1; // Truncate to leave space for null terminator
}
if (copy_len > 0) {
memcpy(state_buf, msg.state, copy_len);
}
state_buf[copy_len] = '\0';
it.callback(StringRef(state_buf, copy_len));
// Create temporary string for callback (callback takes const std::string &)
// Handle empty state
std::string state(!msg.state.empty() ? msg.state.c_str() : "", msg.state.size());
it.callback(state);
}
}
#endif
@@ -1857,15 +1826,9 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) {
// Do not set last_traffic_ on send
return true;
}
#ifdef USE_API_PASSWORD
void APIConnection::on_unauthenticated_access() {
this->on_fatal_error();
this->log_client_(ESPHOME_LOG_LEVEL_DEBUG, LOG_STR("no authentication"));
}
#endif
void APIConnection::on_no_setup_connection() {
this->on_fatal_error();
this->log_client_(ESPHOME_LOG_LEVEL_DEBUG, LOG_STR("no connection setup"));
ESP_LOGD(TAG, "%s (%s) no connection setup", this->client_info_.name.c_str(), this->client_info_.peername.c_str());
}
void APIConnection::on_fatal_error() {
this->helper_->close();
@@ -1911,9 +1874,9 @@ bool APIConnection::schedule_batch_() {
}
void APIConnection::process_batch_() {
// Ensure PacketInfo remains trivially destructible for our placement new approach
static_assert(std::is_trivially_destructible<PacketInfo>::value,
"PacketInfo must remain trivially destructible with this placement-new approach");
// Ensure MessageInfo remains trivially destructible for our placement new approach
static_assert(std::is_trivially_destructible<MessageInfo>::value,
"MessageInfo must remain trivially destructible with this placement-new approach");
if (this->deferred_batch_.empty()) {
this->flags_.batch_scheduled = false;
@@ -1953,12 +1916,12 @@ void APIConnection::process_batch_() {
return;
}
size_t packets_to_process = std::min(num_items, MAX_PACKETS_PER_BATCH);
size_t messages_to_process = std::min(num_items, MAX_MESSAGES_PER_BATCH);
// Stack-allocated array for packet info
alignas(PacketInfo) char packet_info_storage[MAX_PACKETS_PER_BATCH * sizeof(PacketInfo)];
PacketInfo *packet_info = reinterpret_cast<PacketInfo *>(packet_info_storage);
size_t packet_count = 0;
// Stack-allocated array for message info
alignas(MessageInfo) char message_info_storage[MAX_MESSAGES_PER_BATCH * sizeof(MessageInfo)];
MessageInfo *message_info = reinterpret_cast<MessageInfo *>(message_info_storage);
size_t message_count = 0;
// Cache these values to avoid repeated virtual calls
const uint8_t header_padding = this->helper_->frame_header_padding();
@@ -1989,7 +1952,7 @@ void APIConnection::process_batch_() {
uint32_t current_offset = 0;
// Process items and encode directly to buffer (up to our limit)
for (size_t i = 0; i < packets_to_process; i++) {
for (size_t i = 0; i < messages_to_process; i++) {
const auto &item = this->deferred_batch_[i];
// Try to encode message
// The creator will calculate overhead to determine if the message fits
@@ -2003,11 +1966,11 @@ void APIConnection::process_batch_() {
// 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;
// Use placement new to construct PacketInfo in pre-allocated stack array
// This avoids default-constructing all MAX_PACKETS_PER_BATCH elements
// Explicit destruction is not needed because PacketInfo is trivially destructible,
// Use placement new to construct MessageInfo in pre-allocated stack array
// This avoids default-constructing all MAX_MESSAGES_PER_BATCH elements
// Explicit destruction is not needed because MessageInfo is trivially destructible,
// as ensured by the static_assert in its definition.
new (&packet_info[packet_count++]) PacketInfo(item.message_type, current_offset, proto_payload_size);
new (&message_info[message_count++]) MessageInfo(item.message_type, current_offset, proto_payload_size);
// Update tracking variables
items_processed++;
@@ -2031,9 +1994,9 @@ void APIConnection::process_batch_() {
shared_buf.resize(shared_buf.size() + footer_size);
}
// Send all collected packets
APIError err = this->helper_->write_protobuf_packets(ProtoWriteBuffer{&shared_buf},
std::span<const PacketInfo>(packet_info, packet_count));
// Send all collected messages
APIError err = this->helper_->write_protobuf_messages(ProtoWriteBuffer{&shared_buf},
std::span<const MessageInfo>(message_info, message_count));
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
this->fatal_error_with_log_(LOG_STR("Batch write failed"), err);
}
@@ -2113,18 +2076,9 @@ void APIConnection::process_state_subscriptions_() {
}
#endif // USE_API_HOMEASSISTANT_STATES
void APIConnection::log_client_(int level, const LogString *message) {
char peername[socket::PEERNAME_MAX_LEN];
this->helper_->getpeername_to(peername);
esp_log_printf_(level, TAG, __LINE__, ESPHOME_LOG_FORMAT("%s (%s): %s"), this->helper_->get_client_name(), peername,
LOG_STR_ARG(message));
}
void APIConnection::log_warning_(const LogString *message, APIError err) {
char peername[socket::PEERNAME_MAX_LEN];
this->helper_->getpeername_to(peername);
ESP_LOGW(TAG, "%s (%s): %s %s errno=%d", this->helper_->get_client_name(), peername, LOG_STR_ARG(message),
LOG_STR_ARG(api_error_to_logstr(err)), errno);
ESP_LOGW(TAG, "%s (%s): %s %s errno=%d", this->client_info_.name.c_str(), this->client_info_.peername.c_str(),
LOG_STR_ARG(message), LOG_STR_ARG(api_error_to_logstr(err)), errno);
}
} // namespace esphome::api

View File

@@ -9,13 +9,18 @@
#include "esphome/core/application.h"
#include "esphome/core/component.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/string_ref.h"
#include <functional>
#include <vector>
namespace esphome::api {
// Client information structure
struct ClientInfo {
std::string name; // Client name from Hello message
std::string peername; // IP:port from socket
};
// Keepalive timeout in milliseconds
static constexpr uint32_t KEEPALIVE_TIMEOUT_MS = 60000;
// Maximum number of entities to process in a single batch during initial state/info sending
@@ -23,14 +28,9 @@ static constexpr uint32_t KEEPALIVE_TIMEOUT_MS = 60000;
// TODO: Remove MAX_INITIAL_PER_BATCH_LEGACY before 2026.7.0 - all clients should support API 1.14 by then
static constexpr size_t MAX_INITIAL_PER_BATCH_LEGACY = 24; // For clients < API 1.14 (includes object_id)
static constexpr size_t MAX_INITIAL_PER_BATCH = 34; // For clients >= API 1.14 (no object_id)
// Maximum number of packets to process in a single batch (platform-dependent)
// This limit exists to prevent stack overflow from the PacketInfo array in process_batch_
// Each PacketInfo is 8 bytes, so 64 * 8 = 512 bytes, 32 * 8 = 256 bytes
#if defined(USE_ESP32) || defined(USE_HOST)
static constexpr size_t MAX_PACKETS_PER_BATCH = 64; // ESP32 has 8KB+ stack, HOST has plenty
#else
static constexpr size_t MAX_PACKETS_PER_BATCH = 32; // ESP8266/RP2040/etc have smaller stacks
#endif
// Verify MAX_MESSAGES_PER_BATCH (defined in api_frame_helper.h) can hold the initial batch
static_assert(MAX_MESSAGES_PER_BATCH >= MAX_INITIAL_PER_BATCH,
"MAX_MESSAGES_PER_BATCH must be >= MAX_INITIAL_PER_BATCH");
class APIConnection final : public APIServerConnection {
public:
@@ -198,9 +198,6 @@ class APIConnection final : public APIServerConnection {
void on_get_time_response(const GetTimeResponse &value) override;
#endif
bool send_hello_response(const HelloRequest &msg) override;
#ifdef USE_API_PASSWORD
bool send_authenticate_response(const AuthenticationRequest &msg) override;
#endif
bool send_disconnect_response(const DisconnectRequest &msg) override;
bool send_ping_response(const PingRequest &msg) override;
bool send_device_info_response(const DeviceInfoRequest &msg) override;
@@ -256,9 +253,6 @@ class APIConnection final : public APIServerConnection {
}
void on_fatal_error() override;
#ifdef USE_API_PASSWORD
void on_unauthenticated_access() override;
#endif
void on_no_setup_connection() override;
ProtoWriteBuffer create_buffer(uint32_t reserve_size) override {
// FIXME: ensure no recursive writes can happen
@@ -285,11 +279,8 @@ class APIConnection final : public APIServerConnection {
bool try_to_clear_buffer(bool log_out_of_space);
bool send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) override;
const char *get_name() const { return this->helper_->get_client_name(); }
/// Get peer name (IP address) into a stack buffer - avoids heap allocation
size_t get_peername_to(std::span<char, socket::PEERNAME_MAX_LEN> buf) const {
return this->helper_->getpeername_to(buf);
}
const std::string &get_name() const { return this->client_info_.name; }
const std::string &get_peername() const { return this->client_info_.peername; }
protected:
// Helper function to handle authentication completion
@@ -535,7 +526,10 @@ class APIConnection final : public APIServerConnection {
std::unique_ptr<camera::CameraImageReader> image_reader_;
#endif
// Group 3: 4-byte types
// Group 3: Client info struct (24 bytes on 32-bit: 2 strings × 12 bytes each)
ClientInfo client_info_;
// Group 4: 4-byte types
uint32_t last_traffic_;
#ifdef USE_API_HOMEASSISTANT_STATES
int state_subs_at_ = -1;
@@ -762,8 +756,6 @@ class APIConnection final : public APIServerConnection {
return this->schedule_batch_();
}
// Helper function to log client messages with name and peername
void log_client_(int level, const LogString *message);
// Helper function to log API errors with errno
void log_warning_(const LogString *message, APIError err);
// Helper to handle fatal errors with logging

View File

@@ -1,5 +1,6 @@
#include "api_frame_helper.h"
#ifdef USE_API
#include "api_connection.h" // For ClientInfo struct
#include "esphome/core/application.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
@@ -15,16 +16,8 @@ static const char *const TAG = "api.frame_helper";
// Maximum bytes to log in hex format (168 * 3 = 504, under TX buffer size of 512)
static constexpr size_t API_MAX_LOG_BYTES = 168;
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
#define HELPER_LOG(msg, ...) \
do { \
char peername__[socket::PEERNAME_MAX_LEN]; \
this->socket_->getpeername_to(peername__); \
ESP_LOGVV(TAG, "%s (%s): " msg, this->client_name_, peername__, ##__VA_ARGS__); \
} while (0)
#else
#define HELPER_LOG(msg, ...) ((void) 0)
#endif
ESP_LOGVV(TAG, "%s (%s): " msg, this->client_info_->name.c_str(), this->client_info_->peername.c_str(), ##__VA_ARGS__)
#ifdef HELPER_LOG_PACKETS
#define LOG_PACKET_RECEIVED(buffer) \

View File

@@ -29,10 +29,14 @@ static constexpr uint16_t MAX_MESSAGE_SIZE = 8192; // 8 KiB for ESP8266
static constexpr uint16_t MAX_MESSAGE_SIZE = 32768; // 32 KiB for ESP32 and other platforms
#endif
class ProtoWriteBuffer;
// Maximum number of messages to batch in a single write operation
// Must be >= MAX_INITIAL_PER_BATCH in api_connection.h (enforced by static_assert there)
static constexpr size_t MAX_MESSAGES_PER_BATCH = 34;
// Max client name length (e.g., "Home Assistant 2026.1.0.dev0" = 28 chars)
static constexpr size_t CLIENT_INFO_NAME_MAX_LEN = 32;
// Forward declaration
struct ClientInfo;
class ProtoWriteBuffer;
struct ReadPacketBuffer {
const uint8_t *data; // Points directly into frame helper's rx_buf_ (valid until next read_packet call)
@@ -40,13 +44,13 @@ struct ReadPacketBuffer {
uint16_t type;
};
// Packed packet info structure to minimize memory usage
struct PacketInfo {
// Packed message info structure to minimize memory usage
struct MessageInfo {
uint16_t offset; // Offset in buffer where message starts
uint16_t payload_size; // Size of the message payload
uint8_t message_type; // Message type (0-255)
PacketInfo(uint8_t type, uint16_t off, uint16_t size) : offset(off), payload_size(size), message_type(type) {}
MessageInfo(uint8_t type, uint16_t off, uint16_t size) : offset(off), payload_size(size), message_type(type) {}
};
enum class APIError : uint16_t {
@@ -82,23 +86,15 @@ const LogString *api_error_to_logstr(APIError err);
class APIFrameHelper {
public:
APIFrameHelper() = default;
explicit APIFrameHelper(std::unique_ptr<socket::Socket> socket) : socket_(std::move(socket)) {}
// Get client name (null-terminated)
const char *get_client_name() const { return this->client_name_; }
// Set client name from buffer with length (truncates if needed)
void set_client_name(const char *name, size_t len) {
size_t copy_len = std::min(len, sizeof(this->client_name_) - 1);
memcpy(this->client_name_, name, copy_len);
this->client_name_[copy_len] = '\0';
}
explicit APIFrameHelper(std::unique_ptr<socket::Socket> socket, const ClientInfo *client_info)
: socket_(std::move(socket)), client_info_(client_info) {}
virtual ~APIFrameHelper() = default;
virtual APIError init() = 0;
virtual APIError loop();
virtual APIError read_packet(ReadPacketBuffer *buffer) = 0;
bool can_write_without_blocking() { return this->state_ == State::DATA && this->tx_buf_count_ == 0; }
std::string getpeername() { return socket_->getpeername(); }
int getpeername(struct sockaddr *addr, socklen_t *addrlen) { return socket_->getpeername(addr, addrlen); }
size_t getpeername_to(std::span<char, socket::PEERNAME_MAX_LEN> buf) { return socket_->getpeername_to(buf); }
APIError close() {
state_ = State::CLOSED;
int err = this->socket_->close();
@@ -116,10 +112,10 @@ class APIFrameHelper {
return APIError::OK;
}
virtual APIError write_protobuf_packet(uint8_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
// Write multiple protobuf messages in a single operation
// messages 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, std::span<const PacketInfo> packets) = 0;
virtual APIError write_protobuf_messages(ProtoWriteBuffer buffer, std::span<const MessageInfo> messages) = 0;
// Get the frame header padding required by this protocol
uint8_t frame_header_padding() const { return frame_header_padding_; }
// Get the frame footer size required by this protocol
@@ -135,12 +131,6 @@ class APIFrameHelper {
// Use swap trick since shrink_to_fit() is non-binding and may be ignored
std::vector<uint8_t>().swap(this->rx_buf_);
}
// reusable_iovs_: Safe to release unconditionally.
// Only used within write_protobuf_packets() calls - cleared at start,
// populated with pointers, used for writev(), then function returns.
// The iovecs contain stale pointers after the call (data was either sent
// or copied to tx_buf_), and are cleared on next write_protobuf_packets().
std::vector<struct iovec>().swap(this->reusable_iovs_);
}
protected:
@@ -194,11 +184,11 @@ class APIFrameHelper {
// Containers (size varies, but typically 12+ bytes on 32-bit)
std::array<std::unique_ptr<SendBuffer>, API_MAX_SEND_QUEUE> tx_buf_;
std::vector<struct iovec> reusable_iovs_;
std::vector<uint8_t> rx_buf_;
// Client name buffer - stores name from Hello message or initial peername
char client_name_[CLIENT_INFO_NAME_MAX_LEN]{};
// Pointer to client info (4 bytes on 32-bit)
// Note: The pointed-to ClientInfo object must outlive this APIFrameHelper instance.
const ClientInfo *client_info_{nullptr};
// Group smaller types together
uint16_t rx_buf_len_ = 0;

View File

@@ -27,16 +27,8 @@ static constexpr size_t PROLOGUE_INIT_LEN = 12; // strlen("NoiseAPIInit")
// Maximum bytes to log in hex format (168 * 3 = 504, under TX buffer size of 512)
static constexpr size_t API_MAX_LOG_BYTES = 168;
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
#define HELPER_LOG(msg, ...) \
do { \
char peername__[socket::PEERNAME_MAX_LEN]; \
this->socket_->getpeername_to(peername__); \
ESP_LOGVV(TAG, "%s (%s): " msg, this->client_name_, peername__, ##__VA_ARGS__); \
} while (0)
#else
#define HELPER_LOG(msg, ...) ((void) 0)
#endif
ESP_LOGVV(TAG, "%s (%s): " msg, this->client_info_->name.c_str(), this->client_info_->peername.c_str(), ##__VA_ARGS__)
#ifdef HELPER_LOG_PACKETS
#define LOG_PACKET_RECEIVED(buffer) \
@@ -437,12 +429,12 @@ APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) {
APIError APINoiseFrameHelper::write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) {
// Resize to include MAC space (required for Noise encryption)
buffer.get_buffer()->resize(buffer.get_buffer()->size() + frame_footer_size_);
PacketInfo packet{type, 0,
static_cast<uint16_t>(buffer.get_buffer()->size() - frame_header_padding_ - frame_footer_size_)};
return write_protobuf_packets(buffer, std::span<const PacketInfo>(&packet, 1));
MessageInfo msg{type, 0,
static_cast<uint16_t>(buffer.get_buffer()->size() - frame_header_padding_ - frame_footer_size_)};
return write_protobuf_messages(buffer, std::span<const MessageInfo>(&msg, 1));
}
APIError APINoiseFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, std::span<const PacketInfo> packets) {
APIError APINoiseFrameHelper::write_protobuf_messages(ProtoWriteBuffer buffer, std::span<const MessageInfo> messages) {
APIError aerr = state_action_();
if (aerr != APIError::OK) {
return aerr;
@@ -452,20 +444,20 @@ APIError APINoiseFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, st
return APIError::WOULD_BLOCK;
}
if (packets.empty()) {
if (messages.empty()) {
return APIError::OK;
}
uint8_t *buffer_data = buffer.get_buffer()->data();
this->reusable_iovs_.clear();
this->reusable_iovs_.reserve(packets.size());
// Stack-allocated iovec array - no heap allocation
StaticVector<struct iovec, MAX_MESSAGES_PER_BATCH> iovs;
uint16_t total_write_len = 0;
// We need to encrypt each packet in place
for (const auto &packet : packets) {
// We need to encrypt each message in place
for (const auto &msg : messages) {
// The buffer already has padding at offset
uint8_t *buf_start = buffer_data + packet.offset;
uint8_t *buf_start = buffer_data + msg.offset;
// Write noise header
buf_start[0] = 0x01; // indicator
@@ -473,10 +465,10 @@ APIError APINoiseFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, st
// Write message header (to be encrypted)
const uint8_t msg_offset = 3;
buf_start[msg_offset] = static_cast<uint8_t>(packet.message_type >> 8); // type high byte
buf_start[msg_offset + 1] = static_cast<uint8_t>(packet.message_type); // type low byte
buf_start[msg_offset + 2] = static_cast<uint8_t>(packet.payload_size >> 8); // data_len high byte
buf_start[msg_offset + 3] = static_cast<uint8_t>(packet.payload_size); // data_len low byte
buf_start[msg_offset] = static_cast<uint8_t>(msg.message_type >> 8); // type high byte
buf_start[msg_offset + 1] = static_cast<uint8_t>(msg.message_type); // type low byte
buf_start[msg_offset + 2] = static_cast<uint8_t>(msg.payload_size >> 8); // data_len high byte
buf_start[msg_offset + 3] = static_cast<uint8_t>(msg.payload_size); // data_len low byte
// payload data is already in the buffer starting at offset + 7
// Make sure we have space for MAC
@@ -485,8 +477,8 @@ APIError APINoiseFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, st
// Encrypt the message in place
NoiseBuffer mbuf;
noise_buffer_init(mbuf);
noise_buffer_set_inout(mbuf, buf_start + msg_offset, 4 + packet.payload_size,
4 + packet.payload_size + frame_footer_size_);
noise_buffer_set_inout(mbuf, buf_start + msg_offset, 4 + msg.payload_size,
4 + msg.payload_size + frame_footer_size_);
int err = noise_cipherstate_encrypt(send_cipher_, &mbuf);
APIError aerr =
@@ -498,14 +490,14 @@ APIError APINoiseFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, st
buf_start[1] = static_cast<uint8_t>(mbuf.size >> 8);
buf_start[2] = static_cast<uint8_t>(mbuf.size);
// Add iovec for this encrypted packet
size_t packet_len = static_cast<size_t>(3 + mbuf.size); // indicator + size + encrypted data
this->reusable_iovs_.push_back({buf_start, packet_len});
total_write_len += packet_len;
// Add iovec for this encrypted message
size_t msg_len = static_cast<size_t>(3 + mbuf.size); // indicator + size + encrypted data
iovs.push_back({buf_start, msg_len});
total_write_len += msg_len;
}
// Send all encrypted packets in one writev call
return this->write_raw_(this->reusable_iovs_.data(), this->reusable_iovs_.size(), total_write_len);
// Send all encrypted messages in one writev call
return this->write_raw_(iovs.data(), iovs.size(), total_write_len);
}
APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, uint16_t len) {

View File

@@ -9,8 +9,8 @@ namespace esphome::api {
class APINoiseFrameHelper final : public APIFrameHelper {
public:
APINoiseFrameHelper(std::unique_ptr<socket::Socket> socket, APINoiseContext &ctx)
: APIFrameHelper(std::move(socket)), ctx_(ctx) {
APINoiseFrameHelper(std::unique_ptr<socket::Socket> socket, APINoiseContext &ctx, const ClientInfo *client_info)
: APIFrameHelper(std::move(socket), client_info), ctx_(ctx) {
// Noise header structure:
// Pos 0: indicator (0x01)
// Pos 1-2: encrypted payload size (16-bit big-endian)
@@ -23,7 +23,7 @@ class APINoiseFrameHelper final : public APIFrameHelper {
APIError loop() override;
APIError read_packet(ReadPacketBuffer *buffer) override;
APIError write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) override;
APIError write_protobuf_packets(ProtoWriteBuffer buffer, std::span<const PacketInfo> packets) override;
APIError write_protobuf_messages(ProtoWriteBuffer buffer, std::span<const MessageInfo> messages) override;
protected:
APIError state_action_();

View File

@@ -1,6 +1,7 @@
#include "api_frame_helper_plaintext.h"
#ifdef USE_API
#ifdef USE_API_PLAINTEXT
#include "api_connection.h" // For ClientInfo struct
#include "esphome/core/application.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
@@ -20,16 +21,8 @@ static const char *const TAG = "api.plaintext";
// Maximum bytes to log in hex format (168 * 3 = 504, under TX buffer size of 512)
static constexpr size_t API_MAX_LOG_BYTES = 168;
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
#define HELPER_LOG(msg, ...) \
do { \
char peername__[socket::PEERNAME_MAX_LEN]; \
this->socket_->getpeername_to(peername__); \
ESP_LOGVV(TAG, "%s (%s): " msg, this->client_name_, peername__, ##__VA_ARGS__); \
} while (0)
#else
#define HELPER_LOG(msg, ...) ((void) 0)
#endif
ESP_LOGVV(TAG, "%s (%s): " msg, this->client_info_->name.c_str(), this->client_info_->peername.c_str(), ##__VA_ARGS__)
#ifdef HELPER_LOG_PACKETS
#define LOG_PACKET_RECEIVED(buffer) \
@@ -237,29 +230,30 @@ APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
return APIError::OK;
}
APIError APIPlaintextFrameHelper::write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) {
PacketInfo packet{type, 0, static_cast<uint16_t>(buffer.get_buffer()->size() - frame_header_padding_)};
return write_protobuf_packets(buffer, std::span<const PacketInfo>(&packet, 1));
MessageInfo msg{type, 0, static_cast<uint16_t>(buffer.get_buffer()->size() - frame_header_padding_)};
return write_protobuf_messages(buffer, std::span<const MessageInfo>(&msg, 1));
}
APIError APIPlaintextFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer, std::span<const PacketInfo> packets) {
APIError APIPlaintextFrameHelper::write_protobuf_messages(ProtoWriteBuffer buffer,
std::span<const MessageInfo> messages) {
if (state_ != State::DATA) {
return APIError::BAD_STATE;
}
if (packets.empty()) {
if (messages.empty()) {
return APIError::OK;
}
uint8_t *buffer_data = buffer.get_buffer()->data();
this->reusable_iovs_.clear();
this->reusable_iovs_.reserve(packets.size());
// Stack-allocated iovec array - no heap allocation
StaticVector<struct iovec, MAX_MESSAGES_PER_BATCH> iovs;
uint16_t total_write_len = 0;
for (const auto &packet : packets) {
for (const auto &msg : messages) {
// Calculate varint sizes for header layout
uint8_t size_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(packet.payload_size));
uint8_t type_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(packet.message_type));
uint8_t size_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(msg.payload_size));
uint8_t type_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(msg.message_type));
uint8_t total_header_len = 1 + size_varint_len + type_varint_len;
// Calculate where to start writing the header
@@ -287,25 +281,25 @@ APIError APIPlaintextFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer
//
// 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 = buffer_data + packet.offset;
uint8_t *buf_start = buffer_data + msg.offset;
uint32_t header_offset = frame_header_padding_ - total_header_len;
// Write the plaintext header
buf_start[header_offset] = 0x00; // indicator
// Encode varints directly into buffer
ProtoVarInt(packet.payload_size).encode_to_buffer_unchecked(buf_start + header_offset + 1, size_varint_len);
ProtoVarInt(packet.message_type)
ProtoVarInt(msg.payload_size).encode_to_buffer_unchecked(buf_start + header_offset + 1, size_varint_len);
ProtoVarInt(msg.message_type)
.encode_to_buffer_unchecked(buf_start + header_offset + 1 + size_varint_len, type_varint_len);
// Add iovec for this packet (header + payload)
size_t packet_len = static_cast<size_t>(total_header_len + packet.payload_size);
this->reusable_iovs_.push_back({buf_start + header_offset, packet_len});
total_write_len += packet_len;
// Add iovec for this message (header + payload)
size_t msg_len = static_cast<size_t>(total_header_len + msg.payload_size);
iovs.push_back({buf_start + header_offset, msg_len});
total_write_len += msg_len;
}
// Send all packets in one writev call
return write_raw_(this->reusable_iovs_.data(), this->reusable_iovs_.size(), total_write_len);
// Send all messages in one writev call
return write_raw_(iovs.data(), iovs.size(), total_write_len);
}
} // namespace esphome::api

View File

@@ -7,7 +7,8 @@ namespace esphome::api {
class APIPlaintextFrameHelper final : public APIFrameHelper {
public:
explicit APIPlaintextFrameHelper(std::unique_ptr<socket::Socket> socket) : APIFrameHelper(std::move(socket)) {
APIPlaintextFrameHelper(std::unique_ptr<socket::Socket> socket, const ClientInfo *client_info)
: APIFrameHelper(std::move(socket), client_info) {
// Plaintext header structure (worst case):
// Pos 0: indicator (0x00)
// Pos 1-3: payload size varint (up to 3 bytes)
@@ -20,7 +21,7 @@ class APIPlaintextFrameHelper final : public APIFrameHelper {
APIError loop() override;
APIError read_packet(ReadPacketBuffer *buffer) override;
APIError write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) override;
APIError write_protobuf_packets(ProtoWriteBuffer buffer, std::span<const PacketInfo> packets) override;
APIError write_protobuf_messages(ProtoWriteBuffer buffer, std::span<const MessageInfo> messages) override;
protected:
APIError try_read_frame_();

View File

@@ -43,21 +43,6 @@ void HelloResponse::calculate_size(ProtoSize &size) const {
size.add_length(1, this->server_info_ref_.size());
size.add_length(1, this->name_ref_.size());
}
#ifdef USE_API_PASSWORD
bool AuthenticationRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1: {
this->password = StringRef(reinterpret_cast<const char *>(value.data()), value.size());
break;
}
default:
return false;
}
return true;
}
void AuthenticationResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->invalid_password); }
void AuthenticationResponse::calculate_size(ProtoSize &size) const { size.add_bool(1, this->invalid_password); }
#endif
#ifdef USE_AREAS
void AreaInfo::encode(ProtoWriteBuffer buffer) const {
buffer.encode_uint32(1, this->area_id);
@@ -81,9 +66,6 @@ void DeviceInfo::calculate_size(ProtoSize &size) const {
}
#endif
void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
#ifdef USE_API_PASSWORD
buffer.encode_bool(1, this->uses_password);
#endif
buffer.encode_string(2, this->name_ref_);
buffer.encode_string(3, this->mac_address_ref_);
buffer.encode_string(4, this->esphome_version_ref_);
@@ -139,9 +121,6 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
#endif
}
void DeviceInfoResponse::calculate_size(ProtoSize &size) const {
#ifdef USE_API_PASSWORD
size.add_bool(1, this->uses_password);
#endif
size.add_length(1, this->name_ref_.size());
size.add_length(1, this->mac_address_ref_.size());
size.add_length(1, this->esphome_version_ref_.size());

View File

@@ -393,39 +393,6 @@ class HelloResponse final : public ProtoMessage {
protected:
};
#ifdef USE_API_PASSWORD
class AuthenticationRequest final : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 3;
static constexpr uint8_t ESTIMATED_SIZE = 9;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "authentication_request"; }
#endif
StringRef password{};
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
};
class AuthenticationResponse final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 4;
static constexpr uint8_t ESTIMATED_SIZE = 2;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "authentication_response"; }
#endif
bool invalid_password{false};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
};
#endif
class DisconnectRequest final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 5;
@@ -525,12 +492,9 @@ class DeviceInfo final : public ProtoMessage {
class DeviceInfoResponse final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 10;
static constexpr uint16_t ESTIMATED_SIZE = 257;
static constexpr uint8_t ESTIMATED_SIZE = 255;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "device_info_response"; }
#endif
#ifdef USE_API_PASSWORD
bool uses_password{false};
#endif
StringRef name_ref_{};
void set_name(const StringRef &ref) { this->name_ref_ = ref; }
@@ -1046,7 +1010,7 @@ class SubscribeLogsRequest final : public ProtoDecodableMessage {
class SubscribeLogsResponse final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 29;
static constexpr uint8_t ESTIMATED_SIZE = 11;
static constexpr uint8_t ESTIMATED_SIZE = 21;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "subscribe_logs_response"; }
#endif
@@ -1069,7 +1033,7 @@ class SubscribeLogsResponse final : public ProtoMessage {
class NoiseEncryptionSetKeyRequest final : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 124;
static constexpr uint8_t ESTIMATED_SIZE = 9;
static constexpr uint8_t ESTIMATED_SIZE = 19;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "noise_encryption_set_key_request"; }
#endif
@@ -1161,7 +1125,7 @@ class HomeassistantActionRequest final : public ProtoMessage {
class HomeassistantActionResponse final : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 130;
static constexpr uint8_t ESTIMATED_SIZE = 24;
static constexpr uint8_t ESTIMATED_SIZE = 34;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "homeassistant_action_response"; }
#endif
@@ -1388,7 +1352,7 @@ class ListEntitiesCameraResponse final : public InfoResponseProtoMessage {
class CameraImageResponse final : public StateResponseProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 44;
static constexpr uint8_t ESTIMATED_SIZE = 20;
static constexpr uint8_t ESTIMATED_SIZE = 30;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "camera_image_response"; }
#endif
@@ -2123,7 +2087,7 @@ class BluetoothGATTReadRequest final : public ProtoDecodableMessage {
class BluetoothGATTReadResponse final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 74;
static constexpr uint8_t ESTIMATED_SIZE = 17;
static constexpr uint8_t ESTIMATED_SIZE = 27;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "bluetooth_gatt_read_response"; }
#endif
@@ -2146,7 +2110,7 @@ class BluetoothGATTReadResponse final : public ProtoMessage {
class BluetoothGATTWriteRequest final : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 75;
static constexpr uint8_t ESTIMATED_SIZE = 19;
static constexpr uint8_t ESTIMATED_SIZE = 29;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "bluetooth_gatt_write_request"; }
#endif
@@ -2182,7 +2146,7 @@ class BluetoothGATTReadDescriptorRequest final : public ProtoDecodableMessage {
class BluetoothGATTWriteDescriptorRequest final : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 77;
static constexpr uint8_t ESTIMATED_SIZE = 17;
static constexpr uint8_t ESTIMATED_SIZE = 27;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "bluetooth_gatt_write_descriptor_request"; }
#endif
@@ -2218,7 +2182,7 @@ class BluetoothGATTNotifyRequest final : public ProtoDecodableMessage {
class BluetoothGATTNotifyDataResponse final : public ProtoMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 79;
static constexpr uint8_t ESTIMATED_SIZE = 17;
static constexpr uint8_t ESTIMATED_SIZE = 27;
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "bluetooth_gatt_notify_data_response"; }
#endif

View File

@@ -748,18 +748,6 @@ void HelloResponse::dump_to(std::string &out) const {
dump_field(out, "server_info", this->server_info_ref_);
dump_field(out, "name", this->name_ref_);
}
#ifdef USE_API_PASSWORD
void AuthenticationRequest::dump_to(std::string &out) const {
MessageDumpHelper helper(out, "AuthenticationRequest");
out.append(" password: ");
out.append("'").append(this->password.c_str(), this->password.size()).append("'");
out.append("\n");
}
void AuthenticationResponse::dump_to(std::string &out) const {
MessageDumpHelper helper(out, "AuthenticationResponse");
dump_field(out, "invalid_password", this->invalid_password);
}
#endif
void DisconnectRequest::dump_to(std::string &out) const { out.append("DisconnectRequest {}"); }
void DisconnectResponse::dump_to(std::string &out) const { out.append("DisconnectResponse {}"); }
void PingRequest::dump_to(std::string &out) const { out.append("PingRequest {}"); }
@@ -782,9 +770,6 @@ void DeviceInfo::dump_to(std::string &out) const {
#endif
void DeviceInfoResponse::dump_to(std::string &out) const {
MessageDumpHelper helper(out, "DeviceInfoResponse");
#ifdef USE_API_PASSWORD
dump_field(out, "uses_password", this->uses_password);
#endif
dump_field(out, "name", this->name_ref_);
dump_field(out, "mac_address", this->mac_address_ref_);
dump_field(out, "esphome_version", this->esphome_version_ref_);

View File

@@ -24,17 +24,6 @@ void APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
this->on_hello_request(msg);
break;
}
#ifdef USE_API_PASSWORD
case AuthenticationRequest::MESSAGE_TYPE: {
AuthenticationRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_authentication_request: %s", msg.dump().c_str());
#endif
this->on_authentication_request(msg);
break;
}
#endif
case DisconnectRequest::MESSAGE_TYPE: {
DisconnectRequest msg;
// Empty message: no decode needed
@@ -643,13 +632,6 @@ void APIServerConnection::on_hello_request(const HelloRequest &msg) {
this->on_fatal_error();
}
}
#ifdef USE_API_PASSWORD
void APIServerConnection::on_authentication_request(const AuthenticationRequest &msg) {
if (!this->send_authenticate_response(msg)) {
this->on_fatal_error();
}
}
#endif
void APIServerConnection::on_disconnect_request(const DisconnectRequest &msg) {
if (!this->send_disconnect_response(msg)) {
this->on_fatal_error();
@@ -841,10 +823,7 @@ void APIServerConnection::on_z_wave_proxy_request(const ZWaveProxyRequest &msg)
void APIServerConnection::read_message(uint32_t msg_size, uint32_t msg_type, const uint8_t *msg_data) {
// Check authentication/connection requirements for messages
switch (msg_type) {
case HelloRequest::MESSAGE_TYPE: // No setup required
#ifdef USE_API_PASSWORD
case AuthenticationRequest::MESSAGE_TYPE: // No setup required
#endif
case HelloRequest::MESSAGE_TYPE: // No setup required
case DisconnectRequest::MESSAGE_TYPE: // No setup required
case PingRequest::MESSAGE_TYPE: // No setup required
break; // Skip all checks for these messages

View File

@@ -26,10 +26,6 @@ class APIServerConnectionBase : public ProtoService {
virtual void on_hello_request(const HelloRequest &value){};
#ifdef USE_API_PASSWORD
virtual void on_authentication_request(const AuthenticationRequest &value){};
#endif
virtual void on_disconnect_request(const DisconnectRequest &value){};
virtual void on_disconnect_response(const DisconnectResponse &value){};
virtual void on_ping_request(const PingRequest &value){};
@@ -228,9 +224,6 @@ class APIServerConnectionBase : public ProtoService {
class APIServerConnection : public APIServerConnectionBase {
public:
virtual bool send_hello_response(const HelloRequest &msg) = 0;
#ifdef USE_API_PASSWORD
virtual bool send_authenticate_response(const AuthenticationRequest &msg) = 0;
#endif
virtual bool send_disconnect_response(const DisconnectRequest &msg) = 0;
virtual bool send_ping_response(const PingRequest &msg) = 0;
virtual bool send_device_info_response(const DeviceInfoRequest &msg) = 0;
@@ -357,9 +350,6 @@ class APIServerConnection : public APIServerConnectionBase {
#endif
protected:
void on_hello_request(const HelloRequest &msg) override;
#ifdef USE_API_PASSWORD
void on_authentication_request(const AuthenticationRequest &msg) override;
#endif
void on_disconnect_request(const DisconnectRequest &msg) override;
void on_ping_request(const PingRequest &msg) override;
void on_device_info_request(const DeviceInfoRequest &msg) override;

View File

@@ -125,18 +125,15 @@ void APIServer::loop() {
if (!sock)
break;
char peername[socket::PEERNAME_MAX_LEN];
sock->getpeername_to(peername);
// Check if we're at the connection limit
if (this->clients_.size() >= this->max_connections_) {
ESP_LOGW(TAG, "Max connections (%d), rejecting %s", this->max_connections_, peername);
ESP_LOGW(TAG, "Max connections (%d), rejecting %s", this->max_connections_, sock->getpeername().c_str());
// Immediately close - socket destructor will handle cleanup
sock.reset();
continue;
}
ESP_LOGD(TAG, "Accept %s", peername);
ESP_LOGD(TAG, "Accept %s", sock->getpeername().c_str());
auto *conn = new APIConnection(std::move(sock), this);
this->clients_.emplace_back(conn);
@@ -169,7 +166,8 @@ void APIServer::loop() {
// Network is down - disconnect all clients
for (auto &client : this->clients_) {
client->on_fatal_error();
client->log_client_(ESPHOME_LOG_LEVEL_WARN, LOG_STR("Network down; disconnect"));
ESP_LOGW(TAG, "%s (%s): Network down; disconnect", client->client_info_.name.c_str(),
client->client_info_.peername.c_str());
}
// Continue to process and clean up the clients below
}
@@ -187,14 +185,12 @@ void APIServer::loop() {
// Rare case: handle disconnection
#ifdef USE_API_CLIENT_DISCONNECTED_TRIGGER
char peername_buf[socket::PEERNAME_MAX_LEN];
client->get_peername_to(peername_buf);
this->client_disconnected_trigger_->trigger(std::string(client->get_name()), std::string(peername_buf));
this->client_disconnected_trigger_->trigger(client->client_info_.name, client->client_info_.peername);
#endif
#ifdef USE_API_USER_DEFINED_ACTION_RESPONSES
this->unregister_active_action_calls_for_connection(client.get());
#endif
ESP_LOGV(TAG, "Remove connection %s", client->get_name());
ESP_LOGV(TAG, "Remove connection %s", client->client_info_.name.c_str());
// Swap with the last element and pop (avoids expensive vector shifts)
if (client_index < this->clients_.size() - 1) {
@@ -228,38 +224,6 @@ void APIServer::dump_config() {
#endif
}
#ifdef USE_API_PASSWORD
bool APIServer::check_password(const uint8_t *password_data, size_t password_len) const {
// depend only on input password length
const char *a = this->password_.c_str();
uint32_t len_a = this->password_.length();
const char *b = reinterpret_cast<const char *>(password_data);
uint32_t len_b = password_len;
// disable optimization with volatile
volatile uint32_t length = len_b;
volatile const char *left = nullptr;
volatile const char *right = b;
uint8_t result = 0;
if (len_a == length) {
left = *((volatile const char **) &a);
result = 0;
}
if (len_a != length) {
left = b;
result = 1;
}
for (size_t i = 0; i < length; i++) {
result |= *left++ ^ *right++; // NOLINT
}
return result == 0;
}
#endif
void APIServer::handle_disconnect(APIConnection *conn) {}
// Macro for controller update dispatch
@@ -381,10 +345,6 @@ float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI;
void APIServer::set_port(uint16_t port) { this->port_ = port; }
#ifdef USE_API_PASSWORD
void APIServer::set_password(const std::string &password) { this->password_ = password; }
#endif
void APIServer::set_batch_delay(uint16_t batch_delay) { this->batch_delay_ = batch_delay; }
#ifdef USE_API_HOMEASSISTANT_SERVICES
@@ -428,8 +388,8 @@ void APIServer::handle_action_response(uint32_t call_id, bool success, StringRef
#ifdef USE_API_HOMEASSISTANT_STATES
// Helper to add subscription (reduces duplication)
void APIServer::add_state_subscription_(const char *entity_id, const char *attribute, std::function<void(StringRef)> f,
bool once) {
void APIServer::add_state_subscription_(const char *entity_id, const char *attribute,
std::function<void(std::string)> f, bool once) {
this->state_subs_.push_back(HomeAssistantStateSubscription{
.entity_id = entity_id, .attribute = attribute, .callback = std::move(f), .once = once,
// entity_id_dynamic_storage and attribute_dynamic_storage remain nullptr (no heap allocation)
@@ -438,7 +398,7 @@ void APIServer::add_state_subscription_(const char *entity_id, const char *attri
// Helper to add subscription with heap-allocated strings (reduces duplication)
void APIServer::add_state_subscription_(std::string entity_id, optional<std::string> attribute,
std::function<void(StringRef)> f, bool once) {
std::function<void(std::string)> f, bool once) {
HomeAssistantStateSubscription sub;
// Allocate heap storage for the strings
sub.entity_id_dynamic_storage = std::make_unique<std::string>(std::move(entity_id));
@@ -458,43 +418,23 @@ void APIServer::add_state_subscription_(std::string entity_id, optional<std::str
// New const char* overload (for internal components - zero allocation)
void APIServer::subscribe_home_assistant_state(const char *entity_id, const char *attribute,
std::function<void(StringRef)> f) {
std::function<void(std::string)> f) {
this->add_state_subscription_(entity_id, attribute, std::move(f), false);
}
void APIServer::get_home_assistant_state(const char *entity_id, const char *attribute,
std::function<void(StringRef)> f) {
std::function<void(std::string)> f) {
this->add_state_subscription_(entity_id, attribute, std::move(f), true);
}
// std::string overload with StringRef callback (zero-allocation callback)
// Existing std::string overload (for custom_api_device.h - heap allocation)
void APIServer::subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
std::function<void(StringRef)> f) {
std::function<void(std::string)> f) {
this->add_state_subscription_(std::move(entity_id), std::move(attribute), std::move(f), false);
}
void APIServer::get_home_assistant_state(std::string entity_id, optional<std::string> attribute,
std::function<void(StringRef)> f) {
this->add_state_subscription_(std::move(entity_id), std::move(attribute), std::move(f), true);
}
// Legacy helper: wraps std::string callback and delegates to StringRef version
void APIServer::add_state_subscription_(std::string entity_id, optional<std::string> attribute,
std::function<void(const std::string &)> f, bool once) {
// Wrap callback to convert StringRef -> std::string, then delegate
this->add_state_subscription_(std::move(entity_id), std::move(attribute),
std::function<void(StringRef)>([f = std::move(f)](StringRef state) { f(state.str()); }),
once);
}
// Legacy std::string overload (for custom_api_device.h - converts StringRef to std::string)
void APIServer::subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
std::function<void(const std::string &)> f) {
this->add_state_subscription_(std::move(entity_id), std::move(attribute), std::move(f), false);
}
void APIServer::get_home_assistant_state(std::string entity_id, optional<std::string> attribute,
std::function<void(const std::string &)> f) {
std::function<void(std::string)> f) {
this->add_state_subscription_(std::move(entity_id), std::move(attribute), std::move(f), true);
}

View File

@@ -10,7 +10,6 @@
#include "esphome/core/component.h"
#include "esphome/core/controller.h"
#include "esphome/core/log.h"
#include "esphome/core/string_ref.h"
#include "list_entities.h"
#include "subscribe_state.h"
#ifdef USE_LOGGER
@@ -60,10 +59,6 @@ class APIServer : public Component,
#endif
#ifdef USE_CAMERA
void on_camera_image(const std::shared_ptr<camera::CameraImage> &image) override;
#endif
#ifdef USE_API_PASSWORD
bool check_password(const uint8_t *password_data, size_t password_len) const;
void set_password(const std::string &password);
#endif
void set_port(uint16_t port);
void set_reboot_timeout(uint32_t reboot_timeout);
@@ -196,7 +191,7 @@ class APIServer : public Component,
struct HomeAssistantStateSubscription {
const char *entity_id; // Pointer to flash (internal) or heap (external)
const char *attribute; // Pointer to flash or nullptr (nullptr means no attribute)
std::function<void(StringRef)> callback;
std::function<void(std::string)> callback;
bool once;
// Dynamic storage for external components using std::string API (custom_api_device.h)
@@ -206,20 +201,14 @@ class APIServer : public Component,
};
// New const char* overload (for internal components - zero allocation)
void subscribe_home_assistant_state(const char *entity_id, const char *attribute, std::function<void(StringRef)> f);
void get_home_assistant_state(const char *entity_id, const char *attribute, std::function<void(StringRef)> f);
void subscribe_home_assistant_state(const char *entity_id, const char *attribute, std::function<void(std::string)> f);
void get_home_assistant_state(const char *entity_id, const char *attribute, std::function<void(std::string)> f);
// std::string overload with StringRef callback (for custom_api_device.h with zero-allocation callback)
// Existing std::string overload (for custom_api_device.h - heap allocation)
void subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
std::function<void(StringRef)> f);
std::function<void(std::string)> f);
void get_home_assistant_state(std::string entity_id, optional<std::string> attribute,
std::function<void(StringRef)> f);
// Legacy std::string overload (for custom_api_device.h - converts StringRef to std::string for callback)
void subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
std::function<void(const std::string &)> f);
void get_home_assistant_state(std::string entity_id, optional<std::string> attribute,
std::function<void(const std::string &)> f);
std::function<void(std::string)> f);
const std::vector<HomeAssistantStateSubscription> &get_state_subs() const;
#endif
@@ -243,13 +232,10 @@ class APIServer : public Component,
#endif // USE_API_NOISE
#ifdef USE_API_HOMEASSISTANT_STATES
// Helper methods to reduce code duplication
void add_state_subscription_(const char *entity_id, const char *attribute, std::function<void(StringRef)> f,
void add_state_subscription_(const char *entity_id, const char *attribute, std::function<void(std::string)> f,
bool once);
void add_state_subscription_(std::string entity_id, optional<std::string> attribute, std::function<void(StringRef)> f,
bool once);
// Legacy helper: wraps std::string callback and delegates to StringRef version
void add_state_subscription_(std::string entity_id, optional<std::string> attribute,
std::function<void(const std::string &)> f, bool once);
std::function<void(std::string)> f, bool once);
#endif // USE_API_HOMEASSISTANT_STATES
// Pointers and pointer-like types first (4 bytes each)
std::unique_ptr<socket::Socket> socket_ = nullptr;
@@ -266,9 +252,6 @@ class APIServer : public Component,
// Vectors and strings (12 bytes each on 32-bit)
std::vector<std::unique_ptr<APIConnection>> clients_;
#ifdef USE_API_PASSWORD
std::string password_;
#endif
std::vector<uint8_t> shared_write_buffer_; // Shared proto write buffer for all connections
#ifdef USE_API_HOMEASSISTANT_STATES
std::vector<HomeAssistantStateSubscription> state_subs_;

View File

@@ -16,7 +16,7 @@ with warnings.catch_warnings():
import contextlib
from esphome.const import CONF_KEY, CONF_PASSWORD, CONF_PORT, __version__
from esphome.const import CONF_KEY, CONF_PORT, __version__
from esphome.core import CORE
from . import CONF_ENCRYPTION
@@ -35,7 +35,6 @@ async def async_run_logs(config: dict[str, Any], addresses: list[str]) -> None:
conf = config["api"]
name = config["esphome"]["name"]
port: int = int(conf[CONF_PORT])
password: str = conf[CONF_PASSWORD]
noise_psk: str | None = None
if (encryption := conf.get(CONF_ENCRYPTION)) and (key := encryption.get(CONF_KEY)):
noise_psk = key
@@ -50,7 +49,7 @@ async def async_run_logs(config: dict[str, Any], addresses: list[str]) -> None:
cli = APIClient(
addresses[0], # Primary address for compatibility
port,
password,
"", # Password auth removed in 2026.1.0
client_info=f"ESPHome Logs {__version__}",
noise_psk=noise_psk,
addresses=addresses, # Pass all addresses for automatic retry

View File

@@ -122,36 +122,21 @@ class CustomAPIDevice {
* subscribe_homeassistant_state(&CustomNativeAPI::on_state_changed, "climate.kitchen", "current_temperature");
* }
*
* void on_state_changed(StringRef state) {
* // State of climate.kitchen current_temperature is `state`
* // Use state.c_str() for C string, state.str() for std::string
* void on_state_changed(std::string state) {
* // State of sensor.weather_forecast is `state`
* }
* ```
*
* @tparam T The class type creating the service, automatically deduced from the function pointer.
* @param callback The member function to call when the entity state changes (zero-allocation).
* @param callback The member function to call when the entity state changes.
* @param entity_id The entity_id to track.
* @param attribute The entity state attribute to track.
*/
template<typename T>
void subscribe_homeassistant_state(void (T::*callback)(StringRef), const std::string &entity_id,
const std::string &attribute = "") {
auto f = std::bind(callback, (T *) this, std::placeholders::_1);
global_api_server->subscribe_home_assistant_state(entity_id, optional<std::string>(attribute), std::move(f));
}
/** Subscribe to the state (or attribute state) of an entity from Home Assistant (legacy std::string version).
*
* @deprecated Use the StringRef overload for zero-allocation callbacks. Will be removed in 2027.1.0.
*/
template<typename T>
ESPDEPRECATED("Use void callback(StringRef) instead. Will be removed in 2027.1.0.", "2026.1.0")
void subscribe_homeassistant_state(void (T::*callback)(std::string), const std::string &entity_id,
const std::string &attribute = "") {
auto f = std::bind(callback, (T *) this, std::placeholders::_1);
// Explicit type to disambiguate overload resolution
global_api_server->subscribe_home_assistant_state(entity_id, optional<std::string>(attribute),
std::function<void(const std::string &)>(f));
global_api_server->subscribe_home_assistant_state(entity_id, optional<std::string>(attribute), f);
}
/** Subscribe to the state (or attribute state) of an entity from Home Assistant.
@@ -163,45 +148,23 @@ class CustomAPIDevice {
* subscribe_homeassistant_state(&CustomNativeAPI::on_state_changed, "sensor.weather_forecast");
* }
*
* void on_state_changed(const std::string &entity_id, StringRef state) {
* void on_state_changed(std::string entity_id, std::string state) {
* // State of `entity_id` is `state`
* }
* ```
*
* @tparam T The class type creating the service, automatically deduced from the function pointer.
* @param callback The member function to call when the entity state changes (zero-allocation for state).
* @param callback The member function to call when the entity state changes.
* @param entity_id The entity_id to track.
* @param attribute The entity state attribute to track.
*/
template<typename T>
void subscribe_homeassistant_state(void (T::*callback)(const std::string &, StringRef), const std::string &entity_id,
const std::string &attribute = "") {
auto f = std::bind(callback, (T *) this, entity_id, std::placeholders::_1);
global_api_server->subscribe_home_assistant_state(entity_id, optional<std::string>(attribute), std::move(f));
}
/** Subscribe to the state (or attribute state) of an entity from Home Assistant (legacy std::string version).
*
* @deprecated Use the StringRef overload for zero-allocation callbacks. Will be removed in 2027.1.0.
*/
template<typename T>
ESPDEPRECATED("Use void callback(const std::string &, StringRef) instead. Will be removed in 2027.1.0.", "2026.1.0")
void subscribe_homeassistant_state(void (T::*callback)(std::string, std::string), const std::string &entity_id,
const std::string &attribute = "") {
auto f = std::bind(callback, (T *) this, entity_id, std::placeholders::_1);
// Explicit type to disambiguate overload resolution
global_api_server->subscribe_home_assistant_state(entity_id, optional<std::string>(attribute),
std::function<void(const std::string &)>(f));
global_api_server->subscribe_home_assistant_state(entity_id, optional<std::string>(attribute), f);
}
#else
template<typename T>
void subscribe_homeassistant_state(void (T::*callback)(StringRef), const std::string &entity_id,
const std::string &attribute = "") {
static_assert(sizeof(T) == 0,
"subscribe_homeassistant_state() requires 'homeassistant_states: true' in the 'api:' section "
"of your YAML configuration");
}
template<typename T>
void subscribe_homeassistant_state(void (T::*callback)(std::string), const std::string &entity_id,
const std::string &attribute = "") {
@@ -210,14 +173,6 @@ class CustomAPIDevice {
"of your YAML configuration");
}
template<typename T>
void subscribe_homeassistant_state(void (T::*callback)(const std::string &, StringRef), const std::string &entity_id,
const std::string &attribute = "") {
static_assert(sizeof(T) == 0,
"subscribe_homeassistant_state() requires 'homeassistant_states: true' in the 'api:' section "
"of your YAML configuration");
}
template<typename T>
void subscribe_homeassistant_state(void (T::*callback)(std::string, std::string), const std::string &entity_id,
const std::string &attribute = "") {

View File

@@ -833,9 +833,6 @@ class ProtoService {
virtual bool is_authenticated() = 0;
virtual bool is_connection_setup() = 0;
virtual void on_fatal_error() = 0;
#ifdef USE_API_PASSWORD
virtual void on_unauthenticated_access() = 0;
#endif
virtual void on_no_setup_connection() = 0;
/**
* Create a buffer with a reserved size.
@@ -873,20 +870,7 @@ class ProtoService {
return true;
}
inline bool check_authenticated_() {
#ifdef USE_API_PASSWORD
if (!this->check_connection_setup_()) {
return false;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return false;
}
return true;
#else
return this->check_connection_setup_();
#endif
}
inline bool check_authenticated_() { return this->check_connection_setup_(); }
};
} // namespace esphome::api

View File

@@ -255,7 +255,7 @@ template<typename... Ts> class APIRespondAction : public Action<Ts...> {
bool return_response = std::get<1>(args);
if (!return_response) {
// Client doesn't want response data, just send success/error
this->parent_->send_action_response(call_id, success, error_message);
this->parent_->send_action_response(call_id, success, StringRef(error_message));
return;
}
}
@@ -265,12 +265,12 @@ template<typename... Ts> class APIRespondAction : public Action<Ts...> {
json::JsonBuilder builder;
this->json_builder_(x..., builder.root());
std::string json_str = builder.serialize();
this->parent_->send_action_response(call_id, success, error_message,
this->parent_->send_action_response(call_id, success, StringRef(error_message),
reinterpret_cast<const uint8_t *>(json_str.data()), json_str.size());
return;
}
#endif
this->parent_->send_action_response(call_id, success, error_message);
this->parent_->send_action_response(call_id, success, StringRef(error_message));
}
protected:

View File

@@ -305,12 +305,14 @@ bool AS3935Component::calibrate_oscillator() {
}
void AS3935Component::tune_antenna() {
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);
ESP_LOGI(TAG, "Internal Capacitor is set to: %d", tune_val);
ESP_LOGI(TAG, "Displaying oscillator on INT pin. Measure its frequency - multiply value by Division Ratio");
ESP_LOGI(TAG,
"Starting antenna tuning\n"
"Division Ratio is set to: %d\n"
"Internal Capacitor is set to: %d\n"
"Displaying oscillator on INT pin. Measure its frequency - multiply value by Division Ratio",
div_ratio, tune_val);
this->display_oscillator(true, ANTFREQ);
}

View File

@@ -227,15 +227,14 @@ void ATM90E32Component::log_calibration_status_() {
}
if (offset_mismatch) {
ESP_LOGW(TAG, "[CALIBRATION][%s] ", cs);
ESP_LOGW(TAG,
"[CALIBRATION][%s] ===================== Offset mismatch: using flash values =====================", cs);
ESP_LOGW(TAG, "[CALIBRATION][%s] ------------------------------------------------------------------------------",
cs);
ESP_LOGW(TAG, "[CALIBRATION][%s] | Phase | offset_voltage | offset_current |", cs);
ESP_LOGW(TAG, "[CALIBRATION][%s] | | config | flash | config | flash |", cs);
ESP_LOGW(TAG, "[CALIBRATION][%s] ------------------------------------------------------------------------------",
cs);
"[CALIBRATION][%s] \n"
"[CALIBRATION][%s] ===================== Offset mismatch: using flash values =====================\n"
"[CALIBRATION][%s] ------------------------------------------------------------------------------\n"
"[CALIBRATION][%s] | Phase | offset_voltage | offset_current |\n"
"[CALIBRATION][%s] | | config | flash | config | flash |\n"
"[CALIBRATION][%s] ------------------------------------------------------------------------------",
cs, cs, cs, cs, cs, cs);
for (uint8_t phase = 0; phase < 3; ++phase) {
ESP_LOGW(TAG, "[CALIBRATION][%s] | %c | %6d | %6d | %6d | %6d |", cs, 'A' + phase,
this->config_offset_phase_[phase].voltage_offset_, this->offset_phase_[phase].voltage_offset_,
@@ -245,15 +244,14 @@ void ATM90E32Component::log_calibration_status_() {
"[CALIBRATION][%s] ===============================================================================", cs);
}
if (power_mismatch) {
ESP_LOGW(TAG, "[CALIBRATION][%s] ", cs);
ESP_LOGW(TAG,
"[CALIBRATION][%s] ================= Power offset mismatch: using flash values =================", cs);
ESP_LOGW(TAG, "[CALIBRATION][%s] ------------------------------------------------------------------------------",
cs);
ESP_LOGW(TAG, "[CALIBRATION][%s] | Phase | offset_active_power|offset_reactive_power|", cs);
ESP_LOGW(TAG, "[CALIBRATION][%s] | | config | flash | config | flash |", cs);
ESP_LOGW(TAG, "[CALIBRATION][%s] ------------------------------------------------------------------------------",
cs);
"[CALIBRATION][%s] \n"
"[CALIBRATION][%s] ================= Power offset mismatch: using flash values =================\n"
"[CALIBRATION][%s] ------------------------------------------------------------------------------\n"
"[CALIBRATION][%s] | Phase | offset_active_power|offset_reactive_power|\n"
"[CALIBRATION][%s] | | config | flash | config | flash |\n"
"[CALIBRATION][%s] ------------------------------------------------------------------------------",
cs, cs, cs, cs, cs, cs);
for (uint8_t phase = 0; phase < 3; ++phase) {
ESP_LOGW(TAG, "[CALIBRATION][%s] | %c | %6d | %6d | %6d | %6d |", cs, 'A' + phase,
this->config_power_offset_phase_[phase].active_power_offset,
@@ -265,15 +263,14 @@ void ATM90E32Component::log_calibration_status_() {
"[CALIBRATION][%s] ===============================================================================", cs);
}
if (gain_mismatch) {
ESP_LOGW(TAG, "[CALIBRATION][%s] ", cs);
ESP_LOGW(TAG,
"[CALIBRATION][%s] ====================== Gain mismatch: using flash values =====================", cs);
ESP_LOGW(TAG, "[CALIBRATION][%s] ------------------------------------------------------------------------------",
cs);
ESP_LOGW(TAG, "[CALIBRATION][%s] | Phase | voltage_gain | current_gain |", cs);
ESP_LOGW(TAG, "[CALIBRATION][%s] | | config | flash | config | flash |", cs);
ESP_LOGW(TAG, "[CALIBRATION][%s] ------------------------------------------------------------------------------",
cs);
"[CALIBRATION][%s] \n"
"[CALIBRATION][%s] ====================== Gain mismatch: using flash values =====================\n"
"[CALIBRATION][%s] ------------------------------------------------------------------------------\n"
"[CALIBRATION][%s] | Phase | voltage_gain | current_gain |\n"
"[CALIBRATION][%s] | | config | flash | config | flash |\n"
"[CALIBRATION][%s] ------------------------------------------------------------------------------",
cs, cs, cs, cs, cs, cs);
for (uint8_t phase = 0; phase < 3; ++phase) {
ESP_LOGW(TAG, "[CALIBRATION][%s] | %c | %6u | %6u | %6u | %6u |", cs, 'A' + phase,
this->config_gain_phase_[phase].voltage_gain, this->gain_phase_[phase].voltage_gain,
@@ -286,11 +283,13 @@ void ATM90E32Component::log_calibration_status_() {
ESP_LOGI(TAG, "[CALIBRATION][%s] Power & Voltage/Current offset calibration is disabled. Using config file values.",
cs);
} else if (this->restored_offset_calibration_ && !offset_mismatch) {
ESP_LOGI(TAG, "[CALIBRATION][%s] ", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] ============== Restored offset calibration from memory ==============", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] --------------------------------------------------------------", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] | Phase | offset_voltage | offset_current |", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] --------------------------------------------------------------", cs);
ESP_LOGI(TAG,
"[CALIBRATION][%s]\n"
"[CALIBRATION][%s] ============== Restored offset calibration from memory ==============\n"
"[CALIBRATION][%s] --------------------------------------------------------------\n"
"[CALIBRATION][%s] | Phase | offset_voltage | offset_current |\n"
"[CALIBRATION][%s] --------------------------------------------------------------",
cs, cs, cs, cs, cs);
for (uint8_t phase = 0; phase < 3; phase++) {
ESP_LOGI(TAG, "[CALIBRATION][%s] | %c | %6d | %6d |", cs, 'A' + phase,
this->offset_phase_[phase].voltage_offset_, this->offset_phase_[phase].current_offset_);
@@ -299,11 +298,13 @@ void ATM90E32Component::log_calibration_status_() {
}
if (this->restored_power_offset_calibration_ && !power_mismatch) {
ESP_LOGI(TAG, "[CALIBRATION][%s] ", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] ============ Restored power offset calibration from memory ============", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] ---------------------------------------------------------------------", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] | Phase | offset_active_power | offset_reactive_power |", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] ---------------------------------------------------------------------", cs);
ESP_LOGI(TAG,
"[CALIBRATION][%s]\n"
"[CALIBRATION][%s] ============ Restored power offset calibration from memory ============\n"
"[CALIBRATION][%s] ---------------------------------------------------------------------\n"
"[CALIBRATION][%s] | Phase | offset_active_power | offset_reactive_power |\n"
"[CALIBRATION][%s] ---------------------------------------------------------------------",
cs, cs, cs, cs, cs);
for (uint8_t phase = 0; phase < 3; phase++) {
ESP_LOGI(TAG, "[CALIBRATION][%s] | %c | %6d | %6d |", cs, 'A' + phase,
this->power_offset_phase_[phase].active_power_offset,
@@ -314,17 +315,21 @@ void ATM90E32Component::log_calibration_status_() {
if (!this->enable_gain_calibration_) {
ESP_LOGI(TAG, "[CALIBRATION][%s] Gain calibration is disabled. Using config file values.", cs);
} else if (this->restored_gain_calibration_ && !gain_mismatch) {
ESP_LOGI(TAG, "[CALIBRATION][%s] ", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] ============ Restoring saved gain calibrations to registers ============", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] ---------------------------------------------------------------------", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] | Phase | voltage_gain | current_gain |", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] ---------------------------------------------------------------------", cs);
ESP_LOGI(TAG,
"[CALIBRATION][%s]\n"
"[CALIBRATION][%s] ============ Restoring saved gain calibrations to registers ============\n"
"[CALIBRATION][%s] ---------------------------------------------------------------------\n"
"[CALIBRATION][%s] | Phase | voltage_gain | current_gain |\n"
"[CALIBRATION][%s] ---------------------------------------------------------------------",
cs, cs, cs, cs, cs);
for (uint8_t phase = 0; phase < 3; phase++) {
ESP_LOGI(TAG, "[CALIBRATION][%s] | %c | %6u | %6u |", cs, 'A' + phase,
this->gain_phase_[phase].voltage_gain, this->gain_phase_[phase].current_gain);
}
ESP_LOGI(TAG, "[CALIBRATION][%s] =====================================================================\\n", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] Gain calibration loaded and verified successfully.\n", cs);
ESP_LOGI(TAG,
"[CALIBRATION][%s] =====================================================================\n"
"[CALIBRATION][%s] Gain calibration loaded and verified successfully.\n",
cs, cs);
}
this->calibration_message_printed_ = true;
}
@@ -580,14 +585,14 @@ void ATM90E32Component::run_gain_calibrations() {
float ref_currents[3] = {this->get_reference_current(0), this->get_reference_current(1),
this->get_reference_current(2)};
ESP_LOGI(TAG, "[CALIBRATION][%s] ", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] ========================= Gain Calibration =========================", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] ---------------------------------------------------------------------", cs);
ESP_LOGI(
TAG,
"[CALIBRATION][%s] | Phase | V_meas (V) | I_meas (A) | V_ref | I_ref | V_gain (old→new) | I_gain (old→new) |",
cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] ---------------------------------------------------------------------", cs);
"[CALIBRATION][%s]\n"
"[CALIBRATION][%s] ========================= Gain Calibration =========================\n"
"[CALIBRATION][%s] ---------------------------------------------------------------------\n"
"[CALIBRATION][%s] | Phase | V_meas (V) | I_meas (A) | V_ref | I_ref | V_gain (old→new) | I_gain (old→new) |\n"
"[CALIBRATION][%s] ---------------------------------------------------------------------",
cs, cs, cs, cs, cs);
for (uint8_t phase = 0; phase < 3; phase++) {
float measured_voltage = this->get_phase_voltage_avg_(phase);
@@ -718,11 +723,13 @@ void ATM90E32Component::run_offset_calibrations() {
return;
}
ESP_LOGI(TAG, "[CALIBRATION][%s] ", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] ======================== Offset Calibration ========================", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] ------------------------------------------------------------------", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] | Phase | offset_voltage | offset_current |", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] ------------------------------------------------------------------", cs);
ESP_LOGI(TAG,
"[CALIBRATION][%s]\n"
"[CALIBRATION][%s] ======================== Offset Calibration ========================\n"
"[CALIBRATION][%s] ------------------------------------------------------------------\n"
"[CALIBRATION][%s] | Phase | offset_voltage | offset_current |\n"
"[CALIBRATION][%s] ------------------------------------------------------------------",
cs, cs, cs, cs, cs);
for (uint8_t phase = 0; phase < 3; phase++) {
int16_t voltage_offset = calibrate_offset(phase, true);
@@ -749,11 +756,13 @@ void ATM90E32Component::run_power_offset_calibrations() {
return;
}
ESP_LOGI(TAG, "[CALIBRATION][%s] ", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] ===================== Power Offset Calibration =====================", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] ---------------------------------------------------------------------", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] | Phase | offset_active_power | offset_reactive_power |", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] ---------------------------------------------------------------------", cs);
ESP_LOGI(TAG,
"[CALIBRATION][%s]\n"
"[CALIBRATION][%s] ===================== Power Offset Calibration =====================\n"
"[CALIBRATION][%s] ---------------------------------------------------------------------\n"
"[CALIBRATION][%s] | Phase | offset_active_power | offset_reactive_power |\n"
"[CALIBRATION][%s] ---------------------------------------------------------------------",
cs, cs, cs, cs, cs);
for (uint8_t phase = 0; phase < 3; ++phase) {
int16_t active_offset = calibrate_power_offset(phase, false);
@@ -953,10 +962,12 @@ void ATM90E32Component::restore_power_offset_calibrations_() {
void ATM90E32Component::clear_gain_calibrations() {
const char *cs = this->cs_summary_.c_str();
if (!this->using_saved_calibrations_) {
ESP_LOGI(TAG, "[CALIBRATION][%s] No stored gain calibrations to clear. Current values:", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] ----------------------------------------------------------", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] | Phase | voltage_gain | current_gain |", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] ----------------------------------------------------------", cs);
ESP_LOGI(TAG,
"[CALIBRATION][%s] No stored gain calibrations to clear. Current values:\n"
"[CALIBRATION][%s] ----------------------------------------------------------\n"
"[CALIBRATION][%s] | Phase | voltage_gain | current_gain |\n"
"[CALIBRATION][%s] ----------------------------------------------------------",
cs, cs, cs, cs);
for (int phase = 0; phase < 3; phase++) {
ESP_LOGI(TAG, "[CALIBRATION][%s] | %c | %6u | %6u |", cs, 'A' + phase,
this->gain_phase_[phase].voltage_gain, this->gain_phase_[phase].current_gain);
@@ -965,10 +976,12 @@ void ATM90E32Component::clear_gain_calibrations() {
return;
}
ESP_LOGI(TAG, "[CALIBRATION][%s] Clearing stored gain calibrations and restoring config-defined values", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] ----------------------------------------------------------", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] | Phase | voltage_gain | current_gain |", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] ----------------------------------------------------------", cs);
ESP_LOGI(TAG,
"[CALIBRATION][%s] Clearing stored gain calibrations and restoring config-defined values\n"
"[CALIBRATION][%s] ----------------------------------------------------------\n"
"[CALIBRATION][%s] | Phase | voltage_gain | current_gain |\n"
"[CALIBRATION][%s] ----------------------------------------------------------",
cs, cs, cs, cs);
for (int phase = 0; phase < 3; phase++) {
uint16_t voltage_gain = this->phase_[phase].voltage_gain_;
@@ -1002,10 +1015,12 @@ void ATM90E32Component::clear_gain_calibrations() {
void ATM90E32Component::clear_offset_calibrations() {
const char *cs = this->cs_summary_.c_str();
if (!this->restored_offset_calibration_) {
ESP_LOGI(TAG, "[CALIBRATION][%s] No stored offset calibrations to clear. Current values:", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] --------------------------------------------------------------", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] | Phase | offset_voltage | offset_current |", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] --------------------------------------------------------------", cs);
ESP_LOGI(TAG,
"[CALIBRATION][%s] No stored offset calibrations to clear. Current values:\n"
"[CALIBRATION][%s] --------------------------------------------------------------\n"
"[CALIBRATION][%s] | Phase | offset_voltage | offset_current |\n"
"[CALIBRATION][%s] --------------------------------------------------------------",
cs, cs, cs, cs);
for (uint8_t phase = 0; phase < 3; phase++) {
ESP_LOGI(TAG, "[CALIBRATION][%s] | %c | %6d | %6d |", cs, 'A' + phase,
this->offset_phase_[phase].voltage_offset_, this->offset_phase_[phase].current_offset_);
@@ -1014,10 +1029,12 @@ void ATM90E32Component::clear_offset_calibrations() {
return;
}
ESP_LOGI(TAG, "[CALIBRATION][%s] Clearing stored offset calibrations and restoring config-defined values", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] --------------------------------------------------------------", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] | Phase | offset_voltage | offset_current |", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] --------------------------------------------------------------", cs);
ESP_LOGI(TAG,
"[CALIBRATION][%s] Clearing stored offset calibrations and restoring config-defined values\n"
"[CALIBRATION][%s] --------------------------------------------------------------\n"
"[CALIBRATION][%s] | Phase | offset_voltage | offset_current |\n"
"[CALIBRATION][%s] --------------------------------------------------------------",
cs, cs, cs, cs);
for (uint8_t phase = 0; phase < 3; phase++) {
int16_t voltage_offset =
@@ -1044,10 +1061,12 @@ void ATM90E32Component::clear_offset_calibrations() {
void ATM90E32Component::clear_power_offset_calibrations() {
const char *cs = this->cs_summary_.c_str();
if (!this->restored_power_offset_calibration_) {
ESP_LOGI(TAG, "[CALIBRATION][%s] No stored power offsets to clear. Current values:", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] ---------------------------------------------------------------------", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] | Phase | offset_active_power | offset_reactive_power |", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] ---------------------------------------------------------------------", cs);
ESP_LOGI(TAG,
"[CALIBRATION][%s] No stored power offsets to clear. Current values:\n"
"[CALIBRATION][%s] ---------------------------------------------------------------------\n"
"[CALIBRATION][%s] | Phase | offset_active_power | offset_reactive_power |\n"
"[CALIBRATION][%s] ---------------------------------------------------------------------",
cs, cs, cs, cs);
for (uint8_t phase = 0; phase < 3; phase++) {
ESP_LOGI(TAG, "[CALIBRATION][%s] | %c | %6d | %6d |", cs, 'A' + phase,
this->power_offset_phase_[phase].active_power_offset,
@@ -1057,10 +1076,12 @@ void ATM90E32Component::clear_power_offset_calibrations() {
return;
}
ESP_LOGI(TAG, "[CALIBRATION][%s] Clearing stored power offsets and restoring config-defined values", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] ---------------------------------------------------------------------", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] | Phase | offset_active_power | offset_reactive_power |", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] ---------------------------------------------------------------------", cs);
ESP_LOGI(TAG,
"[CALIBRATION][%s] Clearing stored power offsets and restoring config-defined values\n"
"[CALIBRATION][%s] ---------------------------------------------------------------------\n"
"[CALIBRATION][%s] | Phase | offset_active_power | offset_reactive_power |\n"
"[CALIBRATION][%s] ---------------------------------------------------------------------",
cs, cs, cs, cs);
for (uint8_t phase = 0; phase < 3; phase++) {
int16_t active_offset =

View File

@@ -13,9 +13,10 @@ void ATM90E32GainCalibrationButton::press_action() {
return;
}
ESP_LOGI(TAG, "%s", this->get_name().c_str());
ESP_LOGI(TAG,
"[CALIBRATION] Use gain_ct: & gain_voltage: under each phase_x: in your config file to save these values");
"%s\n"
"[CALIBRATION] Use gain_ct: & gain_voltage: under each phase_x: in your config file to save these values",
this->get_name().c_str());
this->parent_->run_gain_calibrations();
}
@@ -35,10 +36,12 @@ void ATM90E32OffsetCalibrationButton::press_action() {
return;
}
ESP_LOGI(TAG, "%s", this->get_name().c_str());
ESP_LOGI(TAG, "[CALIBRATION] **NOTE: CTs and ACVs must be 0 during this process. USB power only**");
ESP_LOGI(TAG, "[CALIBRATION] Use offset_voltage: & offset_current: under each phase_x: in your config file to save "
"these values");
ESP_LOGI(TAG,
"%s\n"
"[CALIBRATION] **NOTE: CTs and ACVs must be 0 during this process. USB power only**\n"
"[CALIBRATION] Use offset_voltage: & offset_current: under each phase_x: in your config file to save "
"these values",
this->get_name().c_str());
this->parent_->run_offset_calibrations();
}
@@ -58,10 +61,12 @@ void ATM90E32PowerOffsetCalibrationButton::press_action() {
return;
}
ESP_LOGI(TAG, "%s", this->get_name().c_str());
ESP_LOGI(TAG, "[CALIBRATION] **NOTE: CTs must be 0 during this process. Voltage reference should be present**");
ESP_LOGI(TAG, "[CALIBRATION] Use offset_active_power: & offset_reactive_power: under each phase_x: in your config "
"file to save these values");
ESP_LOGI(TAG,
"%s\n"
"[CALIBRATION] **NOTE: CTs must be 0 during this process. Voltage reference should be present**\n"
"[CALIBRATION] Use offset_active_power: & offset_reactive_power: under each phase_x: in your config "
"file to save these values",
this->get_name().c_str());
this->parent_->run_power_offset_calibrations();
}

View File

@@ -216,11 +216,14 @@ bool BedJetHub::discover_characteristics_() {
}
}
ESP_LOGI(TAG, "[%s] Discovered service characteristics: ", this->get_name().c_str());
ESP_LOGI(TAG, " - Command char: 0x%x", this->char_handle_cmd_);
ESP_LOGI(TAG, " - Status char: 0x%x", this->char_handle_status_);
ESP_LOGI(TAG, " - config descriptor: 0x%x", this->config_descr_status_);
ESP_LOGI(TAG, " - Name char: 0x%x", this->char_handle_name_);
ESP_LOGI(TAG,
"[%s] Discovered service characteristics:\n"
" - Command char: 0x%x\n"
" - Status char: 0x%x\n"
" - config descriptor: 0x%x\n"
" - Name char: 0x%x",
this->get_name().c_str(), this->char_handle_cmd_, this->char_handle_status_, this->config_descr_status_,
this->char_handle_name_);
return result;
}

View File

@@ -1,8 +1,8 @@
#include "bh1750.h"
#include "esphome/core/log.h"
#include "esphome/core/application.h"
namespace esphome::bh1750 {
namespace esphome {
namespace bh1750 {
static const char *const TAG = "bh1750.sensor";
@@ -13,31 +13,6 @@ static const uint8_t BH1750_COMMAND_ONE_TIME_L = 0b00100011;
static const uint8_t BH1750_COMMAND_ONE_TIME_H = 0b00100000;
static const uint8_t BH1750_COMMAND_ONE_TIME_H2 = 0b00100001;
static constexpr uint32_t MEASUREMENT_TIMEOUT_MS = 2000;
static constexpr float HIGH_LIGHT_THRESHOLD_LX = 7000.0f;
// Measurement time constants (datasheet values)
static constexpr uint16_t MTREG_DEFAULT = 69;
static constexpr uint16_t MTREG_MIN = 31;
static constexpr uint16_t MTREG_MAX = 254;
static constexpr uint16_t MEAS_TIME_L_MS = 24; // L-resolution max measurement time @ mtreg=69
static constexpr uint16_t MEAS_TIME_H_MS = 180; // H/H2-resolution max measurement time @ mtreg=69
// Conversion constants (datasheet formulas)
static constexpr float RESOLUTION_DIVISOR = 1.2f; // counts to lux conversion divisor
static constexpr float MODE_H2_DIVISOR = 2.0f; // H2 mode has 2x higher resolution
// MTreg calculation constants
static constexpr int COUNTS_TARGET = 50000; // Target counts for optimal range (avoid saturation)
static constexpr int COUNTS_NUMERATOR = 10;
static constexpr int COUNTS_DENOMINATOR = 12;
// MTreg register bit manipulation constants
static constexpr uint8_t MTREG_HI_SHIFT = 5; // High 3 bits start at bit 5
static constexpr uint8_t MTREG_HI_MASK = 0b111; // 3-bit mask for high bits
static constexpr uint8_t MTREG_LO_SHIFT = 0; // Low 5 bits start at bit 0
static constexpr uint8_t MTREG_LO_MASK = 0b11111; // 5-bit mask for low bits
/*
bh1750 properties:
@@ -68,7 +43,74 @@ void BH1750Sensor::setup() {
this->mark_failed();
return;
}
this->state_ = IDLE;
}
void BH1750Sensor::read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function<void(float)> &f) {
// turn on (after one-shot sensor automatically powers down)
uint8_t turn_on = BH1750_COMMAND_POWER_ON;
if (this->write(&turn_on, 1) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Power on failed");
f(NAN);
return;
}
if (active_mtreg_ != mtreg) {
// set mtreg
uint8_t mtreg_hi = BH1750_COMMAND_MT_REG_HI | ((mtreg >> 5) & 0b111);
uint8_t mtreg_lo = BH1750_COMMAND_MT_REG_LO | ((mtreg >> 0) & 0b11111);
if (this->write(&mtreg_hi, 1) != i2c::ERROR_OK || this->write(&mtreg_lo, 1) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Set measurement time failed");
active_mtreg_ = 0;
f(NAN);
return;
}
active_mtreg_ = mtreg;
}
uint8_t cmd;
uint16_t meas_time;
switch (mode) {
case BH1750_MODE_L:
cmd = BH1750_COMMAND_ONE_TIME_L;
meas_time = 24 * mtreg / 69;
break;
case BH1750_MODE_H:
cmd = BH1750_COMMAND_ONE_TIME_H;
meas_time = 180 * mtreg / 69;
break;
case BH1750_MODE_H2:
cmd = BH1750_COMMAND_ONE_TIME_H2;
meas_time = 180 * mtreg / 69;
break;
default:
f(NAN);
return;
}
if (this->write(&cmd, 1) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Start measurement failed");
f(NAN);
return;
}
// probably not needed, but adjust for rounding
meas_time++;
this->set_timeout("read", meas_time, [this, mode, mtreg, f]() {
uint16_t raw_value;
if (this->read(reinterpret_cast<uint8_t *>(&raw_value), 2) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Read data failed");
f(NAN);
return;
}
raw_value = i2c::i2ctohs(raw_value);
float lx = float(raw_value) / 1.2f;
lx *= 69.0f / mtreg;
if (mode == BH1750_MODE_H2)
lx /= 2.0f;
f(lx);
});
}
void BH1750Sensor::dump_config() {
@@ -82,189 +124,45 @@ void BH1750Sensor::dump_config() {
}
void BH1750Sensor::update() {
const uint32_t now = millis();
// Start coarse measurement to determine optimal mode/mtreg
if (this->state_ != IDLE) {
// Safety timeout: reset if stuck
if (now - this->measurement_start_time_ > MEASUREMENT_TIMEOUT_MS) {
ESP_LOGW(TAG, "Measurement timeout, resetting state");
this->state_ = IDLE;
} else {
ESP_LOGW(TAG, "Previous measurement not complete, skipping update");
// first do a quick measurement in L-mode with full range
// to find right range
this->read_lx_(BH1750_MODE_L, 31, [this](float val) {
if (std::isnan(val)) {
this->status_set_warning();
this->publish_state(NAN);
return;
}
}
if (!this->start_measurement_(BH1750_MODE_L, MTREG_MIN, now)) {
this->status_set_warning();
this->publish_state(NAN);
return;
}
this->state_ = WAITING_COARSE_MEASUREMENT;
this->enable_loop(); // Enable loop while measurement in progress
}
void BH1750Sensor::loop() {
const uint32_t now = App.get_loop_component_start_time();
switch (this->state_) {
case IDLE:
// Disable loop when idle to save cycles
this->disable_loop();
break;
case WAITING_COARSE_MEASUREMENT:
if (now - this->measurement_start_time_ >= this->measurement_duration_) {
this->state_ = READING_COARSE_RESULT;
}
break;
case READING_COARSE_RESULT: {
float lx;
if (!this->read_measurement_(lx)) {
this->fail_and_reset_();
break;
}
this->process_coarse_result_(lx);
// Start fine measurement with optimal settings
// fetch millis() again since the read can take a bit
if (!this->start_measurement_(this->fine_mode_, this->fine_mtreg_, millis())) {
this->fail_and_reset_();
break;
}
this->state_ = WAITING_FINE_MEASUREMENT;
break;
BH1750Mode use_mode;
uint8_t use_mtreg;
if (val <= 7000) {
use_mode = BH1750_MODE_H2;
use_mtreg = 254;
} else {
use_mode = BH1750_MODE_H;
// lx = counts / 1.2 * (69 / mtreg)
// -> mtreg = counts / 1.2 * (69 / lx)
// calculate for counts=50000 (allow some range to not saturate, but maximize mtreg)
// -> mtreg = 50000*(10/12)*(69/lx)
int ideal_mtreg = 50000 * 10 * 69 / (12 * (int) val);
use_mtreg = std::min(254, std::max(31, ideal_mtreg));
}
ESP_LOGV(TAG, "L result: %f -> Calculated mode=%d, mtreg=%d", val, (int) use_mode, use_mtreg);
case WAITING_FINE_MEASUREMENT:
if (now - this->measurement_start_time_ >= this->measurement_duration_) {
this->state_ = READING_FINE_RESULT;
this->read_lx_(use_mode, use_mtreg, [this](float val) {
if (std::isnan(val)) {
this->status_set_warning();
this->publish_state(NAN);
return;
}
break;
case READING_FINE_RESULT: {
float lx;
if (!this->read_measurement_(lx)) {
this->fail_and_reset_();
break;
}
ESP_LOGD(TAG, "'%s': Illuminance=%.1flx", this->get_name().c_str(), lx);
ESP_LOGD(TAG, "'%s': Illuminance=%.1flx", this->get_name().c_str(), val);
this->status_clear_warning();
this->publish_state(lx);
this->state_ = IDLE;
break;
}
}
}
bool BH1750Sensor::start_measurement_(BH1750Mode mode, uint8_t mtreg, uint32_t now) {
// Power on
uint8_t turn_on = BH1750_COMMAND_POWER_ON;
if (this->write(&turn_on, 1) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Power on failed");
return false;
}
// Set MTreg if changed
if (this->active_mtreg_ != mtreg) {
uint8_t mtreg_hi = BH1750_COMMAND_MT_REG_HI | ((mtreg >> MTREG_HI_SHIFT) & MTREG_HI_MASK);
uint8_t mtreg_lo = BH1750_COMMAND_MT_REG_LO | ((mtreg >> MTREG_LO_SHIFT) & MTREG_LO_MASK);
if (this->write(&mtreg_hi, 1) != i2c::ERROR_OK || this->write(&mtreg_lo, 1) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Set measurement time failed");
this->active_mtreg_ = 0;
return false;
}
this->active_mtreg_ = mtreg;
}
// Start measurement
uint8_t cmd;
uint16_t meas_time;
switch (mode) {
case BH1750_MODE_L:
cmd = BH1750_COMMAND_ONE_TIME_L;
meas_time = MEAS_TIME_L_MS * mtreg / MTREG_DEFAULT;
break;
case BH1750_MODE_H:
cmd = BH1750_COMMAND_ONE_TIME_H;
meas_time = MEAS_TIME_H_MS * mtreg / MTREG_DEFAULT;
break;
case BH1750_MODE_H2:
cmd = BH1750_COMMAND_ONE_TIME_H2;
meas_time = MEAS_TIME_H_MS * mtreg / MTREG_DEFAULT;
break;
default:
return false;
}
if (this->write(&cmd, 1) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Start measurement failed");
return false;
}
// Store current measurement parameters
this->current_mode_ = mode;
this->current_mtreg_ = mtreg;
this->measurement_start_time_ = now;
this->measurement_duration_ = meas_time + 1; // Add 1ms for safety
return true;
}
bool BH1750Sensor::read_measurement_(float &lx_out) {
uint16_t raw_value;
if (this->read(reinterpret_cast<uint8_t *>(&raw_value), 2) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Read data failed");
return false;
}
raw_value = i2c::i2ctohs(raw_value);
float lx = float(raw_value) / RESOLUTION_DIVISOR;
lx *= float(MTREG_DEFAULT) / this->current_mtreg_;
if (this->current_mode_ == BH1750_MODE_H2) {
lx /= MODE_H2_DIVISOR;
}
lx_out = lx;
return true;
}
void BH1750Sensor::process_coarse_result_(float lx) {
if (std::isnan(lx)) {
// Use defaults if coarse measurement failed
this->fine_mode_ = BH1750_MODE_H2;
this->fine_mtreg_ = MTREG_MAX;
return;
}
if (lx <= HIGH_LIGHT_THRESHOLD_LX) {
this->fine_mode_ = BH1750_MODE_H2;
this->fine_mtreg_ = MTREG_MAX;
} else {
this->fine_mode_ = BH1750_MODE_H;
// lx = counts / 1.2 * (69 / mtreg)
// -> mtreg = counts / 1.2 * (69 / lx)
// calculate for counts=50000 (allow some range to not saturate, but maximize mtreg)
// -> mtreg = 50000*(10/12)*(69/lx)
int ideal_mtreg = COUNTS_TARGET * COUNTS_NUMERATOR * MTREG_DEFAULT / (COUNTS_DENOMINATOR * (int) lx);
this->fine_mtreg_ = std::min((int) MTREG_MAX, std::max((int) MTREG_MIN, ideal_mtreg));
}
ESP_LOGV(TAG, "L result: %.1f -> Calculated mode=%d, mtreg=%d", lx, (int) this->fine_mode_, this->fine_mtreg_);
}
void BH1750Sensor::fail_and_reset_() {
this->status_set_warning();
this->publish_state(NAN);
this->state_ = IDLE;
this->publish_state(val);
});
});
}
float BH1750Sensor::get_setup_priority() const { return setup_priority::DATA; }
} // namespace esphome::bh1750
} // namespace bh1750
} // namespace esphome

View File

@@ -4,9 +4,10 @@
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h"
namespace esphome::bh1750 {
namespace esphome {
namespace bh1750 {
enum BH1750Mode : uint8_t {
enum BH1750Mode {
BH1750_MODE_L,
BH1750_MODE_H,
BH1750_MODE_H2,
@@ -20,36 +21,13 @@ class BH1750Sensor : public sensor::Sensor, public PollingComponent, public i2c:
void setup() override;
void dump_config() override;
void update() override;
void loop() override;
float get_setup_priority() const override;
protected:
// State machine states
enum State : uint8_t {
IDLE,
WAITING_COARSE_MEASUREMENT,
READING_COARSE_RESULT,
WAITING_FINE_MEASUREMENT,
READING_FINE_RESULT,
};
void read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function<void(float)> &f);
// 4-byte aligned members
uint32_t measurement_start_time_{0};
uint32_t measurement_duration_{0};
// 1-byte members grouped together to minimize padding
State state_{IDLE};
BH1750Mode current_mode_{BH1750_MODE_L};
uint8_t current_mtreg_{31};
BH1750Mode fine_mode_{BH1750_MODE_H2};
uint8_t fine_mtreg_{254};
uint8_t active_mtreg_{0};
// Helper methods
bool start_measurement_(BH1750Mode mode, uint8_t mtreg, uint32_t now);
bool read_measurement_(float &lx_out);
void process_coarse_result_(float lx);
void fail_and_reset_();
};
} // namespace esphome::bh1750
} // namespace bh1750
} // namespace esphome

View File

@@ -3,7 +3,7 @@ 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 import mqtt, web_server, zigbee
from esphome.components.const import CONF_ON_STATE_CHANGE
import esphome.config_validation as cv
from esphome.const import (
@@ -439,6 +439,7 @@ def validate_publish_initial_state(value):
_BINARY_SENSOR_SCHEMA = (
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
.extend(cv.MQTT_COMPONENT_SCHEMA)
.extend(zigbee.BINARY_SENSOR_SCHEMA)
.extend(
{
cv.GenerateID(): cv.declare_id(BinarySensor),
@@ -520,6 +521,7 @@ _BINARY_SENSOR_SCHEMA = (
_BINARY_SENSOR_SCHEMA.add_extra(entity_duplicate_validator("binary_sensor"))
_BINARY_SENSOR_SCHEMA.add_extra(zigbee.validate_binary_sensor)
def binary_sensor_schema(
@@ -621,6 +623,8 @@ async def setup_binary_sensor_core_(var, config):
if web_server_config := config.get(CONF_WEB_SERVER):
await web_server.add_entity_config(var, web_server_config)
await zigbee.setup_binary_sensor(var, config)
async def register_binary_sensor(var, config):
if not CORE.has_id(config[CONF_ID]):

View File

@@ -21,8 +21,10 @@ void MultiClickTrigger::on_state_(bool state) {
// Start matching
MultiClickTriggerEvent evt = this->timing_[0];
if (evt.state == state) {
ESP_LOGV(TAG, "START min=%" PRIu32 " max=%" PRIu32, evt.min_length, evt.max_length);
ESP_LOGV(TAG, "Multi Click: Starting multi click action!");
ESP_LOGV(TAG,
"START min=%" PRIu32 " max=%" PRIu32 "\n"
"Multi Click: Starting multi click action!",
evt.min_length, evt.max_length);
this->at_index_ = 1;
if (this->timing_.size() == 1 && evt.max_length == 4294967294UL) {
this->set_timeout("trigger", evt.min_length, [this]() { this->trigger_(); });

View File

@@ -103,8 +103,10 @@ void BLENUS::on_log(uint8_t level, const char *tag, const char *message, size_t
#endif
void BLENUS::dump_config() {
ESP_LOGCONFIG(TAG, "ble nus:");
ESP_LOGCONFIG(TAG, " log: %s", YESNO(this->expose_log_));
ESP_LOGCONFIG(TAG,
"ble nus:\n"
" log: %s",
YESNO(this->expose_log_));
uint32_t mtu = 0;
bt_conn *conn = this->conn_.load();
if (conn) {

View File

@@ -22,13 +22,13 @@ void BP1658CJ::setup() {
this->pwm_amounts_.resize(5, 0);
}
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,
"BP1658CJ:\n"
" Color Channels Max Power: %u\n"
" White Channels Max Power: %u",
this->max_power_color_channels_, this->max_power_white_channels_);
LOG_PIN(" Data Pin: ", this->data_pin_);
LOG_PIN(" Clock Pin: ", this->clock_pin_);
}
void BP1658CJ::loop() {

View File

@@ -0,0 +1,36 @@
import esphome.codegen as cg
from esphome.components import esp32_ble_tracker
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_MAC_ADDRESS
CODEOWNERS = ["@nagyrobi"]
DEPENDENCIES = ["esp32_ble_tracker"]
BLE_DEVICE_SCHEMA = esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA
bthome_mithermometer_ns = cg.esphome_ns.namespace("bthome_mithermometer")
BTHomeMiThermometer = bthome_mithermometer_ns.class_(
"BTHomeMiThermometer", esp32_ble_tracker.ESPBTDeviceListener, cg.Component
)
def bthome_mithermometer_base_schema(extra_schema=None):
if extra_schema is None:
extra_schema = {}
return (
cv.Schema(
{
cv.GenerateID(CONF_ID): cv.declare_id(BTHomeMiThermometer),
cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
}
)
.extend(BLE_DEVICE_SCHEMA)
.extend(cv.COMPONENT_SCHEMA)
.extend(extra_schema)
)
async def setup_bthome_mithermometer(var, config):
await cg.register_component(var, config)
await esp32_ble_tracker.register_ble_device(var, config)
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))

View File

@@ -0,0 +1,298 @@
#include "bthome_ble.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include <array>
#ifdef USE_ESP32
namespace esphome {
namespace bthome_mithermometer {
static const char *const TAG = "bthome_mithermometer";
static std::string format_mac_address(uint64_t address) {
std::array<uint8_t, MAC_ADDRESS_SIZE> mac{};
for (size_t i = 0; i < MAC_ADDRESS_SIZE; i++) {
mac[i] = (address >> ((MAC_ADDRESS_SIZE - 1 - i) * 8)) & 0xFF;
}
char buffer[MAC_ADDRESS_SIZE * 3];
format_mac_addr_upper(mac.data(), buffer);
return buffer;
}
static bool get_bthome_value_length(uint8_t obj_type, size_t &value_length) {
switch (obj_type) {
case 0x00: // packet id
case 0x01: // battery
case 0x09: // count (uint8)
case 0x0F: // generic boolean
case 0x10: // power (bool)
case 0x11: // opening
case 0x15: // battery low
case 0x16: // battery charging
case 0x17: // carbon monoxide
case 0x18: // cold
case 0x19: // connectivity
case 0x1A: // door
case 0x1B: // garage door
case 0x1C: // gas
case 0x1D: // heat
case 0x1E: // light
case 0x1F: // lock
case 0x20: // moisture
case 0x21: // motion
case 0x22: // moving
case 0x23: // occupancy
case 0x24: // plug
case 0x25: // presence
case 0x26: // problem
case 0x27: // running
case 0x28: // safety
case 0x29: // smoke
case 0x2A: // sound
case 0x2B: // tamper
case 0x2C: // vibration
case 0x2D: // water leak
case 0x2E: // humidity (uint8)
case 0x2F: // moisture (uint8)
case 0x46: // UV index
case 0x57: // temperature (sint8)
case 0x58: // temperature (0.35C step)
case 0x59: // count (sint8)
case 0x60: // channel
value_length = 1;
return true;
case 0x02: // temperature (0.01C)
case 0x03: // humidity
case 0x06: // mass (kg)
case 0x07: // mass (lb)
case 0x08: // dewpoint
case 0x0C: // voltage (mV)
case 0x0D: // pm2.5
case 0x0E: // pm10
case 0x12: // CO2
case 0x13: // TVOC
case 0x14: // moisture
case 0x3D: // count (uint16)
case 0x3F: // rotation
case 0x40: // distance (mm)
case 0x41: // distance (m)
case 0x43: // current (A)
case 0x44: // speed
case 0x45: // temperature (0.1C)
case 0x47: // volume (L)
case 0x48: // volume (mL)
case 0x49: // volume flow rate
case 0x4A: // voltage (0.1V)
case 0x51: // acceleration
case 0x52: // gyroscope
case 0x56: // conductivity
case 0x5A: // count (sint16)
case 0x5D: // current (sint16)
case 0x5E: // direction
case 0x5F: // precipitation
case 0x61: // rotational speed
case 0xF0: // button event
value_length = 2;
return true;
case 0x04: // pressure
case 0x05: // illuminance
case 0x0A: // energy
case 0x0B: // power
case 0x42: // duration
case 0x4B: // gas (uint24)
case 0xF2: // firmware version (uint24)
value_length = 3;
return true;
case 0x3E: // count (uint32)
case 0x4C: // gas (uint32)
case 0x4D: // energy (uint32)
case 0x4E: // volume (uint32)
case 0x4F: // water (uint32)
case 0x50: // timestamp
case 0x55: // volume storage
case 0x5B: // count (sint32)
case 0x5C: // power (sint32)
case 0x62: // speed (sint32)
case 0x63: // acceleration (sint32)
case 0xF1: // firmware version (uint32)
value_length = 4;
return true;
default:
return false;
}
}
void BTHomeMiThermometer::dump_config() {
ESP_LOGCONFIG(TAG, "BTHome MiThermometer");
ESP_LOGCONFIG(TAG, " MAC Address: %s", format_mac_address(this->address_).c_str());
LOG_SENSOR(" ", "Temperature", this->temperature_);
LOG_SENSOR(" ", "Humidity", this->humidity_);
LOG_SENSOR(" ", "Battery Level", this->battery_level_);
LOG_SENSOR(" ", "Battery Voltage", this->battery_voltage_);
LOG_SENSOR(" ", "Signal Strength", this->signal_strength_);
}
bool BTHomeMiThermometer::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
bool matched = false;
for (auto &service_data : device.get_service_datas()) {
if (this->handle_service_data_(service_data, device)) {
matched = true;
}
}
if (matched && this->signal_strength_ != nullptr) {
this->signal_strength_->publish_state(device.get_rssi());
}
return matched;
}
bool BTHomeMiThermometer::handle_service_data_(const esp32_ble_tracker::ServiceData &service_data,
const esp32_ble_tracker::ESPBTDevice &device) {
if (!service_data.uuid.contains(0xD2, 0xFC)) {
return false;
}
const auto &data = service_data.data;
if (data.size() < 2) {
ESP_LOGVV(TAG, "BTHome data too short: %zu", data.size());
return false;
}
const uint8_t adv_info = data[0];
const bool is_encrypted = adv_info & 0x01;
const bool mac_included = adv_info & 0x02;
const bool is_trigger_based = adv_info & 0x04;
const uint8_t version = (adv_info >> 5) & 0x07;
if (version != 0x02) {
ESP_LOGVV(TAG, "Unsupported BTHome version %u", version);
return false;
}
if (is_encrypted) {
ESP_LOGV(TAG, "Ignoring encrypted BTHome frame from %s", device.address_str().c_str());
return false;
}
size_t payload_index = 1;
uint64_t source_address = device.address_uint64();
if (mac_included) {
if (data.size() < 7) {
ESP_LOGVV(TAG, "BTHome payload missing MAC address");
return false;
}
source_address = 0;
for (int i = 5; i >= 0; i--) {
source_address = (source_address << 8) | data[1 + i];
}
payload_index = 7;
}
if (source_address != this->address_) {
ESP_LOGVV(TAG, "BTHome frame from unexpected device %s", format_mac_address(source_address).c_str());
return false;
}
if (payload_index >= data.size()) {
ESP_LOGVV(TAG, "BTHome payload empty after header");
return false;
}
bool reported = false;
size_t offset = payload_index;
uint8_t last_type = 0;
while (offset < data.size()) {
const uint8_t obj_type = data[offset++];
size_t value_length = 0;
bool has_length_byte = obj_type == 0x53; // text objects include explicit length
if (has_length_byte) {
if (offset >= data.size()) {
break;
}
value_length = data[offset++];
} else {
if (!get_bthome_value_length(obj_type, value_length)) {
ESP_LOGVV(TAG, "Unknown BTHome object 0x%02X", obj_type);
break;
}
}
if (value_length == 0) {
break;
}
if (offset + value_length > data.size()) {
ESP_LOGVV(TAG, "BTHome object length exceeds payload");
break;
}
const uint8_t *value = &data[offset];
offset += value_length;
if (obj_type < last_type) {
ESP_LOGVV(TAG, "BTHome objects not in ascending order");
}
last_type = obj_type;
switch (obj_type) {
case 0x00: { // packet id
const uint8_t packet_id = value[0];
if (this->last_packet_id_.has_value() && *this->last_packet_id_ == packet_id) {
return reported;
}
this->last_packet_id_ = packet_id;
break;
}
case 0x01: { // battery percentage
if (this->battery_level_ != nullptr) {
this->battery_level_->publish_state(value[0]);
reported = true;
}
break;
}
case 0x0C: { // battery voltage (mV)
if (this->battery_voltage_ != nullptr) {
const uint16_t raw = encode_uint16(value[1], value[0]);
this->battery_voltage_->publish_state(raw * 0.001f);
reported = true;
}
break;
}
case 0x02: { // temperature
if (this->temperature_ != nullptr) {
const int16_t raw = encode_uint16(value[1], value[0]);
this->temperature_->publish_state(raw * 0.01f);
reported = true;
}
break;
}
case 0x03: { // humidity
if (this->humidity_ != nullptr) {
const uint16_t raw = encode_uint16(value[1], value[0]);
this->humidity_->publish_state(raw * 0.01f);
reported = true;
}
break;
}
default:
break;
}
}
if (reported) {
ESP_LOGD(TAG, "BTHome data%sfrom %s", is_trigger_based ? " (triggered) " : " ", device.address_str().c_str());
}
return reported;
}
} // namespace bthome_mithermometer
} // namespace esphome
#endif

View File

@@ -0,0 +1,44 @@
#pragma once
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/core/component.h"
#include <cstdint>
#ifdef USE_ESP32
namespace esphome {
namespace bthome_mithermometer {
class BTHomeMiThermometer : public esp32_ble_tracker::ESPBTDeviceListener, public Component {
public:
void set_address(uint64_t address) { this->address_ = address; }
void set_temperature(sensor::Sensor *temperature) { this->temperature_ = temperature; }
void set_humidity(sensor::Sensor *humidity) { this->humidity_ = humidity; }
void set_battery_level(sensor::Sensor *battery_level) { this->battery_level_ = battery_level; }
void set_battery_voltage(sensor::Sensor *battery_voltage) { this->battery_voltage_ = battery_voltage; }
void set_signal_strength(sensor::Sensor *signal_strength) { this->signal_strength_ = signal_strength; }
void dump_config() override;
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
protected:
bool handle_service_data_(const esp32_ble_tracker::ServiceData &service_data,
const esp32_ble_tracker::ESPBTDevice &device);
uint64_t address_{0};
optional<uint8_t> last_packet_id_{};
sensor::Sensor *temperature_{nullptr};
sensor::Sensor *humidity_{nullptr};
sensor::Sensor *battery_level_{nullptr};
sensor::Sensor *battery_voltage_{nullptr};
sensor::Sensor *signal_strength_{nullptr};
};
} // namespace bthome_mithermometer
} // namespace esphome
#endif

View File

@@ -0,0 +1,88 @@
import esphome.codegen as cg
from esphome.components import sensor
import esphome.config_validation as cv
from esphome.const import (
CONF_BATTERY_LEVEL,
CONF_BATTERY_VOLTAGE,
CONF_HUMIDITY,
CONF_ID,
CONF_SIGNAL_STRENGTH,
CONF_TEMPERATURE,
DEVICE_CLASS_BATTERY,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_SIGNAL_STRENGTH,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_VOLTAGE,
ENTITY_CATEGORY_DIAGNOSTIC,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
UNIT_DECIBEL_MILLIWATT,
UNIT_PERCENT,
UNIT_VOLT,
)
from . import bthome_mithermometer_base_schema, setup_bthome_mithermometer
CODEOWNERS = ["@nagyrobi"]
DEPENDENCIES = ["esp32_ble_tracker"]
CONFIG_SCHEMA = bthome_mithermometer_base_schema(
{
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=2,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=2,
device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(
unit_of_measurement=UNIT_PERCENT,
accuracy_decimals=0,
device_class=DEVICE_CLASS_BATTERY,
state_class=STATE_CLASS_MEASUREMENT,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
),
cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema(
unit_of_measurement=UNIT_VOLT,
accuracy_decimals=3,
device_class=DEVICE_CLASS_VOLTAGE,
state_class=STATE_CLASS_MEASUREMENT,
icon="mdi:battery-plus",
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
),
cv.Optional(CONF_SIGNAL_STRENGTH): sensor.sensor_schema(
unit_of_measurement=UNIT_DECIBEL_MILLIWATT,
accuracy_decimals=0,
device_class=DEVICE_CLASS_SIGNAL_STRENGTH,
state_class=STATE_CLASS_MEASUREMENT,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
),
}
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await setup_bthome_mithermometer(var, config)
if temp_sens := config.get(CONF_TEMPERATURE):
sens = await sensor.new_sensor(temp_sens)
cg.add(var.set_temperature(sens))
if humi_sens := config.get(CONF_HUMIDITY):
sens = await sensor.new_sensor(humi_sens)
cg.add(var.set_humidity(sens))
if batl_sens := config.get(CONF_BATTERY_LEVEL):
sens = await sensor.new_sensor(batl_sens)
cg.add(var.set_battery_level(sens))
if batv_sens := config.get(CONF_BATTERY_VOLTAGE):
sens = await sensor.new_sensor(batv_sens)
cg.add(var.set_battery_voltage(sens))
if sgnl_sens := config.get(CONF_SIGNAL_STRENGTH):
sens = await sensor.new_sensor(sgnl_sens)
cg.add(var.set_signal_strength(sens))

View File

@@ -63,14 +63,14 @@ void CAP1188Component::finish_setup_() {
}
void CAP1188Component::dump_config() {
ESP_LOGCONFIG(TAG, "CAP1188:");
LOG_I2C_DEVICE(this);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
ESP_LOGCONFIG(TAG,
"CAP1188:\n"
" 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_);
LOG_I2C_DEVICE(this);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
switch (this->error_code_) {
case COMMUNICATION_FAILED:

View File

@@ -49,9 +49,11 @@ void CaptivePortal::handle_config(AsyncWebServerRequest *request) {
void CaptivePortal::handle_wifisave(AsyncWebServerRequest *request) {
std::string ssid = request->arg("ssid").c_str(); // NOLINT(readability-redundant-string-cstr)
std::string psk = request->arg("psk").c_str(); // NOLINT(readability-redundant-string-cstr)
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());
ESP_LOGI(TAG,
"Requested WiFi Settings Change:\n"
" SSID='%s'\n"
" Password=" LOG_SECRET("'%s'"),
ssid.c_str(), psk.c_str());
// Defer save to main loop thread to avoid NVS operations from HTTP thread
this->defer([ssid, psk]() { wifi::global_wifi_component->save_wifi_sta(ssid, psk); });
request->redirect(ESPHOME_F("/?save"));

View File

@@ -212,9 +212,8 @@ void CC1101Component::dump_config() {
XTAL_FREQUENCY / (1 << 16);
float symbol_rate = (((256.0f + this->state_.DRATE_M) * (1 << this->state_.DRATE_E)) / (1 << 28)) * XTAL_FREQUENCY;
float bw = XTAL_FREQUENCY / (8.0f * (4 + this->state_.CHANBW_M) * (1 << this->state_.CHANBW_E));
ESP_LOGCONFIG(TAG, "CC1101:");
LOG_PIN(" CS Pin: ", this->cs_);
ESP_LOGCONFIG(TAG,
"CC1101:\n"
" Chip ID: 0x%04X\n"
" Frequency: %" PRId32 " Hz\n"
" Channel: %u\n"
@@ -224,6 +223,7 @@ void CC1101Component::dump_config() {
" Output Power: %.1f dBm",
this->chip_id_, freq, this->state_.CHANNR, MODULATION_NAMES[this->state_.MOD_FORMAT & 0x07],
symbol_rate, bw, this->output_power_effective_);
LOG_PIN(" CS Pin: ", this->cs_);
}
void CC1101Component::begin_tx() {

View File

@@ -21,12 +21,14 @@ void CD74HC4067Component::setup() {
}
void CD74HC4067Component::dump_config() {
ESP_LOGCONFIG(TAG, "CD74HC4067 Multiplexer:");
ESP_LOGCONFIG(TAG,
"CD74HC4067 Multiplexer:\n"
" switch delay: %" PRIu32,
this->switch_delay_);
LOG_PIN(" S0 Pin: ", this->pin_s0_);
LOG_PIN(" S1 Pin: ", this->pin_s1_);
LOG_PIN(" S2 Pin: ", this->pin_s2_);
LOG_PIN(" S3 Pin: ", this->pin_s3_);
ESP_LOGCONFIG(TAG, "switch delay: %" PRIu32, this->switch_delay_);
}
void CD74HC4067Component::activate_pin(uint8_t pin) {

View File

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

View File

@@ -83,14 +83,14 @@ void CST816Touchscreen::update_touches() {
}
void CST816Touchscreen::dump_config() {
ESP_LOGCONFIG(TAG, "CST816 Touchscreen:");
LOG_I2C_DEVICE(this);
LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
ESP_LOGCONFIG(TAG,
"CST816 Touchscreen:\n"
" 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_);
LOG_I2C_DEVICE(this);
LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
const char *name;
switch (this->chip_id_) {
case CST716_CHIP_ID:

View File

@@ -146,8 +146,10 @@ void CurrentBasedCover::dump_config() {
if (this->close_obstacle_current_threshold_ != FLT_MAX) {
ESP_LOGCONFIG(TAG, " Close obstacle current threshold: %.11fA", this->close_obstacle_current_threshold_);
}
ESP_LOGCONFIG(TAG, " Close Duration: %.1fs", this->close_duration_ / 1e3f);
ESP_LOGCONFIG(TAG, "Obstacle Rollback: %.1f%%", this->obstacle_rollback_ * 100);
ESP_LOGCONFIG(TAG,
" Close Duration: %.1fs\n"
"Obstacle Rollback: %.1f%%",
this->close_duration_ / 1e3f, this->obstacle_rollback_ * 100);
if (this->max_duration_ != UINT32_MAX) {
ESP_LOGCONFIG(TAG, "Maximum duration: %.1fs", this->max_duration_ / 1e3f);
}

View File

@@ -31,8 +31,8 @@ void DallasTemperatureSensor::dump_config() {
ESP_LOGW(TAG, " Unable to select an address");
return;
}
LOG_ONE_WIRE_DEVICE(this);
ESP_LOGCONFIG(TAG, " Resolution: %u bits", this->resolution_);
LOG_ONE_WIRE_DEVICE(this);
LOG_UPDATE_INTERVAL(this);
}

View File

@@ -47,14 +47,17 @@ void DebugComponent::get_device_info_(std::string &device_info) {
#if !defined(CLANG_TIDY)
auto reset_reason = get_reset_reason_();
ESP_LOGD(TAG, "Chip ID: 0x%08X", ESP.getChipId());
ESP_LOGD(TAG, "SDK Version: %s", ESP.getSdkVersion());
ESP_LOGD(TAG, "Core Version: %s", ESP.getCoreVersion().c_str());
ESP_LOGD(TAG, "Boot Version=%u Mode=%u", ESP.getBootVersion(), ESP.getBootMode());
ESP_LOGD(TAG, "CPU Frequency: %u", ESP.getCpuFreqMHz());
ESP_LOGD(TAG, "Flash Chip ID=0x%08X", ESP.getFlashChipId());
ESP_LOGD(TAG, "Reset Reason: %s", reset_reason.c_str());
ESP_LOGD(TAG, "Reset Info: %s", ESP.getResetInfo().c_str());
ESP_LOGD(TAG,
"Chip ID: 0x%08X\n"
"SDK Version: %s\n"
"Core Version: %s\n"
"Boot Version=%u Mode=%u\n"
"CPU Frequency: %u\n"
"Flash Chip ID=0x%08X\n"
"Reset Reason: %s\n"
"Reset Info: %s",
ESP.getChipId(), ESP.getSdkVersion(), ESP.getCoreVersion().c_str(), ESP.getBootVersion(), ESP.getBootMode(),
ESP.getCpuFreqMHz(), ESP.getFlashChipId(), reset_reason.c_str(), ESP.getResetInfo().c_str());
device_info += "|Chip: 0x" + format_hex(ESP.getChipId());
device_info += "|SDK: ";

View File

@@ -13,12 +13,15 @@ uint32_t DebugComponent::get_free_heap_() { return lt_heap_get_free(); }
void DebugComponent::get_device_info_(std::string &device_info) {
std::string reset_reason = get_reset_reason_();
ESP_LOGD(TAG, "LibreTiny Version: %s", lt_get_version());
ESP_LOGD(TAG, "Chip: %s (%04x) @ %u MHz", lt_cpu_get_model_name(), lt_cpu_get_model(), lt_cpu_get_freq_mhz());
ESP_LOGD(TAG, "Chip ID: 0x%06X", lt_cpu_get_mac_id());
ESP_LOGD(TAG, "Board: %s", lt_get_board_code());
ESP_LOGD(TAG, "Flash: %u KiB / RAM: %u KiB", lt_flash_get_size() / 1024, lt_ram_get_size() / 1024);
ESP_LOGD(TAG, "Reset Reason: %s", reset_reason.c_str());
ESP_LOGD(TAG,
"LibreTiny Version: %s\n"
"Chip: %s (%04x) @ %u MHz\n"
"Chip ID: 0x%06X\n"
"Board: %s\n"
"Flash: %u KiB / RAM: %u KiB\n"
"Reset Reason: %s",
lt_get_version(), lt_cpu_get_model_name(), lt_cpu_get_model(), lt_cpu_get_freq_mhz(), lt_cpu_get_mac_id(),
lt_get_board_code(), lt_flash_get_size() / 1024, lt_ram_get_size() / 1024, reset_reason.c_str());
device_info += "|Version: ";
device_info += LT_BANNER_STR + 10;

View File

@@ -106,13 +106,13 @@ static void fa_cb(const struct flash_area *fa, void *user_data) {
void DebugComponent::log_partition_info_() {
#if CONFIG_FLASH_MAP_LABELS
ESP_LOGCONFIG(TAG, "ID | Device | Device Name "
"| Label | Offset | Size");
ESP_LOGCONFIG(TAG, "--------------------------------------------"
"| Label | Offset | Size\n"
"--------------------------------------------"
"-----------------------------------------------");
#else
ESP_LOGCONFIG(TAG, "ID | Device | Device Name "
"| Offset | Size");
ESP_LOGCONFIG(TAG, "-----------------------------------------"
"| Offset | Size\n"
"-----------------------------------------"
"------------------------------");
#endif
flash_area_foreach(fa_cb, nullptr);
@@ -300,18 +300,18 @@ void DebugComponent::get_device_info_(std::string &device_info) {
return "Unspecified";
};
ESP_LOGD(TAG, "Code page size: %u, code size: %u, device id: 0x%08x%08x", NRF_FICR->CODEPAGESIZE, NRF_FICR->CODESIZE,
NRF_FICR->DEVICEID[1], NRF_FICR->DEVICEID[0]);
ESP_LOGD(TAG, "Encryption root: 0x%08x%08x%08x%08x, Identity Root: 0x%08x%08x%08x%08x", NRF_FICR->ER[0],
ESP_LOGD(TAG,
"Code page size: %u, code size: %u, device id: 0x%08x%08x\n"
"Encryption root: 0x%08x%08x%08x%08x, Identity Root: 0x%08x%08x%08x%08x\n"
"Device address type: %s, address: %s\n"
"Part code: nRF%x, version: %c%c%c%c, package: %s\n"
"RAM: %ukB, Flash: %ukB, production test: %sdone",
NRF_FICR->CODEPAGESIZE, NRF_FICR->CODESIZE, NRF_FICR->DEVICEID[1], NRF_FICR->DEVICEID[0], NRF_FICR->ER[0],
NRF_FICR->ER[1], NRF_FICR->ER[2], NRF_FICR->ER[3], NRF_FICR->IR[0], NRF_FICR->IR[1], NRF_FICR->IR[2],
NRF_FICR->IR[3]);
ESP_LOGD(TAG, "Device address type: %s, address: %s", (NRF_FICR->DEVICEADDRTYPE & 0x1 ? "Random" : "Public"),
get_mac_address_pretty().c_str());
ESP_LOGD(TAG, "Part code: nRF%x, version: %c%c%c%c, package: %s", NRF_FICR->INFO.PART,
NRF_FICR->INFO.VARIANT >> 24 & 0xFF, NRF_FICR->INFO.VARIANT >> 16 & 0xFF, NRF_FICR->INFO.VARIANT >> 8 & 0xFF,
NRF_FICR->INFO.VARIANT & 0xFF, package(NRF_FICR->INFO.PACKAGE));
ESP_LOGD(TAG, "RAM: %ukB, Flash: %ukB, production test: %sdone", NRF_FICR->INFO.RAM, NRF_FICR->INFO.FLASH,
(NRF_FICR->PRODTEST[0] == 0xBB42319F ? "" : "not "));
NRF_FICR->IR[3], (NRF_FICR->DEVICEADDRTYPE & 0x1 ? "Random" : "Public"), get_mac_address_pretty().c_str(),
NRF_FICR->INFO.PART, NRF_FICR->INFO.VARIANT >> 24 & 0xFF, NRF_FICR->INFO.VARIANT >> 16 & 0xFF,
NRF_FICR->INFO.VARIANT >> 8 & 0xFF, NRF_FICR->INFO.VARIANT & 0xFF, package(NRF_FICR->INFO.PACKAGE),
NRF_FICR->INFO.RAM, NRF_FICR->INFO.FLASH, (NRF_FICR->PRODTEST[0] == 0xBB42319F ? "" : "not "));
bool n_reset_enabled = NRF_UICR->PSELRESET[0] == NRF_UICR->PSELRESET[1] &&
(NRF_UICR->PSELRESET[0] & UICR_PSELRESET_CONNECT_Msk) == UICR_PSELRESET_CONNECT_Connected
<< UICR_PSELRESET_CONNECT_Pos;
@@ -329,9 +329,10 @@ void DebugComponent::get_device_info_(std::string &device_info) {
#else
ESP_LOGD(TAG, "bootloader: Adafruit, version %u.%u.%u", (BOOTLOADER_VERSION_REGISTER >> 16) & 0xFF,
(BOOTLOADER_VERSION_REGISTER >> 8) & 0xFF, BOOTLOADER_VERSION_REGISTER & 0xFF);
ESP_LOGD(TAG, "MBR bootloader addr 0x%08x, UICR bootloader addr 0x%08x", read_mem_u32(MBR_BOOTLOADER_ADDR),
NRF_UICR->NRFFW[0]);
ESP_LOGD(TAG, "MBR param page addr 0x%08x, UICR param page addr 0x%08x", read_mem_u32(MBR_PARAM_PAGE_ADDR),
ESP_LOGD(TAG,
"MBR bootloader addr 0x%08x, UICR bootloader addr 0x%08x\n"
"MBR param page addr 0x%08x, UICR param page addr 0x%08x",
read_mem_u32(MBR_BOOTLOADER_ADDR), NRF_UICR->NRFFW[0], read_mem_u32(MBR_PARAM_PAGE_ADDR),
NRF_UICR->NRFFW[1]);
if (is_sd_present()) {
uint32_t const sd_id = sd_id_get();
@@ -368,8 +369,10 @@ void DebugComponent::get_device_info_(std::string &device_info) {
}
return res;
};
ESP_LOGD(TAG, "NRFFW %s", uicr(NRF_UICR->NRFFW, 13).c_str());
ESP_LOGD(TAG, "NRFHW %s", uicr(NRF_UICR->NRFHW, 12).c_str());
ESP_LOGD(TAG,
"NRFFW %s\n"
"NRFHW %s",
uicr(NRF_UICR->NRFFW, 13).c_str(), uicr(NRF_UICR->NRFHW, 12).c_str());
}
void DebugComponent::update_platform_() {}

View File

@@ -179,8 +179,10 @@ uint8_t DetRangeCfgCommand::on_message(std::string &message) {
ESP_LOGE(TAG, "Cannot configure range config. Sensor is not stopped!");
return 1; // Command done
} else if (message == "Done") {
ESP_LOGI(TAG, "Updated detection area config:");
ESP_LOGI(TAG, "Detection area 1 from %.02fm to %.02fm.", this->min1_, this->max1_);
ESP_LOGI(TAG,
"Updated detection area config:\n"
"Detection area 1 from %.02fm to %.02fm.",
this->min1_, this->max1_);
if (this->min2_ >= 0 && this->max2_ >= 0) {
ESP_LOGI(TAG, "Detection area 2 from %.02fm to %.02fm.", this->min2_, this->max2_);
}
@@ -209,9 +211,11 @@ uint8_t SetLatencyCommand::on_message(std::string &message) {
ESP_LOGE(TAG, "Cannot configure output latency. Sensor is not stopped!");
return 1; // Command done
} else if (message == "Done") {
ESP_LOGI(TAG, "Updated output latency config:");
ESP_LOGI(TAG, "Signal that someone was detected is delayed by %.03f s.", this->delay_after_detection_);
ESP_LOGI(TAG, "Signal that nobody is detected anymore is delayed by %.03f s.", this->delay_after_disappear_);
ESP_LOGI(TAG,
"Updated output latency config:\n"
"Signal that someone was detected is delayed by %.03f s.\n"
"Signal that nobody is detected anymore is delayed by %.03f s.",
this->delay_after_detection_, this->delay_after_disappear_);
ESP_LOGD(TAG, "Used command: %s", this->cmd_.c_str());
return 1; // Command done
}

View File

@@ -17,11 +17,14 @@ void DHT::setup() {
}
void DHT::dump_config() {
ESP_LOGCONFIG(TAG, "DHT:");
ESP_LOGCONFIG(TAG,
"DHT:\n"
" %sModel: %s\n"
" Internal pull-up: %s",
this->is_auto_detect_ ? "Auto-detected " : "",
this->model_ == DHT_MODEL_DHT11 ? "DHT11" : "DHT22 or equivalent",
ONOFF(this->t_pin_->get_flags() & gpio::FLAG_PULLUP));
LOG_PIN(" Pin: ", this->t_pin_);
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->t_pin_->get_flags() & gpio::FLAG_PULLUP));
LOG_UPDATE_INTERVAL(this);
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);

View File

@@ -63,13 +63,11 @@ def validate_auto_clear(value):
return cv.boolean(value)
def basic_display_schema(default_update_interval: str = "1s") -> cv.Schema:
"""Create a basic display schema with configurable default update interval."""
return cv.Schema(
{
cv.Exclusive(CONF_LAMBDA, CONF_LAMBDA): cv.lambda_,
}
).extend(cv.polling_component_schema(default_update_interval))
BASIC_DISPLAY_SCHEMA = cv.Schema(
{
cv.Exclusive(CONF_LAMBDA, CONF_LAMBDA): cv.lambda_,
}
).extend(cv.polling_component_schema("1s"))
def _validate_test_card(config):
@@ -83,41 +81,34 @@ def _validate_test_card(config):
return config
def full_display_schema(default_update_interval: str = "1s") -> cv.Schema:
"""Create a full display schema with configurable default update interval."""
schema = basic_display_schema(default_update_interval).extend(
{
cv.Optional(CONF_ROTATION): validate_rotation,
cv.Exclusive(CONF_PAGES, CONF_LAMBDA): cv.All(
cv.ensure_list(
{
cv.GenerateID(): cv.declare_id(DisplayPage),
cv.Required(CONF_LAMBDA): cv.lambda_,
}
),
cv.Length(min=1),
),
cv.Optional(CONF_ON_PAGE_CHANGE): automation.validate_automation(
FULL_DISPLAY_SCHEMA = BASIC_DISPLAY_SCHEMA.extend(
{
cv.Optional(CONF_ROTATION): validate_rotation,
cv.Exclusive(CONF_PAGES, CONF_LAMBDA): cv.All(
cv.ensure_list(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
DisplayOnPageChangeTrigger
),
cv.Optional(CONF_FROM): cv.use_id(DisplayPage),
cv.Optional(CONF_TO): cv.use_id(DisplayPage),
cv.GenerateID(): cv.declare_id(DisplayPage),
cv.Required(CONF_LAMBDA): cv.lambda_,
}
),
cv.Optional(
CONF_AUTO_CLEAR_ENABLED, default=CONF_UNSPECIFIED
): validate_auto_clear,
cv.Optional(CONF_SHOW_TEST_CARD): cv.boolean,
}
)
schema.add_extra(_validate_test_card)
return schema
BASIC_DISPLAY_SCHEMA = basic_display_schema("1s")
FULL_DISPLAY_SCHEMA = full_display_schema("1s")
cv.Length(min=1),
),
cv.Optional(CONF_ON_PAGE_CHANGE): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
DisplayOnPageChangeTrigger
),
cv.Optional(CONF_FROM): cv.use_id(DisplayPage),
cv.Optional(CONF_TO): cv.use_id(DisplayPage),
}
),
cv.Optional(
CONF_AUTO_CLEAR_ENABLED, default=CONF_UNSPECIFIED
): validate_auto_clear,
cv.Optional(CONF_SHOW_TEST_CARD): cv.boolean,
}
)
FULL_DISPLAY_SCHEMA.add_extra(_validate_test_card)
async def setup_display_core_(var, config):

View File

@@ -153,8 +153,10 @@ void EmmetiClimate::reverse_add_(T val, size_t len, esphome::remote_base::Remote
bool EmmetiClimate::check_checksum_(uint8_t checksum) {
uint8_t expected = this->gen_checksum_();
ESP_LOGV(TAG, "Expected checksum: %X", expected);
ESP_LOGV(TAG, "Checksum received: %X", checksum);
ESP_LOGV(TAG,
"Expected checksum: %X\n"
"Checksum received: %X",
expected, checksum);
return checksum == expected;
}
@@ -264,8 +266,10 @@ bool EmmetiClimate::on_receive(remote_base::RemoteReceiveData data) {
}
}
ESP_LOGD(TAG, "Swing: %d", (curr_state.bitmap >> 1) & 0x01);
ESP_LOGD(TAG, "Sleep: %d", (curr_state.bitmap >> 2) & 0x01);
ESP_LOGD(TAG,
"Swing: %d\n"
"Sleep: %d",
(curr_state.bitmap >> 1) & 0x01, (curr_state.bitmap >> 2) & 0x01);
for (size_t pos = 0; pos < 4; pos++) {
if (data.expect_item(EMMETI_BIT_MARK, EMMETI_ONE_SPACE)) {
@@ -291,10 +295,13 @@ bool EmmetiClimate::on_receive(remote_base::RemoteReceiveData data) {
}
}
ESP_LOGD(TAG, "Turbo: %d", (curr_state.bitmap >> 3) & 0x01);
ESP_LOGD(TAG, "Light: %d", (curr_state.bitmap >> 4) & 0x01);
ESP_LOGD(TAG, "Tree: %d", (curr_state.bitmap >> 5) & 0x01);
ESP_LOGD(TAG, "Blow: %d", (curr_state.bitmap >> 6) & 0x01);
ESP_LOGD(TAG,
"Turbo: %d\n"
"Light: %d\n"
"Tree: %d\n"
"Blow: %d",
(curr_state.bitmap >> 3) & 0x01, (curr_state.bitmap >> 4) & 0x01, (curr_state.bitmap >> 5) & 0x01,
(curr_state.bitmap >> 6) & 0x01);
uint16_t control_data = 0;
for (size_t pos = 0; pos < 11; pos++) {

View File

@@ -104,10 +104,12 @@ void EndstopCover::loop() {
}
void EndstopCover::dump_config() {
LOG_COVER("", "Endstop Cover", this);
ESP_LOGCONFIG(TAG,
" Open Duration: %.1fs\n"
" Close Duration: %.1fs",
this->open_duration_ / 1e3f, this->close_duration_ / 1e3f);
LOG_BINARY_SENSOR(" ", "Open Endstop", this->open_endstop_);
ESP_LOGCONFIG(TAG, " Open Duration: %.1fs", this->open_duration_ / 1e3f);
LOG_BINARY_SENSOR(" ", "Close Endstop", this->close_endstop_);
ESP_LOGCONFIG(TAG, " Close Duration: %.1fs", this->close_duration_ / 1e3f);
}
float EndstopCover::get_setup_priority() const { return setup_priority::DATA; }
void EndstopCover::stop_prev_trigger_() {

View File

@@ -151,14 +151,16 @@ void ENS160Component::update() {
}
// verbose status logging
ESP_LOGV(TAG, "Status: ENS160 STATAS bit 0x%x",
(ENS160_DATA_STATUS_STATAS & (status_value)) == ENS160_DATA_STATUS_STATAS);
ESP_LOGV(TAG, "Status: ENS160 STATER bit 0x%x",
(ENS160_DATA_STATUS_STATER & (status_value)) == ENS160_DATA_STATUS_STATER);
ESP_LOGV(TAG, "Status: ENS160 VALIDITY FLAG 0x%02x", (ENS160_DATA_STATUS_VALIDITY & status_value) >> 2);
ESP_LOGV(TAG, "Status: ENS160 NEWDAT bit 0x%x",
(ENS160_DATA_STATUS_NEWDAT & (status_value)) == ENS160_DATA_STATUS_NEWDAT);
ESP_LOGV(TAG, "Status: ENS160 NEWGPR bit 0x%x",
ESP_LOGV(TAG,
"Status: ENS160 STATAS bit 0x%x\n"
"Status: ENS160 STATER bit 0x%x\n"
"Status: ENS160 VALIDITY FLAG 0x%02x\n"
"Status: ENS160 NEWDAT bit 0x%x\n"
"Status: ENS160 NEWGPR bit 0x%x",
(ENS160_DATA_STATUS_STATAS & (status_value)) == ENS160_DATA_STATUS_STATAS,
(ENS160_DATA_STATUS_STATER & (status_value)) == ENS160_DATA_STATUS_STATER,
(ENS160_DATA_STATUS_VALIDITY & status_value) >> 2,
(ENS160_DATA_STATUS_NEWDAT & (status_value)) == ENS160_DATA_STATUS_NEWDAT,
(ENS160_DATA_STATUS_NEWGPR & (status_value)) == ENS160_DATA_STATUS_NEWGPR);
data_ready = ENS160_DATA_STATUS_NEWDAT & status_value;

View File

@@ -31,7 +31,6 @@ from esphome.const import (
CONF_TRANSFORM,
CONF_UPDATE_INTERVAL,
CONF_WIDTH,
SCHEDULER_DONT_RUN,
)
from esphome.cpp_generator import RawExpression
from esphome.final_validate import full_config
@@ -73,10 +72,12 @@ TRANSFORM_OPTIONS = {CONF_MIRROR_X, CONF_MIRROR_Y, CONF_SWAP_XY}
def model_schema(config):
model = MODELS[config[CONF_MODEL]]
class_name = epaper_spi_ns.class_(model.class_name, EPaperBase)
minimum_update_interval = update_interval(
model.get_default(CONF_MINIMUM_UPDATE_INTERVAL, "1s")
)
cv_dimensions = cv.Optional if model.get_default(CONF_WIDTH) else cv.Required
return (
display.full_display_schema("60s")
.extend(
display.FULL_DISPLAY_SCHEMA.extend(
spi.spi_device_schema(
cs_pin_required=False,
default_mode="MODE0",
@@ -93,6 +94,9 @@ def model_schema(config):
{
cv.Optional(CONF_ROTATION, default=0): validate_rotation,
cv.Required(CONF_MODEL): cv.one_of(model.name, upper=True),
cv.Optional(CONF_UPDATE_INTERVAL, default=cv.UNDEFINED): cv.All(
update_interval, cv.Range(min=minimum_update_interval)
),
cv.Optional(CONF_TRANSFORM): cv.Schema(
{
cv.Required(CONF_MIRROR_X): cv.boolean,
@@ -146,22 +150,15 @@ def _final_validate(config):
global_config = full_config.get()
from esphome.components.lvgl import DOMAIN as LVGL_DOMAIN
# If no drawing methods are configured, and LVGL is not enabled, show a test card
if (
CONF_LAMBDA not in config
and CONF_PAGES not in config
and LVGL_DOMAIN not in global_config
):
config[CONF_SHOW_TEST_CARD] = True
interval = config[CONF_UPDATE_INTERVAL]
if interval != SCHEDULER_DONT_RUN:
model = MODELS[config[CONF_MODEL]]
minimum = update_interval(model.get_default(CONF_MINIMUM_UPDATE_INTERVAL, "1s"))
if interval < minimum:
raise cv.Invalid(
f"update_interval must be at least {minimum} for {model.name}, got {interval}"
)
if CONF_LAMBDA not in config and CONF_PAGES not in config:
if LVGL_DOMAIN in global_config:
if CONF_UPDATE_INTERVAL not in config:
config[CONF_UPDATE_INTERVAL] = update_interval("never")
else:
# If no drawing methods are configured, and LVGL is not enabled, show a test card
config[CONF_SHOW_TEST_CARD] = True
elif CONF_UPDATE_INTERVAL not in config:
config[CONF_UPDATE_INTERVAL] = update_interval("1min")
return config

View File

@@ -331,20 +331,21 @@ void HOT EPaperBase::draw_pixel_at(int x, int y, Color color) {
void EPaperBase::dump_config() {
LOG_DISPLAY("", "E-Paper SPI", this);
ESP_LOGCONFIG(TAG, " Model: %s", this->name_);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
LOG_PIN(" DC Pin: ", this->dc_pin_);
LOG_PIN(" Busy Pin: ", this->busy_pin_);
LOG_PIN(" CS Pin: ", this->cs_);
LOG_UPDATE_INTERVAL(this);
ESP_LOGCONFIG(TAG,
" Model: %s\n"
" SPI Data Rate: %uMHz\n"
" Full update every: %d\n"
" Swap X/Y: %s\n"
" Mirror X: %s\n"
" Mirror Y: %s",
(unsigned) (this->data_rate_ / 1000000), this->full_update_every_, YESNO(this->transform_ & SWAP_XY),
YESNO(this->transform_ & MIRROR_X), YESNO(this->transform_ & MIRROR_Y));
this->name_, (unsigned) (this->data_rate_ / 1000000), this->full_update_every_,
YESNO(this->transform_ & SWAP_XY), YESNO(this->transform_ & MIRROR_X),
YESNO(this->transform_ & MIRROR_Y));
LOG_PIN(" Reset Pin: ", this->reset_pin_);
LOG_PIN(" DC Pin: ", this->dc_pin_);
LOG_PIN(" Busy Pin: ", this->busy_pin_);
LOG_PIN(" CS Pin: ", this->cs_);
LOG_UPDATE_INTERVAL(this);
}
} // namespace esphome::epaper_spi

View File

@@ -76,6 +76,12 @@ class EPaperBase : public Display,
return 0;
}
void fill(Color color) override {
// If clipping is active, fall back to base implementation
if (this->get_clipping().is_set()) {
Display::fill(color);
return;
}
auto pixel_color = color_to_bit(color) ? 0xFF : 0x00;
// We store 8 pixels per byte

View File

@@ -97,6 +97,12 @@ void EPaperSpectraE6::deep_sleep() {
}
void EPaperSpectraE6::fill(Color color) {
// If clipping is active, fall back to base implementation
if (this->get_clipping().is_set()) {
EPaperBase::fill(color);
return;
}
auto pixel_color = color_to_hex(color);
// We store 2 pixels per byte

View File

@@ -210,9 +210,11 @@ bool ES8388::set_dac_output(DacOutputLine 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);
ESP_LOGV(TAG,
"Setting ES8388_DACPOWER to 0x%02X\n"
"Setting ES8388_DACCONTROL24 / ES8388_DACCONTROL25 to 0x%02X\n"
"Setting ES8388_DACCONTROL26 / ES8388_DACCONTROL27 to 0x%02X",
dac_power, reg_out1, reg_out2);
ES8388_ERROR_CHECK(this->write_byte(ES8388_DACCONTROL24, reg_out1)); // LOUT1VOL
ES8388_ERROR_CHECK(this->write_byte(ES8388_DACCONTROL25, reg_out1)); // ROUT1VOL

View File

@@ -644,6 +644,7 @@ CONF_DISABLE_VFS_SUPPORT_SELECT = "disable_vfs_support_select"
CONF_DISABLE_VFS_SUPPORT_DIR = "disable_vfs_support_dir"
CONF_FREERTOS_IN_IRAM = "freertos_in_iram"
CONF_RINGBUF_IN_IRAM = "ringbuf_in_iram"
CONF_HEAP_IN_IRAM = "heap_in_iram"
CONF_LOOP_TASK_STACK_SIZE = "loop_task_stack_size"
# VFS requirement tracking
@@ -651,9 +652,6 @@ CONF_LOOP_TASK_STACK_SIZE = "loop_task_stack_size"
KEY_VFS_SELECT_REQUIRED = "vfs_select_required"
KEY_VFS_DIR_REQUIRED = "vfs_dir_required"
# Ring buffer IRAM requirement tracking
KEY_RINGBUF_IN_IRAM = "ringbuf_in_iram"
def require_vfs_select() -> None:
"""Mark that VFS select support is required by a component.
@@ -673,17 +671,6 @@ def require_vfs_dir() -> None:
CORE.data[KEY_VFS_DIR_REQUIRED] = True
def enable_ringbuf_in_iram() -> None:
"""Keep ring buffer functions in IRAM instead of moving them to flash.
Call this from components that use esphome/core/ring_buffer.cpp and need
the ring buffer functions to remain in IRAM for performance reasons
(e.g., voice assistants, audio components).
This prevents CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH from being enabled.
"""
CORE.data[KEY_RINGBUF_IN_IRAM] = True
def _parse_idf_component(value: str) -> ConfigType:
"""Parse IDF component shorthand syntax like 'owner/component^version'"""
# Match operator followed by version-like string (digit or *)
@@ -759,6 +746,7 @@ FRAMEWORK_SCHEMA = cv.Schema(
cv.Optional(CONF_DISABLE_VFS_SUPPORT_DIR, default=True): cv.boolean,
cv.Optional(CONF_FREERTOS_IN_IRAM, default=False): cv.boolean,
cv.Optional(CONF_RINGBUF_IN_IRAM, default=False): cv.boolean,
cv.Optional(CONF_HEAP_IN_IRAM, default=False): cv.boolean,
cv.Optional(CONF_EXECUTE_FROM_PSRAM, default=False): cv.boolean,
cv.Optional(CONF_LOOP_TASK_STACK_SIZE, default=8192): cv.int_range(
min=8192, max=32768
@@ -1094,20 +1082,22 @@ async def to_code(config):
add_idf_sdkconfig_option("CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH", True)
# Place ring buffer functions into flash instead of IRAM by default
# This saves IRAM but may impact performance for audio/voice components.
# Components that need ring buffer in IRAM call enable_ringbuf_in_iram().
# Users can also set ringbuf_in_iram: true to force IRAM placement.
# In ESP-IDF 6.0 flash placement becomes the default.
if conf[CONF_ADVANCED][CONF_RINGBUF_IN_IRAM] or CORE.data.get(
KEY_RINGBUF_IN_IRAM, False
):
# User config or component requires ring buffer in IRAM for performance
# This saves IRAM. In ESP-IDF 6.0 flash placement becomes the default.
# Users can set ringbuf_in_iram: true as an escape hatch if they encounter issues.
if conf[CONF_ADVANCED][CONF_RINGBUF_IN_IRAM]:
# User requests ring buffer in IRAM
# IDF 6.0+: will need CONFIG_RINGBUF_PLACE_ISR_FUNCTIONS_INTO_FLASH=n
add_idf_sdkconfig_option("CONFIG_RINGBUF_PLACE_ISR_FUNCTIONS_INTO_FLASH", False)
else:
# No component needs it - place in flash to save IRAM
# Place in flash to save IRAM (default)
add_idf_sdkconfig_option("CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH", True)
# Place heap functions into flash to save IRAM (~4-6KB savings)
# Safe as long as heap functions are not called from ISRs (which they shouldn't be)
# Users can set heap_in_iram: true as an escape hatch if needed
if not conf[CONF_ADVANCED][CONF_HEAP_IN_IRAM]:
add_idf_sdkconfig_option("CONFIG_HEAP_PLACE_FUNCTION_INTO_FLASH", True)
# Setup watchdog
add_idf_sdkconfig_option("CONFIG_ESP_TASK_WDT", True)
add_idf_sdkconfig_option("CONFIG_ESP_TASK_WDT_PANIC", True)

View File

@@ -85,6 +85,7 @@ void ESP32InternalGPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpi
break;
}
gpio_set_intr_type(this->get_pin_num(), idf_type);
gpio_intr_enable(this->get_pin_num());
if (!isr_service_installed) {
auto res = gpio_install_isr_service(ESP_INTR_FLAG_LEVEL3);
if (res != ESP_OK) {
@@ -94,7 +95,6 @@ void ESP32InternalGPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpi
isr_service_installed = true;
}
gpio_isr_handler_add(this->get_pin_num(), func, arg);
gpio_intr_enable(this->get_pin_num());
}
size_t ESP32InternalGPIOPin::dump_summary(char *buffer, size_t len) const {

View File

@@ -23,9 +23,11 @@ struct NVSData {
size_t len;
void set_data(const uint8_t *src, size_t size) {
this->data = std::make_unique<uint8_t[]>(size);
if (!this->data || this->len != size) {
this->data = std::make_unique<uint8_t[]>(size);
this->len = size;
}
memcpy(this->data.get(), src, size);
this->len = size;
}
};

View File

@@ -22,7 +22,6 @@ from esphome.core import CORE, CoroPriority, TimePeriod, coroutine_with_priority
import esphome.final_validate as fv
DEPENDENCIES = ["esp32"]
AUTO_LOAD = ["socket"]
CODEOWNERS = ["@jesserockz", "@Rapsssito", "@bdraco"]
DOMAIN = "esp32_ble"

View File

@@ -98,10 +98,6 @@ void ESP32BLE::advertising_set_service_data(const std::vector<uint8_t> &data) {
}
void ESP32BLE::advertising_set_manufacturer_data(const std::vector<uint8_t> &data) {
this->advertising_set_manufacturer_data(std::span<const uint8_t>(data));
}
void ESP32BLE::advertising_set_manufacturer_data(std::span<const uint8_t> data) {
this->advertising_init_();
this->advertising_->set_manufacturer_data(data);
this->advertising_start();

View File

@@ -118,7 +118,6 @@ class ESP32BLE : public Component {
void advertising_start();
void advertising_set_service_data(const std::vector<uint8_t> &data);
void advertising_set_manufacturer_data(const std::vector<uint8_t> &data);
void advertising_set_manufacturer_data(std::span<const uint8_t> data);
void advertising_set_appearance(uint16_t appearance) { this->appearance_ = appearance; }
void advertising_set_service_data_and_name(std::span<const uint8_t> data, bool include_name);
void advertising_add_service_uuid(ESPBTUUID uuid);

View File

@@ -59,10 +59,6 @@ void BLEAdvertising::set_service_data(const std::vector<uint8_t> &data) {
}
void BLEAdvertising::set_manufacturer_data(const std::vector<uint8_t> &data) {
this->set_manufacturer_data(std::span<const uint8_t>(data));
}
void BLEAdvertising::set_manufacturer_data(std::span<const uint8_t> data) {
delete[] this->advertising_data_.p_manufacturer_data;
this->advertising_data_.p_manufacturer_data = nullptr;
this->advertising_data_.manufacturer_len = data.size();

View File

@@ -37,7 +37,6 @@ class BLEAdvertising {
void set_scan_response(bool scan_response) { this->scan_response_ = scan_response; }
void set_min_preferred_interval(uint16_t interval) { this->advertising_data_.min_interval = interval; }
void set_manufacturer_data(const std::vector<uint8_t> &data);
void set_manufacturer_data(std::span<const uint8_t> data);
void set_appearance(uint16_t appearance) { this->advertising_data_.appearance = appearance; }
void set_service_data(const std::vector<uint8_t> &data);
void set_service_data(std::span<const uint8_t> data);

View File

@@ -1,6 +1,5 @@
#include "esp32_ble_beacon.h"
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
#ifdef USE_ESP32

View File

@@ -70,9 +70,9 @@ float BLEClientBase::get_setup_priority() const { return setup_priority::AFTER_B
void BLEClientBase::dump_config() {
ESP_LOGCONFIG(TAG,
" Address: %s\n"
" Auto-Connect: %s",
this->address_str(), TRUEFALSE(this->auto_connect_));
ESP_LOGCONFIG(TAG, " State: %s", espbt::client_state_to_string(this->state()));
" Auto-Connect: %s\n"
" State: %s",
this->address_str(), TRUEFALSE(this->auto_connect_), espbt::client_state_to_string(this->state()));
if (this->status_ == ESP_GATT_NO_RESOURCES) {
ESP_LOGE(TAG, " Failed due to no resources. Try to reduce number of BLE clients in config.");
} else if (this->status_ != ESP_GATT_OK) {
@@ -415,8 +415,10 @@ bool BLEClientBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
for (auto &svc : this->services_) {
char uuid_buf[espbt::UUID_STR_LEN];
svc->uuid.to_str(uuid_buf);
ESP_LOGV(TAG, "[%d] [%s] Service UUID: %s", this->connection_index_, this->address_str_, uuid_buf);
ESP_LOGV(TAG, "[%d] [%s] start_handle: 0x%x end_handle: 0x%x", this->connection_index_, this->address_str_,
ESP_LOGV(TAG,
"[%d] [%s] Service UUID: %s\n"
"[%d] [%s] start_handle: 0x%x end_handle: 0x%x",
this->connection_index_, this->address_str_, uuid_buf, this->connection_index_, this->address_str_,
svc->start_handle, svc->end_handle);
}
#endif

View File

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

View File

@@ -98,7 +98,13 @@ template<typename... Ts> class ESP32BLEStartScanAction : public Action<Ts...> {
TEMPLATABLE_VALUE(bool, continuous)
void play(const Ts &...x) override {
this->parent_->set_scan_continuous(this->continuous_.value(x...));
this->parent_->start_scan();
// Only call start_scan() if scanner is IDLE
// For other states (STARTING, RUNNING, STOPPING, FAILED), the normal state
// machine flow will eventually transition back to IDLE, at which point
// loop() will see scan_continuous_ and restart scanning if it is true.
if (this->parent_->get_scanner_state() == ScannerState::IDLE) {
this->parent_->start_scan();
}
}
protected:

View File

@@ -657,8 +657,10 @@ void ESP32BLETracker::dump_config() {
" Continuous Scanning: %s",
this->scan_duration_, this->scan_interval_ * 0.625f, this->scan_window_ * 0.625f,
this->scan_active_ ? "ACTIVE" : "PASSIVE", YESNO(this->scan_continuous_));
ESP_LOGCONFIG(TAG, " Scanner State: %s", this->scanner_state_to_string_(this->scanner_state_));
ESP_LOGCONFIG(TAG, " Connecting: %d, discovered: %d, disconnecting: %d", this->client_state_counts_.connecting,
ESP_LOGCONFIG(TAG,
" Scanner State: %s\n"
" Connecting: %d, discovered: %d, disconnecting: %d",
this->scanner_state_to_string_(this->scanner_state_), this->client_state_counts_.connecting,
this->client_state_counts_.discovered, this->client_state_counts_.disconnecting);
if (this->scan_start_fail_count_) {
ESP_LOGCONFIG(TAG, " Scan Start Fail Count: %d", this->scan_start_fail_count_);

View File

@@ -41,11 +41,13 @@ void Esp32HostedUpdate::setup() {
if (this->firmware_size_ >= app_desc_offset + sizeof(esp_app_desc_t)) {
esp_app_desc_t *app_desc = (esp_app_desc_t *) (this->firmware_data_ + app_desc_offset);
if (app_desc->magic_word == ESP_APP_DESC_MAGIC_WORD) {
ESP_LOGD(TAG, "Firmware version: %s", app_desc->version);
ESP_LOGD(TAG, "Project name: %s", app_desc->project_name);
ESP_LOGD(TAG, "Build date: %s", app_desc->date);
ESP_LOGD(TAG, "Build time: %s", app_desc->time);
ESP_LOGD(TAG, "IDF version: %s", app_desc->idf_ver);
ESP_LOGD(TAG,
"Firmware version: %s\n"
"Project name: %s\n"
"Build date: %s\n"
"Build time: %s\n"
"IDF version: %s",
app_desc->version, app_desc->project_name, app_desc->date, app_desc->time, app_desc->idf_ver);
this->update_info_.latest_version = app_desc->version;
if (this->update_info_.latest_version != this->update_info_.current_version) {
this->state_ = update::UPDATE_STATE_AVAILABLE;

View File

@@ -3,7 +3,7 @@ import esphome.codegen as cg
from esphome.components import binary_sensor, esp32_ble, improv_base, output
from esphome.components.esp32_ble import BTLoggers
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_ON_STATE, CONF_TRIGGER_ID
from esphome.const import CONF_ID, CONF_ON_START, CONF_ON_STATE, CONF_TRIGGER_ID
AUTO_LOAD = ["esp32_ble_server", "improv_base"]
CODEOWNERS = ["@jesserockz"]
@@ -15,7 +15,6 @@ CONF_BLE_SERVER_ID = "ble_server_id"
CONF_IDENTIFY_DURATION = "identify_duration"
CONF_ON_PROVISIONED = "on_provisioned"
CONF_ON_PROVISIONING = "on_provisioning"
CONF_ON_START = "on_start"
CONF_ON_STOP = "on_stop"
CONF_STATUS_INDICATOR = "status_indicator"
CONF_WIFI_TIMEOUT = "wifi_timeout"

View File

@@ -98,7 +98,7 @@ void ESP32RMTLEDStripLightOutput::setup() {
channel.trans_queue_depth = 1;
channel.flags.io_loop_back = 0;
channel.flags.io_od_mode = 0;
channel.flags.invert_out = 0;
channel.flags.invert_out = this->invert_out_;
channel.flags.with_dma = this->use_dma_;
channel.intr_priority = 0;
if (rmt_new_tx_channel(&channel, &this->channel_) != ESP_OK) {

View File

@@ -49,6 +49,7 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight {
}
void set_pin(uint8_t pin) { this->pin_ = pin; }
void set_inverted(bool inverted) { this->invert_out_ = inverted; }
void set_num_leds(uint16_t num_leds) { this->num_leds_ = num_leds; }
void set_is_rgbw(bool is_rgbw) { this->is_rgbw_ = is_rgbw; }
void set_is_wrgb(bool is_wrgb) { this->is_wrgb_ = is_wrgb; }
@@ -93,6 +94,7 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight {
bool is_wrgb_{false};
bool use_dma_{false};
bool use_psram_{false};
bool invert_out_{false};
RGBOrder rgb_order_{ORDER_RGB};

View File

@@ -8,9 +8,11 @@ from esphome.components.const import CONF_USE_PSRAM
import esphome.config_validation as cv
from esphome.const import (
CONF_CHIPSET,
CONF_INVERTED,
CONF_IS_RGBW,
CONF_MAX_REFRESH_RATE,
CONF_NUM_LEDS,
CONF_NUMBER,
CONF_OUTPUT_ID,
CONF_PIN,
CONF_RGB_ORDER,
@@ -71,7 +73,7 @@ CONFIG_SCHEMA = cv.All(
light.ADDRESSABLE_LIGHT_SCHEMA.extend(
{
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(ESP32RMTLEDStripLightOutput),
cv.Required(CONF_PIN): pins.internal_gpio_output_pin_number,
cv.Required(CONF_PIN): pins.internal_gpio_output_pin_schema,
cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
cv.Required(CONF_RGB_ORDER): cv.enum(RGB_ORDERS, upper=True),
cv.SplitDefault(
@@ -132,7 +134,9 @@ async def to_code(config):
await cg.register_component(var, config)
cg.add(var.set_num_leds(config[CONF_NUM_LEDS]))
cg.add(var.set_pin(config[CONF_PIN]))
cg.add(var.set_pin(config[CONF_PIN][CONF_NUMBER]))
if config[CONF_PIN][CONF_INVERTED]:
cg.add(var.set_inverted(True))
if CONF_MAX_REFRESH_RATE in config:
cg.add(var.set_max_refresh_rate(config[CONF_MAX_REFRESH_RATE]))

View File

@@ -18,9 +18,11 @@ void ESP8266PWM::setup() {
this->turn_off();
}
void ESP8266PWM::dump_config() {
ESP_LOGCONFIG(TAG, "ESP8266 PWM:");
ESP_LOGCONFIG(TAG,
"ESP8266 PWM:\n"
" Frequency: %.1f Hz",
this->frequency_);
LOG_PIN(" Pin: ", this->pin_);
ESP_LOGCONFIG(TAG, " Frequency: %.1f Hz", this->frequency_);
LOG_FLOAT_OUTPUT(this);
}
void HOT ESP8266PWM::write_state(float state) {

View File

@@ -21,9 +21,11 @@ void EspLdo::setup() {
}
}
void EspLdo::dump_config() {
ESP_LOGCONFIG(TAG, "ESP LDO Channel %d:", this->channel_);
ESP_LOGCONFIG(TAG, " Voltage: %fV", this->voltage_);
ESP_LOGCONFIG(TAG, " Adjustable: %s", YESNO(this->adjustable_));
ESP_LOGCONFIG(TAG,
"ESP LDO Channel %d:\n"
" Voltage: %fV\n"
" Adjustable: %s",
this->channel_, this->voltage_, YESNO(this->adjustable_));
}
void EspLdo::adjust_voltage(float voltage) {

View File

@@ -4,7 +4,6 @@
#include "esphome/components/sha256/sha256.h"
#endif
#include "esphome/components/network/util.h"
#include "esphome/components/socket/socket.h"
#include "esphome/components/ota/ota_backend.h"
#include "esphome/components/ota/ota_backend_esp8266.h"
#include "esphome/components/ota/ota_backend_arduino_libretiny.h"
@@ -444,9 +443,7 @@ void ESPHomeOTAComponent::log_socket_error_(const LogString *msg) {
void ESPHomeOTAComponent::log_read_error_(const LogString *what) { ESP_LOGW(TAG, "Read %s failed", LOG_STR_ARG(what)); }
void ESPHomeOTAComponent::log_start_(const LogString *phase) {
char peername[socket::PEERNAME_MAX_LEN];
this->client_->getpeername_to(peername);
ESP_LOGD(TAG, "Starting %s from %s", LOG_STR_ARG(phase), peername);
ESP_LOGD(TAG, "Starting %s from %s", LOG_STR_ARG(phase), this->client_->getpeername().c_str());
}
void ESPHomeOTAComponent::log_remote_closed_(const LogString *during) {

View File

@@ -64,18 +64,6 @@ static const LogString *espnow_error_to_str(esp_err_t error) {
}
}
std::string peer_str(uint8_t *peer) {
if (peer == nullptr || peer[0] == 0) {
return "[Not Set]";
} else if (memcmp(peer, ESPNOW_BROADCAST_ADDR, ESP_NOW_ETH_ALEN) == 0) {
return "[Broadcast]";
} else if (memcmp(peer, ESPNOW_MULTICAST_ADDR, ESP_NOW_ETH_ALEN) == 0) {
return "[Multicast]";
} else {
return format_mac_address_pretty(peer);
}
}
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
void on_send_report(const esp_now_send_info_t *info, esp_now_send_status_t status)
#else
@@ -140,11 +128,13 @@ void ESPNowComponent::dump_config() {
ESP_LOGCONFIG(TAG, " Disabled");
return;
}
char own_addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
format_mac_addr_upper(this->own_address_, own_addr_buf);
ESP_LOGCONFIG(TAG,
" Own address: %s\n"
" Version: v%" PRIu32 "\n"
" Wi-Fi channel: %d",
format_mac_address_pretty(this->own_address_).c_str(), version, this->wifi_channel_);
own_addr_buf, version, this->wifi_channel_);
#ifdef USE_WIFI
ESP_LOGCONFIG(TAG, " Wi-Fi enabled: %s", YESNO(this->is_wifi_enabled()));
#endif
@@ -300,9 +290,12 @@ void ESPNowComponent::loop() {
// Intentionally left as if instead of else in case the peer is added above
if (esp_now_is_peer_exist(info.src_addr)) {
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
char src_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
char dst_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
char hex_buf[format_hex_pretty_size(ESP_NOW_MAX_DATA_LEN)];
ESP_LOGV(TAG, "<<< [%s -> %s] %s", format_mac_address_pretty(info.src_addr).c_str(),
format_mac_address_pretty(info.des_addr).c_str(),
format_mac_addr_upper(info.src_addr, src_buf);
format_mac_addr_upper(info.des_addr, dst_buf);
ESP_LOGV(TAG, "<<< [%s -> %s] %s", src_buf, dst_buf,
format_hex_pretty_to(hex_buf, packet->packet_.receive.data, packet->packet_.receive.size));
#endif
if (memcmp(info.des_addr, ESPNOW_BROADCAST_ADDR, ESP_NOW_ETH_ALEN) == 0) {
@@ -321,8 +314,9 @@ void ESPNowComponent::loop() {
}
case ESPNowPacket::SENT: {
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
ESP_LOGV(TAG, ">>> [%s] %s", format_mac_address_pretty(packet->packet_.sent.address).c_str(),
LOG_STR_ARG(espnow_error_to_str(packet->packet_.sent.status)));
char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
format_mac_addr_upper(packet->packet_.sent.address, addr_buf);
ESP_LOGV(TAG, ">>> [%s] %s", addr_buf, LOG_STR_ARG(espnow_error_to_str(packet->packet_.sent.status)));
#endif
if (this->current_send_packet_ != nullptr) {
this->current_send_packet_->callback_(packet->packet_.sent.status);
@@ -409,8 +403,9 @@ void ESPNowComponent::send_() {
this->current_send_packet_ = packet;
esp_err_t err = esp_now_send(packet->address_, packet->data_, packet->size_);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to send packet to %s - %s", format_mac_address_pretty(packet->address_).c_str(),
LOG_STR_ARG(espnow_error_to_str(err)));
char addr_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
format_mac_addr_upper(packet->address_, addr_buf);
ESP_LOGE(TAG, "Failed to send packet to %s - %s", addr_buf, LOG_STR_ARG(espnow_error_to_str(err)));
if (packet->callback_ != nullptr) {
packet->callback_(err);
}
@@ -439,8 +434,9 @@ esp_err_t ESPNowComponent::add_peer(const uint8_t *peer) {
esp_err_t err = esp_now_add_peer(&peer_info);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to add peer %s - %s", format_mac_address_pretty(peer).c_str(),
LOG_STR_ARG(espnow_error_to_str(err)));
char peer_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
format_mac_addr_upper(peer, peer_buf);
ESP_LOGE(TAG, "Failed to add peer %s - %s", peer_buf, LOG_STR_ARG(espnow_error_to_str(err)));
this->status_momentary_warning("peer-add-failed");
return err;
}
@@ -468,8 +464,9 @@ esp_err_t ESPNowComponent::del_peer(const uint8_t *peer) {
if (esp_now_is_peer_exist(peer)) {
esp_err_t err = esp_now_del_peer(peer);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to delete peer %s - %s", format_mac_address_pretty(peer).c_str(),
LOG_STR_ARG(espnow_error_to_str(err)));
char peer_buf[MAC_ADDRESS_PRETTY_BUFFER_SIZE];
format_mac_addr_upper(peer, peer_buf);
ESP_LOGE(TAG, "Failed to delete peer %s - %s", peer_buf, LOG_STR_ARG(espnow_error_to_str(err)));
this->status_momentary_warning("peer-del-failed");
return err;
}

View File

@@ -21,9 +21,11 @@ void ESPNowTransport::setup() {
return;
}
ESP_LOGI(TAG, "Registering ESP-NOW handlers");
ESP_LOGI(TAG, "Peer address: %02X:%02X:%02X:%02X:%02X:%02X", this->peer_address_[0], this->peer_address_[1],
this->peer_address_[2], this->peer_address_[3], this->peer_address_[4], this->peer_address_[5]);
ESP_LOGI(TAG,
"Registering ESP-NOW handlers\n"
"Peer address: %02X:%02X:%02X:%02X:%02X:%02X",
this->peer_address_[0], this->peer_address_[1], this->peer_address_[2], this->peer_address_[3],
this->peer_address_[4], this->peer_address_[5]);
// Register received handler
this->parent_->register_received_handler(this);

View File

@@ -779,8 +779,8 @@ void EthernetComponent::ksz8081_set_clock_reference_(esp_eth_mac_t *mac) {
ESPHL_ERROR_CHECK(err, "Read PHY Control 2 failed");
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
char hex_buf[format_hex_pretty_size(PHY_REG_SIZE)];
ESP_LOGVV(TAG, "KSZ8081 PHY Control 2: %s", format_hex_pretty_to(hex_buf, (uint8_t *) &phy_control_2, PHY_REG_SIZE));
#endif
ESP_LOGVV(TAG, "KSZ8081 PHY Control 2: %s", format_hex_pretty_to(hex_buf, (uint8_t *) &phy_control_2, PHY_REG_SIZE));
/*
* Bit 7 is `RMII Reference Clock Select`. Default is `0`.
@@ -797,10 +797,8 @@ void EthernetComponent::ksz8081_set_clock_reference_(esp_eth_mac_t *mac) {
ESPHL_ERROR_CHECK(err, "Write PHY Control 2 failed");
err = mac->read_phy_reg(mac, this->phy_addr_, KSZ80XX_PC2R_REG_ADDR, &(phy_control_2));
ESPHL_ERROR_CHECK(err, "Read PHY Control 2 failed");
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
ESP_LOGVV(TAG, "KSZ8081 PHY Control 2: %s",
format_hex_pretty_to(hex_buf, (uint8_t *) &phy_control_2, PHY_REG_SIZE));
#endif
}
}
#endif // USE_ETHERNET_KSZ8081
@@ -815,8 +813,10 @@ void EthernetComponent::write_phy_register_(esp_eth_mac_t *mac, PHYRegister regi
ESPHL_ERROR_CHECK(err, "Select PHY Register page failed");
}
ESP_LOGD(TAG, "Writing to PHY Register Address: 0x%02" PRIX32, register_data.address);
ESP_LOGD(TAG, "Writing to PHY Register Value: 0x%04" PRIX32, register_data.value);
ESP_LOGD(TAG,
"Writing to PHY Register Address: 0x%02" PRIX32 "\n"
"Writing to PHY Register Value: 0x%04" PRIX32,
register_data.address, register_data.value);
err = mac->write_phy_reg(mac, this->phy_addr_, register_data.address, register_data.value);
ESPHL_ERROR_CHECK(err, "Writing PHY Register failed");

View File

@@ -148,10 +148,13 @@ void EzoPMP::read_command_result_() {
char current_char = response_buffer[i];
if (current_char == '\0') {
ESP_LOGV(TAG, "Read Response from device: %s", (char *) response_buffer);
ESP_LOGV(TAG, "First Component: %s", (char *) first_parameter_buffer);
ESP_LOGV(TAG, "Second Component: %s", (char *) second_parameter_buffer);
ESP_LOGV(TAG, "Third Component: %s", (char *) third_parameter_buffer);
ESP_LOGV(TAG,
"Read Response from device: %s\n"
"First Component: %s\n"
"Second Component: %s\n"
"Third Component: %s",
(char *) response_buffer, (char *) first_parameter_buffer, (char *) second_parameter_buffer,
(char *) third_parameter_buffer);
break;
}

View File

@@ -30,8 +30,8 @@ static bool was_power_cycled() {
void FactoryResetComponent::dump_config() {
uint8_t count = 0;
this->flash_.load(&count);
ESP_LOGCONFIG(TAG, "Factory Reset by Reset:");
ESP_LOGCONFIG(TAG,
"Factory Reset by Reset:\n"
" Max interval between resets: %u seconds\n"
" Current count: %u\n"
" Factory reset after %u resets",

View File

@@ -71,7 +71,7 @@ void FanCall::validate_() {
auto traits = this->parent_.get_traits();
if (this->speed_.has_value()) {
this->speed_ = clamp(*this->speed_, 1, static_cast<int>(traits.supported_speed_count()));
this->speed_ = clamp(*this->speed_, 1, traits.supported_speed_count());
// https://developers.home-assistant.io/docs/core/entity/fan/#preset-modes
// "Manually setting a speed must disable any set preset mode"
@@ -179,8 +179,10 @@ void Fan::add_on_state_callback(std::function<void()> &&callback) { this->state_
void Fan::publish_state() {
auto traits = this->get_traits();
ESP_LOGD(TAG, "'%s' - Sending state:", this->name_.c_str());
ESP_LOGD(TAG, " State: %s", ONOFF(this->state));
ESP_LOGD(TAG,
"'%s' - Sending state:\n"
" State: %s",
this->name_.c_str(), ONOFF(this->state));
if (traits.supports_speed()) {
ESP_LOGD(TAG, " Speed: %d", this->speed);
}

View File

@@ -11,7 +11,7 @@ namespace fan {
class FanTraits {
public:
FanTraits() = default;
FanTraits(bool oscillation, bool speed, bool direction, uint8_t speed_count)
FanTraits(bool oscillation, bool speed, bool direction, int speed_count)
: oscillation_(oscillation), speed_(speed), direction_(direction), speed_count_(speed_count) {}
/// Return if this fan supports oscillation.
@@ -23,9 +23,9 @@ class FanTraits {
/// Set whether this fan supports speed levels.
void set_speed(bool speed) { this->speed_ = speed; }
/// Return how many speed levels the fan has
uint8_t supported_speed_count() const { return this->speed_count_; }
int supported_speed_count() const { return this->speed_count_; }
/// Set how many speed levels this fan has.
void set_supported_speed_count(uint8_t speed_count) { this->speed_count_ = speed_count; }
void set_supported_speed_count(int speed_count) { this->speed_count_ = speed_count; }
/// Return if this fan supports changing direction
bool supports_direction() const { return this->direction_; }
/// Set whether this fan supports changing direction
@@ -64,7 +64,7 @@ class FanTraits {
bool oscillation_{false};
bool speed_{false};
bool direction_{false};
uint8_t speed_count_{};
int speed_count_{};
std::vector<const char *> preset_modes_{};
};

View File

@@ -95,11 +95,13 @@ void GCJA5Component::parse_data_() {
if (!this->first_status_log_) {
this->first_status_log_ = true;
ESP_LOGI(TAG, "GCJA5 Status");
ESP_LOGI(TAG, "Overall Status : %i", (status >> 6) & 0x03);
ESP_LOGI(TAG, "PD Status : %i", (status >> 4) & 0x03);
ESP_LOGI(TAG, "LD Status : %i", (status >> 2) & 0x03);
ESP_LOGI(TAG, "Fan Status : %i", (status >> 0) & 0x03);
ESP_LOGI(TAG,
"GCJA5 Status\n"
"Overall Status : %i\n"
"PD Status : %i\n"
"LD Status : %i\n"
"Fan Status : %i",
(status >> 6) & 0x03, (status >> 4) & 0x03, (status >> 2) & 0x03, (status >> 0) & 0x03);
}
}

View File

@@ -27,8 +27,10 @@ void GLR01I2CComponent::setup() {
}
void GLR01I2CComponent::dump_config() {
ESP_LOGCONFIG(TAG, "GL-R01 I2C:");
ESP_LOGCONFIG(TAG, " Firmware Version: 0x%04X", this->version_);
ESP_LOGCONFIG(TAG,
"GL-R01 I2C:\n"
" Firmware Version: 0x%04X",
this->version_);
LOG_I2C_DEVICE(this);
LOG_SENSOR(" ", "Distance", this);
}

View File

@@ -11,6 +11,7 @@ from esphome.const import (
CONF_SPEED,
DEVICE_CLASS_SPEED,
STATE_CLASS_MEASUREMENT,
STATE_CLASS_MEASUREMENT_ANGLE,
UNIT_DEGREES,
UNIT_KILOMETER_PER_HOUR,
UNIT_METER,
@@ -21,6 +22,7 @@ CONF_HDOP = "hdop"
ICON_ALTIMETER = "mdi:altimeter"
ICON_COMPASS = "mdi:compass"
ICON_CIRCLE_DOUBLE = "mdi:circle-double"
ICON_LATITUDE = "mdi:latitude"
ICON_LONGITUDE = "mdi:longitude"
ICON_SATELLITE = "mdi:satellite-variant"
@@ -50,7 +52,7 @@ CONFIG_SCHEMA = cv.All(
unit_of_measurement=UNIT_DEGREES,
icon=ICON_LONGITUDE,
accuracy_decimals=6,
state_class=STATE_CLASS_MEASUREMENT,
state_class=STATE_CLASS_MEASUREMENT_ANGLE,
),
cv.Optional(CONF_SPEED): sensor.sensor_schema(
unit_of_measurement=UNIT_KILOMETER_PER_HOUR,
@@ -63,7 +65,7 @@ CONFIG_SCHEMA = cv.All(
unit_of_measurement=UNIT_DEGREES,
icon=ICON_COMPASS,
accuracy_decimals=2,
state_class=STATE_CLASS_MEASUREMENT,
state_class=STATE_CLASS_MEASUREMENT_ANGLE,
),
cv.Optional(CONF_ALTITUDE): sensor.sensor_schema(
unit_of_measurement=UNIT_METER,
@@ -72,11 +74,14 @@ CONFIG_SCHEMA = cv.All(
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_SATELLITES): sensor.sensor_schema(
# no unit_of_measurement
icon=ICON_SATELLITE,
accuracy_decimals=0,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_HDOP): sensor.sensor_schema(
# no unit_of_measurement
icon=ICON_CIRCLE_DOUBLE,
accuracy_decimals=3,
state_class=STATE_CLASS_MEASUREMENT,
),

View File

@@ -39,7 +39,7 @@ CONFIG_SCHEMA = (
cv.Optional(CONF_DECAY_MODE, default="SLOW"): cv.enum(
DECAY_MODE_OPTIONS, upper=True
),
cv.Optional(CONF_SPEED_COUNT, default=100): cv.int_range(min=1, max=255),
cv.Optional(CONF_SPEED_COUNT, default=100): cv.int_range(min=1),
cv.Optional(CONF_ENABLE_PIN): cv.use_id(output.FloatOutput),
cv.Optional(CONF_PRESET_MODES): validate_preset_modes,
}

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