1
0
mirror of https://github.com/esphome/esphome.git synced 2025-11-03 16:41:50 +00:00

Compare commits

...

459 Commits

Author SHA1 Message Date
J. Nick Koston
fcdf209ac3 wip 2025-05-08 18:45:57 -05:00
J. Nick Koston
4c1111a395 devug 2025-05-08 17:20:06 -05:00
J. Nick Koston
57425a765b devug 2025-05-08 17:18:43 -05:00
J. Nick Koston
abb09b7fee wip 2025-05-08 16:43:00 -05:00
J. Nick Koston
b69fd2762e wip 2025-05-08 16:42:26 -05:00
J. Nick Koston
a38f0067ae debug 2025-05-08 16:10:52 -05:00
J. Nick Koston
00e128bdd5 Fix heap tracing function scope issues
- Add extern \C\ linkage to heap tracing functions
- Forward declare the functions in the API server implementation
- Ensures the heap tracing functions are accessible from any namespace
2025-05-08 15:29:11 -05:00
J. Nick Koston
35238c1437 Add heap tracing capability to API component
- Add heap tracing configuration options to API component
- Implement periodic heap trace dumping (every 30 seconds)
- Configure ESP-IDF settings for heap tracing
- Add sample YAML configuration
- Useful for debugging memory reallocation overhead issues
2025-05-08 15:25:06 -05:00
Samuel Sieb
8e29437900 [key_collector] enable/disable (#8718)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2025-05-08 20:26:10 +12:00
J. Nick Koston
9e64e71cdf Require reserve_size in create_buffer to reduce realloc overhead (#8715)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-08 05:50:20 +00:00
J. Nick Koston
ef2621aa54 Reserve space in the frame helper when we know in advance how much we need (#8716) 2025-05-08 17:43:39 +12:00
J. Nick Koston
882273cb56 Avoid Reallocation When Sending Logging Messages (#8714) 2025-05-08 04:19:53 +00:00
J. Nick Koston
ad2b74d9b4 Correct Protobuf Wire Type for encode_fixed64 (#8713) 2025-05-08 16:01:10 +12:00
J. Nick Koston
26669bd1b6 Preallocate Buffer Space for ESP32-CAM (#8712) 2025-05-08 16:00:34 +12:00
J. Nick Koston
54ead9a6b4 Reserve buffer space to avoid frequent realloc when generating protobuf messages (#8707) 2025-05-07 21:56:54 -05:00
Clyde Stubbs
d60e1f02c0 [packet_transport] Make some arguments const (#8700)
Co-authored-by: clydeps <U5yx99dok9>
2025-05-08 10:22:56 +12:00
dependabot[bot]
213648564c Bump yamllint from 1.37.0 to 1.37.1 (#8705)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-05-08 10:19:23 +12:00
dependabot[bot]
8bdbde9732 Bump pylint from 3.3.6 to 3.3.7 (#8706)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-08 07:50:13 +12:00
Kevin Ahrendt
e988762576 [i2s_audio, mixer, resampler, speaker] Simplify duration played callback (#8703) 2025-05-06 23:42:59 -05:00
Jesse Hills
75496849eb [mics_4514] Add default device class to CO sensor (#8710) 2025-05-06 18:57:18 -05:00
Kevin Ahrendt
39b119e9cc [micro_wake_word] Experimental cutoff adjustments and uses mic sample rate (#8702) 2025-05-06 16:48:56 -05:00
dependabot[bot]
4d43caf6c1 Bump aioesphomeapi from 30.0.1 to 30.1.0 (#8652)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-06 09:41:14 -05:00
dependabot[bot]
ce5e1a6294 Bump setuptools from 79.0.1 to 80.3.1 (#8696)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-06 09:40:58 -05:00
Kevin Ahrendt
88be14aaa3 [audio, microphone] Quantization Improvements (#8695) 2025-05-06 09:23:50 +12:00
Clyde Stubbs
1ac56b06c5 [arduino] Always include Arduino.h for Arduino (#8693) 2025-05-05 08:25:24 +00:00
Edward Firmo
8bbc509b0b [nextion] Adds a command pacer with command_spacing attribute (#7948)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-05-05 20:08:16 +12:00
Clyde Stubbs
6f35d0ac88 [cst226] Add support for cst226 binary sensor (#8381) 2025-05-05 19:56:30 +12:00
Clyde Stubbs
3b8a5db97c [syslog] Implement logging via syslog (#8637)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-05-05 16:48:13 +12:00
Clyde Stubbs
b8d83d0765 [debug] Show source of last software reboot (#8595) 2025-05-04 23:31:37 -05:00
Clyde Stubbs
e7a2b395fd [uart] Add packet_transport platform (#8214)
Co-authored-by: Faidon Liambotis <paravoid@debian.org>
Co-authored-by: clydeps <U5yx99dok9>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-05-05 16:15:46 +12:00
Clyde Stubbs
ad99d7fb45 [image] Support the other Pictogrammers icon sets memory: and mdil: (#8676) 2025-05-05 15:31:16 +12:00
Clyde Stubbs
0b032e5c19 [lvgl] Add refresh action to re-evaluate initial widget properties (#8675) 2025-05-05 15:26:16 +12:00
Clyde Stubbs
c7523ace78 [lvgl] Fix image property processing (#8691) 2025-05-05 12:31:22 +12:00
Clyde Stubbs
2a6827e1d2 [lvgl] Allow padding to be negative (#8671) 2025-05-05 12:30:11 +12:00
Clyde Stubbs
125aff79ec [as3935_i2c] Remove redundant includes (#8677) 2025-05-05 12:28:00 +12:00
Clyde Stubbs
a31d8ec309 [packages] Allow list instead of dict for packages (#8688) 2025-05-05 12:26:59 +12:00
Clyde Stubbs
3ed03edfec [display] Fix Rect::inside (#8679) 2025-05-05 12:04:33 +12:00
Clyde Stubbs
4dc6cbe2d7 [esp32_ble_server] Add appearance advertising field (#8672) 2025-05-05 12:02:33 +12:00
Clyde Stubbs
524cd4b4e3 [packet_transport] Extract packet encoding functionality (#8187) 2025-05-05 09:29:17 +12:00
Thomas Rupprecht
84ebbf0762 [climate_ir_lg] use this-> (#8687) 2025-05-05 09:21:57 +12:00
Thomas Rupprecht
670ad7192c unify lowercase x in hexadecimal values (#8686) 2025-05-05 08:47:57 +12:00
Pat Satyshur
bc6ee20270 Add CONF_CONTINUOUS to const.py (#8682) 2025-05-03 22:44:54 -05:00
Thomas Rupprecht
e869a3aec3 [climate] Fix typo and use `this->` (#8678) 2025-05-03 22:41:52 -05:00
Jani
8aff6d2fdd Add GDEY0583T81 support (#8668) 2025-05-03 13:02:35 +10:00
Michał Obrembski
8d33c6de36 Added Banking support to tca9555, fixed input bug (#8003)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-05-03 10:54:27 +12:00
DJTerentjev
f4b5f32cb4 Update const.py (#8665) 2025-05-01 20:43:58 -05:00
Kevin Ahrendt
2eb9582d0f [micro_wake_word] Clarify spectrogram features calculation (#8669) 2025-05-01 14:04:23 -05:00
Kevin Ahrendt
db97440b04 [microphone] Add software mute and fix wrong type for automations (#8667) 2025-05-01 14:02:33 -05:00
Kevin Ahrendt
ced7ae1d7a [debug] add missing header (#8666) 2025-05-01 08:50:32 -04:00
Trent Houliston
d6699fa3c0 Check for missed pulse_meter ISRs in the main loop (#6126) 2025-05-01 12:29:12 +00:00
functionpointer
836e5ffa43 [mlx90393] Add verification for register contents (#8279)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-05-02 00:01:02 +12:00
Kevin Ahrendt
c7f597bc75 [voice_assistant] voice assistant can configure enabled wake words (#8657) 2025-05-01 11:11:09 +00:00
Clyde Stubbs
e215fafebe [esp32, debug] Add `cpu_frequency` config option and debug sensor (#8542) 2025-05-01 03:28:07 -05:00
Ralf Habacker
da9c755f67 Add to_ntc_resistance|temperature sensor filter (esphome/feature-requests#2967) (#7898)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
2025-05-01 07:53:12 +00:00
Clyde Stubbs
087ff865a7 [binary_sensor] initial state refactor (#8648)
Co-authored-by: Zsombor Welker <flaktack@welker.hu>
2025-05-01 15:58:35 +12:00
scaiper
8cd62c0308 support self-signed cert in mqtt (#8650) 2025-05-01 15:57:52 +12:00
rwrozelle
f5241ff777 Fix CONFIG_LWIP_TCP_RCV_SCALE and CONFIG_TCP_WND_DEFAULT (#8425) 2025-05-01 15:55:30 +12:00
Clyde Stubbs
1aa2b79311 [i2c] Allow buffers in PSRAM (#8640) 2025-05-01 03:54:56 +00:00
Benjamin Pearce
2dca2d5f85 Daikin IR Climate Remote Target Temperature and Fan Modes (#7946)
Co-authored-by: Benjamin Pearce <gitlab@bcpearce.com>
2025-05-01 15:52:51 +12:00
lastradanet
f03b42ced5 Adding timing budget support for vl53l0x (#7991)
Co-authored-by: Brian Davis <bdavis@mimecast.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-05-01 15:17:27 +12:00
Jesse Hills
0f8a0af244 [defines] Fix USE_MICRO_WAKE_WORD position (#8663) 2025-04-30 21:32:23 -05:00
Keith Burzinski
62646f5f32 [remote_base] Fix compile error on IDF (#8664) 2025-04-30 21:30:36 -05:00
uae007
71f81d2f18 Component pca9685 - phase_begin always set to zero (#8379)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
2025-05-01 02:27:59 +00:00
nworbneb
4ec8414050 [alarm_control_panel] Allow sensor to trigger when alarm disarmed (#7746) 2025-05-01 14:27:14 +12:00
Anton Sergunov
807925fd38 Fix second scrolling run ussue (#8347) 2025-05-01 14:03:35 +12:00
Pat Satyshur
b597565165 Add a function to return the I2C address from an I2CDevice object (#8454)
Co-authored-by: Djordje Mandic <6750655+DjordjeMandic@users.noreply.github.com>
2025-05-01 13:14:29 +12:00
Jannik
9a9b91b180 Fix HLW8012 sensor not returning values if change_mode_every is set to never (#8456) 2025-05-01 13:12:51 +12:00
Simon
9dcf295df8 [gree] Add support for YAG remotes (#7418) 2025-05-01 13:12:17 +12:00
Andrew J.Swan
e8a3de2642 Bump FastLed version to 3.9.16 (#8402) 2025-05-01 13:07:55 +12:00
Ben Winslow
d2b4dba51f Fix typo preventing tt21100 from autosetting the touchscreen res. (#8662) 2025-05-01 12:55:36 +12:00
Kevin Ahrendt
bf527b0331 [microphone] Bugfix: protect against starting mic if already started (#8656) 2025-05-01 12:45:33 +12:00
Kevin Ahrendt
cdc77506de [micro_wake_word] add new VPE features (#8655) 2025-05-01 12:22:48 +12:00
Stanislav Meduna
6de6a0c82c Only warn if the component blocked for a longer time than the last time (#8064) 2025-05-01 11:57:01 +12:00
Kevin Ahrendt
20062576a3 [i2s_audio] Move microphone reads into a task (#8651)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-04-30 21:50:56 +12:00
Clyde Stubbs
07ba9fdf8f [canbus] Add callback for use by other components (#8578)
Co-authored-by: clydeps <U5yx99dok9>
2025-04-30 21:10:54 +12:00
Jesse Hills
caa255f5d1 [media_player] Fix actions with id as value (#8654) 2025-04-30 20:08:46 +12:00
StriboYar
c0be2c14f3 [debug] Fix compile errors when using the ESP32-C2 (#7474)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-04-30 06:15:56 +00:00
Kevin Ahrendt
9f629dcaa2 [i2s_audio, microphone, micro_wake_word, voice_assistant] Use microphone source to process incoming audio (#8645)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-04-30 10:27:03 +12:00
Thomas Rupprecht
0fe6c65ba3 [adc] sort variants and add links to reference implementations (#8327) 2025-04-29 15:08:08 -05:00
Thomas Rupprecht
c756bb3b3e [pmsa003i] code improvements (#8485) 2025-04-29 14:29:04 -05:00
Jesse Hills
ecb91b0101 [bluetooth_proxy] Allow changing active/passive via api (#8649) 2025-04-29 12:43:55 +00:00
cvwillegen
5f9a509bdc Add code to send/receive GoBox infrared control messages. (#7554)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-04-29 20:21:05 +12:00
Jesse Hills
dc6dd9fe0d Merge branch 'release' into dev 2025-04-29 14:21:09 +12:00
Jesse Hills
5baa034d0d Merge pull request #8647 from esphome/bump-2025.4.1
2025.4.1
2025-04-29 14:20:26 +12:00
Thomas Rupprecht
b8ba26787e [pmsx003] Refactor Imports, Extract Constants, Improve Data Handling & Logging (#8344) 2025-04-28 19:24:48 -05:00
Kevin Ahrendt
844569e96b [audio, microphone] Add MicrophoneSource helper class (#8641)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-04-29 00:05:07 +00:00
Jesse Hills
43580739ac Ensure new const file stays in order (#8642) 2025-04-28 18:58:13 -05:00
aanban
c9f7ab6948 add beo4_protocol to remote_base component (#8307) 2025-04-29 11:50:40 +12:00
Jesse Hills
7900660bb8 Bump version to 2025.4.1 2025-04-29 11:46:20 +12:00
Steffen Banhardt
f096567ac7 Update ens160_base.cpp – fix wrong double negative (#8639) 2025-04-29 11:46:19 +12:00
Clyde Stubbs
5bfb5ccc34 [core] Fix setting of log level/verbose (#8600) 2025-04-29 11:46:19 +12:00
Jesse Hills
1c60038111 [watchdog] Fix for variants with single core (#8602) 2025-04-29 11:46:19 +12:00
Clyde Stubbs
b940db6549 [online_image] Fix printf format; comment fixes (#8607) 2025-04-29 11:46:19 +12:00
J. Nick Koston
aa6e172e14 Fix BLE connection loop caused by timeout and pending disconnect race (#8597) 2025-04-29 11:46:19 +12:00
Clyde Stubbs
86033b6612 [lvgl] Ensure pages are created on the correct display (#8596) 2025-04-29 11:46:19 +12:00
Jesse Hills
59b4a1f554 Fix psram below idf 5 (#8584) 2025-04-29 11:46:19 +12:00
Jesse Hills
b5bdfb3089 [http_request] Fix request headers (#8644)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-04-29 11:45:41 +12:00
Jesse Hills
a31a5e74bd [const] Move CONF_GAIN_FACTOR to const.py (#8646) 2025-04-29 11:35:38 +12:00
Jesse Hills
629481a526 [esp32_ble] Remove explicit and now incorrect ble override for esp32-c6 (#8643) 2025-04-29 10:46:39 +12:00
Steffen Banhardt
3291a11824 Update ens160_base.cpp – fix wrong double negative (#8639) 2025-04-29 07:18:46 +12:00
baal86
d2ee2d3b23 Fix support for ESP32-H2 in deep_sleep (#8290) 2025-04-28 00:21:24 -05:00
Nate Clark
253e3ec6f6 [mdns] Support templatable config options for MDNS extra services (#8606) 2025-04-28 16:27:39 +12:00
Ben Winslow
fdc4ec8a57 [touchscreen] Clear interrupt flag before reading touch data. (#8632) 2025-04-28 14:29:47 +12:00
Lucas Hartmann
1da0dff8b1 Take advantage of clipping to speed image drawing. (#8630) 2025-04-28 14:18:47 +12:00
Clyde Stubbs
38dae8489e [http_request] Implement for host platform (#8040) 2025-04-28 13:45:28 +12:00
Clyde Stubbs
22c0e1079e [const] Create component-level const repository (#8385) 2025-04-28 12:23:18 +12:00
Clyde Stubbs
2d3f141140 [core] Fix setting of log level/verbose (#8600) 2025-04-28 12:19:50 +12:00
Kevin Ahrendt
e49252ca3d [voice_assistant] Use mic callback and remove esp_adf code (#8627)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-04-28 00:15:28 +00:00
Kevin Ahrendt
c9d1476ae0 [voice_assisant] support start/continue conversation and deallocate buffers (#8610) 2025-04-28 11:30:21 +12:00
Kevin Ahrendt
ee646d7324 [micro_wake_word] Use microphone callback and avoid unnecessary allocation attempts (#8626) 2025-04-28 11:23:25 +12:00
Kevin Ahrendt
e557bca420 [i2s_audio] Microphone reads in loop for callbacks shouldn't ever delay (#8625) 2025-04-28 11:19:01 +12:00
Jesse Hills
adcd6517db [docker] Use new base container image (#8582) 2025-04-28 11:14:50 +12:00
Thomas Rupprecht
4c8f5275f9 replace http with https (#8628) 2025-04-25 14:47:45 -05:00
dependabot[bot]
526db0102c Bump actions/setup-python from 5.5.0 to 5.6.0 in /.github/actions/restore-python (#8616) 2025-04-24 16:18:33 -10:00
dependabot[bot]
8a3fe9ce4c Bump actions/setup-python from 5.5.0 to 5.6.0 (#8618) 2025-04-24 16:18:13 -10:00
dependabot[bot]
fb97ef33a8 Bump setuptools from 78.1.0 to 79.0.1 (#8614)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-24 16:17:39 -10:00
dependabot[bot]
805a6d85a5 Bump ruff from 0.11.6 to 0.11.7 (#8615)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-24 16:12:13 -10:00
dependabot[bot]
8f9fbb15b8 Bump docker/build-push-action from 6.15.0 to 6.16.0 in /.github/actions/build-image (#8619)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-24 15:31:50 -10:00
Guillermo Ruffino
3d24dea455 fix schema-gen-ci failures (#8621) 2025-04-24 15:30:22 -10:00
dependabot[bot]
666d5374ea Bump actions/download-artifact from 4.2.1 to 4.3.0 (#8617)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-24 13:08:24 -10:00
luar123
6792ff6d58 [i2s_audio, i2s_audio_microphone, i2s_audio_speaker] Add basic support for new esp-idf 5.x.x i2s driver. (#8181) 2025-04-24 15:33:58 -05:00
Guillermo Ruffino
f29ccb9e75 Schema gen action (#8593)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-04-24 15:43:37 +12:00
Jesse Hills
911bd54765 [watchdog] Fix for variants with single core (#8602) 2025-04-23 03:49:33 -05:00
Clyde Stubbs
89b1b12993 [online_image] Fix printf format; comment fixes (#8607) 2025-04-23 03:47:15 -05:00
Djordje Mandic
33d79e03d9 [sht4x] Reduce warn spam, added communication check in setup (#8250)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-04-23 03:45:29 -05:00
Craig Andrews
991f3d3a10 [http_request] Ability to get response headers (#8224)
Co-authored-by: guillempages <guillempages@users.noreply.github.com>
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
2025-04-23 14:30:50 +10:00
J. Nick Koston
97823ddd16 Rewrite BLE scanner to use a state machine (#8601) 2025-04-22 08:09:28 -10:00
Vasil Kalchev
6ff180152a Implement min_power for component ac_dimmer using method trailing (#8472) 2025-04-22 07:39:21 -03:00
bdm310
dbb7cbed3e [lvgl] Fix unexpected widget update behavior (#8260) 2025-04-22 20:13:43 +10:00
J. Nick Koston
fbf00f0af4 Fix BLE connection loop caused by timeout and pending disconnect race (#8597) 2025-04-22 21:17:09 +12:00
Roving Ronin
82c6a40371 Update const.py - Add UNIT_MEGAJOULE = "MJ" (#8594) 2025-04-22 09:13:31 +12:00
Clyde Stubbs
0242ac56df [lvgl] Ensure pages are created on the correct display (#8596) 2025-04-22 08:51:52 +12:00
J. Nick Koston
b82666002d Bump esp-idf to 5.1.6 (#8566)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-04-18 05:21:07 +00:00
Jonathan Swoboda
e11883e431 [psram] Add version check to fix 5.3.2 (#8588) 2025-04-17 21:06:41 -05:00
dependabot[bot]
ff5b9df607 Bump ruff from 0.11.5 to 0.11.6 (#8587)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-17 20:24:31 +00:00
Jesse Hills
e5b7e3039a Fix psram below idf 5 (#8584) 2025-04-17 00:04:43 -05:00
Jonathan Swoboda
31ed1eb6f0 [remote_receiver] Filtering fixes (#7777)
Co-authored-by: Jonathan Swoboda <jonathan.swoboda>
2025-04-16 23:33:46 -05:00
Michael Giacomelli
0c3daab649 Remove duplicate co2 and pressure constants (#8583) 2025-04-17 13:40:33 +12:00
dependabot[bot]
816371e3e9 Bump aioesphomeapi from 29.10.0 to 30.0.1 (#8579) 2025-04-16 15:24:15 -10:00
David Woodhouse
3c7bb65a23 Sort resolved IP addresses for dashboard (#8536)
Co-authored-by: J. Nick Koston <nick+github@koston.org>
2025-04-17 13:19:55 +12:00
Jonathan Swoboda
4a65fd76b3 [internal_temperature] Add p4 ifdefs (#8484) 2025-04-17 13:19:33 +12:00
Clyde Stubbs
2704db5eef [analog_threshold] Make thresholds templatable (#8452) 2025-04-17 13:19:12 +12:00
Clyde Stubbs
f10bc73d31 [mapping] Implement yaml-configured maps (#8333) 2025-04-17 13:18:48 +12:00
Clyde Stubbs
55e099450c [uptime] Add format config for text_sensor (#8304) 2025-04-17 13:18:08 +12:00
Andrew J.Swan
248dbd32a5 Add CUBIC PM2005/PM2105 Laser Particle Sensor Module (#8292)
Co-authored-by: Djordje <6750655+DjordjeMandic@users.noreply.github.com>
2025-04-17 13:17:35 +12:00
Jonathan Swoboda
a7b676231a [esp32_rmt_led_strip] Add use_dma option (#8270) 2025-04-17 13:16:10 +12:00
Keith Burzinski
2fd5f9ac58 [api] Allow noise encryption key to be set at runtime (#7296)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-04-17 13:15:25 +12:00
Jordan Zucker
ca4838a5f4 [prometheus] Add climate metrics (#8247) 2025-04-17 13:13:24 +12:00
Jesse Hills
1b72550236 Merge branch 'release' into dev 2025-04-17 12:56:09 +12:00
Jesse Hills
71afd49e3e Merge pull request #8581 from esphome/bump-2025.4.0
2025.4.0
2025-04-17 12:55:25 +12:00
Thomas Rupprecht
e5d718d1b1 fix typo USE_ESP32_VARIANT_ESP32H6 (#8580) 2025-04-16 21:23:58 +00:00
Jesse Hills
61f33d6401 Bump version to 2025.4.0 2025-04-17 09:03:08 +12:00
Jesse Hills
af9b568778 Merge branch 'beta' into dev 2025-04-16 12:13:17 +12:00
Jesse Hills
4a1eec567f Merge pull request #8573 from esphome/bump-2025.4.0b3
2025.4.0b3
2025-04-16 12:12:43 +12:00
Jesse Hills
5706b8476f Bump version to 2025.4.0b3 2025-04-16 08:48:38 +12:00
Marius Greuel
8981a86793 SML runtime optimizations (#8571) 2025-04-16 08:48:38 +12:00
Guillermo Ruffino
a7fd6dc382 Fix vscode validation not showing error squiggles (#8500)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-04-16 08:48:38 +12:00
dependabot[bot]
cb0a87c1f9 Bump zeroconf from 0.146.4 to 0.146.5 (#8569)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-16 08:48:38 +12:00
Jonathan Swoboda
b913a0b178 [am2315c] Use warning not fail during update (#8499) 2025-04-16 08:48:38 +12:00
David Woodhouse
214454ff51 Fix '--device MQTT' for devices with static IP (#8535) 2025-04-16 08:48:38 +12:00
J. Nick Koston
3677ef71d1 Add typing to protobuf code generator (#8541) 2025-04-16 08:19:22 +12:00
Marius Greuel
7e133171e0 SML runtime optimizations (#8571) 2025-04-16 07:58:14 +12:00
Guillermo Ruffino
bc56d319b5 Fix vscode validation not showing error squiggles (#8500)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-04-16 07:52:07 +12:00
dependabot[bot]
c423a6fb61 Bump codecov/codecov-action from 5.4.0 to 5.4.2 (#8572)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-15 19:48:51 +00:00
dependabot[bot]
4034bf4f04 Bump zeroconf from 0.146.4 to 0.146.5 (#8569)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-15 07:58:57 +00:00
Jonathan Swoboda
477abc05ae [am2315c] Use warning not fail during update (#8499) 2025-04-15 17:00:44 +12:00
David Woodhouse
ff2b93a3e4 Fix '--device MQTT' for devices with static IP (#8535) 2025-04-15 16:30:07 +12:00
Calum McConnell
a52d6388a9 Use python3 in place of python, as some systems don't emulate the former (#8568) 2025-04-15 04:13:37 +00:00
Clyde Stubbs
6259ca9ded [lvgl] Small buffers in internal RAM (#8523) 2025-04-15 13:10:44 +12:00
Jesse Hills
f6ef50505b Merge branch 'beta' into dev 2025-04-15 12:13:03 +12:00
Jesse Hills
b4cf437761 Merge pull request #8567 from esphome/bump-2025.4.0b2
2025.4.0b2
2025-04-15 12:12:31 +12:00
Jesse Hills
1d9f5f1f1e Bump version to 2025.4.0b2 2025-04-15 09:35:53 +12:00
Jonathan Swoboda
e47489708e Bump esphome-dashboard to 20250415.0 (#8565) 2025-04-15 09:35:53 +12:00
dependabot[bot]
8e1bdcd211 Bump zeroconf from 0.146.3 to 0.146.4 (#8563)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-04-15 09:35:53 +12:00
dependabot[bot]
3432d73584 Bump aioesphomeapi from 29.9.0 to 29.10.0 (#8562)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-15 09:35:53 +12:00
Clyde Stubbs
2bb86641f8 [lvgl] Ensure captured lambdas are in correct order (#8560) 2025-04-15 09:35:53 +12:00
Mischa Siekmann
6ca72a3a26 Speaker-Media-Player: Fix potential deadlock in audio pipeline (#8548) 2025-04-15 09:35:53 +12:00
Clyde Stubbs
c215098cb7 [lvgl] Add restore_value to select and number (#8494) 2025-04-15 09:35:53 +12:00
Clyde Stubbs
566968b6be [lvgl] Fix initial focus on roller (#8547) 2025-04-15 09:35:53 +12:00
Clyde Stubbs
fe51ee6257 [axs15231] Don't overwrite manual dimensions (#8553) 2025-04-15 09:35:53 +12:00
J. Nick Koston
2c499b326a Include MAC address in noise hello (#8551) 2025-04-15 09:35:53 +12:00
Clyde Stubbs
7c4ab7abfe [lvgl] Fix use of image without canvas (Bugfix) (#8540) 2025-04-15 09:35:52 +12:00
Jonathan Swoboda
3c242b7296 Bump esphome-dashboard to 20250415.0 (#8565) 2025-04-14 21:03:43 +00:00
Jesse Hills
00dd5b8339 [ci] Pin codecov action to v5.4.0 (#8564) 2025-04-15 08:09:51 +12:00
dependabot[bot]
a007a8237a Bump zeroconf from 0.146.3 to 0.146.4 (#8563)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-04-15 08:07:29 +12:00
dependabot[bot]
9b86cc37f0 Bump aioesphomeapi from 29.9.0 to 29.10.0 (#8562)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-15 08:06:48 +12:00
Clyde Stubbs
2dfcf950fa [lvgl] Ensure captured lambdas are in correct order (#8560) 2025-04-15 07:39:56 +12:00
Mischa Siekmann
5908b93e82 Speaker-Media-Player: Fix potential deadlock in audio pipeline (#8548) 2025-04-14 13:51:10 -05:00
Norbert Schulz
995db1f961 Add support for Waveshare 5.65" ACeP 7-Color display (#8557) 2025-04-14 12:02:40 +10:00
dependabot[bot]
abcc656a6f Bump ruff from 0.11.4 to 0.11.5 (#8546)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-13 14:41:33 -10:00
tomaszduda23
4a9f323d92 [core] make require_framework_version generic (#8412) 2025-04-14 10:59:40 +12:00
Calum McConnell
34a4e70cc5 Update setup to make .temp directory (#8558) 2025-04-14 10:48:45 +12:00
Clyde Stubbs
fb5d697c22 [lvgl] Add restore_value to select and number (#8494) 2025-04-14 10:45:30 +12:00
Clyde Stubbs
df4642208e [lvgl] Fix initial focus on roller (#8547) 2025-04-14 10:41:42 +12:00
Clyde Stubbs
264e234efc [axs15231] Don't overwrite manual dimensions (#8553) 2025-04-14 10:41:11 +12:00
J. Nick Koston
ca78dd44b5 Include MAC address in noise hello (#8551) 2025-04-12 09:16:14 -10:00
Jonathan Swoboda
7edf458898 [esp32] Allow pioarduino version 5.3.3 and 5.5.0 (#8526) 2025-04-11 21:34:43 -05:00
Clyde Stubbs
d9873e24a7 [lvgl] Fix use of image without canvas (Bugfix) (#8540) 2025-04-10 01:28:44 +00:00
dependabot[bot]
645bd490ba Bump pytest-cov from 6.0.0 to 6.1.1 (#8537)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-09 10:28:22 -10:00
dependabot[bot]
27f6d00e7a Bump ruff from 0.11.2 to 0.11.4 (#8538)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-09 10:28:07 -10:00
Jesse Hills
f9d668eeca Merge branch 'beta' into dev 2025-04-09 19:50:59 +12:00
Jesse Hills
92d1557efd Merge pull request #8534 from esphome/bump-2025.4.0b1
2025.4.0b1
2025-04-09 19:50:23 +12:00
Jesse Hills
6b930595e2 Bump version to 2025.5.0-dev 2025-04-09 14:19:05 +12:00
Jesse Hills
4a1cbfc533 Bump version to 2025.4.0b1 2025-04-09 14:19:05 +12:00
Clyde Stubbs
1f7a84cc8e [lvgl] Implement canvas widget (#8504) 2025-04-09 12:15:39 +12:00
David Woodhouse
8c5adfb33f real_time_clock: Apply timezone immediately in set_timezone() (#8531) 2025-04-09 12:03:38 +12:00
dependabot[bot]
399c9ba4be Bump pytest from 8.2.0 to 8.3.5 (#8528) 2025-04-08 23:38:06 +00:00
Clyde Stubbs
a866370a2e [spi] Implement octal mode (#8386) 2025-04-09 11:07:59 +12:00
Clyde Stubbs
6240bfff97 [lvgl] Make line points templatable (#8502) 2025-04-09 11:03:29 +12:00
Clyde Stubbs
1c72fd4674 [lvgl] add on_boot trigger (#8498) 2025-04-09 11:00:39 +12:00
J. Nick Koston
2291a1dc39 Bump aioesphomeapi to 29.9.0 (#8522) 2025-04-09 10:58:26 +12:00
J. Nick Koston
8269e2c961 Ensure plaintext responds with bad indicator byte before dropping the connection (#8521) 2025-04-09 10:27:23 +12:00
Clyde Stubbs
23dec912ad [psram] Allow use of experimental 120MHz octal mode (#8519) 2025-04-07 12:49:40 +00:00
Clyde Stubbs
9637ef35bd [component] Show error message for failed component (#8478) 2025-04-07 09:26:34 +00:00
J. Nick Koston
23e5cdb30e Rework max connections for BLE to avoid exceeding the hard limit (#8303) 2025-04-07 00:48:12 +00:00
dependabot[bot]
f3b1b11eba Bump flake8 from 7.0.0 to 7.2.0 (#8493)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-04-06 00:58:39 +00:00
dependabot[bot]
5ceba618f6 Bump setuptools from 76.0.0 to 78.1.0 (#8512)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-05 21:28:37 +00:00
victorclaessen
99d5ca3266 [ethernet_info] return actual ethernet MAC address (#8492)
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
2025-04-04 18:59:46 +11:00
Jonathan Swoboda
219ba6152c [CI] Clang tidy fixes for 5.3.2 (#8510)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-04-03 07:32:17 +00:00
Jonathan Swoboda
ef0f969604 [core, qspi_dbi] Clang tidy fixes for 5.3.2 (#8509) 2025-04-03 02:03:04 -05:00
Curtis Malainey
82adcd656f [nau7802] fix bad blocking code (#8070) 2025-04-02 23:04:43 +00:00
Jesse Hills
fe35eee8df Update emails from nabucasa to OHF (#8508) 2025-04-03 11:08:46 +13:00
Shivam Maurya
864dd69038 Bump platformio to 6.1.18 (#8430)
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-04-02 21:38:13 +00:00
dependabot[bot]
79f198ebff Bump zeroconf from 0.146.1 to 0.146.3 (#8507)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-02 20:47:46 +00:00
dependabot[bot]
4b0622aa23 Bump voluptuous from 0.14.2 to 0.15.2 (#8506)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-02 10:14:12 -10:00
dependabot[bot]
4ecc72ed54 Bump pyupgrade from 3.15.2 to 3.19.1 (#8496)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-02 23:50:30 +13:00
dependabot[bot]
791740e554 Bump yamllint from 1.35.1 to 1.37.0 (#8495)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-02 23:18:01 +13:00
dependabot[bot]
6bccc7e389 Bump ruamel-yaml from 0.18.6 to 0.18.10 (#8446)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-04-02 23:17:34 +13:00
dependabot[bot]
655075e71b Bump actions/upload-artifact from 4.6.1 to 4.6.2 (#8435)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-02 23:17:08 +13:00
dependabot[bot]
1df1e3cf48 Bump actions/download-artifact from 4.1.9 to 4.2.1 (#8434)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-02 23:16:42 +13:00
dependabot[bot]
05e52cae2b Bump docker/login-action from 3.3.0 to 3.4.0 in the docker-actions group (#8408)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-02 23:16:31 +13:00
dependabot[bot]
be60d9be9b Bump peter-evans/create-pull-request from 7.0.7 to 7.0.8 (#8362)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-02 23:16:17 +13:00
Oliver Kleinecke
e3eb3ee5d2 Add support for MCP4461 quad i2c digipot/rheostat (#8180)
Co-authored-by: Oliver Kleinecke <kleinecke.oliver@googlemail.com>
Co-authored-by: Djordje Mandic <6750655+DjordjeMandic@users.noreply.github.com>
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-04-01 23:55:06 +00:00
Clyde Stubbs
0812b3dd70 [lvgl] Add some defines (#8501) 2025-04-01 22:42:23 +00:00
NP v/d Spek
28a9f12595 Move CONF_DEFAULT to const.py (#8497) 2025-03-31 22:48:43 +00:00
Keith Burzinski
36b75c3faa Merge branch 'release' into dev 2025-03-31 17:07:16 -05:00
Keith Burzinski
584c5bd5be Merge pull request #8489 from esphome/bump-2025.3.3
2025.3.3
2025-03-31 17:07:02 -05:00
dependabot[bot]
bc372dbeb2 Bump platformio from 6.1.16 to 6.1.18 (#8449)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-31 21:28:47 +00:00
dependabot[bot]
37a03de849 Bump async-timeout from 4.0.3 to 5.0.1 (#8491)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-31 20:57:23 +00:00
dependabot[bot]
c8395cdf0a Bump pytest-asyncio from 0.25.3 to 0.26.0 (#8490)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-31 20:26:22 +00:00
Keith Burzinski
79c8a55459 Bump version to 2025.3.3 2025-03-31 12:48:16 -05:00
Kevin Ahrendt
36d6fe29f2 [speaker] Bugfixes: two pause state issues (#8488) 2025-03-31 12:48:16 -05:00
Clyde Stubbs
e1868ddecb [lvgl] Implement switch restore (#8481) 2025-03-31 12:48:16 -05:00
Kevin Ahrendt
6151644b96 [speaker] Bugfix: Media player always unpauses when receiving a stop command (#8474) 2025-03-31 12:48:15 -05:00
J. Nick Koston
a4914eb5b7 Bump ESP mdns to 1.8.2 (#8482) 2025-03-31 12:48:15 -05:00
Clyde Stubbs
57a57f0d6a [display] Don't assume glyph x_offset is zero. (#8473) 2025-03-31 12:48:15 -05:00
Kevin Ahrendt
7e9f93a290 [speaker] Bugfixes: two pause state issues (#8488) 2025-03-31 17:02:53 +00:00
dependabot[bot]
402ada07b5 Bump pytest-cov from 5.0.0 to 6.0.0 (#8462)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-31 14:33:00 +02:00
dependabot[bot]
9aa9abfc08 Bump actions/setup-python from 5.4.0 to 5.5.0 in /.github/actions/restore-python (#8467)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-31 14:28:21 +02:00
dependabot[bot]
4c1f83614b Bump actions/setup-python from 5.4.0 to 5.5.0 (#8468)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-31 14:28:11 +02:00
Clyde Stubbs
d1763f9831 [psram] 120MHz does not work in octal mode (#8477) 2025-03-31 09:03:59 +00:00
dependabot[bot]
c49391427f Bump ruff from 0.11.0 to 0.11.2 (#8461)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-31 01:52:10 +00:00
Clyde Stubbs
c42343be3a [lvgl] Implement switch restore (#8481) 2025-03-30 09:09:19 +00:00
Kevin Ahrendt
ffc233d99d [speaker] Bugfix: Media player always unpauses when receiving a stop command (#8474) 2025-03-30 08:41:08 +00:00
Jonathan Swoboda
5ed0046bdd [esp32] Allow pioarduino version 5.4.1 (#8480)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-03-29 07:28:10 +00:00
J. Nick Koston
2e16dd788c Bump ESP mdns to 1.8.2 (#8482) 2025-03-28 23:29:53 -05:00
Clyde Stubbs
58fe8b39b2 [scheduler] Properly handle millis() overflow (#8197) 2025-03-27 02:09:22 -05:00
Clyde Stubbs
ccd55a8e84 [display] Don't assume glyph x_offset is zero. (#8473) 2025-03-27 00:31:55 +00:00
Patrick
4bb59ce1d1 [esp32_can] Configurable enqueue timeout (#8453) 2025-03-26 04:06:23 +00:00
Keith Burzinski
bb988604c8 Merge branch 'release' into dev 2025-03-25 18:06:56 -05:00
Keith Burzinski
573088aadb Merge pull request #8469 from esphome/bump-2025.3.2
2025.3.2
2025-03-25 18:06:42 -05:00
Keith Burzinski
031b1c8bd0 Bump version to 2025.3.2 2025-03-25 15:22:11 -05:00
Keith Burzinski
f95b2ba898 [ld2450] Fix bluetooth state not reported correctly (#8458) 2025-03-25 15:22:11 -05:00
Kevin Ahrendt
ea4b573f9a [speaker] Bugfix: Fix rapidly adding items to playlist (#8466)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-03-25 15:22:11 -05:00
Kevin Ahrendt
8fcbd57f2f [media_player] Don't reset enqueue command (#8465) 2025-03-25 15:22:11 -05:00
Samuel Sieb
f131186e6b fix 1bpp rendering (#8463) 2025-03-25 15:22:11 -05:00
Clyde Stubbs
20c7778524 [font] More robust handling of fixed font sizes. (#8443)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-03-25 15:22:11 -05:00
Clyde Stubbs
2d8e86324b [gt911][cst226][ektf2232] Swap x and y calibration values (#8450)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-03-25 15:22:10 -05:00
Keith Burzinski
2dfd28ba3e [ld2450] Fix bluetooth state not reported correctly (#8458) 2025-03-25 15:21:10 -05:00
Kevin Ahrendt
63221d7a1f [speaker] Bugfix: Fix rapidly adding items to playlist (#8466)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-03-25 20:20:56 +00:00
Kevin Ahrendt
fb9a15f0af [media_player] Don't reset enqueue command (#8465) 2025-03-25 11:16:12 -05:00
Samuel Sieb
ce2e966005 fix 1bpp rendering (#8463) 2025-03-25 19:33:53 +11:00
Jonathan Swoboda
e7d1072c85 [core] Fix s2 build after crc header fix (#8459) 2025-03-24 18:04:25 -05:00
Clyde Stubbs
bc999b50b3 [font] More robust handling of fixed font sizes. (#8443)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-03-24 09:35:42 +00:00
Clyde Stubbs
6cfe3ac44d [gt911][cst226][ektf2232] Swap x and y calibration values (#8450)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-03-24 09:07:21 +00:00
Jonathan Swoboda
6787730aa4 [core] Fix 5.4.0 build issue (#8455)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-03-24 02:46:31 +00:00
Keith Burzinski
48a7927a60 Merge branch 'release' into dev 2025-03-22 23:45:32 -05:00
Keith Burzinski
8ea4d8402f Merge pull request #8451 from esphome/bump-2025.3.1
2025.3.1
2025-03-22 23:45:18 -05:00
Keith Burzinski
2c53408cfc Bump version to 2025.3.1 2025-03-22 23:14:32 -05:00
Clyde Stubbs
33dce6e522 [lvgl] Ensure non-zero screen dimensions during init (#8444) 2025-03-22 23:14:32 -05:00
Clyde Stubbs
e213932b7c [lvgl] Set correct buffer size (#8442) 2025-03-22 23:14:32 -05:00
Clyde Stubbs
42fb0e2809 [ft63x6] Get correct dimensions from display (#8417) 2025-03-22 23:14:31 -05:00
Clyde Stubbs
acce0bc45b [lvgl] Ensure non-zero screen dimensions during init (#8444) 2025-03-22 23:09:29 -05:00
dependabot[bot]
f5885de6f1 Bump pytest-asyncio from 0.23.6 to 0.25.3 (#8447)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-21 22:19:09 +00:00
dependabot[bot]
17e3bb7324 Bump aioesphomeapi from 29.6.0 to 29.7.0 (#8448)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-21 11:44:47 -10:00
Clyde Stubbs
d891521ce2 [lvgl] Set correct buffer size (#8442) 2025-03-21 04:12:27 +00:00
Clyde Stubbs
3320e4112b [cli] Add --reset and --upload_speed options (#8380) 2025-03-20 21:38:59 -05:00
Jonathan Swoboda
c0e4701e1d [esp32] Allow pioarduino versions 5.3.2 and 5.4.0 (#8440)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-03-20 21:31:58 -05:00
dependabot[bot]
f3390ff7f5 Bump tzlocal from 5.2 to 5.3.1 (#8423)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-20 14:41:23 -10:00
dependabot[bot]
dfbfb2a2bb Update wheel requirement from ~=0.43.0 to >=0.43,<0.46 (#8421)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-20 13:08:25 -10:00
dependabot[bot]
874026ca8f Bump pylint from 3.2.7 to 3.3.6 (#8441)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2025-03-20 22:07:38 +00:00
dependabot[bot]
4adda632bb Bump ruff from 0.9.2 to 0.11.0 (#8409) 2025-03-20 09:51:23 -10:00
Clyde Stubbs
6ea89644e7 [ft63x6] Get correct dimensions from display (#8417) 2025-03-19 23:37:44 -05:00
Keith Burzinski
64ff62c005 Merge branch 'release' into dev 2025-03-19 23:37:27 -05:00
Keith Burzinski
c4de9e87e4 Merge pull request #8438 from esphome/bump-2025.3.0
2025.3.0
2025-03-19 23:37:13 -05:00
Keith Burzinski
918924d697 Bump version to 2025.3.0 2025-03-19 20:54:32 -05:00
dependabot[bot]
43805e6c56 Bump actions/cache from 4.2.2 to 4.2.3 (#8433)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-19 12:46:38 -10:00
dependabot[bot]
38bbfaccc6 Bump actions/cache from 4.2.2 to 4.2.3 in /.github/actions/restore-python (#8437)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-19 12:46:01 -10:00
Keith Burzinski
56b32aae11 Merge branch 'beta' into dev 2025-03-19 16:01:54 -05:00
Keith Burzinski
e2c16b4baa Merge pull request #8436 from esphome/bump-2025.3.0b5
2025.3.0b5
2025-03-19 16:01:39 -05:00
Keith Burzinski
10a9162f48 Bump version to 2025.3.0b5 2025-03-19 14:36:04 -05:00
Kevin Ahrendt
fbc884772c [audio] Bugfix: fix flac decoding glitches by using esp-audio-libs v1.1.3 (#8431) 2025-03-19 14:36:03 -05:00
Kevin Ahrendt
cbf68f1fd2 [audio] Bugfix: fix flac decoding glitches by using esp-audio-libs v1.1.3 (#8431) 2025-03-19 10:14:42 -05:00
Keith Burzinski
cf227d6f32 Merge branch 'beta' into dev 2025-03-18 15:21:26 -05:00
Keith Burzinski
54e3153f27 Merge pull request #8428 from esphome/bump-2025.3.0b4
2025.3.0b4
2025-03-18 15:21:15 -05:00
Keith Burzinski
c2e0a01106 Bump version to 2025.3.0b4 2025-03-18 14:43:26 -05:00
Clyde Stubbs
d2c2439b97 [core] Handle mis-typed platform name more cleanly (#8424) 2025-03-18 14:43:25 -05:00
Keith Burzinski
a8d33dd26a [docker] Bump libfreetype (#8426) 2025-03-18 14:43:25 -05:00
Keith Burzinski
da41a9204e [docker] Bump curl, git, openssh-client, libopenjp2-7, nginx-light (#8419) 2025-03-18 14:43:25 -05:00
Clyde Stubbs
f993bb08c7 [core] Handle mis-typed platform name more cleanly (#8424) 2025-03-18 14:42:14 -05:00
Keith Burzinski
afa481aeea [docker] Bump libfreetype (#8426) 2025-03-18 17:28:15 +13:00
Keith Burzinski
dfb162e7a6 [docker] Bump curl, git, openssh-client, libopenjp2-7, nginx-light (#8419) 2025-03-17 01:55:29 -05:00
Keith Burzinski
098921b88f Merge branch 'beta' into dev 2025-03-16 01:54:05 -05:00
Keith Burzinski
5c6368b6b8 Merge pull request #8415 from esphome/bump-2025.3.0b3
2025.3.0b3
2025-03-16 01:53:55 -05:00
Keith Burzinski
9bd7060f6b Bump version to 2025.3.0b3 2025-03-16 01:23:06 -05:00
Mikkel Jeppesen
fb1d178abc Added getters for graphs ymin and ymax (#8112)
Co-authored-by: guillempages <guillempages@users.noreply.github.com>
2025-03-16 01:23:06 -05:00
Clyde Stubbs
90c96a0a0f [font] Fix issues with bitmap fonts (#8407) 2025-03-16 01:23:05 -05:00
dependabot[bot]
c63a545750 Bump esphome-glyphsets from 0.1.0 to 0.2.0 (#8403)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-14 21:36:12 -10:00
dependabot[bot]
89f82be4cd Bump puremagic from 1.27 to 1.28 (#8406)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-14 21:35:51 -10:00
dependabot[bot]
c336dd9436 Bump setuptools from 69.2.0 to 76.0.0 (#8405)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-14 21:35:15 -10:00
Mikkel Jeppesen
fa25cebed5 Added getters for graphs ymin and ymax (#8112)
Co-authored-by: guillempages <guillempages@users.noreply.github.com>
2025-03-15 18:55:20 +13:00
Clyde Stubbs
7679c716b3 [font] Fix issues with bitmap fonts (#8407) 2025-03-14 20:17:16 +11:00
Keith Burzinski
225e2585e8 Merge branch 'beta' into dev 2025-03-13 01:31:27 -05:00
Keith Burzinski
1bdf0fdc57 Merge pull request #8400 from esphome/bump-2025.3.0b2
2025.3.0b2
2025-03-13 01:31:17 -05:00
Keith Burzinski
4d95ff2ae0 Bump version to 2025.3.0b2 2025-03-12 23:23:27 -05:00
dependabot[bot]
f36d400058 Bump tornado from 6.4 to 6.4.2 (#8398)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-12 23:23:26 -05:00
J. Nick Koston
c63cf9d151 Bump cryptography to 44.0.2 (#8399) 2025-03-12 23:23:26 -05:00
J. Nick Koston
0a02c1461e Rework pyproject.toml to make it parseable by dependabot (#8397) 2025-03-12 23:23:26 -05:00
J. Nick Koston
b3a69c6c05 Bump aioesphomeapi to 29.6.0 (#8396) 2025-03-12 23:23:26 -05:00
Kevin Ahrendt
dd113f2972 [api] add voice assistant announce to the api (#8395) 2025-03-12 23:23:26 -05:00
Kevin Ahrendt
3c5a0091ee [core] add reallocation support to RAMAllocator (#8390) 2025-03-12 23:23:26 -05:00
Kevin Ahrendt
bf65b73569 [speaker, resampler, mixer] Make volume and mute getters virtual (#8391) 2025-03-12 23:23:26 -05:00
Kevin Ahrendt
a2b123a29a [audio, mixer] Memory and CPU performance improvements (#8387) 2025-03-12 23:23:25 -05:00
J. Nick Koston
3575f52cdf Bump mdns library to 1.8.0 (#8378)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-03-12 23:23:25 -05:00
dependabot[bot]
c90185854e Bump tornado from 6.4 to 6.4.2 (#8398)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-12 23:22:08 -05:00
J. Nick Koston
7d8c39d295 Bump cryptography to 44.0.2 (#8399) 2025-03-12 23:21:45 -05:00
J. Nick Koston
59d282489a Rework pyproject.toml to make it parseable by dependabot (#8397) 2025-03-13 01:16:59 +00:00
J. Nick Koston
f9a0a63290 Bump aioesphomeapi to 29.6.0 (#8396) 2025-03-13 01:00:31 +00:00
Kevin Ahrendt
00000e0ea8 [api] add voice assistant announce to the api (#8395) 2025-03-12 14:35:10 -10:00
Kevin Ahrendt
bd853e6883 [core] add reallocation support to RAMAllocator (#8390) 2025-03-13 07:04:05 +11:00
Kevin Ahrendt
64d1d93fe0 [speaker, resampler, mixer] Make volume and mute getters virtual (#8391) 2025-03-12 14:34:38 -05:00
Kevin Ahrendt
266c2ef337 [audio, mixer] Memory and CPU performance improvements (#8387) 2025-03-12 14:18:31 -05:00
J. Nick Koston
35199c9b96 Bump mdns library to 1.8.0 (#8378)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-03-12 14:18:21 -05:00
Keith Burzinski
0a29138045 Merge branch 'beta' into dev 2025-03-12 03:40:43 -05:00
Keith Burzinski
52269305ec Merge pull request #8389 from esphome/bump-2025.3.0b1
2025.3.0b1
2025-03-12 03:40:31 -05:00
Keith Burzinski
04dc0ed129 Bump version to 2025.4.0-dev 2025-03-12 01:11:50 -05:00
Keith Burzinski
37fabd7c0a Bump version to 2025.3.0b1 2025-03-12 01:11:50 -05:00
djasper-ha
4aa7ad1e33 mcp2515: Add missing CFG1 assignment to be able to use 50kbps with a 16MHz crystal. (#8375) 2025-03-11 22:31:01 +11:00
J. Nick Koston
42e432754e Bump zeroconf to 0.146.1 (#8365)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-03-11 08:08:02 +00:00
Shivam Maurya
2379f02008 Bump esptool to 4.8.1latest (#8367)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
2025-03-11 02:34:47 -05:00
J. Nick Koston
d3145dd95b Bump aioesphomeapi to 29.5.1 (#8364) 2025-03-11 02:31:09 -05:00
Clyde Stubbs
ab77dd691b Revert "[io_bus] Initial implementation" (#8384) 2025-03-11 20:02:01 +13:00
Clyde Stubbs
b54c0fd60a [cst816] Remove binary sensor (#8377) 2025-03-09 23:54:40 -05:00
Clyde Stubbs
75d1eeeffe [touchscreen] Axis swap bugfix (#8376) 2025-03-09 22:04:34 -05:00
Dennis Marinus
10cea51739 allow touchscreen buttons outside of display dimensions (#8296)
Co-authored-by: Dennis Marinus <dmarinus@apple.com>
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
2025-03-08 10:41:54 +11:00
Quentin Raynaud
83e090cc7e [time] fix recalc_timestamp_local (#8239) 2025-03-07 00:34:04 -08:00
Clyde Stubbs
583f8f598a [lvgl] Fix initialisation race condition (Bugfix) (#8369) 2025-03-07 01:58:21 -06:00
Chris Djali
3e9556c6c2 Initialise h-bridge switch to requested initial state (#8363) 2025-03-06 16:43:04 -08:00
Kevin Ahrendt
83cba0d7bd [i2s_audio] Bugfix: Speaker incorrectly delays when sending data (#8361) 2025-03-05 21:32:45 -06:00
tomaszduda23
1d6d0d66dc [udp] fix clang tidy (#8351) 2025-03-03 15:08:42 -06:00
Gustavo de León
4ed78023b6 [bmp085] Fix error in read of pressure (#8359) 2025-03-03 15:06:30 -06:00
Damien Sorel
323209523b [ld2450] fix null exception & zone target_count not published (#8348)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-03-03 10:44:15 -06:00
Jesse Hills
46a4f4eba9 Merge branch 'release' into dev 2025-03-03 21:24:04 +13:00
Jesse Hills
53fda0e96d Merge pull request #8358 from esphome/bump-2025.2.2
2025.2.2
2025-03-03 21:23:20 +13:00
Jesse Hills
0350eafc1e [helpers] Allow RAMAllocator to be told the size of the object manually (#8356) 2025-03-03 01:11:19 -06:00
Jesse Hills
7b8e68c73a Bump version to 2025.2.2 2025-03-03 17:15:40 +13:00
Jesse Hills
db666e44a7 [ltr390] Move calculation to allow dynamic setting of gain and resolution (#8343) 2025-03-03 17:15:40 +13:00
J. Nick Koston
903d033e0f Bump aioesphomeapi to 29.3.2 (#8353) 2025-03-03 17:15:40 +13:00
Kevin Ahrendt
19d938ce48 [audio] Determine http timeout based on duration since last successful read (#8341) 2025-03-03 17:15:40 +13:00
J. Nick Koston
653318479a Fix end_of_scan_ not being called while disconnecting (#8328) 2025-03-03 17:15:40 +13:00
Jesse Hills
2af5fd5210 [ltr390] Move calculation to allow dynamic setting of gain and resolution (#8343) 2025-03-03 15:35:52 +13:00
J. Nick Koston
29e388b231 Bump aioesphomeapi to 29.3.2 (#8353) 2025-03-03 15:35:32 +13:00
Jesse Hills
d9e23fdb5c [dashboard] Rename trash/delete to archive (#8357) 2025-03-03 15:24:05 +13:00
dependabot[bot]
10eacaccba Bump docker/setup-qemu-action from 3.5.0 to 3.6.0 in the docker-actions group (#8346)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-02 22:23:26 +01:00
Samuel Sieb
23687b2afd [tmp1075] fix component for TMP1075N (#8317) 2025-03-02 07:10:18 -08:00
Clyde Stubbs
f11ad9ad5b [io_bus] Initial implementation (#8227) 2025-02-28 16:04:36 +13:00
Timo Beckers
74a25a7e76 Cover component for Tormatic and Novoferm garage doors (#5933) 2025-02-28 15:57:30 +13:00
Kevin Ahrendt
23e04e18f8 [audio] Determine http timeout based on duration since last successful read (#8341) 2025-02-28 11:43:51 +13:00
tomaszduda23
aed5020a83 [nrf52, core] unified way how all platforms handle SplitDefault (#7715)
Co-authored-by: Tomasz Duda <tomaszduda23@gmai.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-02-28 09:24:28 +13:00
Jesse Hills
476f1b701b [zeroconf] Ruff formatting (#8338) 2025-02-28 09:12:21 +13:00
dependabot[bot]
7c3a7b68d3 Bump actions/cache from 4.2.1 to 4.2.2 in /.github/actions/restore-python (#8337)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-28 09:12:04 +13:00
dependabot[bot]
75dc0d3fb7 Bump actions/cache from 4.2.1 to 4.2.2 (#8336)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-28 09:11:40 +13:00
Clyde Stubbs
9bc4f68d87 [font] Use freetype instead of Pillow for font rendering (#8300) 2025-02-28 08:50:51 +13:00
functionpointer
1029202848 [mlx90393] Fix inverted gain and resolution. Expose temperature_compensation and hallconf. (#7635)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-02-28 07:28:12 +13:00
dependabot[bot]
a831905bba Bump docker/build-push-action from 6.14.0 to 6.15.0 in /.github/actions/build-image (#8332)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-28 07:00:23 +13:00
dependabot[bot]
faffd79545 Bump actions/download-artifact from 4.1.8 to 4.1.9 (#8331)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-28 07:00:10 +13:00
dependabot[bot]
7714147071 Bump the docker-actions group with 2 updates (#8330)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-28 06:59:58 +13:00
Keith Burzinski
4da42dedc8 [ld2450] Fix misplaced `ifdef` and related logic (#8335) 2025-02-28 06:58:19 +13:00
J. Nick Koston
28f283d545 Fix end_of_scan_ not being called while disconnecting (#8328) 2025-02-28 06:56:55 +13:00
J. Nick Koston
3048f303c5 dashboard: Implement automatic ping fallback (#8263) 2025-02-27 15:17:07 +00:00
J. Nick Koston
63a7234767 Include the bluetooth mac address in the device info when proxy is enabled (#8203) 2025-02-27 13:37:11 +00:00
Anton Viktorov
c19621e238 MSA311 and MSA301 accelerometer support (#6795)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
2025-02-27 14:48:47 +11:00
barchasse38
bc96eb9d52 Update arduino-heatpumpir and add new protocol for Panasonic AC (#8309) 2025-02-26 04:29:33 -06:00
Keith Burzinski
7375dde39c [ld2450] Fix for "unknown" sensor states (#8305) 2025-02-25 20:49:12 -06:00
Pawel
1b7111affb Add option to include vars in remote packages (#7606)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-02-26 14:02:51 +13:00
tomaszduda23
a511926aed [core] SplitDefault unit test (#8324) 2025-02-26 11:29:55 +13:00
Jesse Hills
6b36cb95c9 Merge branch 'release' into dev 2025-02-26 11:01:56 +13:00
Jesse Hills
c13174c318 Merge pull request #8326 from esphome/bump-2025.2.1
2025.2.1
2025-02-26 11:01:14 +13:00
Jonathan Swoboda
d5da341138 [i2c] Fix i2c issue on idf 5.3 (#8283) 2025-02-26 10:49:09 +13:00
Jesse Hills
8fa157581e Bump version to 2025.2.1 2025-02-26 09:49:22 +13:00
Keith Burzinski
7114d6bdd1 [esp32_touch] Fix variants, add tests for variants (#8320) 2025-02-26 09:49:22 +13:00
J. Nick Koston
eca0c21966 Fix bluetooth race when disconnect called while still connecting (#8297) 2025-02-26 09:49:22 +13:00
esphomebot
20c9c410af Update webserver local assets to 20250224-195901 (#8312) 2025-02-26 09:49:22 +13:00
J. Nick Koston
79af437f48 Fix BLE max notifications with ESP-IDF 5.x (#8301) 2025-02-26 09:49:22 +13:00
J. Nick Koston
6e27003787 Bump aioesphomeapi to 29.1.1 (#8274) 2025-02-26 09:49:22 +13:00
tomaszduda23
b7b2f3e61c [core] make upload_program more generic (#8321) 2025-02-26 09:24:05 +13:00
Keith Burzinski
9448737a92 [esp32_touch] Fix variants, add tests for variants (#8320) 2025-02-26 09:14:39 +13:00
J. Nick Koston
6f2bf4ec4c Fix bluetooth race when disconnect called while still connecting (#8297) 2025-02-26 09:13:30 +13:00
kkosik20
54cea6c41e Adding support for chsc6x touch controller (#8258) 2025-02-25 15:03:28 +11:00
tomaszduda23
e754d0a58b [i2c] python code style (#8311) 2025-02-25 16:10:49 +13:00
Nick Kinnan
5e44a035a3 web_server: ensure fair network sharing + prevent lost state changes via deferred publish at high event load (#7538)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-02-25 13:19:31 +11:00
rforro
c424fea524 ili9xxx: Add support for GC9D01N circle display (#8302) 2025-02-25 10:45:45 +11:00
Nick Kinnan
6aba1dbd73 [api] ensure fair network sharing + prevent lost state changes via deferred publish at high event load (#7547)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-02-25 10:20:21 +11:00
dependabot[bot]
422fb8f1a5 Bump actions/upload-artifact from 4.6.0 to 4.6.1 (#8295) 2025-02-25 10:04:00 +13:00
dependabot[bot]
2988bbb8ce Bump peter-evans/create-pull-request from 7.0.6 to 7.0.7 (#8314) 2025-02-25 10:03:18 +13:00
esphomebot
59299bffc8 Update webserver local assets to 20250224-195901 (#8312) 2025-02-25 10:02:54 +13:00
Kevin Ahrendt
3410aee42e [socket] add connect method (#8308) 2025-02-25 09:32:54 +13:00
J. Nick Koston
96682f5cbe Fix BLE max notifications with ESP-IDF 5.x (#8301) 2025-02-24 14:12:15 +00:00
J. Nick Koston
bfa3254d6c Bump aioesphomeapi to 29.1.1 (#8274) 2025-02-24 07:34:20 +13:00
tomaszduda23
990d1e3bb0 [ota] set USE_OTA_VERSION 2 in defines (#8299) 2025-02-24 07:33:52 +13:00
tomaszduda23
755b0bbfc7 [core, dashboard] load external component to get get_download_types (#8139) 2025-02-22 14:19:17 -06:00
Katherine Whitlock
c281351732 Finish up transition from black-format to ruff (#8294) 2025-02-21 13:02:55 -06:00
dependabot[bot]
9f603a474f Bump docker/build-push-action from 6.13.0 to 6.14.0 in /.github/actions/build-image (#8281)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-21 00:59:18 +01:00
Hareesh M U
bf739506c3 [ld2450] Add new component (#5674)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: Marcus Better <marcus@better.se>
Co-authored-by: Trevor Schirmer <24777085+TrevorSchirmer@users.noreply.github.com>
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-02-20 03:16:08 -06:00
Katherine Whitlock
3020083564 Ruff format for CI (#8276) 2025-02-19 13:24:43 -06:00
Jesse Hills
31e90e5544 Merge branch 'release' into dev 2025-02-19 22:19:56 +13:00
Jesse Hills
7c9726859f Merge pull request #8275 from esphome/bump-2025.2.0
2025.2.0
2025-02-19 22:19:09 +13:00
dependabot[bot]
7529fb10b4 Bump actions/cache from 4.2.0 to 4.2.1 (#8271)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-19 16:46:22 +13:00
Jesse Hills
ba79e2d7b1 Bump version to 2025.2.0 2025-02-19 13:40:15 +13:00
dependabot[bot]
7006bd24a5 Bump actions/cache from 4.2.0 to 4.2.1 in /.github/actions/restore-python (#8273) 2025-02-18 23:23:56 +00:00
Jesse Hills
58311c9a0d Merge branch 'beta' into dev 2025-02-19 10:44:39 +13:00
J. Nick Koston
02bf33c548 Bump zeroconf to 0.145.1 (#8267) 2025-02-18 17:38:41 +00:00
G-Two
b3db04a3d3 Increase default repeat delay for Toto remote transmitter protocol (#8265) 2025-02-19 06:30:03 +13:00
J. Nick Koston
56034e3e79 Bump openssh-client to 1:9.2p1-2+deb12u4 to fix docker builds (#8269) 2025-02-19 06:11:58 +13:00
J. Nick Koston
abbd72e802 Use the process CPU count to determine how many children to create (#8268) 2025-02-19 06:10:33 +13:00
Jesse Hills
1257640e48 Merge branch 'beta' into dev 2025-02-18 14:14:05 +13:00
J. Nick Koston
74f7197543 Bump aioesphomeapi to 29.1.0 (#8105) 2025-02-17 16:27:06 -06:00
J. Nick Koston
c21b8bd417 Switch to native arm runners for docker CI (#8262) 2025-02-18 11:19:11 +13:00
J. Nick Koston
1eb658cc5b Replace glyphsets with esphome_glyphsets (#8261) 2025-02-17 21:48:24 +00:00
Jesse Hills
8b251efb75 Merge branch 'beta' into dev 2025-02-17 13:10:17 +13:00
Ali Jafri
a47e27885f DHT platform now supports modules with inbuilt external resistor (#8257) 2025-02-17 11:05:54 +13:00
J. Nick Koston
2e66b33672 Bump zeroconf to 0.144.3 (#8253) 2025-02-17 08:53:19 +13:00
Djordje Mandic
e21ef22706 [scd30] Increase minimal CONF_UPDATE_INTERVAL from 1 to 2 seconds (#8256) 2025-02-17 08:09:42 +13:00
Samuel Sieb
93c2878c21 don't crash on null pages (#8254)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2025-02-16 01:02:51 -06:00
Jesse Hills
b3ad6a03e6 Merge branch 'beta' into dev 2025-02-14 16:47:17 +13:00
Jesse Hills
143b0d3de4 Fix crash when storage file doesnt exist yet (#8249) 2025-02-14 14:27:11 +13:00
Dániel Márai
788c41e6f4 Add support for the DAC on the S2 (#8030)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2025-02-14 13:15:01 +13:00
Jonathan Swoboda
46b6dcdfbf [logger] Fix bug causing global log level to be overwritten (#8248) 2025-02-13 19:56:08 +00:00
Jesse Hills
d05f641dd0 Merge branch 'beta' into dev 2025-02-13 21:31:40 +13:00
Keith Burzinski
fa029e8fc7 [modbus_controller] Extend tests (#8245) 2025-02-13 20:40:02 +13:00
Keith Burzinski
ace953bd50 [modbus_controller] Remove stream dependency (#8244) 2025-02-13 04:34:16 +00:00
Keith Burzinski
e190ef9e9b [graph] Remove `stream` dependency (#8243) 2025-02-13 03:37:29 +00:00
Gábor Poczkodi
2868210d46 [cse7766] Remove stream dependency (#7720)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2025-02-13 03:07:14 +00:00
Jesse Hills
72f6461871 [core] Fix `config_dir` for dashboard (#8242) 2025-02-13 02:57:57 +00:00
J. Nick Koston
4a95468fd2 Bump zeroconf to 0.144.1 (#8238) 2025-02-13 14:17:00 +13:00
Jesse Hills
43319d4c8a [core] Ignore dot-prefixed config entries when looking for target platform (#8240) 2025-02-12 21:05:46 +00:00
guillempages
3b7a7a2262 [online_image]Fix reset if buffer not allocated (#8236) 2025-02-12 20:55:32 +11:00
Jesse Hills
de2d21862b Merge branch 'beta' into dev 2025-02-12 17:24:36 +13:00
Jesse Hills
ab0d38fbda Bump version to 2025.3.0-dev 2025-02-12 13:53:43 +13:00
1245 changed files with 42262 additions and 12675 deletions

View File

@@ -31,7 +31,7 @@
"ms-python.python", "ms-python.python",
"ms-python.pylint", "ms-python.pylint",
"ms-python.flake8", "ms-python.flake8",
"ms-python.black-formatter", "charliermarsh.ruff",
"visualstudioexptteam.vscodeintellicode", "visualstudioexptteam.vscodeintellicode",
// yaml // yaml
"redhat.vscode-yaml", "redhat.vscode-yaml",
@@ -49,14 +49,11 @@
"flake8.args": [ "flake8.args": [
"--config=${workspaceFolder}/.flake8" "--config=${workspaceFolder}/.flake8"
], ],
"black-formatter.args": [ "ruff.configuration": "${workspaceFolder}/pyproject.toml",
"--config",
"${workspaceFolder}/pyproject.toml"
],
"[python]": { "[python]": {
// VS will say "Value is not accepted" before building the devcontainer, but the warning // VS will say "Value is not accepted" before building the devcontainer, but the warning
// should go away after build is completed. // should go away after build is completed.
"editor.defaultFormatter": "ms-python.black-formatter" "editor.defaultFormatter": "charliermarsh.ruff"
}, },
"editor.formatOnPaste": false, "editor.formatOnPaste": false,
"editor.formatOnSave": true, "editor.formatOnSave": true,

View File

@@ -114,4 +114,5 @@ config/
examples/ examples/
Dockerfile Dockerfile
.git/ .git/
tests/build/ tests/
.*

4
.github/FUNDING.yml vendored
View File

@@ -1,4 +0,0 @@
---
# These are supported funding model platforms
custom: https://www.nabucasa.com

View File

@@ -1,15 +1,11 @@
name: Build Image name: Build Image
inputs: inputs:
platform:
description: "Platform to build for"
required: true
example: "linux/amd64"
target: target:
description: "Target to build" description: "Target to build"
required: true required: true
example: "docker" example: "docker"
baseimg: build_type:
description: "Base image type" description: "Build type"
required: true required: true
example: "docker" example: "docker"
suffix: suffix:
@@ -19,6 +15,11 @@ inputs:
description: "Version to build" description: "Version to build"
required: true required: true
example: "2023.12.0" example: "2023.12.0"
base_os:
description: "Base OS to use"
required: false
default: "debian"
example: "debian"
runs: runs:
using: "composite" using: "composite"
steps: steps:
@@ -46,52 +47,52 @@ runs:
- name: Build and push to ghcr by digest - name: Build and push to ghcr by digest
id: build-ghcr id: build-ghcr
uses: docker/build-push-action@v6.13.0 uses: docker/build-push-action@v6.16.0
env: env:
DOCKER_BUILD_SUMMARY: false DOCKER_BUILD_SUMMARY: false
DOCKER_BUILD_RECORD_UPLOAD: false DOCKER_BUILD_RECORD_UPLOAD: false
with: with:
context: . context: .
file: ./docker/Dockerfile file: ./docker/Dockerfile
platforms: ${{ inputs.platform }}
target: ${{ inputs.target }} target: ${{ inputs.target }}
cache-from: type=gha cache-from: type=gha
cache-to: ${{ steps.cache-to.outputs.value }} cache-to: ${{ steps.cache-to.outputs.value }}
build-args: | build-args: |
BASEIMGTYPE=${{ inputs.baseimg }} BUILD_TYPE=${{ inputs.build_type }}
BUILD_VERSION=${{ inputs.version }} BUILD_VERSION=${{ inputs.version }}
BUILD_OS=${{ inputs.base_os }}
outputs: | outputs: |
type=image,name=ghcr.io/${{ steps.tags.outputs.image_name }},push-by-digest=true,name-canonical=true,push=true type=image,name=ghcr.io/${{ steps.tags.outputs.image_name }},push-by-digest=true,name-canonical=true,push=true
- name: Export ghcr digests - name: Export ghcr digests
shell: bash shell: bash
run: | run: |
mkdir -p /tmp/digests/${{ inputs.target }}/ghcr mkdir -p /tmp/digests/${{ inputs.build_type }}/ghcr
digest="${{ steps.build-ghcr.outputs.digest }}" digest="${{ steps.build-ghcr.outputs.digest }}"
touch "/tmp/digests/${{ inputs.target }}/ghcr/${digest#sha256:}" touch "/tmp/digests/${{ inputs.build_type }}/ghcr/${digest#sha256:}"
- name: Build and push to dockerhub by digest - name: Build and push to dockerhub by digest
id: build-dockerhub id: build-dockerhub
uses: docker/build-push-action@v6.13.0 uses: docker/build-push-action@v6.16.0
env: env:
DOCKER_BUILD_SUMMARY: false DOCKER_BUILD_SUMMARY: false
DOCKER_BUILD_RECORD_UPLOAD: false DOCKER_BUILD_RECORD_UPLOAD: false
with: with:
context: . context: .
file: ./docker/Dockerfile file: ./docker/Dockerfile
platforms: ${{ inputs.platform }}
target: ${{ inputs.target }} target: ${{ inputs.target }}
cache-from: type=gha cache-from: type=gha
cache-to: ${{ steps.cache-to.outputs.value }} cache-to: ${{ steps.cache-to.outputs.value }}
build-args: | build-args: |
BASEIMGTYPE=${{ inputs.baseimg }} BUILD_TYPE=${{ inputs.build_type }}
BUILD_VERSION=${{ inputs.version }} BUILD_VERSION=${{ inputs.version }}
BUILD_OS=${{ inputs.base_os }}
outputs: | outputs: |
type=image,name=docker.io/${{ steps.tags.outputs.image_name }},push-by-digest=true,name-canonical=true,push=true type=image,name=docker.io/${{ steps.tags.outputs.image_name }},push-by-digest=true,name-canonical=true,push=true
- name: Export dockerhub digests - name: Export dockerhub digests
shell: bash shell: bash
run: | run: |
mkdir -p /tmp/digests/${{ inputs.target }}/dockerhub mkdir -p /tmp/digests/${{ inputs.build_type }}/dockerhub
digest="${{ steps.build-dockerhub.outputs.digest }}" digest="${{ steps.build-dockerhub.outputs.digest }}"
touch "/tmp/digests/${{ inputs.target }}/dockerhub/${digest#sha256:}" touch "/tmp/digests/${{ inputs.build_type }}/dockerhub/${digest#sha256:}"

View File

@@ -17,12 +17,12 @@ runs:
steps: steps:
- name: Set up Python ${{ inputs.python-version }} - name: Set up Python ${{ inputs.python-version }}
id: python id: python
uses: actions/setup-python@v5.4.0 uses: actions/setup-python@v5.6.0
with: with:
python-version: ${{ inputs.python-version }} python-version: ${{ inputs.python-version }}
- name: Restore Python virtual environment - name: Restore Python virtual environment
id: cache-venv id: cache-venv
uses: actions/cache/restore@v4.2.0 uses: actions/cache/restore@v4.2.3
with: with:
path: venv path: venv
# yamllint disable-line rule:line-length # yamllint disable-line rule:line-length
@@ -34,7 +34,7 @@ runs:
python -m venv venv python -m venv venv
source venv/bin/activate source venv/bin/activate
python --version python --version
pip install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt pip install -r requirements.txt -r requirements_test.txt
pip install -e . pip install -e .
- name: Create Python virtual environment - name: Create Python virtual environment
if: steps.cache-venv.outputs.cache-hit != 'true' && runner.os == 'Windows' if: steps.cache-venv.outputs.cache-hit != 'true' && runner.os == 'Windows'
@@ -43,5 +43,5 @@ runs:
python -m venv venv python -m venv venv
./venv/Scripts/activate ./venv/Scripts/activate
python --version python --version
pip install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt pip install -r requirements.txt -r requirements_test.txt
pip install -e . pip install -e .

View File

@@ -17,7 +17,6 @@ updates:
docker-actions: docker-actions:
applies-to: version-updates applies-to: version-updates
patterns: patterns:
- "docker/setup-qemu-action"
- "docker/login-action" - "docker/login-action"
- "docker/setup-buildx-action" - "docker/setup-buildx-action"
- package-ecosystem: github-actions - package-ecosystem: github-actions

View File

@@ -23,7 +23,7 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v4.1.7 uses: actions/checkout@v4.1.7
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5.4.0 uses: actions/setup-python@v5.6.0
with: with:
python-version: "3.11" python-version: "3.11"

View File

@@ -33,22 +33,23 @@ concurrency:
jobs: jobs:
check-docker: check-docker:
name: Build docker containers name: Build docker containers
runs-on: ubuntu-latest runs-on: ${{ matrix.os }}
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
arch: [amd64, aarch64] os: ["ubuntu-24.04", "ubuntu-24.04-arm"]
build_type: ["ha-addon", "docker", "lint"] build_type:
- "ha-addon"
- "docker"
# - "lint"
steps: steps:
- uses: actions/checkout@v4.1.7 - uses: actions/checkout@v4.1.7
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5.4.0 uses: actions/setup-python@v5.6.0
with: with:
python-version: "3.9" python-version: "3.9"
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.9.0 uses: docker/setup-buildx-action@v3.10.0
- name: Set up QEMU
uses: docker/setup-qemu-action@v3.4.0
- name: Set TAG - name: Set TAG
run: | run: |
@@ -58,6 +59,6 @@ jobs:
run: | run: |
docker/build.py \ docker/build.py \
--tag "${TAG}" \ --tag "${TAG}" \
--arch "${{ matrix.arch }}" \ --arch "${{ matrix.os == 'ubuntu-24.04-arm' && 'aarch64' || 'amd64' }}" \
--build-type "${{ matrix.build_type }}" \ --build-type "${{ matrix.build_type }}" \
build build

View File

@@ -39,15 +39,15 @@ jobs:
uses: actions/checkout@v4.1.7 uses: actions/checkout@v4.1.7
- name: Generate cache-key - name: Generate cache-key
id: cache-key id: cache-key
run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT run: echo key="${{ hashFiles('requirements.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT
- name: Set up Python ${{ env.DEFAULT_PYTHON }} - name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python id: python
uses: actions/setup-python@v5.4.0 uses: actions/setup-python@v5.6.0
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment - name: Restore Python virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v4.2.0 uses: actions/cache@v4.2.3
with: with:
path: venv path: venv
# yamllint disable-line rule:line-length # yamllint disable-line rule:line-length
@@ -58,11 +58,11 @@ jobs:
python -m venv venv python -m venv venv
. venv/bin/activate . venv/bin/activate
python --version python --version
pip install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt pip install -r requirements.txt -r requirements_test.txt
pip install -e . pip install -e .
black: ruff:
name: Check black name: Check ruff
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
needs: needs:
- common - common
@@ -74,10 +74,10 @@ jobs:
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }} cache-key: ${{ needs.common.outputs.cache-key }}
- name: Run black - name: Run Ruff
run: | run: |
. venv/bin/activate . venv/bin/activate
black --verbose esphome tests ruff format esphome tests
- name: Suggested changes - name: Suggested changes
run: script/ci-suggest-changes run: script/ci-suggest-changes
if: always() if: always()
@@ -165,6 +165,7 @@ jobs:
. venv/bin/activate . venv/bin/activate
script/ci-custom.py script/ci-custom.py
script/build_codeowners.py --check script/build_codeowners.py --check
script/build_language_schema.py --check
pytest: pytest:
name: Run pytest name: Run pytest
@@ -220,7 +221,7 @@ jobs:
. venv/bin/activate . venv/bin/activate
pytest -vv --cov-report=xml --tb=native tests pytest -vv --cov-report=xml --tb=native tests
- name: Upload coverage to Codecov - name: Upload coverage to Codecov
uses: codecov/codecov-action@v5 uses: codecov/codecov-action@v5.4.2
with: with:
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}
@@ -255,7 +256,7 @@ jobs:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
needs: needs:
- common - common
- black - ruff
- ci-custom - ci-custom
- clang-format - clang-format
- flake8 - flake8
@@ -303,14 +304,14 @@ jobs:
- name: Cache platformio - name: Cache platformio
if: github.ref == 'refs/heads/dev' if: github.ref == 'refs/heads/dev'
uses: actions/cache@v4.2.0 uses: actions/cache@v4.2.3
with: with:
path: ~/.platformio path: ~/.platformio
key: platformio-${{ matrix.pio_cache_key }} key: platformio-${{ matrix.pio_cache_key }}
- name: Cache platformio - name: Cache platformio
if: github.ref != 'refs/heads/dev' if: github.ref != 'refs/heads/dev'
uses: actions/cache/restore@v4.2.0 uses: actions/cache/restore@v4.2.3
with: with:
path: ~/.platformio path: ~/.platformio
key: platformio-${{ matrix.pio_cache_key }} key: platformio-${{ matrix.pio_cache_key }}
@@ -482,7 +483,7 @@ jobs:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
needs: needs:
- common - common
- black - ruff
- ci-custom - ci-custom
- clang-format - clang-format
- flake8 - flake8

View File

@@ -1,11 +1,11 @@
{ {
"problemMatcher": [ "problemMatcher": [
{ {
"owner": "black", "owner": "ruff",
"severity": "error", "severity": "error",
"pattern": [ "pattern": [
{ {
"regexp": "^(.*): (Please format this file with the black formatter)", "regexp": "^(.*): (Please format this file with the ruff formatter)",
"file": 1, "file": 1,
"message": 2 "message": 2
} }

View File

@@ -53,7 +53,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v4.1.7 - uses: actions/checkout@v4.1.7
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5.4.0 uses: actions/setup-python@v5.6.0
with: with:
python-version: "3.x" python-version: "3.x"
- name: Set up python environment - name: Set up python environment
@@ -68,39 +68,39 @@ jobs:
uses: pypa/gh-action-pypi-publish@v1.12.4 uses: pypa/gh-action-pypi-publish@v1.12.4
deploy-docker: deploy-docker:
name: Build ESPHome ${{ matrix.platform }} name: Build ESPHome ${{ matrix.platform.arch }}
if: github.repository == 'esphome/esphome' if: github.repository == 'esphome/esphome'
permissions: permissions:
contents: read contents: read
packages: write packages: write
runs-on: ubuntu-latest runs-on: ${{ matrix.platform.os }}
needs: [init] needs: [init]
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
platform: platform:
- linux/amd64 - arch: amd64
- linux/arm64 os: "ubuntu-24.04"
- arch: arm64
os: "ubuntu-24.04-arm"
steps: steps:
- uses: actions/checkout@v4.1.7 - uses: actions/checkout@v4.1.7
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v5.4.0 uses: actions/setup-python@v5.6.0
with: with:
python-version: "3.9" python-version: "3.9"
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.9.0 uses: docker/setup-buildx-action@v3.10.0
- name: Set up QEMU
if: matrix.platform != 'linux/amd64'
uses: docker/setup-qemu-action@v3.4.0
- name: Log in to docker hub - name: Log in to docker hub
uses: docker/login-action@v3.3.0 uses: docker/login-action@v3.4.0
with: with:
username: ${{ secrets.DOCKER_USER }} username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASSWORD }} password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to the GitHub container registry - name: Log in to the GitHub container registry
uses: docker/login-action@v3.3.0 uses: docker/login-action@v3.4.0
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
@@ -109,45 +109,36 @@ jobs:
- name: Build docker - name: Build docker
uses: ./.github/actions/build-image uses: ./.github/actions/build-image
with: with:
platform: ${{ matrix.platform }} target: final
target: docker build_type: docker
baseimg: docker
suffix: "" suffix: ""
version: ${{ needs.init.outputs.tag }} version: ${{ needs.init.outputs.tag }}
- name: Build ha-addon - name: Build ha-addon
uses: ./.github/actions/build-image uses: ./.github/actions/build-image
with: with:
platform: ${{ matrix.platform }} target: final
target: hassio build_type: ha-addon
baseimg: hassio
suffix: "hassio" suffix: "hassio"
version: ${{ needs.init.outputs.tag }} version: ${{ needs.init.outputs.tag }}
- name: Build lint # - name: Build lint
uses: ./.github/actions/build-image # uses: ./.github/actions/build-image
with: # with:
platform: ${{ matrix.platform }} # target: lint
target: lint # build_type: lint
baseimg: docker # suffix: lint
suffix: lint # version: ${{ needs.init.outputs.tag }}
version: ${{ needs.init.outputs.tag }}
- name: Sanitize platform name
id: sanitize
run: |
echo "${{ matrix.platform }}" | sed 's|/|-|g' > /tmp/platform
echo name=$(cat /tmp/platform) >> $GITHUB_OUTPUT
- name: Upload digests - name: Upload digests
uses: actions/upload-artifact@v4.6.0 uses: actions/upload-artifact@v4.6.2
with: with:
name: digests-${{ steps.sanitize.outputs.name }} name: digests-${{ matrix.platform.arch }}
path: /tmp/digests path: /tmp/digests
retention-days: 1 retention-days: 1
deploy-manifest: deploy-manifest:
name: Publish ESPHome ${{ matrix.image.title }} to ${{ matrix.registry }} name: Publish ESPHome ${{ matrix.image.build_type }} to ${{ matrix.registry }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: needs:
- init - init
@@ -160,15 +151,12 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
image: image:
- title: "ha-addon" - build_type: "docker"
target: "hassio"
suffix: "hassio"
- title: "docker"
target: "docker"
suffix: "" suffix: ""
- title: "lint" - build_type: "ha-addon"
target: "lint" suffix: "hassio"
suffix: "lint" # - build_type: "lint"
# suffix: "lint"
registry: registry:
- ghcr - ghcr
- dockerhub - dockerhub
@@ -176,24 +164,24 @@ jobs:
- uses: actions/checkout@v4.1.7 - uses: actions/checkout@v4.1.7
- name: Download digests - name: Download digests
uses: actions/download-artifact@v4.1.8 uses: actions/download-artifact@v4.3.0
with: with:
pattern: digests-* pattern: digests-*
path: /tmp/digests path: /tmp/digests
merge-multiple: true merge-multiple: true
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.9.0 uses: docker/setup-buildx-action@v3.10.0
- name: Log in to docker hub - name: Log in to docker hub
if: matrix.registry == 'dockerhub' if: matrix.registry == 'dockerhub'
uses: docker/login-action@v3.3.0 uses: docker/login-action@v3.4.0
with: with:
username: ${{ secrets.DOCKER_USER }} username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASSWORD }} password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to the GitHub container registry - name: Log in to the GitHub container registry
if: matrix.registry == 'ghcr' if: matrix.registry == 'ghcr'
uses: docker/login-action@v3.3.0 uses: docker/login-action@v3.4.0
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
@@ -212,7 +200,7 @@ jobs:
done done
- name: Create manifest list and push - name: Create manifest list and push
working-directory: /tmp/digests/${{ matrix.image.target }}/${{ matrix.registry }} working-directory: /tmp/digests/${{ matrix.image.build_type }}/${{ matrix.registry }}
run: | run: |
docker buildx imagetools create $(jq -Rcnr 'inputs | . / "," | map("-t " + .) | join(" ")' <<< "${{ steps.tags.outputs.tags}}") \ docker buildx imagetools create $(jq -Rcnr 'inputs | . / "," | map("-t " + .) | join(" ")' <<< "${{ steps.tags.outputs.tags}}") \
$(printf '${{ steps.tags.outputs.image }}@sha256:%s ' *) $(printf '${{ steps.tags.outputs.image }}@sha256:%s ' *)

View File

@@ -22,7 +22,7 @@ jobs:
path: lib/home-assistant path: lib/home-assistant
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v5.4.0 uses: actions/setup-python@v5.6.0
with: with:
python-version: 3.12 python-version: 3.12
@@ -36,11 +36,11 @@ jobs:
python ./script/sync-device_class.py python ./script/sync-device_class.py
- name: Commit changes - name: Commit changes
uses: peter-evans/create-pull-request@v7.0.6 uses: peter-evans/create-pull-request@v7.0.8
with: with:
commit-message: "Synchronise Device Classes from Home Assistant" commit-message: "Synchronise Device Classes from Home Assistant"
committer: esphomebot <esphome@nabucasa.com> committer: esphomebot <esphome@openhomefoundation.org>
author: esphomebot <esphome@nabucasa.com> author: esphomebot <esphome@openhomefoundation.org>
branch: sync/device-classes branch: sync/device-classes
delete-branch: true delete-branch: true
title: "Synchronise Device Classes from Home Assistant" title: "Synchronise Device Classes from Home Assistant"

View File

@@ -4,7 +4,7 @@
repos: repos:
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version. # Ruff version.
rev: v0.5.4 rev: v0.11.0
hooks: hooks:
# Run the linter. # Run the linter.
- id: ruff - id: ruff
@@ -12,11 +12,11 @@ repos:
# Run the formatter. # Run the formatter.
- id: ruff-format - id: ruff-format
- repo: https://github.com/PyCQA/flake8 - repo: https://github.com/PyCQA/flake8
rev: 6.1.0 rev: 7.2.0
hooks: hooks:
- id: flake8 - id: flake8
additional_dependencies: additional_dependencies:
- flake8-docstrings==1.5.0 - flake8-docstrings==1.7.0
- pydocstyle==5.1.1 - pydocstyle==5.1.1
files: ^(esphome|tests)/.+\.py$ files: ^(esphome|tests)/.+\.py$
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
@@ -33,7 +33,7 @@ repos:
- id: pyupgrade - id: pyupgrade
args: [--py39-plus] args: [--py39-plus]
- repo: https://github.com/adrienverge/yamllint.git - repo: https://github.com/adrienverge/yamllint.git
rev: v1.35.1 rev: v1.37.1
hooks: hooks:
- id: yamllint - id: yamllint
- repo: https://github.com/pre-commit/mirrors-clang-format - repo: https://github.com/pre-commit/mirrors-clang-format

View File

@@ -93,10 +93,12 @@ esphome/components/captive_portal/* @OttoWinter
esphome/components/ccs811/* @habbie esphome/components/ccs811/* @habbie
esphome/components/cd74hc4067/* @asoehlke esphome/components/cd74hc4067/* @asoehlke
esphome/components/ch422g/* @clydebarrow @jesterret esphome/components/ch422g/* @clydebarrow @jesterret
esphome/components/chsc6x/* @kkosik20
esphome/components/climate/* @esphome/core esphome/components/climate/* @esphome/core
esphome/components/climate_ir/* @glmnet esphome/components/climate_ir/* @glmnet
esphome/components/color_temperature/* @jesserockz esphome/components/color_temperature/* @jesserockz
esphome/components/combination/* @Cat-Ion @kahrendt esphome/components/combination/* @Cat-Ion @kahrendt
esphome/components/const/* @esphome/core
esphome/components/coolix/* @glmnet esphome/components/coolix/* @glmnet
esphome/components/copy/* @OttoWinter esphome/components/copy/* @OttoWinter
esphome/components/cover/* @esphome/core esphome/components/cover/* @esphome/core
@@ -234,6 +236,7 @@ esphome/components/kuntze/* @ssieb
esphome/components/lcd_menu/* @numo68 esphome/components/lcd_menu/* @numo68
esphome/components/ld2410/* @regevbr @sebcaps esphome/components/ld2410/* @regevbr @sebcaps
esphome/components/ld2420/* @descipher esphome/components/ld2420/* @descipher
esphome/components/ld2450/* @hareeshmu
esphome/components/ledc/* @OttoWinter esphome/components/ledc/* @OttoWinter
esphome/components/libretiny/* @kuba2k2 esphome/components/libretiny/* @kuba2k2
esphome/components/libretiny_pwm/* @kuba2k2 esphome/components/libretiny_pwm/* @kuba2k2
@@ -248,6 +251,7 @@ esphome/components/ltr501/* @latonita
esphome/components/ltr_als_ps/* @latonita esphome/components/ltr_als_ps/* @latonita
esphome/components/lvgl/* @clydebarrow esphome/components/lvgl/* @clydebarrow
esphome/components/m5stack_8angle/* @rnauber esphome/components/m5stack_8angle/* @rnauber
esphome/components/mapping/* @clydebarrow
esphome/components/matrix_keypad/* @ssieb esphome/components/matrix_keypad/* @ssieb
esphome/components/max17043/* @blacknell esphome/components/max17043/* @blacknell
esphome/components/max31865/* @DAVe3283 esphome/components/max31865/* @DAVe3283
@@ -264,6 +268,7 @@ esphome/components/mcp23x17_base/* @jesserockz
esphome/components/mcp23xxx_base/* @jesserockz esphome/components/mcp23xxx_base/* @jesserockz
esphome/components/mcp2515/* @danielschramm @mvturnho esphome/components/mcp2515/* @danielschramm @mvturnho
esphome/components/mcp3204/* @rsumner esphome/components/mcp3204/* @rsumner
esphome/components/mcp4461/* @p1ngb4ck
esphome/components/mcp4728/* @berfenger esphome/components/mcp4728/* @berfenger
esphome/components/mcp47a1/* @jesserockz esphome/components/mcp47a1/* @jesserockz
esphome/components/mcp9600/* @mreditor97 esphome/components/mcp9600/* @mreditor97
@@ -273,7 +278,7 @@ esphome/components/mdns/* @esphome/core
esphome/components/media_player/* @jesserockz esphome/components/media_player/* @jesserockz
esphome/components/micro_wake_word/* @jesserockz @kahrendt esphome/components/micro_wake_word/* @jesserockz @kahrendt
esphome/components/micronova/* @jorre05 esphome/components/micronova/* @jorre05
esphome/components/microphone/* @jesserockz esphome/components/microphone/* @jesserockz @kahrendt
esphome/components/mics_4514/* @jesserockz esphome/components/mics_4514/* @jesserockz
esphome/components/midea/* @dudanov esphome/components/midea/* @dudanov
esphome/components/midea_ir/* @dudanov esphome/components/midea_ir/* @dudanov
@@ -297,6 +302,7 @@ esphome/components/mopeka_std_check/* @Fabian-Schmidt
esphome/components/mpl3115a2/* @kbickar esphome/components/mpl3115a2/* @kbickar
esphome/components/mpu6886/* @fabaff esphome/components/mpu6886/* @fabaff
esphome/components/ms8607/* @e28eta esphome/components/ms8607/* @e28eta
esphome/components/msa3xx/* @latonita
esphome/components/nau7802/* @cujomalainey esphome/components/nau7802/* @cujomalainey
esphome/components/network/* @esphome/core esphome/components/network/* @esphome/core
esphome/components/nextion/* @edwardtfn @senexcrenshaw esphome/components/nextion/* @edwardtfn @senexcrenshaw
@@ -313,6 +319,7 @@ esphome/components/online_image/* @clydebarrow @guillempages
esphome/components/opentherm/* @olegtarasov esphome/components/opentherm/* @olegtarasov
esphome/components/ota/* @esphome/core esphome/components/ota/* @esphome/core
esphome/components/output/* @esphome/core esphome/components/output/* @esphome/core
esphome/components/packet_transport/* @clydebarrow
esphome/components/pca6416a/* @Mat931 esphome/components/pca6416a/* @Mat931
esphome/components/pca9554/* @clydebarrow @hwstar esphome/components/pca9554/* @clydebarrow @hwstar
esphome/components/pcf85063/* @brogon esphome/components/pcf85063/* @brogon
@@ -320,7 +327,9 @@ esphome/components/pcf8563/* @KoenBreeman
esphome/components/pid/* @OttoWinter esphome/components/pid/* @OttoWinter
esphome/components/pipsolar/* @andreashergert1984 esphome/components/pipsolar/* @andreashergert1984
esphome/components/pm1006/* @habbie esphome/components/pm1006/* @habbie
esphome/components/pm2005/* @andrewjswan
esphome/components/pmsa003i/* @sjtrny esphome/components/pmsa003i/* @sjtrny
esphome/components/pmsx003/* @ximex
esphome/components/pmwcs3/* @SeByDocKy esphome/components/pmwcs3/* @SeByDocKy
esphome/components/pn532/* @OttoWinter @jesserockz esphome/components/pn532/* @OttoWinter @jesserockz
esphome/components/pn532_i2c/* @OttoWinter @jesserockz esphome/components/pn532_i2c/* @OttoWinter @jesserockz
@@ -420,6 +429,7 @@ esphome/components/sun/* @OttoWinter
esphome/components/sun_gtil2/* @Mat931 esphome/components/sun_gtil2/* @Mat931
esphome/components/switch/* @esphome/core esphome/components/switch/* @esphome/core
esphome/components/switch/binary_sensor/* @ssieb esphome/components/switch/binary_sensor/* @ssieb
esphome/components/syslog/* @clydebarrow
esphome/components/t6615/* @tylermenezes esphome/components/t6615/* @tylermenezes
esphome/components/tc74/* @sethgirvan esphome/components/tc74/* @sethgirvan
esphome/components/tca9548a/* @andreashergert1984 esphome/components/tca9548a/* @andreashergert1984
@@ -445,6 +455,7 @@ esphome/components/tmp102/* @timsavage
esphome/components/tmp1075/* @sybrenstuvel esphome/components/tmp1075/* @sybrenstuvel
esphome/components/tmp117/* @Azimath esphome/components/tmp117/* @Azimath
esphome/components/tof10120/* @wstrzalka esphome/components/tof10120/* @wstrzalka
esphome/components/tormatic/* @ti-mo
esphome/components/toshiba/* @kbx81 esphome/components/toshiba/* @kbx81
esphome/components/touchscreen/* @jesserockz @nielsnl68 esphome/components/touchscreen/* @jesserockz @nielsnl68
esphome/components/tsl2591/* @wjcarpenter esphome/components/tsl2591/* @wjcarpenter
@@ -458,6 +469,7 @@ esphome/components/tuya/switch/* @jesserockz
esphome/components/tuya/text_sensor/* @dentra esphome/components/tuya/text_sensor/* @dentra
esphome/components/uart/* @esphome/core esphome/components/uart/* @esphome/core
esphome/components/uart/button/* @ssieb esphome/components/uart/button/* @ssieb
esphome/components/uart/packet_transport/* @clydebarrow
esphome/components/udp/* @clydebarrow esphome/components/udp/* @clydebarrow
esphome/components/ufire_ec/* @pvizeli esphome/components/ufire_ec/* @pvizeli
esphome/components/ufire_ise/* @pvizeli esphome/components/ufire_ise/* @pvizeli

View File

@@ -34,7 +34,7 @@ This Code of Conduct applies both within project spaces and in public spaces whe
## Enforcement ## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at esphome@nabucasa.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at esphome@openhomefoundation.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.

View File

@@ -1,131 +1,54 @@
# Build these with the build.py script ARG BUILD_VERSION=dev
# Example: ARG BUILD_OS=alpine
# python3 docker/build.py --tag dev --arch amd64 --build-type docker build ARG BUILD_BASE_VERSION=2025.04.0
ARG BUILD_TYPE=docker
# One of "docker", "hassio" FROM ghcr.io/esphome/docker-base:${BUILD_OS}-${BUILD_BASE_VERSION} AS base-source-docker
ARG BASEIMGTYPE=docker FROM ghcr.io/esphome/docker-base:${BUILD_OS}-ha-addon-${BUILD_BASE_VERSION} AS base-source-ha-addon
ARG BUILD_TYPE
FROM base-source-${BUILD_TYPE} AS base
# https://github.com/hassio-addons/addon-debian-base/releases RUN git config --system --add safe.directory "*"
FROM ghcr.io/hassio-addons/debian-base:7.2.0 AS base-hassio
# https://hub.docker.com/_/debian?tab=tags&page=1&name=bookworm
FROM debian:12.2-slim AS base-docker
FROM base-${BASEIMGTYPE} AS base RUN pip install uv==0.6.14
COPY requirements.txt /
ARG TARGETARCH
ARG TARGETVARIANT
# Note that --break-system-packages is used below because
# https://peps.python.org/pep-0668/ added a safety check that prevents
# installing packages with the same name as a system package. This is
# not a problem for us because we are not concerned about overwriting
# system packages because we are running in an isolated container.
RUN \ RUN \
apt-get update \ uv pip install --no-cache-dir \
# Use pinned versions so that we get updates with build caching -r /requirements.txt
&& apt-get install -y --no-install-recommends \
python3-pip=23.0.1+dfsg-1 \
python3-setuptools=66.1.1-1+deb12u1 \
python3-venv=3.11.2-1+b1 \
python3-wheel=0.38.4-2 \
iputils-ping=3:20221126-1+deb12u1 \
git=1:2.39.5-0+deb12u1 \
curl=7.88.1-10+deb12u8 \
openssh-client=1:9.2p1-2+deb12u4 \
python3-cffi=1.15.1-5 \
libcairo2=1.16.0-7 \
libmagic1=1:5.44-3 \
patch=2.7.6-7 \
&& rm -rf \
/tmp/* \
/var/{cache,log}/* \
/var/lib/apt/lists/*
ENV \
# Fix click python3 lang warning https://click.palletsprojects.com/en/7.x/python3/
LANG=C.UTF-8 LC_ALL=C.UTF-8 \
# Store globally installed pio libs in /piolibs
PLATFORMIO_GLOBALLIB_DIR=/piolibs
RUN \ RUN \
pip3 install \ platformio settings set enable_telemetry No \
--break-system-packages --no-cache-dir \
# Keep platformio version in sync with requirements.txt
platformio==6.1.16 \
# Change some platformio settings
&& platformio settings set enable_telemetry No \
&& platformio settings set check_platformio_interval 1000000 \ && platformio settings set check_platformio_interval 1000000 \
&& mkdir -p /piolibs && mkdir -p /piolibs
# First install requirements to leverage caching when requirements don't change
# tmpfs is for https://github.com/rust-lang/cargo/issues/8719
COPY requirements.txt requirements_optional.txt /
RUN --mount=type=tmpfs,target=/root/.cargo <<END-OF-RUN
# Fail on any non-zero status
set -e
# install build tools in case wheels are not available
BUILD_DEPS="
build-essential=12.9
python3-dev=3.11.2-1+b1
zlib1g-dev=1:1.2.13.dfsg-1
libjpeg-dev=1:2.1.5-2
libfreetype-dev=2.12.1+dfsg-5+deb12u3
libssl-dev=3.0.15-1~deb12u1
libffi-dev=3.4.4-1
cargo=0.66.0+ds1-1
pkg-config=1.8.1-1
"
LIB_DEPS="
libtiff6=4.5.0-6+deb12u1
libopenjp2-7=2.5.0-2
"
if [ "$TARGETARCH$TARGETVARIANT" = "arm64" ]
then
apt-get update
apt-get install -y --no-install-recommends $BUILD_DEPS $LIB_DEPS
fi
CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse CARGO_HOME=/root/.cargo
pip3 install --break-system-packages --no-cache-dir -r /requirements.txt -r /requirements_optional.txt
if [ "$TARGETARCH$TARGETVARIANT" = "arm64" ]
then
apt-get remove -y --purge --auto-remove $BUILD_DEPS
rm -rf /tmp/* /var/{cache,log}/* /var/lib/apt/lists/*
fi
END-OF-RUN
COPY script/platformio_install_deps.py platformio.ini / COPY script/platformio_install_deps.py platformio.ini /
RUN /platformio_install_deps.py /platformio.ini --libraries RUN /platformio_install_deps.py /platformio.ini --libraries
# Avoid unsafe git error when container user and file config volume permissions don't match ARG BUILD_VERSION
RUN git config --system --add safe.directory '*'
LABEL \
org.opencontainers.image.authors="The ESPHome Authors" \
org.opencontainers.image.title="ESPHome" \
org.opencontainers.image.description="ESPHome is a system to configure your microcontrollers by simple yet powerful configuration files and control them remotely through Home Automation systems" \
org.opencontainers.image.url="https://esphome.io/" \
org.opencontainers.image.documentation="https://esphome.io/" \
org.opencontainers.image.source="https://github.com/esphome/esphome" \
org.opencontainers.image.licenses="ESPHome" \
org.opencontainers.image.version=${BUILD_VERSION}
# ======================= docker-type image ======================= # ======================= docker-type image =======================
FROM base AS docker FROM base AS base-docker
# Copy esphome and install
COPY . /esphome
RUN pip3 install --break-system-packages --no-cache-dir -e /esphome
# Settings for dashboard
ENV USERNAME="" PASSWORD=""
# Expose the dashboard to Docker # Expose the dashboard to Docker
EXPOSE 6052 EXPOSE 6052
# Run healthcheck (heartbeat) # Run healthcheck (heartbeat)
HEALTHCHECK --interval=30s --timeout=30s \ HEALTHCHECK --interval=30s --timeout=30s \
CMD curl --fail http://localhost:6052/version -A "HealthCheck" || exit 1 CMD curl --fail http://localhost:6052/version -A "HealthCheck" || exit 1
COPY docker/docker_entrypoint.sh /entrypoint.sh COPY docker/docker_entrypoint.sh /entrypoint.sh
@@ -139,43 +62,13 @@ ENTRYPOINT ["/entrypoint.sh"]
CMD ["dashboard", "/config"] CMD ["dashboard", "/config"]
ARG BUILD_VERSION=dev # ======================= ha-addon-type image =======================
FROM base AS base-ha-addon
# Labels
LABEL \
org.opencontainers.image.authors="The ESPHome Authors" \
org.opencontainers.image.title="ESPHome" \
org.opencontainers.image.description="ESPHome is a system to configure your microcontrollers by simple yet powerful configuration files and control them remotely through Home Automation systems" \
org.opencontainers.image.url="https://esphome.io/" \
org.opencontainers.image.documentation="https://esphome.io/" \
org.opencontainers.image.source="https://github.com/esphome/esphome" \
org.opencontainers.image.licenses="ESPHome" \
org.opencontainers.image.version=${BUILD_VERSION}
# ======================= hassio-type image =======================
FROM base AS hassio
RUN \
apt-get update \
# Use pinned versions so that we get updates with build caching
&& apt-get install -y --no-install-recommends \
nginx-light=1.22.1-9 \
&& rm -rf \
/tmp/* \
/var/{cache,log}/* \
/var/lib/apt/lists/*
ARG BUILD_VERSION=dev
# Copy root filesystem # Copy root filesystem
COPY docker/ha-addon-rootfs/ / COPY docker/ha-addon-rootfs/ /
# Copy esphome and install ARG BUILD_VERSION
COPY . /esphome
RUN pip3 install --break-system-packages --no-cache-dir -e /esphome
# Labels
LABEL \ LABEL \
io.hass.name="ESPHome" \ io.hass.name="ESPHome" \
io.hass.description="ESPHome is a system to configure your microcontrollers by simple yet powerful configuration files and control them remotely through Home Automation systems" \ io.hass.description="ESPHome is a system to configure your microcontrollers by simple yet powerful configuration files and control them remotely through Home Automation systems" \
@@ -183,35 +76,9 @@ LABEL \
io.hass.version="${BUILD_VERSION}" io.hass.version="${BUILD_VERSION}"
# io.hass.arch is inherited from addon-debian-base # io.hass.arch is inherited from addon-debian-base
ARG BUILD_TYPE
FROM base-${BUILD_TYPE} AS final
# Copy esphome and install
COPY . /esphome
# ======================= lint-type image ======================= RUN uv pip install --no-cache-dir -e /esphome
FROM base AS lint
ENV \
PLATFORMIO_CORE_DIR=/esphome/.temp/platformio
RUN \
curl -L https://apt.llvm.org/llvm-snapshot.gpg.key -o /etc/apt/trusted.gpg.d/apt.llvm.org.asc \
&& echo "deb http://apt.llvm.org/bookworm/ llvm-toolchain-bookworm-18 main" > /etc/apt/sources.list.d/llvm.sources.list \
&& apt-get update \
# Use pinned versions so that we get updates with build caching
&& apt-get install -y --no-install-recommends \
clang-format-13=1:13.0.1-11+b2 \
patch=2.7.6-7 \
software-properties-common=0.99.30-4.1~deb12u1 \
nano=7.2-1+deb12u1 \
build-essential=12.9 \
python3-dev=3.11.2-1+b1 \
clang-tidy-18=1:18.1.8~++20240731024826+3b5b5c1ec4a3-1~exp1~20240731144843.145 \
&& rm -rf \
/tmp/* \
/var/{cache,log}/* \
/var/lib/apt/lists/*
COPY requirements_test.txt /
RUN pip3 install --break-system-packages --no-cache-dir -r /requirements_test.txt
VOLUME ["/esphome"]
WORKDIR /esphome

View File

@@ -54,7 +54,7 @@ manifest_parser = subparsers.add_parser(
class DockerParams: class DockerParams:
build_to: str build_to: str
manifest_to: str manifest_to: str
baseimgtype: str build_type: str
platform: str platform: str
target: str target: str
@@ -66,24 +66,19 @@ class DockerParams:
TYPE_LINT: "esphome/esphome-lint", TYPE_LINT: "esphome/esphome-lint",
}[build_type] }[build_type]
build_to = f"{prefix}-{arch}" build_to = f"{prefix}-{arch}"
baseimgtype = {
TYPE_DOCKER: "docker",
TYPE_HA_ADDON: "hassio",
TYPE_LINT: "docker",
}[build_type]
platform = { platform = {
ARCH_AMD64: "linux/amd64", ARCH_AMD64: "linux/amd64",
ARCH_AARCH64: "linux/arm64", ARCH_AARCH64: "linux/arm64",
}[arch] }[arch]
target = { target = {
TYPE_DOCKER: "docker", TYPE_DOCKER: "final",
TYPE_HA_ADDON: "hassio", TYPE_HA_ADDON: "final",
TYPE_LINT: "lint", TYPE_LINT: "lint",
}[build_type] }[build_type]
return cls( return cls(
build_to=build_to, build_to=build_to,
manifest_to=prefix, manifest_to=prefix,
baseimgtype=baseimgtype, build_type=build_type,
platform=platform, platform=platform,
target=target, target=target,
) )
@@ -145,7 +140,7 @@ def main():
"buildx", "buildx",
"build", "build",
"--build-arg", "--build-arg",
f"BASEIMGTYPE={params.baseimgtype}", f"BUILD_TYPE={params.build_type}",
"--build-arg", "--build-arg",
f"BUILD_VERSION={args.tag}", f"BUILD_VERSION={args.tag}",
"--cache-from", "--cache-from",

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import re
import argparse import argparse
import re
CHANNEL_DEV = "dev" CHANNEL_DEV = "dev"
CHANNEL_BETA = "beta" CHANNEL_BETA = "beta"

View File

@@ -23,10 +23,6 @@ if bashio::config.true 'streamer_mode'; then
export ESPHOME_STREAMER_MODE=true export ESPHOME_STREAMER_MODE=true
fi fi
if bashio::config.true 'status_use_ping'; then
export ESPHOME_DASHBOARD_USE_PING=true
fi
if bashio::config.has_value 'relative_url'; then if bashio::config.has_value 'relative_url'; then
export ESPHOME_DASHBOARD_RELATIVE_URL=$(bashio::config 'relative_url') export ESPHOME_DASHBOARD_RELATIVE_URL=$(bashio::config 'relative_url')
fi fi

View File

@@ -2,6 +2,7 @@
import argparse import argparse
from datetime import datetime from datetime import datetime
import functools import functools
import importlib
import logging import logging
import os import os
import re import re
@@ -66,7 +67,7 @@ def choose_prompt(options, purpose: str = None):
return options[0][1] return options[0][1]
safe_print( safe_print(
f'Found multiple options{f" for {purpose}" if purpose else ""}, please choose one:' f"Found multiple options{f' for {purpose}' if purpose else ''}, please choose one:"
) )
for i, (desc, _) in enumerate(options): for i, (desc, _) in enumerate(options):
safe_print(f" [{i + 1}] {desc}") safe_print(f" [{i + 1}] {desc}")
@@ -132,7 +133,7 @@ def get_port_type(port):
return "NETWORK" return "NETWORK"
def run_miniterm(config, port): def run_miniterm(config, port, args):
import serial import serial
from esphome import platformio_api from esphome import platformio_api
@@ -153,7 +154,7 @@ def run_miniterm(config, port):
# We can't set to False by default since it leads to toggling and hence # We can't set to False by default since it leads to toggling and hence
# ESP32 resets on some platforms. # ESP32 resets on some platforms.
if config["logger"][CONF_DEASSERT_RTS_DTR]: if config["logger"][CONF_DEASSERT_RTS_DTR] or args.reset:
ser.dtr = False ser.dtr = False
ser.rts = False ser.rts = False
@@ -243,11 +244,11 @@ def compile_program(args, config):
return 0 if idedata is not None else 1 return 0 if idedata is not None else 1
def upload_using_esptool(config, port, file): def upload_using_esptool(config, port, file, speed):
from esphome import platformio_api from esphome import platformio_api
first_baudrate = config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get( first_baudrate = speed or config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get(
"upload_speed", 460800 "upload_speed", os.getenv("ESPHOME_UPLOAD_SPEED", "460800")
) )
if file is not None: if file is not None:
@@ -336,11 +337,18 @@ def check_permissions(port):
def upload_program(config, args, host): def upload_program(config, args, host):
try:
module = importlib.import_module("esphome.components." + CORE.target_platform)
if getattr(module, "upload_program")(config, args, host):
return 0
except AttributeError:
pass
if get_port_type(host) == "SERIAL": if get_port_type(host) == "SERIAL":
check_permissions(host) check_permissions(host)
if CORE.target_platform in (PLATFORM_ESP32, PLATFORM_ESP8266): if CORE.target_platform in (PLATFORM_ESP32, PLATFORM_ESP8266):
file = getattr(args, "file", None) file = getattr(args, "file", None)
return upload_using_esptool(config, host, file) return upload_using_esptool(config, host, file, args.upload_speed)
if CORE.target_platform in (PLATFORM_RP2040): if CORE.target_platform in (PLATFORM_RP2040):
return upload_using_platformio(config, args.device) return upload_using_platformio(config, args.device)
@@ -367,10 +375,12 @@ def upload_program(config, args, host):
password = ota_conf.get(CONF_PASSWORD, "") password = ota_conf.get(CONF_PASSWORD, "")
if ( if (
not is_ip_address(CORE.address) # pylint: disable=too-many-boolean-expressions CONF_MQTT in config # pylint: disable=too-many-boolean-expressions
and (get_port_type(host) == "MQTT" or config[CONF_MDNS][CONF_DISABLED])
and CONF_MQTT in config
and (not args.device or args.device in ("MQTT", "OTA")) and (not args.device or args.device in ("MQTT", "OTA"))
and (
((config[CONF_MDNS][CONF_DISABLED]) and not is_ip_address(CORE.address))
or get_port_type(host) == "MQTT"
)
): ):
from esphome import mqtt from esphome import mqtt
@@ -389,7 +399,7 @@ def show_logs(config, args, port):
raise EsphomeError("Logger is not configured!") raise EsphomeError("Logger is not configured!")
if get_port_type(port) == "SERIAL": if get_port_type(port) == "SERIAL":
check_permissions(port) check_permissions(port)
return run_miniterm(config, port) return run_miniterm(config, port, args)
if get_port_type(port) == "NETWORK" and "api" in config: if get_port_type(port) == "NETWORK" and "api" in config:
if config[CONF_MDNS][CONF_DISABLED] and CONF_MQTT in config: if config[CONF_MDNS][CONF_DISABLED] and CONF_MQTT in config:
from esphome import mqtt from esphome import mqtt
@@ -834,6 +844,10 @@ def parse_args(argv):
"--device", "--device",
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.", help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
) )
parser_upload.add_argument(
"--upload_speed",
help="Override the default or configured upload speed.",
)
parser_upload.add_argument( parser_upload.add_argument(
"--file", "--file",
help="Manually specify the binary file to upload.", help="Manually specify the binary file to upload.",
@@ -852,6 +866,13 @@ def parse_args(argv):
"--device", "--device",
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.", help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
) )
parser_logs.add_argument(
"--reset",
"-r",
action="store_true",
help="Reset the device before starting serial logs.",
default=os.getenv("ESPHOME_SERIAL_LOGGING_RESET"),
)
parser_discover = subparsers.add_parser( parser_discover = subparsers.add_parser(
"discover", "discover",
@@ -874,9 +895,20 @@ def parse_args(argv):
"--device", "--device",
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.", help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
) )
parser_run.add_argument(
"--upload_speed",
help="Override the default or configured upload speed.",
)
parser_run.add_argument( parser_run.add_argument(
"--no-logs", help="Disable starting logs.", action="store_true" "--no-logs", help="Disable starting logs.", action="store_true"
) )
parser_run.add_argument(
"--reset",
"-r",
action="store_true",
help="Reset the device before starting serial logs.",
default=os.getenv("ESPHOME_SERIAL_LOGGING_RESET"),
)
parser_clean = subparsers.add_parser( parser_clean = subparsers.add_parser(
"clean-mqtt", "clean-mqtt",

View File

@@ -1,10 +1,10 @@
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import sensor, uart from esphome.components import sensor, uart
from esphome.const import ( from esphome.const import (
DEVICE_CLASS_DISTANCE,
ICON_ARROW_EXPAND_VERTICAL,
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
UNIT_METER, UNIT_METER,
ICON_ARROW_EXPAND_VERTICAL,
DEVICE_CLASS_DISTANCE,
) )
CODEOWNERS = ["@MrSuicideParrot"] CODEOWNERS = ["@MrSuicideParrot"]

View File

@@ -1,9 +1,9 @@
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import sensor, uart from esphome.components import sensor, uart
from esphome.const import ( from esphome.const import (
STATE_CLASS_MEASUREMENT,
ICON_ARROW_EXPAND_VERTICAL,
DEVICE_CLASS_DISTANCE, DEVICE_CLASS_DISTANCE,
ICON_ARROW_EXPAND_VERTICAL,
STATE_CLASS_MEASUREMENT,
UNIT_MILLIMETER, UNIT_MILLIMETER,
) )

View File

@@ -1,10 +1,9 @@
from esphome import pins from esphome import pins
import esphome.codegen as cg
from esphome.components import stepper from esphome.components import stepper
import esphome.config_validation as cv import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.const import CONF_DIR_PIN, CONF_ID, CONF_SLEEP_PIN, CONF_STEP_PIN from esphome.const import CONF_DIR_PIN, CONF_ID, CONF_SLEEP_PIN, CONF_STEP_PIN
a4988_ns = cg.esphome_ns.namespace("a4988") a4988_ns = cg.esphome_ns.namespace("a4988")
A4988 = a4988_ns.class_("A4988", stepper.Stepper, cg.Component) A4988 = a4988_ns.class_("A4988", stepper.Stepper, cg.Component)

View File

@@ -1,12 +1,12 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor from esphome.components import sensor
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_EQUATION,
CONF_HUMIDITY, CONF_HUMIDITY,
CONF_TEMPERATURE, CONF_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
CONF_EQUATION,
ICON_WATER, ICON_WATER,
STATE_CLASS_MEASUREMENT,
UNIT_GRAMS_PER_CUBIC_METER, UNIT_GRAMS_PER_CUBIC_METER,
) )

View File

@@ -114,13 +114,14 @@ void IRAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
// fully off, disable output immediately // fully off, disable output immediately
this->gate_pin.digital_write(false); this->gate_pin.digital_write(false);
} else { } else {
auto min_us = this->cycle_time_us * this->min_power / 1000;
if (this->method == DIM_METHOD_TRAILING) { if (this->method == DIM_METHOD_TRAILING) {
this->enable_time_us = 1; // cannot be 0 this->enable_time_us = 1; // cannot be 0
this->disable_time_us = std::max((uint32_t) 10, this->value * this->cycle_time_us / 65535); // calculate time until disable in µs with integer arithmetic and take into account min_power
this->disable_time_us = std::max((uint32_t) 10, this->value * (this->cycle_time_us - min_us) / 65535 + min_us);
} else { } else {
// calculate time until enable in µs: (1.0-value)*cycle_time, but with integer arithmetic // calculate time until enable in µs: (1.0-value)*cycle_time, but with integer arithmetic
// also take into account min_power // also take into account min_power
auto min_us = this->cycle_time_us * this->min_power / 1000;
this->enable_time_us = std::max((uint32_t) 1, ((65535 - this->value) * (this->cycle_time_us - min_us)) / 65535); this->enable_time_us = std::max((uint32_t) 1, ((65535 - this->value) * (this->cycle_time_us - min_us)) / 65535);
if (this->method == DIM_METHOD_LEADING_PULSE) { if (this->method == DIM_METHOD_LEADING_PULSE) {

View File

@@ -1,8 +1,8 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins from esphome import pins
import esphome.codegen as cg
from esphome.components import output from esphome.components import output
from esphome.const import CONF_ID, CONF_MIN_POWER, CONF_METHOD import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_METHOD, CONF_MIN_POWER
CODEOWNERS = ["@glmnet"] CODEOWNERS = ["@glmnet"]

View File

@@ -1,8 +1,8 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import uart from esphome.components import uart
from esphome.components.light.types import AddressableLightEffect
from esphome.components.light.effects import register_addressable_effect from esphome.components.light.effects import register_addressable_effect
from esphome.components.light.types import AddressableLightEffect
import esphome.config_validation as cv
from esphome.const import CONF_NAME, CONF_UART_ID from esphome.const import CONF_NAME, CONF_UART_ID
DEPENDENCIES = ["uart"] DEPENDENCIES = ["uart"]

View File

@@ -47,9 +47,10 @@ SAMPLING_MODES = {
adc1_channel_t = cg.global_ns.enum("adc1_channel_t") adc1_channel_t = cg.global_ns.enum("adc1_channel_t")
adc2_channel_t = cg.global_ns.enum("adc2_channel_t") adc2_channel_t = cg.global_ns.enum("adc2_channel_t")
# From https://github.com/espressif/esp-idf/blob/master/components/driver/include/driver/adc_common.h
# pin to adc1 channel mapping # pin to adc1 channel mapping
# https://github.com/espressif/esp-idf/blob/v4.4.8/components/driver/include/driver/adc.h
ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = { ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = {
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32/include/soc/adc_channel.h
VARIANT_ESP32: { VARIANT_ESP32: {
36: adc1_channel_t.ADC1_CHANNEL_0, 36: adc1_channel_t.ADC1_CHANNEL_0,
37: adc1_channel_t.ADC1_CHANNEL_1, 37: adc1_channel_t.ADC1_CHANNEL_1,
@@ -60,6 +61,41 @@ ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = {
34: adc1_channel_t.ADC1_CHANNEL_6, 34: adc1_channel_t.ADC1_CHANNEL_6,
35: adc1_channel_t.ADC1_CHANNEL_7, 35: adc1_channel_t.ADC1_CHANNEL_7,
}, },
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c2/include/soc/adc_channel.h
VARIANT_ESP32C2: {
0: adc1_channel_t.ADC1_CHANNEL_0,
1: adc1_channel_t.ADC1_CHANNEL_1,
2: adc1_channel_t.ADC1_CHANNEL_2,
3: adc1_channel_t.ADC1_CHANNEL_3,
4: adc1_channel_t.ADC1_CHANNEL_4,
},
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c3/include/soc/adc_channel.h
VARIANT_ESP32C3: {
0: adc1_channel_t.ADC1_CHANNEL_0,
1: adc1_channel_t.ADC1_CHANNEL_1,
2: adc1_channel_t.ADC1_CHANNEL_2,
3: adc1_channel_t.ADC1_CHANNEL_3,
4: adc1_channel_t.ADC1_CHANNEL_4,
},
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c6/include/soc/adc_channel.h
VARIANT_ESP32C6: {
0: adc1_channel_t.ADC1_CHANNEL_0,
1: adc1_channel_t.ADC1_CHANNEL_1,
2: adc1_channel_t.ADC1_CHANNEL_2,
3: adc1_channel_t.ADC1_CHANNEL_3,
4: adc1_channel_t.ADC1_CHANNEL_4,
5: adc1_channel_t.ADC1_CHANNEL_5,
6: adc1_channel_t.ADC1_CHANNEL_6,
},
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32h2/include/soc/adc_channel.h
VARIANT_ESP32H2: {
1: adc1_channel_t.ADC1_CHANNEL_0,
2: adc1_channel_t.ADC1_CHANNEL_1,
3: adc1_channel_t.ADC1_CHANNEL_2,
4: adc1_channel_t.ADC1_CHANNEL_3,
5: adc1_channel_t.ADC1_CHANNEL_4,
},
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32s2/include/soc/adc_channel.h
VARIANT_ESP32S2: { VARIANT_ESP32S2: {
1: adc1_channel_t.ADC1_CHANNEL_0, 1: adc1_channel_t.ADC1_CHANNEL_0,
2: adc1_channel_t.ADC1_CHANNEL_1, 2: adc1_channel_t.ADC1_CHANNEL_1,
@@ -72,6 +108,7 @@ ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = {
9: adc1_channel_t.ADC1_CHANNEL_8, 9: adc1_channel_t.ADC1_CHANNEL_8,
10: adc1_channel_t.ADC1_CHANNEL_9, 10: adc1_channel_t.ADC1_CHANNEL_9,
}, },
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32s3/include/soc/adc_channel.h
VARIANT_ESP32S3: { VARIANT_ESP32S3: {
1: adc1_channel_t.ADC1_CHANNEL_0, 1: adc1_channel_t.ADC1_CHANNEL_0,
2: adc1_channel_t.ADC1_CHANNEL_1, 2: adc1_channel_t.ADC1_CHANNEL_1,
@@ -84,40 +121,12 @@ ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = {
9: adc1_channel_t.ADC1_CHANNEL_8, 9: adc1_channel_t.ADC1_CHANNEL_8,
10: adc1_channel_t.ADC1_CHANNEL_9, 10: adc1_channel_t.ADC1_CHANNEL_9,
}, },
VARIANT_ESP32C3: {
0: adc1_channel_t.ADC1_CHANNEL_0,
1: adc1_channel_t.ADC1_CHANNEL_1,
2: adc1_channel_t.ADC1_CHANNEL_2,
3: adc1_channel_t.ADC1_CHANNEL_3,
4: adc1_channel_t.ADC1_CHANNEL_4,
},
VARIANT_ESP32C2: {
0: adc1_channel_t.ADC1_CHANNEL_0,
1: adc1_channel_t.ADC1_CHANNEL_1,
2: adc1_channel_t.ADC1_CHANNEL_2,
3: adc1_channel_t.ADC1_CHANNEL_3,
4: adc1_channel_t.ADC1_CHANNEL_4,
},
VARIANT_ESP32C6: {
0: adc1_channel_t.ADC1_CHANNEL_0,
1: adc1_channel_t.ADC1_CHANNEL_1,
2: adc1_channel_t.ADC1_CHANNEL_2,
3: adc1_channel_t.ADC1_CHANNEL_3,
4: adc1_channel_t.ADC1_CHANNEL_4,
5: adc1_channel_t.ADC1_CHANNEL_5,
6: adc1_channel_t.ADC1_CHANNEL_6,
},
VARIANT_ESP32H2: {
1: adc1_channel_t.ADC1_CHANNEL_0,
2: adc1_channel_t.ADC1_CHANNEL_1,
3: adc1_channel_t.ADC1_CHANNEL_2,
4: adc1_channel_t.ADC1_CHANNEL_3,
5: adc1_channel_t.ADC1_CHANNEL_4,
},
} }
# pin to adc2 channel mapping
# https://github.com/espressif/esp-idf/blob/v4.4.8/components/driver/include/driver/adc.h
ESP32_VARIANT_ADC2_PIN_TO_CHANNEL = { ESP32_VARIANT_ADC2_PIN_TO_CHANNEL = {
# TODO: add other variants # https://github.com/espressif/esp-idf/blob/master/components/soc/esp32/include/soc/adc_channel.h
VARIANT_ESP32: { VARIANT_ESP32: {
4: adc2_channel_t.ADC2_CHANNEL_0, 4: adc2_channel_t.ADC2_CHANNEL_0,
0: adc2_channel_t.ADC2_CHANNEL_1, 0: adc2_channel_t.ADC2_CHANNEL_1,
@@ -130,6 +139,19 @@ ESP32_VARIANT_ADC2_PIN_TO_CHANNEL = {
25: adc2_channel_t.ADC2_CHANNEL_8, 25: adc2_channel_t.ADC2_CHANNEL_8,
26: adc2_channel_t.ADC2_CHANNEL_9, 26: adc2_channel_t.ADC2_CHANNEL_9,
}, },
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c2/include/soc/adc_channel.h
VARIANT_ESP32C2: {
5: adc2_channel_t.ADC2_CHANNEL_0,
},
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c3/include/soc/adc_channel.h
VARIANT_ESP32C3: {
5: adc2_channel_t.ADC2_CHANNEL_0,
},
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c6/include/soc/adc_channel.h
VARIANT_ESP32C6: {}, # no ADC2
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32h2/include/soc/adc_channel.h
VARIANT_ESP32H2: {}, # no ADC2
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32s2/include/soc/adc_channel.h
VARIANT_ESP32S2: { VARIANT_ESP32S2: {
11: adc2_channel_t.ADC2_CHANNEL_0, 11: adc2_channel_t.ADC2_CHANNEL_0,
12: adc2_channel_t.ADC2_CHANNEL_1, 12: adc2_channel_t.ADC2_CHANNEL_1,
@@ -142,6 +164,7 @@ ESP32_VARIANT_ADC2_PIN_TO_CHANNEL = {
19: adc2_channel_t.ADC2_CHANNEL_8, 19: adc2_channel_t.ADC2_CHANNEL_8,
20: adc2_channel_t.ADC2_CHANNEL_9, 20: adc2_channel_t.ADC2_CHANNEL_9,
}, },
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32s3/include/soc/adc_channel.h
VARIANT_ESP32S3: { VARIANT_ESP32S3: {
11: adc2_channel_t.ADC2_CHANNEL_0, 11: adc2_channel_t.ADC2_CHANNEL_0,
12: adc2_channel_t.ADC2_CHANNEL_1, 12: adc2_channel_t.ADC2_CHANNEL_1,
@@ -154,12 +177,6 @@ ESP32_VARIANT_ADC2_PIN_TO_CHANNEL = {
19: adc2_channel_t.ADC2_CHANNEL_8, 19: adc2_channel_t.ADC2_CHANNEL_8,
20: adc2_channel_t.ADC2_CHANNEL_9, 20: adc2_channel_t.ADC2_CHANNEL_9,
}, },
VARIANT_ESP32C3: {
5: adc2_channel_t.ADC2_CHANNEL_0,
},
VARIANT_ESP32C2: {},
VARIANT_ESP32C6: {},
VARIANT_ESP32H2: {},
} }

View File

@@ -1,6 +1,6 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import spi from esphome.components import spi
import esphome.config_validation as cv
from esphome.const import CONF_ID from esphome.const import CONF_ID
DEPENDENCIES = ["spi"] DEPENDENCIES = ["spi"]

View File

@@ -1,9 +1,9 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, voltage_sampler from esphome.components import sensor, voltage_sampler
from esphome.const import CONF_ID, CONF_CHANNEL import esphome.config_validation as cv
from esphome.const import CONF_CHANNEL, CONF_ID
from .. import adc128s102_ns, ADC128S102 from .. import ADC128S102, adc128s102_ns
AUTO_LOAD = ["voltage_sampler"] AUTO_LOAD = ["voltage_sampler"]
DEPENDENCIES = ["adc128s102"] DEPENDENCIES = ["adc128s102"]

View File

@@ -1,15 +1,15 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import display, light from esphome.components import display, light
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_ADDRESSABLE_LIGHT_ID,
CONF_HEIGHT,
CONF_ID, CONF_ID,
CONF_LAMBDA, CONF_LAMBDA,
CONF_PAGES, CONF_PAGES,
CONF_ADDRESSABLE_LIGHT_ID,
CONF_HEIGHT,
CONF_WIDTH,
CONF_UPDATE_INTERVAL,
CONF_PIXEL_MAPPER, CONF_PIXEL_MAPPER,
CONF_UPDATE_INTERVAL,
CONF_WIDTH,
) )
CODEOWNERS = ["@justfalter"] CODEOWNERS = ["@justfalter"]

View File

@@ -1,7 +1,7 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, i2c
from esphome import pins from esphome import pins
import esphome.codegen as cg
from esphome.components import i2c, sensor
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_ACTIVE_POWER, CONF_ACTIVE_POWER,
CONF_APPARENT_POWER, CONF_APPARENT_POWER,

View File

@@ -1,27 +1,27 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor
from esphome import pins from esphome import pins
import esphome.codegen as cg
from esphome.components import sensor
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_FREQUENCY,
CONF_IRQ_PIN, CONF_IRQ_PIN,
CONF_VOLTAGE, CONF_VOLTAGE,
CONF_FREQUENCY,
CONF_VOLTAGE_GAIN, CONF_VOLTAGE_GAIN,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_APPARENT_POWER, DEVICE_CLASS_APPARENT_POWER,
DEVICE_CLASS_POWER, DEVICE_CLASS_CURRENT,
DEVICE_CLASS_REACTIVE_POWER,
DEVICE_CLASS_POWER_FACTOR,
DEVICE_CLASS_VOLTAGE,
DEVICE_CLASS_FREQUENCY, DEVICE_CLASS_FREQUENCY,
DEVICE_CLASS_POWER,
DEVICE_CLASS_POWER_FACTOR,
DEVICE_CLASS_REACTIVE_POWER,
DEVICE_CLASS_VOLTAGE,
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
UNIT_VOLT,
UNIT_HERTZ,
UNIT_AMPERE, UNIT_AMPERE,
UNIT_VOLT_AMPS, UNIT_HERTZ,
UNIT_WATT,
UNIT_VOLT_AMPS_REACTIVE,
UNIT_PERCENT, UNIT_PERCENT,
UNIT_VOLT,
UNIT_VOLT_AMPS,
UNIT_VOLT_AMPS_REACTIVE,
UNIT_WATT,
) )
CONF_CURRENT_A = "current_a" CONF_CURRENT_A = "current_a"

View File

@@ -1,9 +1,8 @@
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import ade7953_base, i2c
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import i2c, ade7953_base
from esphome.const import CONF_ID from esphome.const import CONF_ID
DEPENDENCIES = ["i2c"] DEPENDENCIES = ["i2c"]
AUTO_LOAD = ["ade7953_base"] AUTO_LOAD = ["ade7953_base"]

View File

@@ -1,9 +1,8 @@
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import ade7953_base, spi
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import spi, ade7953_base
from esphome.const import CONF_ID from esphome.const import CONF_ID
DEPENDENCIES = ["spi"] DEPENDENCIES = ["spi"]
AUTO_LOAD = ["ade7953_base"] AUTO_LOAD = ["ade7953_base"]

View File

@@ -1,6 +1,6 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c from esphome.components import i2c
import esphome.config_validation as cv
from esphome.const import CONF_ID from esphome.const import CONF_ID
DEPENDENCIES = ["i2c"] DEPENDENCIES = ["i2c"]

View File

@@ -1,17 +1,18 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, voltage_sampler from esphome.components import sensor, voltage_sampler
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_GAIN, CONF_GAIN,
CONF_ID,
CONF_MULTIPLEXER, CONF_MULTIPLEXER,
CONF_RESOLUTION, CONF_RESOLUTION,
CONF_SAMPLE_RATE, CONF_SAMPLE_RATE,
DEVICE_CLASS_VOLTAGE, DEVICE_CLASS_VOLTAGE,
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
UNIT_VOLT, UNIT_VOLT,
CONF_ID,
) )
from .. import ads1115_ns, ADS1115Component, CONF_ADS1115_ID
from .. import CONF_ADS1115_ID, ADS1115Component, ads1115_ns
AUTO_LOAD = ["voltage_sampler"] AUTO_LOAD = ["voltage_sampler"]
DEPENDENCIES = ["ads1115"] DEPENDENCIES = ["ads1115"]

View File

@@ -1,6 +1,6 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import spi from esphome.components import spi
import esphome.config_validation as cv
from esphome.const import CONF_ID from esphome.const import CONF_ID
CODEOWNERS = ["@solomondg1"] CODEOWNERS = ["@solomondg1"]

View File

@@ -1,17 +1,18 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, voltage_sampler from esphome.components import sensor, voltage_sampler
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_GAIN, CONF_GAIN,
CONF_MULTIPLEXER, CONF_MULTIPLEXER,
DEVICE_CLASS_VOLTAGE, CONF_TYPE,
DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_VOLTAGE,
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS, UNIT_CELSIUS,
UNIT_VOLT, UNIT_VOLT,
CONF_TYPE,
) )
from .. import ads1118_ns, ADS1118, CONF_ADS1118_ID
from .. import ADS1118, CONF_ADS1118_ID, ads1118_ns
AUTO_LOAD = ["voltage_sampler"] AUTO_LOAD = ["voltage_sampler"]
DEPENDENCIES = ["ads1118"] DEPENDENCIES = ["ads1118"]

View File

@@ -1,21 +1,21 @@
import esphome.codegen as cg
from esphome import automation from esphome import automation
import esphome.config_validation as cv import esphome.codegen as cg
from esphome.components import i2c, sensor from esphome.components import i2c, sensor
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_ADDRESS,
CONF_ID, CONF_ID,
ICON_RADIATOR, CONF_MODE,
ICON_RESTART, CONF_TVOC,
CONF_VALUE,
CONF_VERSION,
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
ENTITY_CATEGORY_DIAGNOSTIC, ENTITY_CATEGORY_DIAGNOSTIC,
ICON_RADIATOR,
ICON_RESTART,
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
UNIT_OHM, UNIT_OHM,
UNIT_PARTS_PER_BILLION, UNIT_PARTS_PER_BILLION,
CONF_ADDRESS,
CONF_TVOC,
CONF_VERSION,
CONF_MODE,
CONF_VALUE,
) )
CONF_RESISTANCE = "resistance" CONF_RESISTANCE = "resistance"

View File

@@ -1,16 +1,16 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor from esphome.components import i2c, sensor
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_HUMIDITY, CONF_HUMIDITY,
CONF_ID, CONF_ID,
CONF_TEMPERATURE, CONF_TEMPERATURE,
CONF_VARIANT,
DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS, UNIT_CELSIUS,
UNIT_PERCENT, UNIT_PERCENT,
CONF_VARIANT,
) )
DEPENDENCIES = ["i2c"] DEPENDENCIES = ["i2c"]

View File

@@ -1,6 +1,6 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import esp32_ble_tracker from esphome.components import esp32_ble_tracker
import esphome.config_validation as cv
from esphome.const import CONF_ID from esphome.const import CONF_ID
DEPENDENCIES = ["esp32_ble_tracker"] DEPENDENCIES = ["esp32_ble_tracker"]

View File

@@ -1,18 +1,17 @@
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import ble_client, sensor
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import sensor, ble_client
from esphome.const import ( from esphome.const import (
CONF_BATTERY_VOLTAGE, CONF_BATTERY_VOLTAGE,
CONF_HUMIDITY, CONF_HUMIDITY,
CONF_PRESSURE, CONF_PRESSURE,
CONF_TEMPERATURE, CONF_TEMPERATURE,
CONF_TVOC, CONF_TVOC,
DEVICE_CLASS_VOLTAGE,
DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_PRESSURE, DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS, DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
DEVICE_CLASS_VOLTAGE,
ENTITY_CATEGORY_DIAGNOSTIC, ENTITY_CATEGORY_DIAGNOSTIC,
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS, UNIT_CELSIUS,

View File

@@ -1,10 +1,7 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import airthings_wave_base from esphome.components import airthings_wave_base
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import CONF_ID
CONF_ID,
)
DEPENDENCIES = airthings_wave_base.DEPENDENCIES DEPENDENCIES = airthings_wave_base.DEPENDENCIES

View File

@@ -1,20 +1,19 @@
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import airthings_wave_base, sensor
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import sensor, airthings_wave_base
from esphome.const import ( from esphome.const import (
DEVICE_CLASS_CARBON_DIOXIDE, CONF_CO2,
STATE_CLASS_MEASUREMENT,
ICON_RADIOACTIVE,
CONF_ID, CONF_ID,
CONF_ILLUMINANCE,
CONF_RADON, CONF_RADON,
CONF_RADON_LONG_TERM, CONF_RADON_LONG_TERM,
CONF_CO2, DEVICE_CLASS_CARBON_DIOXIDE,
UNIT_BECQUEREL_PER_CUBIC_METER,
UNIT_PARTS_PER_MILLION,
CONF_ILLUMINANCE,
UNIT_LUX,
DEVICE_CLASS_ILLUMINANCE, DEVICE_CLASS_ILLUMINANCE,
ICON_RADIOACTIVE,
STATE_CLASS_MEASUREMENT,
UNIT_BECQUEREL_PER_CUBIC_METER,
UNIT_LUX,
UNIT_PARTS_PER_MILLION,
) )
DEPENDENCIES = airthings_wave_base.DEPENDENCIES DEPENDENCIES = airthings_wave_base.DEPENDENCIES

View File

@@ -1,20 +1,20 @@
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import ble_client, sensor
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import sensor, ble_client
from esphome.const import ( from esphome.const import (
CONF_ID,
CONF_CURRENT, CONF_CURRENT,
CONF_FLOW, CONF_FLOW,
CONF_HEAD, CONF_HEAD,
CONF_ID,
CONF_POWER, CONF_POWER,
CONF_SPEED, CONF_SPEED,
CONF_VOLTAGE, CONF_VOLTAGE,
UNIT_AMPERE, UNIT_AMPERE,
UNIT_CUBIC_METER_PER_HOUR,
UNIT_METER,
UNIT_REVOLUTIONS_PER_MINUTE,
UNIT_VOLT, UNIT_VOLT,
UNIT_WATT, UNIT_WATT,
UNIT_METER,
UNIT_CUBIC_METER_PER_HOUR,
UNIT_REVOLUTIONS_PER_MINUTE,
) )
alpha3_ns = cg.esphome_ns.namespace("alpha3") alpha3_ns = cg.esphome_ns.namespace("alpha3")

View File

@@ -128,7 +128,7 @@ void AM2315C::update() {
data[2] = 0x00; data[2] = 0x00;
if (this->write(data, 3) != i2c::ERROR_OK) { if (this->write(data, 3) != i2c::ERROR_OK) {
ESP_LOGE(TAG, "Write failed!"); ESP_LOGE(TAG, "Write failed!");
this->mark_failed(); this->status_set_warning();
return; return;
} }
@@ -138,12 +138,12 @@ void AM2315C::update() {
uint8_t status = 0; uint8_t status = 0;
if (this->read(&status, 1) != i2c::ERROR_OK) { if (this->read(&status, 1) != i2c::ERROR_OK) {
ESP_LOGE(TAG, "Read failed!"); ESP_LOGE(TAG, "Read failed!");
this->mark_failed(); this->status_set_warning();
return; return;
} }
if ((status & 0x80) == 0x80) { if ((status & 0x80) == 0x80) {
ESP_LOGE(TAG, "HW still busy!"); ESP_LOGE(TAG, "HW still busy!");
this->mark_failed(); this->status_set_warning();
return; return;
} }
@@ -151,7 +151,7 @@ void AM2315C::update() {
uint8_t data[7]; uint8_t data[7];
if (this->read(data, 7) != i2c::ERROR_OK) { if (this->read(data, 7) != i2c::ERROR_OK) {
ESP_LOGE(TAG, "Read failed!"); ESP_LOGE(TAG, "Read failed!");
this->mark_failed(); this->status_set_warning();
return; return;
} }

View File

@@ -1,6 +1,6 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor from esphome.components import i2c, sensor
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_HUMIDITY, CONF_HUMIDITY,
CONF_ID, CONF_ID,

View File

@@ -1,6 +1,6 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor from esphome.components import i2c, sensor
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_HUMIDITY, CONF_HUMIDITY,
CONF_ID, CONF_ID,

View File

@@ -1,6 +1,6 @@
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import ble_client, cover
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import cover, ble_client
from esphome.const import CONF_ID, CONF_PIN from esphome.const import CONF_ID, CONF_PIN
CODEOWNERS = ["@buxtronix"] CODEOWNERS = ["@buxtronix"]

View File

@@ -1,12 +1,12 @@
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import ble_client, sensor
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import sensor, ble_client
from esphome.const import ( from esphome.const import (
CONF_ID,
CONF_BATTERY_LEVEL, CONF_BATTERY_LEVEL,
CONF_ID,
CONF_ILLUMINANCE,
DEVICE_CLASS_BATTERY, DEVICE_CLASS_BATTERY,
ENTITY_CATEGORY_DIAGNOSTIC, ENTITY_CATEGORY_DIAGNOSTIC,
CONF_ILLUMINANCE,
ICON_BRIGHTNESS_5, ICON_BRIGHTNESS_5,
UNIT_PERCENT, UNIT_PERCENT,
) )

View File

@@ -14,7 +14,8 @@ void AnalogThresholdBinarySensor::setup() {
if (std::isnan(sensor_value)) { if (std::isnan(sensor_value)) {
this->publish_initial_state(false); this->publish_initial_state(false);
} else { } else {
this->publish_initial_state(sensor_value >= (this->lower_threshold_ + this->upper_threshold_) / 2.0f); this->publish_initial_state(sensor_value >=
(this->lower_threshold_.value() + this->upper_threshold_.value()) / 2.0f);
} }
} }
@@ -24,7 +25,8 @@ void AnalogThresholdBinarySensor::set_sensor(sensor::Sensor *analog_sensor) {
this->sensor_->add_on_state_callback([this](float sensor_value) { this->sensor_->add_on_state_callback([this](float sensor_value) {
// if there is an invalid sensor reading, ignore the change and keep the current state // if there is an invalid sensor reading, ignore the change and keep the current state
if (!std::isnan(sensor_value)) { if (!std::isnan(sensor_value)) {
this->publish_state(sensor_value >= (this->state ? this->lower_threshold_ : this->upper_threshold_)); this->publish_state(sensor_value >=
(this->state ? this->lower_threshold_.value() : this->upper_threshold_.value()));
} }
}); });
} }
@@ -32,8 +34,8 @@ void AnalogThresholdBinarySensor::set_sensor(sensor::Sensor *analog_sensor) {
void AnalogThresholdBinarySensor::dump_config() { void AnalogThresholdBinarySensor::dump_config() {
LOG_BINARY_SENSOR("", "Analog Threshold Binary Sensor", this); LOG_BINARY_SENSOR("", "Analog Threshold Binary Sensor", this);
LOG_SENSOR(" ", "Sensor", this->sensor_); LOG_SENSOR(" ", "Sensor", this->sensor_);
ESP_LOGCONFIG(TAG, " Upper threshold: %.11f", this->upper_threshold_); ESP_LOGCONFIG(TAG, " Upper threshold: %.11f", this->upper_threshold_.value());
ESP_LOGCONFIG(TAG, " Lower threshold: %.11f", this->lower_threshold_); ESP_LOGCONFIG(TAG, " Lower threshold: %.11f", this->lower_threshold_.value());
} }
} // namespace analog_threshold } // namespace analog_threshold

View File

@@ -15,14 +15,13 @@ class AnalogThresholdBinarySensor : public Component, public binary_sensor::Bina
float get_setup_priority() const override { return setup_priority::DATA; } float get_setup_priority() const override { return setup_priority::DATA; }
void set_sensor(sensor::Sensor *analog_sensor); void set_sensor(sensor::Sensor *analog_sensor);
void set_upper_threshold(float threshold) { this->upper_threshold_ = threshold; } template<typename T> void set_upper_threshold(T upper_threshold) { this->upper_threshold_ = upper_threshold; }
void set_lower_threshold(float threshold) { this->lower_threshold_ = threshold; } template<typename T> void set_lower_threshold(T lower_threshold) { this->lower_threshold_ = lower_threshold; }
protected: protected:
sensor::Sensor *sensor_{nullptr}; sensor::Sensor *sensor_{nullptr};
TemplatableValue<float> upper_threshold_{};
float upper_threshold_; TemplatableValue<float> lower_threshold_{};
float lower_threshold_;
}; };
} // namespace analog_threshold } // namespace analog_threshold

View File

@@ -1,10 +1,7 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import binary_sensor, sensor from esphome.components import binary_sensor, sensor
from esphome.const import ( import esphome.config_validation as cv
CONF_SENSOR_ID, from esphome.const import CONF_SENSOR_ID, CONF_THRESHOLD
CONF_THRESHOLD,
)
analog_threshold_ns = cg.esphome_ns.namespace("analog_threshold") analog_threshold_ns = cg.esphome_ns.namespace("analog_threshold")
@@ -21,11 +18,11 @@ CONFIG_SCHEMA = (
{ {
cv.Required(CONF_SENSOR_ID): cv.use_id(sensor.Sensor), cv.Required(CONF_SENSOR_ID): cv.use_id(sensor.Sensor),
cv.Required(CONF_THRESHOLD): cv.Any( cv.Required(CONF_THRESHOLD): cv.Any(
cv.float_, cv.templatable(cv.float_),
cv.Schema( cv.Schema(
{ {
cv.Required(CONF_UPPER): cv.float_, cv.Required(CONF_UPPER): cv.templatable(cv.float_),
cv.Required(CONF_LOWER): cv.float_, cv.Required(CONF_LOWER): cv.templatable(cv.float_),
} }
), ),
), ),
@@ -42,9 +39,11 @@ async def to_code(config):
sens = await cg.get_variable(config[CONF_SENSOR_ID]) sens = await cg.get_variable(config[CONF_SENSOR_ID])
cg.add(var.set_sensor(sens)) cg.add(var.set_sensor(sens))
if isinstance(config[CONF_THRESHOLD], float): if isinstance(config[CONF_THRESHOLD], dict):
cg.add(var.set_upper_threshold(config[CONF_THRESHOLD])) lower = await cg.templatable(config[CONF_THRESHOLD][CONF_LOWER], [], float)
cg.add(var.set_lower_threshold(config[CONF_THRESHOLD])) upper = await cg.templatable(config[CONF_THRESHOLD][CONF_UPPER], [], float)
else: else:
cg.add(var.set_upper_threshold(config[CONF_THRESHOLD][CONF_UPPER])) lower = await cg.templatable(config[CONF_THRESHOLD], [], float)
cg.add(var.set_lower_threshold(config[CONF_THRESHOLD][CONF_LOWER])) upper = lower
cg.add(var.set_upper_threshold(upper))
cg.add(var.set_lower_threshold(lower))

View File

@@ -1,6 +1,6 @@
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import ble_client, climate
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import climate, ble_client
from esphome.const import CONF_ID, CONF_UNIT_OF_MEASUREMENT from esphome.const import CONF_ID, CONF_UNIT_OF_MEASUREMENT
UNITS = { UNITS = {

View File

@@ -2,8 +2,8 @@
# https://www.mouser.ca/datasheet/2/678/AVGO_S_A0002854364_1-2574547.pdf # https://www.mouser.ca/datasheet/2/678/AVGO_S_A0002854364_1-2574547.pdf
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor from esphome.components import i2c, sensor
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_GAIN, CONF_GAIN,
DEVICE_CLASS_ILLUMINANCE, DEVICE_CLASS_ILLUMINANCE,

View File

@@ -1,6 +1,6 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c from esphome.components import i2c
import esphome.config_validation as cv
from esphome.const import CONF_ID from esphome.const import CONF_ID
DEPENDENCIES = ["i2c"] DEPENDENCIES = ["i2c"]

View File

@@ -1,7 +1,8 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import binary_sensor from esphome.components import binary_sensor
import esphome.config_validation as cv
from esphome.const import CONF_DIRECTION, DEVICE_CLASS_MOVING from esphome.const import CONF_DIRECTION, DEVICE_CLASS_MOVING
from . import APDS9960, CONF_APDS9960_ID from . import APDS9960, CONF_APDS9960_ID
DEPENDENCIES = ["apds9960"] DEPENDENCIES = ["apds9960"]

View File

@@ -1,12 +1,13 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor from esphome.components import sensor
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_TYPE, CONF_TYPE,
ICON_LIGHTBULB,
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
UNIT_PERCENT, UNIT_PERCENT,
ICON_LIGHTBULB,
) )
from . import APDS9960, CONF_APDS9960_ID from . import APDS9960, CONF_APDS9960_ID
DEPENDENCIES = ["apds9960"] DEPENDENCIES = ["apds9960"]

View File

@@ -1,8 +1,10 @@
import base64 import base64
import logging
from esphome import automation from esphome import automation
from esphome.automation import Condition from esphome.automation import Condition
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components.esp32 import add_idf_sdkconfig_option
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_ACTION, CONF_ACTION,
@@ -23,12 +25,14 @@ from esphome.const import (
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
CONF_VARIABLES, CONF_VARIABLES,
) )
from esphome.core import coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
DEPENDENCIES = ["network"] DEPENDENCIES = ["network"]
AUTO_LOAD = ["socket"] AUTO_LOAD = ["socket"]
CODEOWNERS = ["@OttoWinter"] CODEOWNERS = ["@OttoWinter"]
_LOGGER = logging.getLogger(__name__)
api_ns = cg.esphome_ns.namespace("api") api_ns = cg.esphome_ns.namespace("api")
APIServer = api_ns.class_("APIServer", cg.Component, cg.Controller) APIServer = api_ns.class_("APIServer", cg.Component, cg.Controller)
HomeAssistantServiceCallAction = api_ns.class_( HomeAssistantServiceCallAction = api_ns.class_(
@@ -49,6 +53,11 @@ SERVICE_ARG_NATIVE_TYPES = {
"string[]": cg.std_vector.template(cg.std_string), "string[]": cg.std_vector.template(cg.std_string),
} }
CONF_ENCRYPTION = "encryption" CONF_ENCRYPTION = "encryption"
CONF_HEAP_TRACING = "heap_tracing"
CONF_HEAP_TRACING_STANDALONE = "standalone" # vs SYSTEM
CONF_HEAP_TRACING_RECORDS = "num_records"
CONF_HEAP_TASK_TRACKING = "task_tracking"
CONF_HEAP_TASK_MAX = "max_tasks"
def validate_encryption_key(value): def validate_encryption_key(value):
@@ -82,6 +91,35 @@ ACTIONS_SCHEMA = automation.validate_automation(
), ),
) )
ENCRYPTION_SCHEMA = cv.Schema(
{
cv.Optional(CONF_KEY): validate_encryption_key,
}
)
def _encryption_schema(config):
if config is None:
config = {}
return ENCRYPTION_SCHEMA(config)
HEAP_TRACING_SCHEMA = cv.Schema(
{
cv.Optional(CONF_HEAP_TRACING_STANDALONE, default=True): cv.boolean,
cv.Optional(CONF_HEAP_TRACING_RECORDS, default=100): cv.positive_int,
cv.Optional(CONF_HEAP_TASK_TRACKING, default=True): cv.boolean,
cv.Optional(CONF_HEAP_TASK_MAX, default=10): cv.positive_int,
}
)
def _heap_tracing_schema(config):
if config is None:
config = {}
return HEAP_TRACING_SCHEMA(config)
CONFIG_SCHEMA = cv.All( CONFIG_SCHEMA = cv.All(
cv.Schema( cv.Schema(
{ {
@@ -95,11 +133,8 @@ CONFIG_SCHEMA = cv.All(
CONF_SERVICES, group_of_exclusion=CONF_ACTIONS CONF_SERVICES, group_of_exclusion=CONF_ACTIONS
): ACTIONS_SCHEMA, ): ACTIONS_SCHEMA,
cv.Exclusive(CONF_ACTIONS, group_of_exclusion=CONF_ACTIONS): ACTIONS_SCHEMA, cv.Exclusive(CONF_ACTIONS, group_of_exclusion=CONF_ACTIONS): ACTIONS_SCHEMA,
cv.Optional(CONF_ENCRYPTION): cv.Schema( cv.Optional(CONF_ENCRYPTION): _encryption_schema,
{ cv.Optional(CONF_HEAP_TRACING): _heap_tracing_schema,
cv.Required(CONF_KEY): validate_encryption_key,
}
),
cv.Optional(CONF_ON_CLIENT_CONNECTED): automation.validate_automation( cv.Optional(CONF_ON_CLIENT_CONNECTED): automation.validate_automation(
single=True single=True
), ),
@@ -151,14 +186,90 @@ async def to_code(config):
config[CONF_ON_CLIENT_DISCONNECTED], config[CONF_ON_CLIENT_DISCONNECTED],
) )
if encryption_config := config.get(CONF_ENCRYPTION): if (encryption_config := config.get(CONF_ENCRYPTION, None)) is not None:
decoded = base64.b64decode(encryption_config[CONF_KEY]) if key := encryption_config.get(CONF_KEY):
cg.add(var.set_noise_psk(list(decoded))) decoded = base64.b64decode(key)
cg.add(var.set_noise_psk(list(decoded)))
else:
# No key provided, but encryption desired
# This will allow a plaintext client to provide a noise key,
# send it to the device, and then switch to noise.
# The key will be saved in flash and used for future connections
# and plaintext disabled. Only a factory reset can remove it.
cg.add_define("USE_API_PLAINTEXT")
cg.add_define("USE_API_NOISE") cg.add_define("USE_API_NOISE")
cg.add_library("esphome/noise-c", "0.1.6") cg.add_library("esphome/noise-c", "0.1.6")
else: else:
cg.add_define("USE_API_PLAINTEXT") cg.add_define("USE_API_PLAINTEXT")
# Handle heap tracing configuration if ESP32 platform and using ESP-IDF
if (heap_tracing_config := config.get(CONF_HEAP_TRACING, None)) is not None:
if CORE.using_esp_idf:
# Enable heap tracing in sdkconfig
add_idf_sdkconfig_option("CONFIG_HEAP_TRACING", True)
add_idf_sdkconfig_option("CONFIG_HEAP_TRACE_STACK_DEPTH", "30")
add_idf_sdkconfig_option("CONFIG_ESP32_APPTRACE_ENABLE", True)
# Set tracing mode (standalone or system)
if heap_tracing_config[CONF_HEAP_TRACING_STANDALONE]:
add_idf_sdkconfig_option("CONFIG_HEAP_TRACING_STANDALONE", True)
else:
add_idf_sdkconfig_option("CONFIG_HEAP_TRACING_SYSTEM", True)
# Enable runtime stats gathering for task info
if heap_tracing_config[CONF_HEAP_TASK_TRACKING]:
add_idf_sdkconfig_option(
"CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS", True
)
add_idf_sdkconfig_option(
"CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS", True
)
add_idf_sdkconfig_option("CONFIG_FREERTOS_USE_TRACE_FACILITY", True)
# Generate code to implement heap tracing
cg.add_global(cg.RawStatement('#include "esp_heap_trace.h"'))
# Define the trace record buffer
num_records = heap_tracing_config[CONF_HEAP_TRACING_RECORDS]
cg.add_global(
cg.RawStatement(
f"static heap_trace_record_t trace_record[{num_records}];"
)
)
# No additional setup needed for task tracking
# Add helper functions for heap tracing with extern "C" to make them globally accessible
cg.add_global(
cg.RawStatement(
"""
// Global heap tracing functions that can be called from any context
extern "C" void start_heap_trace() {
heap_trace_init_standalone(trace_record, """
+ str(num_records)
+ """);
heap_trace_start(HEAP_TRACE_LEAKS);
}
extern "C" void stop_and_dump_heap_trace() {
heap_trace_stop();
heap_trace_dump();
}
"""
)
)
# Add periodic heap trace dumping to the api_server.cpp file
# This will be added in C++ code
cg.add_define("USE_API_HEAP_TRACE")
else:
# Not using ESP-IDF, so we can't use heap tracing
_LOGGER.warning(
"Heap tracing is only available when using ESP-IDF. "
"Disabling heap tracing configuration."
)
cg.add_define("USE_API") cg.add_define("USE_API")
cg.add_global(api_ns.using) cg.add_global(api_ns.using)

View File

@@ -31,6 +31,7 @@ service APIConnection {
option (needs_authentication) = false; option (needs_authentication) = false;
} }
rpc execute_service (ExecuteServiceRequest) returns (void) {} rpc execute_service (ExecuteServiceRequest) returns (void) {}
rpc noise_encryption_set_key (NoiseEncryptionSetKeyRequest) returns (NoiseEncryptionSetKeyResponse) {}
rpc cover_command (CoverCommandRequest) returns (void) {} rpc cover_command (CoverCommandRequest) returns (void) {}
rpc fan_command (FanCommandRequest) returns (void) {} rpc fan_command (FanCommandRequest) returns (void) {}
@@ -60,6 +61,7 @@ service APIConnection {
rpc bluetooth_gatt_notify(BluetoothGATTNotifyRequest) returns (void) {} rpc bluetooth_gatt_notify(BluetoothGATTNotifyRequest) returns (void) {}
rpc subscribe_bluetooth_connections_free(SubscribeBluetoothConnectionsFreeRequest) returns (BluetoothConnectionsFreeResponse) {} rpc subscribe_bluetooth_connections_free(SubscribeBluetoothConnectionsFreeRequest) returns (BluetoothConnectionsFreeResponse) {}
rpc unsubscribe_bluetooth_le_advertisements(UnsubscribeBluetoothLEAdvertisementsRequest) returns (void) {} rpc unsubscribe_bluetooth_le_advertisements(UnsubscribeBluetoothLEAdvertisementsRequest) returns (void) {}
rpc bluetooth_scanner_set_mode(BluetoothScannerSetModeRequest) returns (void) {}
rpc subscribe_voice_assistant(SubscribeVoiceAssistantRequest) returns (void) {} rpc subscribe_voice_assistant(SubscribeVoiceAssistantRequest) returns (void) {}
rpc voice_assistant_get_configuration(VoiceAssistantConfigurationRequest) returns (VoiceAssistantConfigurationResponse) {} rpc voice_assistant_get_configuration(VoiceAssistantConfigurationRequest) returns (VoiceAssistantConfigurationResponse) {}
@@ -227,6 +229,12 @@ message DeviceInfoResponse {
uint32 voice_assistant_feature_flags = 17; uint32 voice_assistant_feature_flags = 17;
string suggested_area = 16; string suggested_area = 16;
// The Bluetooth mac address of the device. For example "AC:BC:32:89:0E:AA"
string bluetooth_mac_address = 18;
// Supports receiving and saving api encryption key
bool api_encryption_supported = 19;
} }
message ListEntitiesRequest { message ListEntitiesRequest {
@@ -651,6 +659,23 @@ message SubscribeLogsResponse {
bool send_failed = 4; bool send_failed = 4;
} }
// ==================== NOISE ENCRYPTION ====================
message NoiseEncryptionSetKeyRequest {
option (id) = 124;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_API_NOISE";
bytes key = 1;
}
message NoiseEncryptionSetKeyResponse {
option (id) = 125;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_API_NOISE";
bool success = 1;
}
// ==================== HOMEASSISTANT.SERVICE ==================== // ==================== HOMEASSISTANT.SERVICE ====================
message SubscribeHomeassistantServicesRequest { message SubscribeHomeassistantServicesRequest {
option (id) = 34; option (id) = 34;
@@ -1448,6 +1473,37 @@ message BluetoothDeviceClearCacheResponse {
int32 error = 3; int32 error = 3;
} }
enum BluetoothScannerState {
BLUETOOTH_SCANNER_STATE_IDLE = 0;
BLUETOOTH_SCANNER_STATE_STARTING = 1;
BLUETOOTH_SCANNER_STATE_RUNNING = 2;
BLUETOOTH_SCANNER_STATE_FAILED = 3;
BLUETOOTH_SCANNER_STATE_STOPPING = 4;
BLUETOOTH_SCANNER_STATE_STOPPED = 5;
}
enum BluetoothScannerMode {
BLUETOOTH_SCANNER_MODE_PASSIVE = 0;
BLUETOOTH_SCANNER_MODE_ACTIVE = 1;
}
message BluetoothScannerStateResponse {
option(id) = 126;
option(source) = SOURCE_SERVER;
option(ifdef) = "USE_BLUETOOTH_PROXY";
BluetoothScannerState state = 1;
BluetoothScannerMode mode = 2;
}
message BluetoothScannerSetModeRequest {
option(id) = 127;
option(source) = SOURCE_CLIENT;
option(ifdef) = "USE_BLUETOOTH_PROXY";
BluetoothScannerMode mode = 1;
}
// ==================== PUSH TO TALK ==================== // ==================== PUSH TO TALK ====================
enum VoiceAssistantSubscribeFlag { enum VoiceAssistantSubscribeFlag {
VOICE_ASSISTANT_SUBSCRIBE_NONE = 0; VOICE_ASSISTANT_SUBSCRIBE_NONE = 0;
@@ -1564,6 +1620,8 @@ message VoiceAssistantAnnounceRequest {
string media_id = 1; string media_id = 1;
string text = 2; string text = 2;
string preannounce_media_id = 3;
bool start_conversation = 4;
} }
message VoiceAssistantAnnounceFinished { message VoiceAssistantAnnounceFinished {

File diff suppressed because it is too large Load Diff

View File

@@ -14,6 +14,46 @@
namespace esphome { namespace esphome {
namespace api { namespace api {
using send_message_t = bool(APIConnection *, void *);
/*
This class holds a pointer to the source component that wants to publish a message, and a pointer to a function that
will lazily publish that message. The two pointers allow dedup in the deferred queue if multiple publishes for the
same component are backed up, and take up only 8 bytes of memory. The entry in the deferred queue (a std::vector) is
the DeferredMessage instance itself (not a pointer to one elsewhere in heap) so still only 8 bytes per entry. Even
100 backed up messages (you'd have to have at least 100 sensors publishing because of dedup) would take up only 0.8
kB.
*/
class DeferredMessageQueue {
struct DeferredMessage {
friend class DeferredMessageQueue;
protected:
void *source_;
send_message_t *send_message_;
public:
DeferredMessage(void *source, send_message_t *send_message) : source_(source), send_message_(send_message) {}
bool operator==(const DeferredMessage &test) const {
return (source_ == test.source_ && send_message_ == test.send_message_);
}
} __attribute__((packed));
protected:
// vector is used very specifically for its zero memory overhead even though items are popped from the front (memory
// footprint is more important than speed here)
std::vector<DeferredMessage> deferred_queue_;
APIConnection *api_connection_;
// helper for allowing only unique entries in the queue
void dmq_push_back_with_dedup_(void *source, send_message_t *send_message);
public:
DeferredMessageQueue(APIConnection *api_connection) : api_connection_(api_connection) {}
void process_queue();
void defer(void *source, send_message_t *send_message);
};
class APIConnection : public APIServerConnection { class APIConnection : public APIServerConnection {
public: public:
APIConnection(std::unique_ptr<socket::Socket> socket, APIServer *parent); APIConnection(std::unique_ptr<socket::Socket> socket, APIServer *parent);
@@ -28,96 +68,140 @@ class APIConnection : public APIServerConnection {
} }
#ifdef USE_BINARY_SENSOR #ifdef USE_BINARY_SENSOR
bool send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor, bool state); bool send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor, bool state);
bool send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor); void send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor);
static bool try_send_binary_sensor_state(APIConnection *api, void *v_binary_sensor);
static bool try_send_binary_sensor_state(APIConnection *api, binary_sensor::BinarySensor *binary_sensor, bool state);
static bool try_send_binary_sensor_info(APIConnection *api, void *v_binary_sensor);
#endif #endif
#ifdef USE_COVER #ifdef USE_COVER
bool send_cover_state(cover::Cover *cover); bool send_cover_state(cover::Cover *cover);
bool send_cover_info(cover::Cover *cover); void send_cover_info(cover::Cover *cover);
static bool try_send_cover_state(APIConnection *api, void *v_cover);
static bool try_send_cover_info(APIConnection *api, void *v_cover);
void cover_command(const CoverCommandRequest &msg) override; void cover_command(const CoverCommandRequest &msg) override;
#endif #endif
#ifdef USE_FAN #ifdef USE_FAN
bool send_fan_state(fan::Fan *fan); bool send_fan_state(fan::Fan *fan);
bool send_fan_info(fan::Fan *fan); void send_fan_info(fan::Fan *fan);
static bool try_send_fan_state(APIConnection *api, void *v_fan);
static bool try_send_fan_info(APIConnection *api, void *v_fan);
void fan_command(const FanCommandRequest &msg) override; void fan_command(const FanCommandRequest &msg) override;
#endif #endif
#ifdef USE_LIGHT #ifdef USE_LIGHT
bool send_light_state(light::LightState *light); bool send_light_state(light::LightState *light);
bool send_light_info(light::LightState *light); void send_light_info(light::LightState *light);
static bool try_send_light_state(APIConnection *api, void *v_light);
static bool try_send_light_info(APIConnection *api, void *v_light);
void light_command(const LightCommandRequest &msg) override; void light_command(const LightCommandRequest &msg) override;
#endif #endif
#ifdef USE_SENSOR #ifdef USE_SENSOR
bool send_sensor_state(sensor::Sensor *sensor, float state); bool send_sensor_state(sensor::Sensor *sensor, float state);
bool send_sensor_info(sensor::Sensor *sensor); void send_sensor_info(sensor::Sensor *sensor);
static bool try_send_sensor_state(APIConnection *api, void *v_sensor);
static bool try_send_sensor_state(APIConnection *api, sensor::Sensor *sensor, float state);
static bool try_send_sensor_info(APIConnection *api, void *v_sensor);
#endif #endif
#ifdef USE_SWITCH #ifdef USE_SWITCH
bool send_switch_state(switch_::Switch *a_switch, bool state); bool send_switch_state(switch_::Switch *a_switch, bool state);
bool send_switch_info(switch_::Switch *a_switch); void send_switch_info(switch_::Switch *a_switch);
static bool try_send_switch_state(APIConnection *api, void *v_a_switch);
static bool try_send_switch_state(APIConnection *api, switch_::Switch *a_switch, bool state);
static bool try_send_switch_info(APIConnection *api, void *v_a_switch);
void switch_command(const SwitchCommandRequest &msg) override; void switch_command(const SwitchCommandRequest &msg) override;
#endif #endif
#ifdef USE_TEXT_SENSOR #ifdef USE_TEXT_SENSOR
bool send_text_sensor_state(text_sensor::TextSensor *text_sensor, std::string state); bool send_text_sensor_state(text_sensor::TextSensor *text_sensor, std::string state);
bool send_text_sensor_info(text_sensor::TextSensor *text_sensor); void send_text_sensor_info(text_sensor::TextSensor *text_sensor);
static bool try_send_text_sensor_state(APIConnection *api, void *v_text_sensor);
static bool try_send_text_sensor_state(APIConnection *api, text_sensor::TextSensor *text_sensor, std::string state);
static bool try_send_text_sensor_info(APIConnection *api, void *v_text_sensor);
#endif #endif
#ifdef USE_ESP32_CAMERA #ifdef USE_ESP32_CAMERA
void send_camera_state(std::shared_ptr<esp32_camera::CameraImage> image); void set_camera_state(std::shared_ptr<esp32_camera::CameraImage> image);
bool send_camera_info(esp32_camera::ESP32Camera *camera); void send_camera_info(esp32_camera::ESP32Camera *camera);
static bool try_send_camera_info(APIConnection *api, void *v_camera);
void camera_image(const CameraImageRequest &msg) override; void camera_image(const CameraImageRequest &msg) override;
#endif #endif
#ifdef USE_CLIMATE #ifdef USE_CLIMATE
bool send_climate_state(climate::Climate *climate); bool send_climate_state(climate::Climate *climate);
bool send_climate_info(climate::Climate *climate); void send_climate_info(climate::Climate *climate);
static bool try_send_climate_state(APIConnection *api, void *v_climate);
static bool try_send_climate_info(APIConnection *api, void *v_climate);
void climate_command(const ClimateCommandRequest &msg) override; void climate_command(const ClimateCommandRequest &msg) override;
#endif #endif
#ifdef USE_NUMBER #ifdef USE_NUMBER
bool send_number_state(number::Number *number, float state); bool send_number_state(number::Number *number, float state);
bool send_number_info(number::Number *number); void send_number_info(number::Number *number);
static bool try_send_number_state(APIConnection *api, void *v_number);
static bool try_send_number_state(APIConnection *api, number::Number *number, float state);
static bool try_send_number_info(APIConnection *api, void *v_number);
void number_command(const NumberCommandRequest &msg) override; void number_command(const NumberCommandRequest &msg) override;
#endif #endif
#ifdef USE_DATETIME_DATE #ifdef USE_DATETIME_DATE
bool send_date_state(datetime::DateEntity *date); bool send_date_state(datetime::DateEntity *date);
bool send_date_info(datetime::DateEntity *date); void send_date_info(datetime::DateEntity *date);
static bool try_send_date_state(APIConnection *api, void *v_date);
static bool try_send_date_info(APIConnection *api, void *v_date);
void date_command(const DateCommandRequest &msg) override; void date_command(const DateCommandRequest &msg) override;
#endif #endif
#ifdef USE_DATETIME_TIME #ifdef USE_DATETIME_TIME
bool send_time_state(datetime::TimeEntity *time); bool send_time_state(datetime::TimeEntity *time);
bool send_time_info(datetime::TimeEntity *time); void send_time_info(datetime::TimeEntity *time);
static bool try_send_time_state(APIConnection *api, void *v_time);
static bool try_send_time_info(APIConnection *api, void *v_time);
void time_command(const TimeCommandRequest &msg) override; void time_command(const TimeCommandRequest &msg) override;
#endif #endif
#ifdef USE_DATETIME_DATETIME #ifdef USE_DATETIME_DATETIME
bool send_datetime_state(datetime::DateTimeEntity *datetime); bool send_datetime_state(datetime::DateTimeEntity *datetime);
bool send_datetime_info(datetime::DateTimeEntity *datetime); void send_datetime_info(datetime::DateTimeEntity *datetime);
static bool try_send_datetime_state(APIConnection *api, void *v_datetime);
static bool try_send_datetime_info(APIConnection *api, void *v_datetime);
void datetime_command(const DateTimeCommandRequest &msg) override; void datetime_command(const DateTimeCommandRequest &msg) override;
#endif #endif
#ifdef USE_TEXT #ifdef USE_TEXT
bool send_text_state(text::Text *text, std::string state); bool send_text_state(text::Text *text, std::string state);
bool send_text_info(text::Text *text); void send_text_info(text::Text *text);
static bool try_send_text_state(APIConnection *api, void *v_text);
static bool try_send_text_state(APIConnection *api, text::Text *text, std::string state);
static bool try_send_text_info(APIConnection *api, void *v_text);
void text_command(const TextCommandRequest &msg) override; void text_command(const TextCommandRequest &msg) override;
#endif #endif
#ifdef USE_SELECT #ifdef USE_SELECT
bool send_select_state(select::Select *select, std::string state); bool send_select_state(select::Select *select, std::string state);
bool send_select_info(select::Select *select); void send_select_info(select::Select *select);
static bool try_send_select_state(APIConnection *api, void *v_select);
static bool try_send_select_state(APIConnection *api, select::Select *select, std::string state);
static bool try_send_select_info(APIConnection *api, void *v_select);
void select_command(const SelectCommandRequest &msg) override; void select_command(const SelectCommandRequest &msg) override;
#endif #endif
#ifdef USE_BUTTON #ifdef USE_BUTTON
bool send_button_info(button::Button *button); void send_button_info(button::Button *button);
static bool try_send_button_info(APIConnection *api, void *v_button);
void button_command(const ButtonCommandRequest &msg) override; void button_command(const ButtonCommandRequest &msg) override;
#endif #endif
#ifdef USE_LOCK #ifdef USE_LOCK
bool send_lock_state(lock::Lock *a_lock, lock::LockState state); bool send_lock_state(lock::Lock *a_lock, lock::LockState state);
bool send_lock_info(lock::Lock *a_lock); void send_lock_info(lock::Lock *a_lock);
static bool try_send_lock_state(APIConnection *api, void *v_a_lock);
static bool try_send_lock_state(APIConnection *api, lock::Lock *a_lock, lock::LockState state);
static bool try_send_lock_info(APIConnection *api, void *v_a_lock);
void lock_command(const LockCommandRequest &msg) override; void lock_command(const LockCommandRequest &msg) override;
#endif #endif
#ifdef USE_VALVE #ifdef USE_VALVE
bool send_valve_state(valve::Valve *valve); bool send_valve_state(valve::Valve *valve);
bool send_valve_info(valve::Valve *valve); void send_valve_info(valve::Valve *valve);
static bool try_send_valve_state(APIConnection *api, void *v_valve);
static bool try_send_valve_info(APIConnection *api, void *v_valve);
void valve_command(const ValveCommandRequest &msg) override; void valve_command(const ValveCommandRequest &msg) override;
#endif #endif
#ifdef USE_MEDIA_PLAYER #ifdef USE_MEDIA_PLAYER
bool send_media_player_state(media_player::MediaPlayer *media_player); bool send_media_player_state(media_player::MediaPlayer *media_player);
bool send_media_player_info(media_player::MediaPlayer *media_player); void send_media_player_info(media_player::MediaPlayer *media_player);
static bool try_send_media_player_state(APIConnection *api, void *v_media_player);
static bool try_send_media_player_info(APIConnection *api, void *v_media_player);
void media_player_command(const MediaPlayerCommandRequest &msg) override; void media_player_command(const MediaPlayerCommandRequest &msg) override;
#endif #endif
bool send_log_message(int level, const char *tag, const char *line); bool try_send_log_message(int level, const char *tag, const char *line);
void send_homeassistant_service_call(const HomeassistantServiceResponse &call) { void send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
if (!this->service_call_subscription_) if (!this->service_call_subscription_)
return; return;
@@ -137,6 +221,7 @@ class APIConnection : public APIServerConnection {
void bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) override; void bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) override;
BluetoothConnectionsFreeResponse subscribe_bluetooth_connections_free( BluetoothConnectionsFreeResponse subscribe_bluetooth_connections_free(
const SubscribeBluetoothConnectionsFreeRequest &msg) override; const SubscribeBluetoothConnectionsFreeRequest &msg) override;
void bluetooth_scanner_set_mode(const BluetoothScannerSetModeRequest &msg) override;
#endif #endif
#ifdef USE_HOMEASSISTANT_TIME #ifdef USE_HOMEASSISTANT_TIME
@@ -160,18 +245,25 @@ class APIConnection : public APIServerConnection {
#ifdef USE_ALARM_CONTROL_PANEL #ifdef USE_ALARM_CONTROL_PANEL
bool send_alarm_control_panel_state(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel); bool send_alarm_control_panel_state(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel);
bool send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel); void send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel);
static bool try_send_alarm_control_panel_state(APIConnection *api, void *v_a_alarm_control_panel);
static bool try_send_alarm_control_panel_info(APIConnection *api, void *v_a_alarm_control_panel);
void alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) override; void alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) override;
#endif #endif
#ifdef USE_EVENT #ifdef USE_EVENT
bool send_event(event::Event *event, std::string event_type); void send_event(event::Event *event, std::string event_type);
bool send_event_info(event::Event *event); void send_event_info(event::Event *event);
static bool try_send_event(APIConnection *api, void *v_event);
static bool try_send_event(APIConnection *api, event::Event *event, std::string event_type);
static bool try_send_event_info(APIConnection *api, void *v_event);
#endif #endif
#ifdef USE_UPDATE #ifdef USE_UPDATE
bool send_update_state(update::UpdateEntity *update); bool send_update_state(update::UpdateEntity *update);
bool send_update_info(update::UpdateEntity *update); void send_update_info(update::UpdateEntity *update);
static bool try_send_update_state(APIConnection *api, void *v_update);
static bool try_send_update_info(APIConnection *api, void *v_update);
void update_command(const UpdateCommandRequest &msg) override; void update_command(const UpdateCommandRequest &msg) override;
#endif #endif
@@ -209,6 +301,9 @@ class APIConnection : public APIServerConnection {
return {}; return {};
} }
void execute_service(const ExecuteServiceRequest &msg) override; void execute_service(const ExecuteServiceRequest &msg) override;
#ifdef USE_API_NOISE
NoiseEncryptionSetKeyResponse noise_encryption_set_key(const NoiseEncryptionSetKeyRequest &msg) override;
#endif
bool is_authenticated() override { return this->connection_state_ == ConnectionState::AUTHENTICATED; } bool is_authenticated() override { return this->connection_state_ == ConnectionState::AUTHENTICATED; }
bool is_connection_setup() override { bool is_connection_setup() override {
@@ -217,9 +312,10 @@ class APIConnection : public APIServerConnection {
void on_fatal_error() override; void on_fatal_error() override;
void on_unauthenticated_access() override; void on_unauthenticated_access() override;
void on_no_setup_connection() override; void on_no_setup_connection() override;
ProtoWriteBuffer create_buffer() override { ProtoWriteBuffer create_buffer(uint32_t reserve_size) override {
// FIXME: ensure no recursive writes can happen // FIXME: ensure no recursive writes can happen
this->proto_write_buffer_.clear(); this->proto_write_buffer_.clear();
this->proto_write_buffer_.reserve(reserve_size);
return {&this->proto_write_buffer_}; return {&this->proto_write_buffer_};
} }
bool send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) override; bool send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) override;
@@ -262,6 +358,7 @@ class APIConnection : public APIServerConnection {
bool service_call_subscription_{false}; bool service_call_subscription_{false};
bool next_close_ = false; bool next_close_ = false;
APIServer *parent_; APIServer *parent_;
DeferredMessageQueue deferred_message_queue_;
InitialStateIterator initial_state_iterator_; InitialStateIterator initial_state_iterator_;
ListEntitiesIterator list_entities_iterator_; ListEntitiesIterator list_entities_iterator_;
int state_subs_at_ = -1; int state_subs_at_ = -1;

View File

@@ -5,6 +5,7 @@
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
#include "esphome/core/application.h" #include "esphome/core/application.h"
#include "proto.h" #include "proto.h"
#include "api_pb2_size.h"
#include <cstring> #include <cstring>
namespace esphome { namespace esphome {
@@ -311,6 +312,10 @@ APIError APINoiseFrameHelper::state_action_() {
const std::string &name = App.get_name(); const std::string &name = App.get_name();
const uint8_t *name_ptr = reinterpret_cast<const uint8_t *>(name.c_str()); const uint8_t *name_ptr = reinterpret_cast<const uint8_t *>(name.c_str());
msg.insert(msg.end(), name_ptr, name_ptr + name.size() + 1); msg.insert(msg.end(), name_ptr, name_ptr + name.size() + 1);
// node mac, terminated by null byte
const std::string &mac = get_mac_address();
const uint8_t *mac_ptr = reinterpret_cast<const uint8_t *>(mac.c_str());
msg.insert(msg.end(), mac_ptr, mac_ptr + mac.size() + 1);
aerr = write_frame_(msg.data(), msg.size()); aerr = write_frame_(msg.data(), msg.size());
if (aerr != APIError::OK) if (aerr != APIError::OK)
@@ -570,6 +575,8 @@ APIError APINoiseFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
if (!tx_buf_.empty()) { if (!tx_buf_.empty()) {
// tx buf not empty, can't write now because then stream would be inconsistent // tx buf not empty, can't write now because then stream would be inconsistent
// Reserve space upfront to avoid multiple reallocations
tx_buf_.reserve(tx_buf_.size() + total_write_len);
for (int i = 0; i < iovcnt; i++) { for (int i = 0; i < iovcnt; i++) {
tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base), tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len); reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
@@ -580,6 +587,8 @@ APIError APINoiseFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
ssize_t sent = socket_->writev(iov, iovcnt); ssize_t sent = socket_->writev(iov, iovcnt);
if (is_would_block(sent)) { if (is_would_block(sent)) {
// operation would block, add buffer to tx_buf // operation would block, add buffer to tx_buf
// Reserve space upfront to avoid multiple reallocations
tx_buf_.reserve(tx_buf_.size() + total_write_len);
for (int i = 0; i < iovcnt; i++) { for (int i = 0; i < iovcnt; i++) {
tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base), tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len); reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
@@ -592,6 +601,10 @@ APIError APINoiseFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
return APIError::SOCKET_WRITE_FAILED; return APIError::SOCKET_WRITE_FAILED;
} else if ((size_t) sent != total_write_len) { } else if ((size_t) sent != total_write_len) {
// partially sent, add end to tx_buf // partially sent, add end to tx_buf
size_t remaining = total_write_len - sent;
// Reserve space upfront to avoid multiple reallocations
tx_buf_.reserve(tx_buf_.size() + remaining);
size_t to_consume = sent; size_t to_consume = sent;
for (int i = 0; i < iovcnt; i++) { for (int i = 0; i < iovcnt; i++) {
if (to_consume >= iov[i].iov_len) { if (to_consume >= iov[i].iov_len) {
@@ -893,8 +906,28 @@ APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
ParsedFrame frame; ParsedFrame frame;
aerr = try_read_frame_(&frame); aerr = try_read_frame_(&frame);
if (aerr != APIError::OK) if (aerr != APIError::OK) {
if (aerr == APIError::BAD_INDICATOR) {
// Make sure to tell the remote that we don't
// understand the indicator byte so it knows
// we do not support it.
struct iovec iov[1];
// The \x00 first byte is the marker for plaintext.
//
// The remote will know how to handle the indicator byte,
// but it likely won't understand the rest of the message.
//
// We must send at least 3 bytes to be read, so we add
// a message after the indicator byte to ensures its long
// enough and can aid in debugging.
const char msg[] = "\x00"
"Bad indicator byte";
iov[0].iov_base = (void *) msg;
iov[0].iov_len = 19;
write_raw_(iov, 1);
}
return aerr; return aerr;
}
buffer->container = std::move(frame.msg); buffer->container = std::move(frame.msg);
buffer->data_offset = 0; buffer->data_offset = 0;
@@ -909,6 +942,8 @@ APIError APIPlaintextFrameHelper::write_packet(uint16_t type, const uint8_t *pay
} }
std::vector<uint8_t> header; std::vector<uint8_t> header;
header.reserve(1 + api::ProtoSize::varint(static_cast<uint32_t>(payload_len)) +
api::ProtoSize::varint(static_cast<uint32_t>(type)));
header.push_back(0x00); header.push_back(0x00);
ProtoVarInt(payload_len).encode(header); ProtoVarInt(payload_len).encode(header);
ProtoVarInt(type).encode(header); ProtoVarInt(type).encode(header);
@@ -970,6 +1005,8 @@ APIError APIPlaintextFrameHelper::write_raw_(const struct iovec *iov, int iovcnt
if (!tx_buf_.empty()) { if (!tx_buf_.empty()) {
// tx buf not empty, can't write now because then stream would be inconsistent // tx buf not empty, can't write now because then stream would be inconsistent
// Reserve space upfront to avoid multiple reallocations
tx_buf_.reserve(tx_buf_.size() + total_write_len);
for (int i = 0; i < iovcnt; i++) { for (int i = 0; i < iovcnt; i++) {
tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base), tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len); reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
@@ -980,6 +1017,8 @@ APIError APIPlaintextFrameHelper::write_raw_(const struct iovec *iov, int iovcnt
ssize_t sent = socket_->writev(iov, iovcnt); ssize_t sent = socket_->writev(iov, iovcnt);
if (is_would_block(sent)) { if (is_would_block(sent)) {
// operation would block, add buffer to tx_buf // operation would block, add buffer to tx_buf
// Reserve space upfront to avoid multiple reallocations
tx_buf_.reserve(tx_buf_.size() + total_write_len);
for (int i = 0; i < iovcnt; i++) { for (int i = 0; i < iovcnt; i++) {
tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base), tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len); reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
@@ -992,6 +1031,10 @@ APIError APIPlaintextFrameHelper::write_raw_(const struct iovec *iov, int iovcnt
return APIError::SOCKET_WRITE_FAILED; return APIError::SOCKET_WRITE_FAILED;
} else if ((size_t) sent != total_write_len) { } else if ((size_t) sent != total_write_len) {
// partially sent, add end to tx_buf // partially sent, add end to tx_buf
size_t remaining = total_write_len - sent;
// Reserve space upfront to avoid multiple reallocations
tx_buf_.reserve(tx_buf_.size() + remaining);
size_t to_consume = sent; size_t to_consume = sent;
for (int i = 0; i < iovcnt; i++) { for (int i = 0; i < iovcnt; i++) {
if (to_consume >= iov[i].iov_len) { if (to_consume >= iov[i].iov_len) {

View File

@@ -1,6 +1,6 @@
#pragma once #pragma once
#include <cstdint>
#include <array> #include <array>
#include <cstdint>
#include "esphome/core/defines.h" #include "esphome/core/defines.h"
namespace esphome { namespace esphome {
@@ -11,11 +11,20 @@ using psk_t = std::array<uint8_t, 32>;
class APINoiseContext { class APINoiseContext {
public: public:
void set_psk(psk_t psk) { psk_ = psk; } void set_psk(psk_t psk) {
const psk_t &get_psk() const { return psk_; } this->psk_ = psk;
bool has_psk = false;
for (auto i : psk) {
has_psk |= i;
}
this->has_psk_ = has_psk;
}
const psk_t &get_psk() const { return this->psk_; }
bool has_psk() const { return this->has_psk_; }
protected: protected:
psk_t psk_; psk_t psk_{};
bool has_psk_{false};
}; };
#endif // USE_API_NOISE #endif // USE_API_NOISE

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
// This file was automatically generated with a tool. // This file was automatically generated with a tool.
// See scripts/api_protobuf/api_protobuf.py // See script/api_protobuf/api_protobuf.py
#include "api_pb2_service.h" #include "api_pb2_service.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
@@ -179,6 +179,16 @@ bool APIServerConnectionBase::send_text_sensor_state_response(const TextSensorSt
bool APIServerConnectionBase::send_subscribe_logs_response(const SubscribeLogsResponse &msg) { bool APIServerConnectionBase::send_subscribe_logs_response(const SubscribeLogsResponse &msg) {
return this->send_message_<SubscribeLogsResponse>(msg, 29); return this->send_message_<SubscribeLogsResponse>(msg, 29);
} }
#ifdef USE_API_NOISE
#endif
#ifdef USE_API_NOISE
bool APIServerConnectionBase::send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_noise_encryption_set_key_response: %s", msg.dump().c_str());
#endif
return this->send_message_<NoiseEncryptionSetKeyResponse>(msg, 125);
}
#endif
bool APIServerConnectionBase::send_homeassistant_service_response(const HomeassistantServiceResponse &msg) { bool APIServerConnectionBase::send_homeassistant_service_response(const HomeassistantServiceResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_homeassistant_service_response: %s", msg.dump().c_str()); ESP_LOGVV(TAG, "send_homeassistant_service_response: %s", msg.dump().c_str());
@@ -462,6 +472,16 @@ bool APIServerConnectionBase::send_bluetooth_device_clear_cache_response(const B
return this->send_message_<BluetoothDeviceClearCacheResponse>(msg, 88); return this->send_message_<BluetoothDeviceClearCacheResponse>(msg, 88);
} }
#endif #endif
#ifdef USE_BLUETOOTH_PROXY
bool APIServerConnectionBase::send_bluetooth_scanner_state_response(const BluetoothScannerStateResponse &msg) {
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "send_bluetooth_scanner_state_response: %s", msg.dump().c_str());
#endif
return this->send_message_<BluetoothScannerStateResponse>(msg, 126);
}
#endif
#ifdef USE_BLUETOOTH_PROXY
#endif
#ifdef USE_VOICE_ASSISTANT #ifdef USE_VOICE_ASSISTANT
#endif #endif
#ifdef USE_VOICE_ASSISTANT #ifdef USE_VOICE_ASSISTANT
@@ -1191,6 +1211,28 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
ESP_LOGVV(TAG, "on_voice_assistant_set_configuration: %s", msg.dump().c_str()); ESP_LOGVV(TAG, "on_voice_assistant_set_configuration: %s", msg.dump().c_str());
#endif #endif
this->on_voice_assistant_set_configuration(msg); this->on_voice_assistant_set_configuration(msg);
#endif
break;
}
case 124: {
#ifdef USE_API_NOISE
NoiseEncryptionSetKeyRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_noise_encryption_set_key_request: %s", msg.dump().c_str());
#endif
this->on_noise_encryption_set_key_request(msg);
#endif
break;
}
case 127: {
#ifdef USE_BLUETOOTH_PROXY
BluetoothScannerSetModeRequest msg;
msg.decode(msg_data, msg_size);
#ifdef HAS_PROTO_MESSAGE_DUMP
ESP_LOGVV(TAG, "on_bluetooth_scanner_set_mode_request: %s", msg.dump().c_str());
#endif
this->on_bluetooth_scanner_set_mode_request(msg);
#endif #endif
break; break;
} }
@@ -1311,6 +1353,22 @@ void APIServerConnection::on_execute_service_request(const ExecuteServiceRequest
} }
this->execute_service(msg); this->execute_service(msg);
} }
#ifdef USE_API_NOISE
void APIServerConnection::on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
NoiseEncryptionSetKeyResponse ret = this->noise_encryption_set_key(msg);
if (!this->send_noise_encryption_set_key_response(ret)) {
this->on_fatal_error();
}
}
#endif
#ifdef USE_COVER #ifdef USE_COVER
void APIServerConnection::on_cover_command_request(const CoverCommandRequest &msg) { void APIServerConnection::on_cover_command_request(const CoverCommandRequest &msg) {
if (!this->is_connection_setup()) { if (!this->is_connection_setup()) {
@@ -1668,6 +1726,19 @@ void APIServerConnection::on_unsubscribe_bluetooth_le_advertisements_request(
this->unsubscribe_bluetooth_le_advertisements(msg); this->unsubscribe_bluetooth_le_advertisements(msg);
} }
#endif #endif
#ifdef USE_BLUETOOTH_PROXY
void APIServerConnection::on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &msg) {
if (!this->is_connection_setup()) {
this->on_no_setup_connection();
return;
}
if (!this->is_authenticated()) {
this->on_unauthenticated_access();
return;
}
this->bluetooth_scanner_set_mode(msg);
}
#endif
#ifdef USE_VOICE_ASSISTANT #ifdef USE_VOICE_ASSISTANT
void APIServerConnection::on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) { void APIServerConnection::on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) {
if (!this->is_connection_setup()) { if (!this->is_connection_setup()) {

View File

@@ -1,5 +1,5 @@
// This file was automatically generated with a tool. // This file was automatically generated with a tool.
// See scripts/api_protobuf/api_protobuf.py // See script/api_protobuf/api_protobuf.py
#pragma once #pragma once
#include "api_pb2.h" #include "api_pb2.h"
@@ -83,6 +83,12 @@ class APIServerConnectionBase : public ProtoService {
#endif #endif
virtual void on_subscribe_logs_request(const SubscribeLogsRequest &value){}; virtual void on_subscribe_logs_request(const SubscribeLogsRequest &value){};
bool send_subscribe_logs_response(const SubscribeLogsResponse &msg); bool send_subscribe_logs_response(const SubscribeLogsResponse &msg);
#ifdef USE_API_NOISE
virtual void on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &value){};
#endif
#ifdef USE_API_NOISE
bool send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyResponse &msg);
#endif
virtual void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &value){}; virtual void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &value){};
bool send_homeassistant_service_response(const HomeassistantServiceResponse &msg); bool send_homeassistant_service_response(const HomeassistantServiceResponse &msg);
virtual void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &value){}; virtual void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &value){};
@@ -228,6 +234,12 @@ class APIServerConnectionBase : public ProtoService {
#ifdef USE_BLUETOOTH_PROXY #ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_device_clear_cache_response(const BluetoothDeviceClearCacheResponse &msg); bool send_bluetooth_device_clear_cache_response(const BluetoothDeviceClearCacheResponse &msg);
#endif #endif
#ifdef USE_BLUETOOTH_PROXY
bool send_bluetooth_scanner_state_response(const BluetoothScannerStateResponse &msg);
#endif
#ifdef USE_BLUETOOTH_PROXY
virtual void on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &value){};
#endif
#ifdef USE_VOICE_ASSISTANT #ifdef USE_VOICE_ASSISTANT
virtual void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &value){}; virtual void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &value){};
#endif #endif
@@ -349,6 +361,9 @@ class APIServerConnection : public APIServerConnectionBase {
virtual void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) = 0; virtual void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) = 0;
virtual GetTimeResponse get_time(const GetTimeRequest &msg) = 0; virtual GetTimeResponse get_time(const GetTimeRequest &msg) = 0;
virtual void execute_service(const ExecuteServiceRequest &msg) = 0; virtual void execute_service(const ExecuteServiceRequest &msg) = 0;
#ifdef USE_API_NOISE
virtual NoiseEncryptionSetKeyResponse noise_encryption_set_key(const NoiseEncryptionSetKeyRequest &msg) = 0;
#endif
#ifdef USE_COVER #ifdef USE_COVER
virtual void cover_command(const CoverCommandRequest &msg) = 0; virtual void cover_command(const CoverCommandRequest &msg) = 0;
#endif #endif
@@ -431,6 +446,9 @@ class APIServerConnection : public APIServerConnectionBase {
#ifdef USE_BLUETOOTH_PROXY #ifdef USE_BLUETOOTH_PROXY
virtual void unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) = 0; virtual void unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
#endif #endif
#ifdef USE_BLUETOOTH_PROXY
virtual void bluetooth_scanner_set_mode(const BluetoothScannerSetModeRequest &msg) = 0;
#endif
#ifdef USE_VOICE_ASSISTANT #ifdef USE_VOICE_ASSISTANT
virtual void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) = 0; virtual void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) = 0;
#endif #endif
@@ -457,6 +475,9 @@ class APIServerConnection : public APIServerConnectionBase {
void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) override; void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) override;
void on_get_time_request(const GetTimeRequest &msg) override; void on_get_time_request(const GetTimeRequest &msg) override;
void on_execute_service_request(const ExecuteServiceRequest &msg) override; void on_execute_service_request(const ExecuteServiceRequest &msg) override;
#ifdef USE_API_NOISE
void on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &msg) override;
#endif
#ifdef USE_COVER #ifdef USE_COVER
void on_cover_command_request(const CoverCommandRequest &msg) override; void on_cover_command_request(const CoverCommandRequest &msg) override;
#endif #endif
@@ -539,6 +560,9 @@ class APIServerConnection : public APIServerConnectionBase {
void on_unsubscribe_bluetooth_le_advertisements_request( void on_unsubscribe_bluetooth_le_advertisements_request(
const UnsubscribeBluetoothLEAdvertisementsRequest &msg) override; const UnsubscribeBluetoothLEAdvertisementsRequest &msg) override;
#endif #endif
#ifdef USE_BLUETOOTH_PROXY
void on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &msg) override;
#endif
#ifdef USE_VOICE_ASSISTANT #ifdef USE_VOICE_ASSISTANT
void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) override; void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) override;
#endif #endif

View File

@@ -0,0 +1,361 @@
#pragma once
#include "proto.h"
#include <cstdint>
#include <string>
namespace esphome {
namespace api {
class ProtoSize {
public:
/**
* @brief ProtoSize class for Protocol Buffer serialization size calculation
*
* This class provides static methods to calculate the exact byte counts needed
* for encoding various Protocol Buffer field types. All methods are designed to be
* efficient for the common case where many fields have default values.
*
* Implements Protocol Buffer encoding size calculation according to:
* https://protobuf.dev/programming-guides/encoding/
*
* Key features:
* - Early-return optimization for zero/default values
* - Direct total_size updates to avoid unnecessary additions
* - Specialized handling for different field types according to protobuf spec
* - Templated helpers for repeated fields and messages
*/
/**
* @brief Calculates the size in bytes needed to encode a uint32_t value as a varint
*
* @param value The uint32_t value to calculate size for
* @return The number of bytes needed to encode the value
*/
static inline uint32_t varint(uint32_t value) {
// Optimized varint size calculation using leading zeros
// Each 7 bits requires one byte in the varint encoding
if (value < 128)
return 1; // 7 bits, common case for small values
// For larger values, count bytes needed based on the position of the highest bit set
if (value < 16384) {
return 2; // 14 bits
} else if (value < 2097152) {
return 3; // 21 bits
} else if (value < 268435456) {
return 4; // 28 bits
} else {
return 5; // 32 bits (maximum for uint32_t)
}
}
/**
* @brief Calculates the size in bytes needed to encode a uint64_t value as a varint
*
* @param value The uint64_t value to calculate size for
* @return The number of bytes needed to encode the value
*/
static inline uint32_t varint(uint64_t value) {
// Handle common case of values fitting in uint32_t (vast majority of use cases)
if (value <= UINT32_MAX) {
return varint(static_cast<uint32_t>(value));
}
// For larger values, determine size based on highest bit position
if (value < (1ULL << 35)) {
return 5; // 35 bits
} else if (value < (1ULL << 42)) {
return 6; // 42 bits
} else if (value < (1ULL << 49)) {
return 7; // 49 bits
} else if (value < (1ULL << 56)) {
return 8; // 56 bits
} else if (value < (1ULL << 63)) {
return 9; // 63 bits
} else {
return 10; // 64 bits (maximum for uint64_t)
}
}
/**
* @brief Calculates the size in bytes needed to encode an int32_t value as a varint
*
* Special handling is needed for negative values, which are sign-extended to 64 bits
* in Protocol Buffers, resulting in a 10-byte varint.
*
* @param value The int32_t value to calculate size for
* @return The number of bytes needed to encode the value
*/
static inline uint32_t varint(int32_t value) {
// Negative values are sign-extended to 64 bits in protocol buffers,
// which always results in a 10-byte varint for negative int32
if (value < 0) {
return 10; // Negative int32 is always 10 bytes long
}
// For non-negative values, use the uint32_t implementation
return varint(static_cast<uint32_t>(value));
}
/**
* @brief Calculates the size in bytes needed to encode an int64_t value as a varint
*
* @param value The int64_t value to calculate size for
* @return The number of bytes needed to encode the value
*/
static inline uint32_t varint(int64_t value) {
// For int64_t, we convert to uint64_t and calculate the size
// This works because the bit pattern determines the encoding size,
// and we've handled negative int32 values as a special case above
return varint(static_cast<uint64_t>(value));
}
/**
* @brief Calculates the size in bytes needed to encode a field ID and wire type
*
* @param field_id The field identifier
* @param type The wire type value (from the WireType enum in the protobuf spec)
* @return The number of bytes needed to encode the field ID and wire type
*/
static inline uint32_t field(uint32_t field_id, uint32_t type) {
uint32_t tag = (field_id << 3) | (type & 0b111);
return varint(tag);
}
/**
* @brief Common parameters for all add_*_field methods
*
* All add_*_field methods follow these common patterns:
*
* @param total_size Reference to the total message size to update
* @param field_id_size Pre-calculated size of the field ID in bytes
* @param value The value to calculate size for (type varies)
* @param force Whether to calculate size even if the value is default/zero/empty
*
* Each method follows this implementation pattern:
* 1. Skip calculation if value is default (0, false, empty) and not forced
* 2. Calculate the size based on the field's encoding rules
* 3. Add the field_id_size + calculated value size to total_size
*/
/**
* @brief Calculates and adds the size of an int32 field to the total message size
*/
static inline void add_int32_field(uint32_t &total_size, uint32_t field_id_size, int32_t value, bool force = false) {
// Skip calculation if value is zero and not forced
if (value == 0 && !force) {
return; // No need to update total_size
}
// Calculate and directly add to total_size
if (value < 0) {
// Negative values are encoded as 10-byte varints in protobuf
total_size += field_id_size + 10;
} else {
// For non-negative values, use the standard varint size
total_size += field_id_size + varint(static_cast<uint32_t>(value));
}
}
/**
* @brief Calculates and adds the size of a uint32 field to the total message size
*/
static inline void add_uint32_field(uint32_t &total_size, uint32_t field_id_size, uint32_t value,
bool force = false) {
// Skip calculation if value is zero and not forced
if (value == 0 && !force) {
return; // No need to update total_size
}
// Calculate and directly add to total_size
total_size += field_id_size + varint(value);
}
/**
* @brief Calculates and adds the size of a boolean field to the total message size
*/
static inline void add_bool_field(uint32_t &total_size, uint32_t field_id_size, bool value, bool force = false) {
// Skip calculation if value is false and not forced
if (!value && !force) {
return; // No need to update total_size
}
// Boolean fields always use 1 byte when true
total_size += field_id_size + 1;
}
/**
* @brief Calculates and adds the size of a fixed field to the total message size
*
* Fixed fields always take exactly N bytes (4 for fixed32/float, 8 for fixed64/double).
*
* @tparam NumBytes The number of bytes for this fixed field (4 or 8)
* @param is_nonzero Whether the value is non-zero
*/
template<uint32_t NumBytes>
static inline void add_fixed_field(uint32_t &total_size, uint32_t field_id_size, bool is_nonzero,
bool force = false) {
// Skip calculation if value is zero and not forced
if (!is_nonzero && !force) {
return; // No need to update total_size
}
// Fixed fields always take exactly NumBytes
total_size += field_id_size + NumBytes;
}
/**
* @brief Calculates and adds the size of an enum field to the total message size
*
* Enum fields are encoded as uint32 varints.
*/
static inline void add_enum_field(uint32_t &total_size, uint32_t field_id_size, uint32_t value, bool force = false) {
// Skip calculation if value is zero and not forced
if (value == 0 && !force) {
return; // No need to update total_size
}
// Enums are encoded as uint32
total_size += field_id_size + varint(value);
}
/**
* @brief Calculates and adds the size of a sint32 field to the total message size
*
* Sint32 fields use ZigZag encoding, which is more efficient for negative values.
*/
static inline void add_sint32_field(uint32_t &total_size, uint32_t field_id_size, int32_t value, bool force = false) {
// Skip calculation if value is zero and not forced
if (value == 0 && !force) {
return; // No need to update total_size
}
// ZigZag encoding for sint32: (n << 1) ^ (n >> 31)
uint32_t zigzag = (static_cast<uint32_t>(value) << 1) ^ (static_cast<uint32_t>(value >> 31));
total_size += field_id_size + varint(zigzag);
}
/**
* @brief Calculates and adds the size of an int64 field to the total message size
*/
static inline void add_int64_field(uint32_t &total_size, uint32_t field_id_size, int64_t value, bool force = false) {
// Skip calculation if value is zero and not forced
if (value == 0 && !force) {
return; // No need to update total_size
}
// Calculate and directly add to total_size
total_size += field_id_size + varint(value);
}
/**
* @brief Calculates and adds the size of a uint64 field to the total message size
*/
static inline void add_uint64_field(uint32_t &total_size, uint32_t field_id_size, uint64_t value,
bool force = false) {
// Skip calculation if value is zero and not forced
if (value == 0 && !force) {
return; // No need to update total_size
}
// Calculate and directly add to total_size
total_size += field_id_size + varint(value);
}
/**
* @brief Calculates and adds the size of a sint64 field to the total message size
*
* Sint64 fields use ZigZag encoding, which is more efficient for negative values.
*/
static inline void add_sint64_field(uint32_t &total_size, uint32_t field_id_size, int64_t value, bool force = false) {
// Skip calculation if value is zero and not forced
if (value == 0 && !force) {
return; // No need to update total_size
}
// ZigZag encoding for sint64: (n << 1) ^ (n >> 63)
uint64_t zigzag = (static_cast<uint64_t>(value) << 1) ^ (static_cast<uint64_t>(value >> 63));
total_size += field_id_size + varint(zigzag);
}
/**
* @brief Calculates and adds the size of a string/bytes field to the total message size
*/
static inline void add_string_field(uint32_t &total_size, uint32_t field_id_size, const std::string &str,
bool force = false) {
// Skip calculation if string is empty and not forced
if (str.empty() && !force) {
return; // No need to update total_size
}
// Calculate and directly add to total_size
const uint32_t str_size = static_cast<uint32_t>(str.size());
total_size += field_id_size + varint(str_size) + str_size;
}
/**
* @brief Calculates and adds the size of a nested message field to the total message size
*
* This helper function directly updates the total_size reference if the nested size
* is greater than zero or force is true.
*
* @param nested_size The pre-calculated size of the nested message
*/
static inline void add_message_field(uint32_t &total_size, uint32_t field_id_size, uint32_t nested_size,
bool force = false) {
// Skip calculation if nested message is empty and not forced
if (nested_size == 0 && !force) {
return; // No need to update total_size
}
// Calculate and directly add to total_size
// Field ID + length varint + nested message content
total_size += field_id_size + varint(nested_size) + nested_size;
}
/**
* @brief Calculates and adds the size of a nested message field to the total message size
*
* This templated version directly takes a message object, calculates its size internally,
* and updates the total_size reference. This eliminates the need for a temporary variable
* at the call site.
*
* @tparam MessageType The type of the nested message (inferred from parameter)
* @param message The nested message object
*/
template<typename MessageType>
static inline void add_message_object(uint32_t &total_size, uint32_t field_id_size, const MessageType &message,
bool force = false) {
uint32_t nested_size = 0;
message.calculate_size(nested_size);
// Use the base implementation with the calculated nested_size
add_message_field(total_size, field_id_size, nested_size, force);
}
/**
* @brief Calculates and adds the sizes of all messages in a repeated field to the total message size
*
* This helper processes a vector of message objects, calculating the size for each message
* and adding it to the total size.
*
* @tparam MessageType The type of the nested messages in the vector
* @param messages Vector of message objects
*/
template<typename MessageType>
static inline void add_repeated_message(uint32_t &total_size, uint32_t field_id_size,
const std::vector<MessageType> &messages) {
// Skip if the vector is empty
if (messages.empty()) {
return;
}
// For repeated fields, always use force=true
for (const auto &message : messages) {
add_message_object(total_size, field_id_size, message, true);
}
}
};
} // namespace api
} // namespace esphome

View File

@@ -14,6 +14,96 @@
#include "esphome/components/logger/logger.h" #include "esphome/components/logger/logger.h"
#endif #endif
#ifdef USE_API_HEAP_TRACE
#include "esp_heap_trace.h"
#include "esp_heap_caps.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
// Forward declare heap tracing functions that will be used in the API class
extern "C" void start_heap_trace();
extern "C" void stop_and_dump_heap_trace();
// Task heap information tracking
extern "C" void dump_task_heap_info() {
// Get basic heap statistics
multi_heap_info_t info;
heap_caps_get_info(&info, MALLOC_CAP_INTERNAL);
ESP_LOGI("HEAP", "=== Task Heap Information ===");
ESP_LOGI("HEAP", "-------------------------------------");
ESP_LOGI("HEAP", "Total free bytes: %u", info.total_free_bytes);
ESP_LOGI("HEAP", "Total allocated bytes: %u", info.total_allocated_bytes);
ESP_LOGI("HEAP", "Minimum free bytes: %u", info.minimum_free_bytes);
ESP_LOGI("HEAP", "Largest free block: %u", info.largest_free_block);
ESP_LOGI("HEAP", "Free blocks: %u", info.free_blocks);
ESP_LOGI("HEAP", "Allocated blocks: %u", info.allocated_blocks);
ESP_LOGI("HEAP", "Total blocks: %u", info.total_blocks);
ESP_LOGI("HEAP", "-------------------------------------");
// Get information about running tasks with a much larger buffer to prevent overflow
// The FreeRTOS functions don't provide a way to check buffer size requirements in advance
static char buffer[2048];
// Zero out the buffer for safety
memset(buffer, 0, sizeof(buffer));
// Get task list
vTaskList(buffer);
// Check if buffer has valid content
if (buffer[0] != '\0') {
ESP_LOGI("HEAP", "Task Information:");
ESP_LOGI("HEAP", "Name State Priority Stack Num");
ESP_LOGI("HEAP", "-------------------------------------");
// Process the buffer line by line to add the log prefix to each line
char *line = strtok(buffer, "\n\r");
int count = 0;
while (line != nullptr && strlen(line) > 0 && count < 20) {
ESP_LOGI("HEAP", "%s", line);
line = strtok(nullptr, "\n\r");
count++;
}
} else {
ESP_LOGE("HEAP", "Could not get task information");
}
ESP_LOGI("HEAP", "-------------------------------------");
// Runtime statistics - use a separate section with a different buffer to avoid corruption
static char stats_buffer[2048];
memset(stats_buffer, 0, sizeof(stats_buffer));
// Get runtime stats
vTaskGetRunTimeStats(stats_buffer);
// Check if buffer has valid content
if (stats_buffer[0] != '\0') {
ESP_LOGI("HEAP", "Task Runtime Statistics:");
ESP_LOGI("HEAP", "Name Time Percentage");
ESP_LOGI("HEAP", "-------------------------------------");
// Process the runtime stats buffer line by line safely
char *line = strtok(stats_buffer, "\n\r");
int count = 0;
// Limit to 20 lines to prevent buffer overruns
while (line != nullptr && count < 20) {
// Skip empty lines
if (strlen(line) > 0) {
ESP_LOGI("HEAP", "%s", line);
}
line = strtok(nullptr, "\n\r");
count++;
}
} else {
ESP_LOGE("HEAP", "Could not get task runtime statistics");
}
ESP_LOGI("HEAP", "-------------------------------------");
}
#endif
#include <algorithm> #include <algorithm>
namespace esphome { namespace esphome {
@@ -22,22 +112,45 @@ namespace api {
static const char *const TAG = "api"; static const char *const TAG = "api";
// APIServer // APIServer
APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
APIServer::APIServer() { global_api_server = this; }
void APIServer::setup() { void APIServer::setup() {
ESP_LOGCONFIG(TAG, "Setting up Home Assistant API server..."); ESP_LOGCONFIG(TAG, "Setting up Home Assistant API server...");
this->setup_controller(); this->setup_controller();
socket_ = socket::socket_ip(SOCK_STREAM, 0);
if (socket_ == nullptr) { #ifdef USE_API_HEAP_TRACE
ESP_LOGW(TAG, "Could not create socket."); ESP_LOGI(TAG, "Initializing heap tracing");
start_heap_trace();
#endif
#ifdef USE_API_NOISE
uint32_t hash = 88491486UL;
this->noise_pref_ = global_preferences->make_preference<SavedNoisePsk>(hash, true);
SavedNoisePsk noise_pref_saved{};
if (this->noise_pref_.load(&noise_pref_saved)) {
ESP_LOGD(TAG, "Loaded saved Noise PSK");
this->set_noise_psk(noise_pref_saved.psk);
}
#endif
this->socket_ = socket::socket_ip(SOCK_STREAM, 0);
if (this->socket_ == nullptr) {
ESP_LOGW(TAG, "Could not create socket");
this->mark_failed(); this->mark_failed();
return; return;
} }
int enable = 1; int enable = 1;
int err = socket_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)); int err = this->socket_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
if (err != 0) { if (err != 0) {
ESP_LOGW(TAG, "Socket unable to set reuseaddr: errno %d", err); ESP_LOGW(TAG, "Socket unable to set reuseaddr: errno %d", err);
// we can still continue // we can still continue
} }
err = socket_->setblocking(false); err = this->socket_->setblocking(false);
if (err != 0) { if (err != 0) {
ESP_LOGW(TAG, "Socket unable to set nonblocking mode: errno %d", err); ESP_LOGW(TAG, "Socket unable to set nonblocking mode: errno %d", err);
this->mark_failed(); this->mark_failed();
@@ -53,14 +166,14 @@ void APIServer::setup() {
return; return;
} }
err = socket_->bind((struct sockaddr *) &server, sl); err = this->socket_->bind((struct sockaddr *) &server, sl);
if (err != 0) { if (err != 0) {
ESP_LOGW(TAG, "Socket unable to bind: errno %d", errno); ESP_LOGW(TAG, "Socket unable to bind: errno %d", errno);
this->mark_failed(); this->mark_failed();
return; return;
} }
err = socket_->listen(4); err = this->socket_->listen(4);
if (err != 0) { if (err != 0) {
ESP_LOGW(TAG, "Socket unable to listen: errno %d", errno); ESP_LOGW(TAG, "Socket unable to listen: errno %d", errno);
this->mark_failed(); this->mark_failed();
@@ -72,7 +185,7 @@ void APIServer::setup() {
logger::global_logger->add_on_log_callback([this](int level, const char *tag, const char *message) { logger::global_logger->add_on_log_callback([this](int level, const char *tag, const char *message) {
for (auto &c : this->clients_) { for (auto &c : this->clients_) {
if (!c->remove_) if (!c->remove_)
c->send_log_message(level, tag, message); c->try_send_log_message(level, tag, message);
} }
}); });
} }
@@ -86,24 +199,25 @@ void APIServer::setup() {
[this](const std::shared_ptr<esp32_camera::CameraImage> &image) { [this](const std::shared_ptr<esp32_camera::CameraImage> &image) {
for (auto &c : this->clients_) { for (auto &c : this->clients_) {
if (!c->remove_) if (!c->remove_)
c->send_camera_state(image); c->set_camera_state(image);
} }
}); });
} }
#endif #endif
} }
void APIServer::loop() { void APIServer::loop() {
// Accept new clients // Accept new clients
while (true) { while (true) {
struct sockaddr_storage source_addr; struct sockaddr_storage source_addr;
socklen_t addr_len = sizeof(source_addr); socklen_t addr_len = sizeof(source_addr);
auto sock = socket_->accept((struct sockaddr *) &source_addr, &addr_len); auto sock = this->socket_->accept((struct sockaddr *) &source_addr, &addr_len);
if (!sock) if (!sock)
break; break;
ESP_LOGD(TAG, "Accepted %s", sock->getpeername().c_str()); ESP_LOGD(TAG, "Accepted %s", sock->getpeername().c_str());
auto *conn = new APIConnection(std::move(sock), this); auto *conn = new APIConnection(std::move(sock), this);
clients_.emplace_back(conn); this->clients_.emplace_back(conn);
conn->start(); conn->start();
} }
@@ -135,17 +249,41 @@ void APIServer::loop() {
this->status_clear_warning(); this->status_clear_warning();
} }
} }
#ifdef USE_API_HEAP_TRACE
// Periodically dump heap trace information (every 30 seconds)
static uint32_t last_heap_trace_dump = 0;
const uint32_t now = millis();
if (now - last_heap_trace_dump > 30000) { // 30 seconds
ESP_LOGI(TAG, "Dumping heap trace information");
stop_and_dump_heap_trace();
// Also dump task-specific heap information
dump_task_heap_info();
// Start a new trace for the next period
start_heap_trace();
last_heap_trace_dump = now;
}
#endif
} }
void APIServer::dump_config() { void APIServer::dump_config() {
ESP_LOGCONFIG(TAG, "API Server:"); ESP_LOGCONFIG(TAG, "API Server:");
ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->port_); ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->port_);
#ifdef USE_API_NOISE #ifdef USE_API_NOISE
ESP_LOGCONFIG(TAG, " Using noise encryption: YES"); ESP_LOGCONFIG(TAG, " Using noise encryption: %s", YESNO(this->noise_ctx_->has_psk()));
if (!this->noise_ctx_->has_psk()) {
ESP_LOGCONFIG(TAG, " Supports noise encryption: YES");
}
#else #else
ESP_LOGCONFIG(TAG, " Using noise encryption: NO"); ESP_LOGCONFIG(TAG, " Using noise encryption: NO");
#endif #endif
} }
bool APIServer::uses_password() const { return !this->password_.empty(); } bool APIServer::uses_password() const { return !this->password_.empty(); }
bool APIServer::check_password(const std::string &password) const { bool APIServer::check_password(const std::string &password) const {
// depend only on input password length // depend only on input password length
const char *a = this->password_.c_str(); const char *a = this->password_.c_str();
@@ -174,7 +312,9 @@ bool APIServer::check_password(const std::string &password) const {
return result == 0; return result == 0;
} }
void APIServer::handle_disconnect(APIConnection *conn) {} void APIServer::handle_disconnect(APIConnection *conn) {}
#ifdef USE_BINARY_SENSOR #ifdef USE_BINARY_SENSOR
void APIServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) { void APIServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) {
if (obj->is_internal()) if (obj->is_internal())
@@ -342,57 +482,6 @@ void APIServer::on_update(update::UpdateEntity *obj) {
} }
#endif #endif
float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI; }
void APIServer::set_port(uint16_t port) { this->port_ = port; }
APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
void APIServer::set_password(const std::string &password) { this->password_ = password; }
void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
for (auto &client : this->clients_) {
client->send_homeassistant_service_call(call);
}
}
APIServer::APIServer() { global_api_server = this; }
void APIServer::subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
std::function<void(std::string)> f) {
this->state_subs_.push_back(HomeAssistantStateSubscription{
.entity_id = std::move(entity_id),
.attribute = std::move(attribute),
.callback = std::move(f),
.once = false,
});
}
void APIServer::get_home_assistant_state(std::string entity_id, optional<std::string> attribute,
std::function<void(std::string)> f) {
this->state_subs_.push_back(HomeAssistantStateSubscription{
.entity_id = std::move(entity_id),
.attribute = std::move(attribute),
.callback = std::move(f),
.once = true,
});
};
const std::vector<APIServer::HomeAssistantStateSubscription> &APIServer::get_state_subs() const {
return this->state_subs_;
}
uint16_t APIServer::get_port() const { return this->port_; }
void APIServer::set_reboot_timeout(uint32_t reboot_timeout) { this->reboot_timeout_ = reboot_timeout; }
#ifdef USE_HOMEASSISTANT_TIME
void APIServer::request_time() {
for (auto &client : this->clients_) {
if (!client->remove_ && client->is_authenticated())
client->send_time_request();
}
}
#endif
bool APIServer::is_connected() const { return !this->clients_.empty(); }
void APIServer::on_shutdown() {
for (auto &c : this->clients_) {
c->send_disconnect_request(DisconnectRequest());
}
delay(10);
}
#ifdef USE_ALARM_CONTROL_PANEL #ifdef USE_ALARM_CONTROL_PANEL
void APIServer::on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) { void APIServer::on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) {
if (obj->is_internal()) if (obj->is_internal())
@@ -402,6 +491,106 @@ void APIServer::on_alarm_control_panel_update(alarm_control_panel::AlarmControlP
} }
#endif #endif
float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI; }
void APIServer::set_port(uint16_t port) { this->port_ = port; }
void APIServer::set_password(const std::string &password) { this->password_ = password; }
void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
for (auto &client : this->clients_) {
client->send_homeassistant_service_call(call);
}
}
void APIServer::subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
std::function<void(std::string)> f) {
this->state_subs_.push_back(HomeAssistantStateSubscription{
.entity_id = std::move(entity_id),
.attribute = std::move(attribute),
.callback = std::move(f),
.once = false,
});
}
void APIServer::get_home_assistant_state(std::string entity_id, optional<std::string> attribute,
std::function<void(std::string)> f) {
this->state_subs_.push_back(HomeAssistantStateSubscription{
.entity_id = std::move(entity_id),
.attribute = std::move(attribute),
.callback = std::move(f),
.once = true,
});
};
const std::vector<APIServer::HomeAssistantStateSubscription> &APIServer::get_state_subs() const {
return this->state_subs_;
}
uint16_t APIServer::get_port() const { return this->port_; }
void APIServer::set_reboot_timeout(uint32_t reboot_timeout) { this->reboot_timeout_ = reboot_timeout; }
#ifdef USE_API_NOISE
bool APIServer::save_noise_psk(psk_t psk, bool make_active) {
auto &old_psk = this->noise_ctx_->get_psk();
if (std::equal(old_psk.begin(), old_psk.end(), psk.begin())) {
ESP_LOGW(TAG, "New PSK matches old");
return true;
}
SavedNoisePsk new_saved_psk{psk};
if (!this->noise_pref_.save(&new_saved_psk)) {
ESP_LOGW(TAG, "Failed to save Noise PSK");
return false;
}
// ensure it's written immediately
if (!global_preferences->sync()) {
ESP_LOGW(TAG, "Failed to sync preferences");
return false;
}
ESP_LOGD(TAG, "Noise PSK saved");
if (make_active) {
this->set_timeout(100, [this, psk]() {
ESP_LOGW(TAG, "Disconnecting all clients to reset connections");
this->set_noise_psk(psk);
for (auto &c : this->clients_) {
c->send_disconnect_request(DisconnectRequest());
}
});
}
return true;
}
#endif
#ifdef USE_HOMEASSISTANT_TIME
void APIServer::request_time() {
for (auto &client : this->clients_) {
if (!client->remove_ && client->is_authenticated())
client->send_time_request();
}
}
#endif
bool APIServer::is_connected() const { return !this->clients_.empty(); }
void APIServer::on_shutdown() {
for (auto &c : this->clients_) {
c->send_disconnect_request(DisconnectRequest());
}
delay(10);
#ifdef USE_API_HEAP_TRACE
// Make sure to stop tracing on shutdown to get final results
ESP_LOGI(TAG, "Final heap trace dump on shutdown");
stop_and_dump_heap_trace();
// Dump final task heap information
ESP_LOGI(TAG, "Final task heap information dump on shutdown");
dump_task_heap_info();
#endif
}
} // namespace api } // namespace api
} // namespace esphome } // namespace esphome
#endif #endif

View File

@@ -19,6 +19,12 @@
namespace esphome { namespace esphome {
namespace api { namespace api {
#ifdef USE_API_NOISE
struct SavedNoisePsk {
psk_t psk;
} PACKED; // NOLINT
#endif
class APIServer : public Component, public Controller { class APIServer : public Component, public Controller {
public: public:
APIServer(); APIServer();
@@ -35,6 +41,7 @@ class APIServer : public Component, public Controller {
void set_reboot_timeout(uint32_t reboot_timeout); void set_reboot_timeout(uint32_t reboot_timeout);
#ifdef USE_API_NOISE #ifdef USE_API_NOISE
bool save_noise_psk(psk_t psk, bool make_active = true);
void set_noise_psk(psk_t psk) { noise_ctx_->set_psk(psk); } void set_noise_psk(psk_t psk) { noise_ctx_->set_psk(psk); }
std::shared_ptr<APINoiseContext> get_noise_ctx() { return noise_ctx_; } std::shared_ptr<APINoiseContext> get_noise_ctx() { return noise_ctx_; }
#endif // USE_API_NOISE #endif // USE_API_NOISE
@@ -142,6 +149,7 @@ class APIServer : public Component, public Controller {
#ifdef USE_API_NOISE #ifdef USE_API_NOISE
std::shared_ptr<APINoiseContext> noise_ctx_ = std::make_shared<APINoiseContext>(); std::shared_ptr<APINoiseContext> noise_ctx_ = std::make_shared<APINoiseContext>();
ESPPreferenceObject noise_pref_;
#endif // USE_API_NOISE #endif // USE_API_NOISE
}; };

View File

@@ -10,37 +10,63 @@ namespace api {
#ifdef USE_BINARY_SENSOR #ifdef USE_BINARY_SENSOR
bool ListEntitiesIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) { bool ListEntitiesIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) {
return this->client_->send_binary_sensor_info(binary_sensor); this->client_->send_binary_sensor_info(binary_sensor);
return true;
} }
#endif #endif
#ifdef USE_COVER #ifdef USE_COVER
bool ListEntitiesIterator::on_cover(cover::Cover *cover) { return this->client_->send_cover_info(cover); } bool ListEntitiesIterator::on_cover(cover::Cover *cover) {
this->client_->send_cover_info(cover);
return true;
}
#endif #endif
#ifdef USE_FAN #ifdef USE_FAN
bool ListEntitiesIterator::on_fan(fan::Fan *fan) { return this->client_->send_fan_info(fan); } bool ListEntitiesIterator::on_fan(fan::Fan *fan) {
this->client_->send_fan_info(fan);
return true;
}
#endif #endif
#ifdef USE_LIGHT #ifdef USE_LIGHT
bool ListEntitiesIterator::on_light(light::LightState *light) { return this->client_->send_light_info(light); } bool ListEntitiesIterator::on_light(light::LightState *light) {
this->client_->send_light_info(light);
return true;
}
#endif #endif
#ifdef USE_SENSOR #ifdef USE_SENSOR
bool ListEntitiesIterator::on_sensor(sensor::Sensor *sensor) { return this->client_->send_sensor_info(sensor); } bool ListEntitiesIterator::on_sensor(sensor::Sensor *sensor) {
this->client_->send_sensor_info(sensor);
return true;
}
#endif #endif
#ifdef USE_SWITCH #ifdef USE_SWITCH
bool ListEntitiesIterator::on_switch(switch_::Switch *a_switch) { return this->client_->send_switch_info(a_switch); } bool ListEntitiesIterator::on_switch(switch_::Switch *a_switch) {
this->client_->send_switch_info(a_switch);
return true;
}
#endif #endif
#ifdef USE_BUTTON #ifdef USE_BUTTON
bool ListEntitiesIterator::on_button(button::Button *button) { return this->client_->send_button_info(button); } bool ListEntitiesIterator::on_button(button::Button *button) {
this->client_->send_button_info(button);
return true;
}
#endif #endif
#ifdef USE_TEXT_SENSOR #ifdef USE_TEXT_SENSOR
bool ListEntitiesIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) { bool ListEntitiesIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) {
return this->client_->send_text_sensor_info(text_sensor); this->client_->send_text_sensor_info(text_sensor);
return true;
} }
#endif #endif
#ifdef USE_LOCK #ifdef USE_LOCK
bool ListEntitiesIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_info(a_lock); } bool ListEntitiesIterator::on_lock(lock::Lock *a_lock) {
this->client_->send_lock_info(a_lock);
return true;
}
#endif #endif
#ifdef USE_VALVE #ifdef USE_VALVE
bool ListEntitiesIterator::on_valve(valve::Valve *valve) { return this->client_->send_valve_info(valve); } bool ListEntitiesIterator::on_valve(valve::Valve *valve) {
this->client_->send_valve_info(valve);
return true;
}
#endif #endif
bool ListEntitiesIterator::on_end() { return this->client_->send_list_info_done(); } bool ListEntitiesIterator::on_end() { return this->client_->send_list_info_done(); }
@@ -52,55 +78,83 @@ bool ListEntitiesIterator::on_service(UserServiceDescriptor *service) {
#ifdef USE_ESP32_CAMERA #ifdef USE_ESP32_CAMERA
bool ListEntitiesIterator::on_camera(esp32_camera::ESP32Camera *camera) { bool ListEntitiesIterator::on_camera(esp32_camera::ESP32Camera *camera) {
return this->client_->send_camera_info(camera); this->client_->send_camera_info(camera);
return true;
} }
#endif #endif
#ifdef USE_CLIMATE #ifdef USE_CLIMATE
bool ListEntitiesIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_info(climate); } bool ListEntitiesIterator::on_climate(climate::Climate *climate) {
this->client_->send_climate_info(climate);
return true;
}
#endif #endif
#ifdef USE_NUMBER #ifdef USE_NUMBER
bool ListEntitiesIterator::on_number(number::Number *number) { return this->client_->send_number_info(number); } bool ListEntitiesIterator::on_number(number::Number *number) {
this->client_->send_number_info(number);
return true;
}
#endif #endif
#ifdef USE_DATETIME_DATE #ifdef USE_DATETIME_DATE
bool ListEntitiesIterator::on_date(datetime::DateEntity *date) { return this->client_->send_date_info(date); } bool ListEntitiesIterator::on_date(datetime::DateEntity *date) {
this->client_->send_date_info(date);
return true;
}
#endif #endif
#ifdef USE_DATETIME_TIME #ifdef USE_DATETIME_TIME
bool ListEntitiesIterator::on_time(datetime::TimeEntity *time) { return this->client_->send_time_info(time); } bool ListEntitiesIterator::on_time(datetime::TimeEntity *time) {
this->client_->send_time_info(time);
return true;
}
#endif #endif
#ifdef USE_DATETIME_DATETIME #ifdef USE_DATETIME_DATETIME
bool ListEntitiesIterator::on_datetime(datetime::DateTimeEntity *datetime) { bool ListEntitiesIterator::on_datetime(datetime::DateTimeEntity *datetime) {
return this->client_->send_datetime_info(datetime); this->client_->send_datetime_info(datetime);
return true;
} }
#endif #endif
#ifdef USE_TEXT #ifdef USE_TEXT
bool ListEntitiesIterator::on_text(text::Text *text) { return this->client_->send_text_info(text); } bool ListEntitiesIterator::on_text(text::Text *text) {
this->client_->send_text_info(text);
return true;
}
#endif #endif
#ifdef USE_SELECT #ifdef USE_SELECT
bool ListEntitiesIterator::on_select(select::Select *select) { return this->client_->send_select_info(select); } bool ListEntitiesIterator::on_select(select::Select *select) {
this->client_->send_select_info(select);
return true;
}
#endif #endif
#ifdef USE_MEDIA_PLAYER #ifdef USE_MEDIA_PLAYER
bool ListEntitiesIterator::on_media_player(media_player::MediaPlayer *media_player) { bool ListEntitiesIterator::on_media_player(media_player::MediaPlayer *media_player) {
return this->client_->send_media_player_info(media_player); this->client_->send_media_player_info(media_player);
return true;
} }
#endif #endif
#ifdef USE_ALARM_CONTROL_PANEL #ifdef USE_ALARM_CONTROL_PANEL
bool ListEntitiesIterator::on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) { bool ListEntitiesIterator::on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
return this->client_->send_alarm_control_panel_info(a_alarm_control_panel); this->client_->send_alarm_control_panel_info(a_alarm_control_panel);
return true;
} }
#endif #endif
#ifdef USE_EVENT #ifdef USE_EVENT
bool ListEntitiesIterator::on_event(event::Event *event) { return this->client_->send_event_info(event); } bool ListEntitiesIterator::on_event(event::Event *event) {
this->client_->send_event_info(event);
return true;
}
#endif #endif
#ifdef USE_UPDATE #ifdef USE_UPDATE
bool ListEntitiesIterator::on_update(update::UpdateEntity *update) { return this->client_->send_update_info(update); } bool ListEntitiesIterator::on_update(update::UpdateEntity *update) {
this->client_->send_update_info(update);
return true;
}
#endif #endif
} // namespace api } // namespace api

View File

@@ -80,6 +80,7 @@ class ListEntitiesIterator : public ComponentIterator {
bool on_update(update::UpdateEntity *update) override; bool on_update(update::UpdateEntity *update) override;
#endif #endif
bool on_end() override; bool on_end() override;
bool completed() { return this->state_ == IteratorState::NONE; }
protected: protected:
APIConnection *client_; APIConnection *client_;

View File

@@ -149,6 +149,18 @@ class ProtoWriteBuffer {
void write(uint8_t value) { this->buffer_->push_back(value); } void write(uint8_t value) { this->buffer_->push_back(value); }
void encode_varint_raw(ProtoVarInt value) { value.encode(*this->buffer_); } void encode_varint_raw(ProtoVarInt value) { value.encode(*this->buffer_); }
void encode_varint_raw(uint32_t value) { this->encode_varint_raw(ProtoVarInt(value)); } void encode_varint_raw(uint32_t value) { this->encode_varint_raw(ProtoVarInt(value)); }
/**
* Encode a field key (tag/wire type combination).
*
* @param field_id Field number (tag) in the protobuf message
* @param type Wire type value:
* - 0: Varint (int32, int64, uint32, uint64, sint32, sint64, bool, enum)
* - 1: 64-bit (fixed64, sfixed64, double)
* - 2: Length-delimited (string, bytes, embedded messages, packed repeated fields)
* - 5: 32-bit (fixed32, sfixed32, float)
*
* Following https://protobuf.dev/programming-guides/encoding/#structure
*/
void encode_field_raw(uint32_t field_id, uint32_t type) { void encode_field_raw(uint32_t field_id, uint32_t type) {
uint32_t val = (field_id << 3) | (type & 0b111); uint32_t val = (field_id << 3) | (type & 0b111);
this->encode_varint_raw(val); this->encode_varint_raw(val);
@@ -157,7 +169,7 @@ class ProtoWriteBuffer {
if (len == 0 && !force) if (len == 0 && !force)
return; return;
this->encode_field_raw(field_id, 2); this->encode_field_raw(field_id, 2); // type 2: Length-delimited string
this->encode_varint_raw(len); this->encode_varint_raw(len);
auto *data = reinterpret_cast<const uint8_t *>(string); auto *data = reinterpret_cast<const uint8_t *>(string);
this->buffer_->insert(this->buffer_->end(), data, data + len); this->buffer_->insert(this->buffer_->end(), data, data + len);
@@ -171,26 +183,26 @@ class ProtoWriteBuffer {
void encode_uint32(uint32_t field_id, uint32_t value, bool force = false) { void encode_uint32(uint32_t field_id, uint32_t value, bool force = false) {
if (value == 0 && !force) if (value == 0 && !force)
return; return;
this->encode_field_raw(field_id, 0); this->encode_field_raw(field_id, 0); // type 0: Varint - uint32
this->encode_varint_raw(value); this->encode_varint_raw(value);
} }
void encode_uint64(uint32_t field_id, uint64_t value, bool force = false) { void encode_uint64(uint32_t field_id, uint64_t value, bool force = false) {
if (value == 0 && !force) if (value == 0 && !force)
return; return;
this->encode_field_raw(field_id, 0); this->encode_field_raw(field_id, 0); // type 0: Varint - uint64
this->encode_varint_raw(ProtoVarInt(value)); this->encode_varint_raw(ProtoVarInt(value));
} }
void encode_bool(uint32_t field_id, bool value, bool force = false) { void encode_bool(uint32_t field_id, bool value, bool force = false) {
if (!value && !force) if (!value && !force)
return; return;
this->encode_field_raw(field_id, 0); this->encode_field_raw(field_id, 0); // type 0: Varint - bool
this->write(0x01); this->write(0x01);
} }
void encode_fixed32(uint32_t field_id, uint32_t value, bool force = false) { void encode_fixed32(uint32_t field_id, uint32_t value, bool force = false) {
if (value == 0 && !force) if (value == 0 && !force)
return; return;
this->encode_field_raw(field_id, 5); this->encode_field_raw(field_id, 5); // type 5: 32-bit fixed32
this->write((value >> 0) & 0xFF); this->write((value >> 0) & 0xFF);
this->write((value >> 8) & 0xFF); this->write((value >> 8) & 0xFF);
this->write((value >> 16) & 0xFF); this->write((value >> 16) & 0xFF);
@@ -200,7 +212,7 @@ class ProtoWriteBuffer {
if (value == 0 && !force) if (value == 0 && !force)
return; return;
this->encode_field_raw(field_id, 5); this->encode_field_raw(field_id, 1); // type 1: 64-bit fixed64
this->write((value >> 0) & 0xFF); this->write((value >> 0) & 0xFF);
this->write((value >> 8) & 0xFF); this->write((value >> 8) & 0xFF);
this->write((value >> 16) & 0xFF); this->write((value >> 16) & 0xFF);
@@ -254,7 +266,7 @@ class ProtoWriteBuffer {
this->encode_uint64(field_id, uvalue, force); this->encode_uint64(field_id, uvalue, force);
} }
template<class C> void encode_message(uint32_t field_id, const C &value, bool force = false) { template<class C> void encode_message(uint32_t field_id, const C &value, bool force = false) {
this->encode_field_raw(field_id, 2); this->encode_field_raw(field_id, 2); // type 2: Length-delimited message
size_t begin = this->buffer_->size(); size_t begin = this->buffer_->size();
value.encode(*this); value.encode(*this);
@@ -276,6 +288,7 @@ class ProtoMessage {
virtual ~ProtoMessage() = default; virtual ~ProtoMessage() = default;
virtual void encode(ProtoWriteBuffer buffer) const = 0; virtual void encode(ProtoWriteBuffer buffer) const = 0;
void decode(const uint8_t *buffer, size_t length); void decode(const uint8_t *buffer, size_t length);
virtual void calculate_size(uint32_t &total_size) const = 0;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
std::string dump() const; std::string dump() const;
virtual void dump_to(std::string &out) const = 0; virtual void dump_to(std::string &out) const = 0;
@@ -298,13 +311,29 @@ class ProtoService {
virtual void on_fatal_error() = 0; virtual void on_fatal_error() = 0;
virtual void on_unauthenticated_access() = 0; virtual void on_unauthenticated_access() = 0;
virtual void on_no_setup_connection() = 0; virtual void on_no_setup_connection() = 0;
virtual ProtoWriteBuffer create_buffer() = 0; /**
* Create a buffer with a reserved size.
* @param reserve_size The number of bytes to pre-allocate in the buffer. This is a hint
* to optimize memory usage and avoid reallocations during encoding.
* Implementations should aim to allocate at least this size.
* @return A ProtoWriteBuffer object with the reserved size.
*/
virtual ProtoWriteBuffer create_buffer(uint32_t reserve_size) = 0;
virtual bool send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) = 0; virtual bool send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) = 0;
virtual bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) = 0; virtual bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) = 0;
// Optimized method that pre-allocates buffer based on message size
template<class C> bool send_message_(const C &msg, uint32_t message_type) { template<class C> bool send_message_(const C &msg, uint32_t message_type) {
auto buffer = this->create_buffer(); uint32_t msg_size = 0;
msg.calculate_size(msg_size);
// Create a pre-sized buffer
auto buffer = this->create_buffer(msg_size);
// Encode message into the buffer
msg.encode(buffer); msg.encode(buffer);
// Send the buffer
return this->send_buffer(buffer, message_type); return this->send_buffer(buffer, message_type);
} }
}; };

View File

@@ -76,6 +76,8 @@ class InitialStateIterator : public ComponentIterator {
#ifdef USE_UPDATE #ifdef USE_UPDATE
bool on_update(update::UpdateEntity *update) override; bool on_update(update::UpdateEntity *update) override;
#endif #endif
bool completed() { return this->state_ == IteratorState::NONE; }
protected: protected:
APIConnection *client_; APIConnection *client_;
}; };

View File

@@ -1,17 +1,17 @@
from esphome import pins
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome import pins
from esphome.const import ( from esphome.const import (
CONF_CALIBRATION,
CONF_CAPACITANCE, CONF_CAPACITANCE,
CONF_DIV_RATIO, CONF_DIV_RATIO,
CONF_INDOOR, CONF_INDOOR,
CONF_IRQ_PIN, CONF_IRQ_PIN,
CONF_LIGHTNING_THRESHOLD, CONF_LIGHTNING_THRESHOLD,
CONF_MASK_DISTURBER, CONF_MASK_DISTURBER,
CONF_CALIBRATION,
CONF_TUNE_ANTENNA,
CONF_NOISE_LEVEL, CONF_NOISE_LEVEL,
CONF_SPIKE_REJECTION, CONF_SPIKE_REJECTION,
CONF_TUNE_ANTENNA,
CONF_WATCHDOG_THRESHOLD, CONF_WATCHDOG_THRESHOLD,
) )

View File

@@ -1,6 +1,7 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import binary_sensor from esphome.components import binary_sensor
import esphome.config_validation as cv
from . import AS3935, CONF_AS3935_ID from . import AS3935, CONF_AS3935_ID
DEPENDENCIES = ["as3935"] DEPENDENCIES = ["as3935"]

View File

@@ -1,13 +1,14 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor from esphome.components import sensor
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_DISTANCE, CONF_DISTANCE,
CONF_LIGHTNING_ENERGY, CONF_LIGHTNING_ENERGY,
UNIT_KILOMETER,
ICON_SIGNAL_DISTANCE_VARIANT,
ICON_FLASH, ICON_FLASH,
ICON_SIGNAL_DISTANCE_VARIANT,
UNIT_KILOMETER,
) )
from . import AS3935, CONF_AS3935_ID from . import AS3935, CONF_AS3935_ID
DEPENDENCIES = ["as3935"] DEPENDENCIES = ["as3935"]

View File

@@ -1,6 +1,6 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import as3935, i2c from esphome.components import as3935, i2c
import esphome.config_validation as cv
from esphome.const import CONF_ID from esphome.const import CONF_ID
AUTO_LOAD = ["as3935"] AUTO_LOAD = ["as3935"]

View File

@@ -1,10 +1,7 @@
#pragma once #pragma once
#include "esphome/core/component.h"
#include "esphome/components/as3935/as3935.h" #include "esphome/components/as3935/as3935.h"
#include "esphome/components/i2c/i2c.h" #include "esphome/components/i2c/i2c.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/binary_sensor/binary_sensor.h"
namespace esphome { namespace esphome {
namespace as3935_i2c { namespace as3935_i2c {

View File

@@ -1,6 +1,6 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import as3935, spi from esphome.components import as3935, spi
import esphome.config_validation as cv
from esphome.const import CONF_ID from esphome.const import CONF_ID
AUTO_LOAD = ["as3935"] AUTO_LOAD = ["as3935"]

View File

@@ -1,12 +1,12 @@
from esphome import pins from esphome import pins
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c from esphome.components import i2c
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_ID,
CONF_DIR_PIN, CONF_DIR_PIN,
CONF_DIRECTION, CONF_DIRECTION,
CONF_HYSTERESIS, CONF_HYSTERESIS,
CONF_ID,
CONF_RANGE, CONF_RANGE,
) )

View File

@@ -1,19 +1,20 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor from esphome.components import sensor
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_ANGLE,
CONF_GAIN,
CONF_ID, CONF_ID,
STATE_CLASS_MEASUREMENT, CONF_MAGNITUDE,
CONF_POSITION,
CONF_STATUS,
ENTITY_CATEGORY_DIAGNOSTIC,
ICON_MAGNET, ICON_MAGNET,
ICON_ROTATE_RIGHT, ICON_ROTATE_RIGHT,
CONF_GAIN, STATE_CLASS_MEASUREMENT,
ENTITY_CATEGORY_DIAGNOSTIC,
CONF_MAGNITUDE,
CONF_STATUS,
CONF_POSITION,
CONF_ANGLE,
) )
from .. import as5600_ns, AS5600Component
from .. import AS5600Component, as5600_ns
CODEOWNERS = ["@ammmze"] CODEOWNERS = ["@ammmze"]
DEPENDENCIES = ["as5600"] DEPENDENCIES = ["as5600"]

View File

@@ -7,7 +7,7 @@
namespace esphome { namespace esphome {
namespace as7341 { namespace as7341 {
static const uint8_t AS7341_CHIP_ID = 0X09; static const uint8_t AS7341_CHIP_ID = 0x09;
static const uint8_t AS7341_CONFIG = 0x70; static const uint8_t AS7341_CONFIG = 0x70;
static const uint8_t AS7341_LED = 0x74; static const uint8_t AS7341_LED = 0x74;

View File

@@ -1,6 +1,6 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor from esphome.components import i2c, sensor
import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_GAIN, CONF_GAIN,
CONF_ID, CONF_ID,
@@ -9,7 +9,6 @@ from esphome.const import (
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
) )
CODEOWNERS = ["@mrgnr"] CODEOWNERS = ["@mrgnr"]
DEPENDENCIES = ["i2c"] DEPENDENCIES = ["i2c"]

View File

@@ -1,13 +1,9 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation, core from esphome import automation, core
from esphome.components import i2c
from esphome.automation import maybe_simple_id from esphome.automation import maybe_simple_id
from esphome.const import ( import esphome.codegen as cg
CONF_ID, from esphome.components import i2c
CONF_FREQUENCY, import esphome.config_validation as cv
) from esphome.const import CONF_FREQUENCY, CONF_ID
CODEOWNERS = ["@X-Ryl669"] CODEOWNERS = ["@X-Ryl669"]
DEPENDENCIES = ["i2c"] DEPENDENCIES = ["i2c"]

View File

@@ -1,10 +1,8 @@
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import switch from esphome.components import switch
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import ( from esphome.const import DEVICE_CLASS_SWITCH, ICON_WIFI
DEVICE_CLASS_SWITCH,
ICON_WIFI,
)
from .. import CONF_AT581X_ID, AT581XComponent, at581x_ns from .. import CONF_AT581X_ID, AT581XComponent, at581x_ns
DEPENDENCIES = ["at581x"] DEPENDENCIES = ["at581x"]

View File

@@ -1,14 +1,14 @@
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import esp32_ble_tracker, sensor
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import sensor, esp32_ble_tracker
from esphome.const import ( from esphome.const import (
CONF_BATTERY_LEVEL, CONF_BATTERY_LEVEL,
CONF_BATTERY_VOLTAGE, CONF_BATTERY_VOLTAGE,
CONF_MAC_ADDRESS,
CONF_HUMIDITY, CONF_HUMIDITY,
CONF_ID,
CONF_MAC_ADDRESS,
CONF_SIGNAL_STRENGTH, CONF_SIGNAL_STRENGTH,
CONF_TEMPERATURE, CONF_TEMPERATURE,
CONF_ID,
DEVICE_CLASS_BATTERY, DEVICE_CLASS_BATTERY,
DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_SIGNAL_STRENGTH, DEVICE_CLASS_SIGNAL_STRENGTH,

View File

@@ -48,6 +48,12 @@ def set_stream_limits(
min_sample_rate: int = _UNDEF, min_sample_rate: int = _UNDEF,
max_sample_rate: int = _UNDEF, max_sample_rate: int = _UNDEF,
): ):
"""Sets the limits for the audio stream that audio component can handle
When the component sinks audio (e.g., a speaker), these indicate the limits to the audio it can receive.
When the component sources audio (e.g., a microphone), these indicate the limits to the audio it can send.
"""
def set_limits_in_config(config): def set_limits_in_config(config):
if min_bits_per_sample is not _UNDEF: if min_bits_per_sample is not _UNDEF:
config[CONF_MIN_BITS_PER_SAMPLE] = min_bits_per_sample config[CONF_MIN_BITS_PER_SAMPLE] = min_bits_per_sample
@@ -69,43 +75,87 @@ def final_validate_audio_schema(
name: str, name: str,
*, *,
audio_device: str, audio_device: str,
bits_per_sample: int, bits_per_sample: int = _UNDEF,
channels: int, channels: int = _UNDEF,
sample_rate: int, sample_rate: int = _UNDEF,
enabled_channels: list[int] = _UNDEF,
audio_device_issue: bool = False,
): ):
"""Validates audio compatibility when passed between different components.
The component derived from ``AUDIO_COMPONENT_SCHEMA`` should call ``set_stream_limits`` in a validator to specify its compatible settings
- If audio_device_issue is True, then the error message indicates the user should adjust the AUDIO_COMPONENT_SCHEMA derived component's configuration to match the values passed to this function
- If audio_device_issue is False, then the error message indicates the user should adjust the configuration of the component calling this function, as it falls out of the valid stream limits
Args:
name (str): Friendly name of the component calling this function with an audio component to validate
audio_device (str): The configuration parameter name that contains the ID of an AUDIO_COMPONENT_SCHEMA derived component to validate against
bits_per_sample (int, optional): The desired bits per sample
channels (int, optional): The desired number of channels
sample_rate (int, optional): The desired sample rate
enabled_channels (list[int], optional): The desired enabled channels
audio_device_issue (bool, optional): Format the error message to indicate the problem is in the configuration for the ``audio_device`` component. Defaults to False.
"""
def validate_audio_compatiblity(audio_config): def validate_audio_compatiblity(audio_config):
audio_schema = {} audio_schema = {}
try: if bits_per_sample is not _UNDEF:
cv.int_range( try:
min=audio_config.get(CONF_MIN_BITS_PER_SAMPLE), cv.int_range(
max=audio_config.get(CONF_MAX_BITS_PER_SAMPLE), min=audio_config.get(CONF_MIN_BITS_PER_SAMPLE),
)(bits_per_sample) max=audio_config.get(CONF_MAX_BITS_PER_SAMPLE),
except cv.Invalid as exc: )(bits_per_sample)
raise cv.Invalid( except cv.Invalid as exc:
f"Invalid configuration for the {name} component. The {CONF_BITS_PER_SAMPLE} {str(exc)}" if audio_device_issue:
) from exc error_string = f"Invalid configuration for the specified {audio_device}. The {name} component requires {bits_per_sample} bits per sample."
else:
error_string = f"Invalid configuration for the {name} component. The {CONF_BITS_PER_SAMPLE} {str(exc)}"
raise cv.Invalid(error_string) from exc
try: if channels is not _UNDEF:
cv.int_range( try:
min=audio_config.get(CONF_MIN_CHANNELS), cv.int_range(
max=audio_config.get(CONF_MAX_CHANNELS), min=audio_config.get(CONF_MIN_CHANNELS),
)(channels) max=audio_config.get(CONF_MAX_CHANNELS),
except cv.Invalid as exc: )(channels)
raise cv.Invalid( except cv.Invalid as exc:
f"Invalid configuration for the {name} component. The {CONF_NUM_CHANNELS} {str(exc)}" if audio_device_issue:
) from exc error_string = f"Invalid configuration for the specified {audio_device}. The {name} component requires {channels} channels."
else:
error_string = f"Invalid configuration for the {name} component. The {CONF_NUM_CHANNELS} {str(exc)}"
raise cv.Invalid(error_string) from exc
try: if sample_rate is not _UNDEF:
cv.int_range( try:
min=audio_config.get(CONF_MIN_SAMPLE_RATE), cv.int_range(
max=audio_config.get(CONF_MAX_SAMPLE_RATE), min=audio_config.get(CONF_MIN_SAMPLE_RATE),
)(sample_rate) max=audio_config.get(CONF_MAX_SAMPLE_RATE),
return cv.Schema(audio_schema, extra=cv.ALLOW_EXTRA)(audio_config) )(sample_rate)
except cv.Invalid as exc: except cv.Invalid as exc:
raise cv.Invalid( if audio_device_issue:
f"Invalid configuration for the {name} component. The {CONF_SAMPLE_RATE} {str(exc)}" error_string = f"Invalid configuration for the specified {audio_device}. The {name} component requires a {sample_rate} sample rate."
) from exc else:
error_string = f"Invalid configuration for the {name} component. The {CONF_SAMPLE_RATE} {str(exc)}"
raise cv.Invalid(error_string) from exc
if enabled_channels is not _UNDEF:
for channel in enabled_channels:
try:
# Channels are 0-indexed
cv.int_range(
min=0,
max=audio_config.get(CONF_MAX_CHANNELS) - 1,
)(channel)
except cv.Invalid as exc:
if audio_device_issue:
error_string = f"Invalid configuration for the specified {audio_device}. The {name} component requires channel {channel}."
else:
error_string = f"Invalid configuration for the {name} component. Enabled channel {channel} {str(exc)}"
raise cv.Invalid(error_string) from exc
return cv.Schema(audio_schema, extra=cv.ALLOW_EXTRA)(audio_config)
return cv.Schema( return cv.Schema(
{ {
@@ -118,4 +168,4 @@ def final_validate_audio_schema(
async def to_code(config): async def to_code(config):
cg.add_library("esphome/esp-audio-libs", "1.1.1") cg.add_library("esphome/esp-audio-libs", "1.1.3")

View File

@@ -135,5 +135,30 @@ const char *audio_file_type_to_string(AudioFileType file_type);
void scale_audio_samples(const int16_t *audio_samples, int16_t *output_buffer, int16_t scale_factor, void scale_audio_samples(const int16_t *audio_samples, int16_t *output_buffer, int16_t scale_factor,
size_t samples_to_scale); size_t samples_to_scale);
/// @brief Unpacks a quantized audio sample into a Q31 fixed point number.
/// @param data Pointer to uint8_t array containing the audio sample
/// @param bytes_per_sample The number of bytes per sample
/// @return Q31 sample
inline int32_t unpack_audio_sample_to_q31(const uint8_t *data, size_t bytes_per_sample) {
int32_t sample = 0;
if (bytes_per_sample == 1) {
sample |= data[0] << 24;
} else if (bytes_per_sample == 2) {
sample |= data[0] << 16;
sample |= data[1] << 24;
} else if (bytes_per_sample == 3) {
sample |= data[0] << 8;
sample |= data[1] << 16;
sample |= data[2] << 24;
} else if (bytes_per_sample == 4) {
sample |= data[0];
sample |= data[1] << 8;
sample |= data[2] << 16;
sample |= data[3] << 24;
}
return sample;
}
} // namespace audio } // namespace audio
} // namespace esphome } // namespace esphome

View File

@@ -66,19 +66,30 @@ esp_err_t AudioDecoder::start(AudioFileType audio_file_type) {
case AudioFileType::FLAC: case AudioFileType::FLAC:
this->flac_decoder_ = make_unique<esp_audio_libs::flac::FLACDecoder>(); this->flac_decoder_ = make_unique<esp_audio_libs::flac::FLACDecoder>();
this->free_buffer_required_ = this->free_buffer_required_ =
this->output_transfer_buffer_->capacity(); // We'll revise this after reading the header this->output_transfer_buffer_->capacity(); // Adjusted and reallocated after reading the header
break; break;
#endif #endif
#ifdef USE_AUDIO_MP3_SUPPORT #ifdef USE_AUDIO_MP3_SUPPORT
case AudioFileType::MP3: case AudioFileType::MP3:
this->mp3_decoder_ = esp_audio_libs::helix_decoder::MP3InitDecoder(); this->mp3_decoder_ = esp_audio_libs::helix_decoder::MP3InitDecoder();
// MP3 always has 1152 samples per chunk
this->free_buffer_required_ = 1152 * sizeof(int16_t) * 2; // samples * size per sample * channels this->free_buffer_required_ = 1152 * sizeof(int16_t) * 2; // samples * size per sample * channels
// Always reallocate the output transfer buffer to the smallest necessary size
this->output_transfer_buffer_->reallocate(this->free_buffer_required_);
break; break;
#endif #endif
case AudioFileType::WAV: case AudioFileType::WAV:
this->wav_decoder_ = make_unique<esp_audio_libs::wav_decoder::WAVDecoder>(); this->wav_decoder_ = make_unique<esp_audio_libs::wav_decoder::WAVDecoder>();
this->wav_decoder_->reset(); this->wav_decoder_->reset();
// Processing WAVs doesn't actually require a specific amount of buffer size, as it is already in PCM format.
// Thus, we don't reallocate to a minimum size.
this->free_buffer_required_ = 1024; this->free_buffer_required_ = 1024;
if (this->output_transfer_buffer_->capacity() < this->free_buffer_required_) {
this->output_transfer_buffer_->reallocate(this->free_buffer_required_);
}
break; break;
case AudioFileType::NONE: case AudioFileType::NONE:
default: default:
@@ -116,10 +127,18 @@ AudioDecoderState AudioDecoder::decode(bool stop_gracefully) {
uint32_t decoding_start = millis(); uint32_t decoding_start = millis();
bool first_loop_iteration = true;
size_t bytes_processed = 0;
size_t bytes_available_before_processing = 0;
while (state == FileDecoderState::MORE_TO_PROCESS) { while (state == FileDecoderState::MORE_TO_PROCESS) {
// Transfer decoded out // Transfer decoded out
if (!this->pause_output_) { if (!this->pause_output_) {
size_t bytes_written = this->output_transfer_buffer_->transfer_data_to_sink(pdMS_TO_TICKS(READ_WRITE_TIMEOUT_MS)); // Never shift the data in the output transfer buffer to avoid unnecessary, slow data moves
size_t bytes_written =
this->output_transfer_buffer_->transfer_data_to_sink(pdMS_TO_TICKS(READ_WRITE_TIMEOUT_MS), false);
if (this->audio_stream_info_.has_value()) { if (this->audio_stream_info_.has_value()) {
this->accumulated_frames_written_ += this->audio_stream_info_.value().bytes_to_frames(bytes_written); this->accumulated_frames_written_ += this->audio_stream_info_.value().bytes_to_frames(bytes_written);
this->playback_ms_ += this->playback_ms_ +=
@@ -138,12 +157,24 @@ AudioDecoderState AudioDecoder::decode(bool stop_gracefully) {
// Decode more audio // Decode more audio
size_t bytes_read = this->input_transfer_buffer_->transfer_data_from_source(pdMS_TO_TICKS(READ_WRITE_TIMEOUT_MS)); // Only shift data on the first loop iteration to avoid unnecessary, slow moves
size_t bytes_read = this->input_transfer_buffer_->transfer_data_from_source(pdMS_TO_TICKS(READ_WRITE_TIMEOUT_MS),
first_loop_iteration);
if ((this->potentially_failed_count_ > 0) && (bytes_read == 0)) { if (!first_loop_iteration && (this->input_transfer_buffer_->available() < bytes_processed)) {
// Less data is available than what was processed in last iteration, so don't attempt to decode.
// This attempts to avoid the decoder from consistently trying to decode an incomplete frame. The transfer buffer
// will shift the remaining data to the start and copy more from the source the next time the decode function is
// called
break;
}
bytes_available_before_processing = this->input_transfer_buffer_->available();
if ((this->potentially_failed_count_ > 10) && (bytes_read == 0)) {
// Failed to decode in last attempt and there is no new data // Failed to decode in last attempt and there is no new data
if (this->input_transfer_buffer_->free() == 0) { if ((this->input_transfer_buffer_->free() == 0) && first_loop_iteration) {
// The input buffer is full. Since it previously failed on the exact same data, we can never recover // The input buffer is full. Since it previously failed on the exact same data, we can never recover
state = FileDecoderState::FAILED; state = FileDecoderState::FAILED;
} else { } else {
@@ -175,6 +206,9 @@ AudioDecoderState AudioDecoder::decode(bool stop_gracefully) {
} }
} }
first_loop_iteration = false;
bytes_processed = bytes_available_before_processing - this->input_transfer_buffer_->available();
if (state == FileDecoderState::POTENTIALLY_FAILED) { if (state == FileDecoderState::POTENTIALLY_FAILED) {
++this->potentially_failed_count_; ++this->potentially_failed_count_;
} else if (state == FileDecoderState::END_OF_FILE) { } else if (state == FileDecoderState::END_OF_FILE) {
@@ -207,13 +241,11 @@ FileDecoderState AudioDecoder::decode_flac_() {
size_t bytes_consumed = this->flac_decoder_->get_bytes_index(); size_t bytes_consumed = this->flac_decoder_->get_bytes_index();
this->input_transfer_buffer_->decrease_buffer_length(bytes_consumed); this->input_transfer_buffer_->decrease_buffer_length(bytes_consumed);
// Reallocate the output transfer buffer to the smallest necessary size
this->free_buffer_required_ = flac_decoder_->get_output_buffer_size_bytes(); this->free_buffer_required_ = flac_decoder_->get_output_buffer_size_bytes();
if (this->output_transfer_buffer_->capacity() < this->free_buffer_required_) { if (!this->output_transfer_buffer_->reallocate(this->free_buffer_required_)) {
// Output buffer is not big enough // Couldn't reallocate output buffer
if (!this->output_transfer_buffer_->reallocate(this->free_buffer_required_)) { return FileDecoderState::FAILED;
// Couldn't reallocate output buffer
return FileDecoderState::FAILED;
}
} }
this->audio_stream_info_ = this->audio_stream_info_ =

View File

@@ -15,6 +15,8 @@ namespace audio {
static const uint32_t READ_WRITE_TIMEOUT_MS = 20; static const uint32_t READ_WRITE_TIMEOUT_MS = 20;
static const uint32_t CONNECTION_TIMEOUT_MS = 5000;
// The number of times the http read times out with no data before throwing an error // The number of times the http read times out with no data before throwing an error
static const uint32_t ERROR_COUNT_NO_DATA_READ_TIMEOUT = 100; static const uint32_t ERROR_COUNT_NO_DATA_READ_TIMEOUT = 100;
@@ -97,7 +99,7 @@ esp_err_t AudioReader::start(const std::string &uri, AudioFileType &file_type) {
client_config.user_data = this; client_config.user_data = this;
client_config.buffer_size = HTTP_STREAM_BUFFER_SIZE; client_config.buffer_size = HTTP_STREAM_BUFFER_SIZE;
client_config.keep_alive_enable = true; client_config.keep_alive_enable = true;
client_config.timeout_ms = 5000; // Shouldn't trigger watchdog resets if caller runs in a task client_config.timeout_ms = CONNECTION_TIMEOUT_MS; // Shouldn't trigger watchdog resets if caller runs in a task
#if CONFIG_MBEDTLS_CERTIFICATE_BUNDLE #if CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
if (uri.find("https:") != std::string::npos) { if (uri.find("https:") != std::string::npos) {
@@ -189,7 +191,7 @@ esp_err_t AudioReader::start(const std::string &uri, AudioFileType &file_type) {
file_type = this->audio_file_type_; file_type = this->audio_file_type_;
} }
this->no_data_read_count_ = 0; this->last_data_read_ms_ = millis();
this->output_transfer_buffer_ = AudioSinkTransferBuffer::create(this->buffer_size_); this->output_transfer_buffer_ = AudioSinkTransferBuffer::create(this->buffer_size_);
if (this->output_transfer_buffer_ == nullptr) { if (this->output_transfer_buffer_ == nullptr) {
@@ -257,22 +259,21 @@ AudioReaderState AudioReader::file_read_() {
} }
AudioReaderState AudioReader::http_read_() { AudioReaderState AudioReader::http_read_() {
this->output_transfer_buffer_->transfer_data_to_sink(pdMS_TO_TICKS(READ_WRITE_TIMEOUT_MS)); this->output_transfer_buffer_->transfer_data_to_sink(pdMS_TO_TICKS(READ_WRITE_TIMEOUT_MS), false);
if (esp_http_client_is_complete_data_received(this->client_)) { if (esp_http_client_is_complete_data_received(this->client_)) {
if (this->output_transfer_buffer_->available() == 0) { if (this->output_transfer_buffer_->available() == 0) {
this->cleanup_connection_(); this->cleanup_connection_();
return AudioReaderState::FINISHED; return AudioReaderState::FINISHED;
} }
} else { } else if (this->output_transfer_buffer_->free() > 0) {
size_t bytes_to_read = this->output_transfer_buffer_->free(); size_t bytes_to_read = this->output_transfer_buffer_->free();
int received_len = int received_len =
esp_http_client_read(this->client_, (char *) this->output_transfer_buffer_->get_buffer_end(), bytes_to_read); esp_http_client_read(this->client_, (char *) this->output_transfer_buffer_->get_buffer_end(), bytes_to_read);
if (received_len > 0) { if (received_len > 0) {
this->output_transfer_buffer_->increase_buffer_length(received_len); this->output_transfer_buffer_->increase_buffer_length(received_len);
this->last_data_read_ms_ = millis();
this->no_data_read_count_ = 0;
} else if (received_len < 0) { } else if (received_len < 0) {
// HTTP read error // HTTP read error
this->cleanup_connection_(); this->cleanup_connection_();
@@ -280,12 +281,11 @@ AudioReaderState AudioReader::http_read_() {
} else { } else {
if (bytes_to_read > 0) { if (bytes_to_read > 0) {
// Read timed out // Read timed out
++this->no_data_read_count_; if ((millis() - this->last_data_read_ms_) > CONNECTION_TIMEOUT_MS) {
if (this->no_data_read_count_ >= ERROR_COUNT_NO_DATA_READ_TIMEOUT) {
// Timed out with no data read too many times, so the http read has failed
this->cleanup_connection_(); this->cleanup_connection_();
return AudioReaderState::FAILED; return AudioReaderState::FAILED;
} }
delay(READ_WRITE_TIMEOUT_MS); delay(READ_WRITE_TIMEOUT_MS);
} }
} }

View File

@@ -71,7 +71,7 @@ class AudioReader {
void cleanup_connection_(); void cleanup_connection_();
size_t buffer_size_; size_t buffer_size_;
uint32_t no_data_read_count_; uint32_t last_data_read_ms_;
esp_http_client_handle_t client_{nullptr}; esp_http_client_handle_t client_{nullptr};

View File

@@ -4,6 +4,8 @@
#include "esphome/core/hal.h" #include "esphome/core/hal.h"
#include <cstring>
namespace esphome { namespace esphome {
namespace audio { namespace audio {
@@ -93,8 +95,9 @@ AudioResamplerState AudioResampler::resample(bool stop_gracefully, int32_t *ms_d
} }
if (!this->pause_output_) { if (!this->pause_output_) {
// Move audio data to the sink // Move audio data to the sink without shifting the data in the output transfer buffer to avoid unnecessary, slow
this->output_transfer_buffer_->transfer_data_to_sink(pdMS_TO_TICKS(READ_WRITE_TIMEOUT_MS)); // data moves
this->output_transfer_buffer_->transfer_data_to_sink(pdMS_TO_TICKS(READ_WRITE_TIMEOUT_MS), false);
} else { } else {
// If paused, block to avoid wasting CPU resources // If paused, block to avoid wasting CPU resources
delay(READ_WRITE_TIMEOUT_MS); delay(READ_WRITE_TIMEOUT_MS);
@@ -115,6 +118,7 @@ AudioResamplerState AudioResampler::resample(bool stop_gracefully, int32_t *ms_d
if ((this->input_stream_info_.get_sample_rate() != this->output_stream_info_.get_sample_rate()) || if ((this->input_stream_info_.get_sample_rate() != this->output_stream_info_.get_sample_rate()) ||
(this->input_stream_info_.get_bits_per_sample() != this->output_stream_info_.get_bits_per_sample())) { (this->input_stream_info_.get_bits_per_sample() != this->output_stream_info_.get_bits_per_sample())) {
// Adjust gain by -3 dB to avoid clipping due to the resampling process
esp_audio_libs::resampler::ResamplerResults results = esp_audio_libs::resampler::ResamplerResults results =
this->resampler_->resample(this->input_transfer_buffer_->get_buffer_start(), this->resampler_->resample(this->input_transfer_buffer_->get_buffer_start(),
this->output_transfer_buffer_->get_buffer_end(), frames_available, frames_free, -3); this->output_transfer_buffer_->get_buffer_end(), frames_available, frames_free, -3);

View File

@@ -6,6 +6,7 @@
#include "audio_transfer_buffer.h" #include "audio_transfer_buffer.h"
#include "esphome/core/defines.h" #include "esphome/core/defines.h"
#include "esphome/core/helpers.h"
#include "esphome/core/ring_buffer.h" #include "esphome/core/ring_buffer.h"
#ifdef USE_SPEAKER #ifdef USE_SPEAKER

View File

@@ -33,12 +33,17 @@ size_t AudioTransferBuffer::free() const {
if (this->buffer_size_ == 0) { if (this->buffer_size_ == 0) {
return 0; return 0;
} }
return this->buffer_size_ - (this->buffer_length_ - (this->data_start_ - this->buffer_)); return this->buffer_size_ - (this->buffer_length_ + (this->data_start_ - this->buffer_));
} }
void AudioTransferBuffer::decrease_buffer_length(size_t bytes) { void AudioTransferBuffer::decrease_buffer_length(size_t bytes) {
this->buffer_length_ -= bytes; this->buffer_length_ -= bytes;
this->data_start_ += bytes; if (this->buffer_length_ > 0) {
this->data_start_ += bytes;
} else {
// All the data in the buffer has been consumed, reset the start pointer
this->data_start_ = this->buffer_;
}
} }
void AudioTransferBuffer::increase_buffer_length(size_t bytes) { this->buffer_length_ += bytes; } void AudioTransferBuffer::increase_buffer_length(size_t bytes) { this->buffer_length_ += bytes; }
@@ -71,7 +76,7 @@ bool AudioTransferBuffer::has_buffered_data() const {
bool AudioTransferBuffer::reallocate(size_t new_buffer_size) { bool AudioTransferBuffer::reallocate(size_t new_buffer_size) {
if (this->buffer_length_ > 0) { if (this->buffer_length_ > 0) {
// Already has data in the buffer, fail // Buffer currently has data, so reallocation is impossible
return false; return false;
} }
this->deallocate_buffer_(); this->deallocate_buffer_();
@@ -106,12 +111,14 @@ void AudioTransferBuffer::deallocate_buffer_() {
this->buffer_length_ = 0; this->buffer_length_ = 0;
} }
size_t AudioSourceTransferBuffer::transfer_data_from_source(TickType_t ticks_to_wait) { size_t AudioSourceTransferBuffer::transfer_data_from_source(TickType_t ticks_to_wait, bool pre_shift) {
// Shift data in buffer to start if (pre_shift) {
if (this->buffer_length_ > 0) { // Shift data in buffer to start
memmove(this->buffer_, this->data_start_, this->buffer_length_); if (this->buffer_length_ > 0) {
memmove(this->buffer_, this->data_start_, this->buffer_length_);
}
this->data_start_ = this->buffer_;
} }
this->data_start_ = this->buffer_;
size_t bytes_to_read = this->free(); size_t bytes_to_read = this->free();
size_t bytes_read = 0; size_t bytes_read = 0;
@@ -125,7 +132,7 @@ size_t AudioSourceTransferBuffer::transfer_data_from_source(TickType_t ticks_to_
return bytes_read; return bytes_read;
} }
size_t AudioSinkTransferBuffer::transfer_data_to_sink(TickType_t ticks_to_wait) { size_t AudioSinkTransferBuffer::transfer_data_to_sink(TickType_t ticks_to_wait, bool post_shift) {
size_t bytes_written = 0; size_t bytes_written = 0;
if (this->available()) { if (this->available()) {
#ifdef USE_SPEAKER #ifdef USE_SPEAKER
@@ -139,11 +146,14 @@ size_t AudioSinkTransferBuffer::transfer_data_to_sink(TickType_t ticks_to_wait)
} }
this->decrease_buffer_length(bytes_written); this->decrease_buffer_length(bytes_written);
}
if (post_shift) {
// Shift unwritten data to the start of the buffer // Shift unwritten data to the start of the buffer
memmove(this->buffer_, this->data_start_, this->buffer_length_); memmove(this->buffer_, this->data_start_, this->buffer_length_);
this->data_start_ = this->buffer_; this->data_start_ = this->buffer_;
} }
return bytes_written; return bytes_written;
} }

View File

@@ -60,6 +60,7 @@ class AudioTransferBuffer {
protected: protected:
/// @brief Allocates the transfer buffer in external memory, if available. /// @brief Allocates the transfer buffer in external memory, if available.
/// @param buffer_size The number of bytes to allocate
/// @return True is successful, false otherwise. /// @return True is successful, false otherwise.
bool allocate_buffer_(size_t buffer_size); bool allocate_buffer_(size_t buffer_size);
@@ -89,8 +90,10 @@ class AudioSinkTransferBuffer : public AudioTransferBuffer {
/// @brief Writes any available data in the transfer buffer to the sink. /// @brief Writes any available data in the transfer buffer to the sink.
/// @param ticks_to_wait FreeRTOS ticks to block while waiting for the sink to have enough space /// @param ticks_to_wait FreeRTOS ticks to block while waiting for the sink to have enough space
/// @param post_shift If true, all remaining data is moved to the start of the buffer after transferring to the sink.
/// Defaults to true.
/// @return Number of bytes written /// @return Number of bytes written
size_t transfer_data_to_sink(TickType_t ticks_to_wait); size_t transfer_data_to_sink(TickType_t ticks_to_wait, bool post_shift = true);
/// @brief Adds a ring buffer as the transfer buffer's sink. /// @brief Adds a ring buffer as the transfer buffer's sink.
/// @param ring_buffer weak_ptr to the allocated ring buffer /// @param ring_buffer weak_ptr to the allocated ring buffer
@@ -125,8 +128,10 @@ class AudioSourceTransferBuffer : public AudioTransferBuffer {
/// @brief Reads any available data from the sink into the transfer buffer. /// @brief Reads any available data from the sink into the transfer buffer.
/// @param ticks_to_wait FreeRTOS ticks to block while waiting for the source to have enough data /// @param ticks_to_wait FreeRTOS ticks to block while waiting for the source to have enough data
/// @param pre_shift If true, any unwritten data is moved to the start of the buffer before transferring from the
/// source. Defaults to true.
/// @return Number of bytes read /// @return Number of bytes read
size_t transfer_data_from_source(TickType_t ticks_to_wait); size_t transfer_data_from_source(TickType_t ticks_to_wait, bool pre_shift = true);
/// @brief Adds a ring buffer as the transfer buffer's source. /// @brief Adds a ring buffer as the transfer buffer's source.
/// @param ring_buffer weak_ptr to the allocated ring buffer /// @param ring_buffer weak_ptr to the allocated ring buffer

View File

@@ -30,8 +30,12 @@ void AXS15231Touchscreen::setup() {
this->interrupt_pin_->setup(); this->interrupt_pin_->setup();
this->attach_interrupt_(this->interrupt_pin_, gpio::INTERRUPT_FALLING_EDGE); this->attach_interrupt_(this->interrupt_pin_, gpio::INTERRUPT_FALLING_EDGE);
} }
this->x_raw_max_ = this->display_->get_native_width(); if (this->x_raw_max_ == 0) {
this->y_raw_max_ = this->display_->get_native_height(); this->x_raw_max_ = this->display_->get_native_width();
}
if (this->y_raw_max_ == 0) {
this->y_raw_max_ = this->display_->get_native_height();
}
ESP_LOGCONFIG(TAG, "AXS15231 Touchscreen setup complete"); ESP_LOGCONFIG(TAG, "AXS15231 Touchscreen setup complete");
} }
@@ -44,7 +48,7 @@ void AXS15231Touchscreen::update_touches() {
err = this->read(data, sizeof(data)); err = this->read(data, sizeof(data));
ERROR_CHECK(err); ERROR_CHECK(err);
this->status_clear_warning(); this->status_clear_warning();
if (data[0] != 0) // no touches if (data[0] != 0 || data[1] == 0) // no touches
return; return;
uint16_t x = encode_uint16(data[2] & 0xF, data[3]); uint16_t x = encode_uint16(data[2] & 0xF, data[3]);
uint16_t y = encode_uint16(data[4] & 0xF, data[5]); uint16_t y = encode_uint16(data[4] & 0xF, data[5]);

View File

@@ -1,13 +1,13 @@
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import esp32_ble_tracker, sensor
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import sensor, esp32_ble_tracker
from esphome.const import ( from esphome.const import (
CONF_BATTERY_VOLTAGE, CONF_BATTERY_VOLTAGE,
CONF_HUMIDITY, CONF_HUMIDITY,
CONF_ID, CONF_ID,
CONF_ILLUMINANCE, CONF_ILLUMINANCE,
CONF_MOISTURE,
CONF_MAC_ADDRESS, CONF_MAC_ADDRESS,
CONF_MOISTURE,
CONF_TEMPERATURE, CONF_TEMPERATURE,
DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_ILLUMINANCE, DEVICE_CLASS_ILLUMINANCE,

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