Compare commits

...

280 Commits

Author SHA1 Message Date
J. Nick Koston 1a55b74357 Merge branch 'dev' into web-server-logs 2026-06-30 14:02:44 -05:00
Jonathan Swoboda 9468ad628c [espnow] Drop oversized received frames to prevent buffer overflow (#17271) 2026-06-30 14:57:34 -04:00
Jonathan Swoboda 990431aa5b [bluetooth_proxy] Fix -Wtype-limits warning with active: false (#17273) 2026-06-30 14:56:30 -04:00
Jonathan Swoboda b79cbcbde7 [espidf] Install native ESP-IDF into a machine-global cache dir (#17306) 2026-06-30 14:56:18 -04:00
Bonne Eggleston afb5922f37 [modbus] Update client components to use ModbusClientDevice (#11987) 2026-06-30 13:26:51 -05:00
Jonathan Swoboda 3035355c0a [ci] Widen import-time margin for CI runner variance (#17287) 2026-06-30 11:38:31 -04:00
Jonathan Swoboda 43b3aa0712 [ci] Fix nRF52 zigbee/network test-grouping conflict (#17295) 2026-06-30 11:37:59 -04:00
Jonathan Swoboda 12b78e7c47 [qmi8658] Pin i2c_id in test config to fix grouped component test conflict (#17303) 2026-06-30 11:37:17 -04:00
Clyde Stubbs fb5d8b5d4c [mipi_spi] Bug fixes (#17247) 2026-06-30 08:43:01 -04:00
Bonne Eggleston 9e72027b64 [devcontainer] Align base image with production, fix Python venv and build tools (#17296)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 08:33:33 -04:00
Edvard Filistovič faa5f72500 [mqtt] Add LN882X (LN882H) platform support (#17297)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-30 08:16:18 -04:00
Jonathan Swoboda 359c6a7265 [libretiny] Update LibreTiny to v1.13.0 (#17288) 2026-06-29 22:34:32 -04:00
J. Nick Koston a031da351d Merge pull request #17294 from esphome/bump-device-builder-1.0.22 2026-06-29 19:29:08 -07:00
esphome[bot] 091b6a0ba0 Bump bundled esphome-device-builder to 1.0.22 2026-06-30 02:07:06 +00:00
Jesse Hills cf9d97d5ae [pixoo] Add Divoom Pixoo display component (#16974) 2026-06-30 11:20:48 +12:00
Clyde Stubbs 3e1a6b4e11 [cst9220] Add CST9220 and CST9217 touchscreen support (#16888) 2026-06-30 11:18:01 +12:00
Clyde Stubbs 5c7245dfcd [qmi8658] Motion platform for QMI8658 IMU (#16889) 2026-06-30 11:04:25 +12:00
Clyde Stubbs 1611345c55 [agents] Add English language AI guidelines for documentation (#17290) 2026-06-29 22:37:17 +00:00
Jonathan Swoboda b36e20d60b Revert "Bump awalsh128/cache-apt-pkgs-action from 1.6.0 to 1.6.2 (#17286)" (#17289) 2026-06-29 18:25:16 -04:00
Ardumine 93eb6f78e0 [network] Enlarge Zephyr net buffer pool and TCP windows on nRF52/Zephyr plataform (#17278) 2026-06-29 17:32:57 -04:00
dependabot[bot] e308075e3f Bump puremagic from 1.30 to 2.2.0 (#17285)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-29 16:12:23 -04:00
dependabot[bot] 405607e9d2 Bump esptool from 5.3.0 to 5.3.1 (#17284)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-29 16:06:36 -04:00
dependabot[bot] 797ed23765 Bump tzlocal from 5.4.3 to 5.4.4 (#17283)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-29 16:02:11 -04:00
dependabot[bot] 8780c7e0ac Bump awalsh128/cache-apt-pkgs-action from 1.6.0 to 1.6.2 (#17286)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-29 16:01:41 -04:00
Jonathan Swoboda 136e343988 [ethernet] Generic and YT8531 PHY over RGMII (gigabit) for ESP32-S31 (#17277) 2026-06-29 14:13:11 -04:00
Jonathan Swoboda b8690c8e31 [core] Drop Python 3.11 support (#17280) 2026-06-29 12:32:28 -04:00
Kevin Ahrendt 2778c62d07 [audio] Bump microMP3 to v0.4.0 (#17279) 2026-06-29 11:33:56 -04:00
Jesse Hills 7984349c36 Merge branch 'release' into dev 2026-06-29 22:32:34 +12:00
Jesse Hills 0cbbd64577 Merge pull request #17223 from esphome/bump-2026.6.3
2026.6.3
2026-06-29 22:31:44 +12:00
Jesse Hills a618ee11b4 Bump version to 2026.6.3 2026-06-29 20:30:24 +12:00
Tom 6251c26cc6 [espnow] Fix espnow crash when send() is called without a callback (#17266) 2026-06-29 20:30:24 +12:00
Jonathan Swoboda 4fbe0d87ec [wifi] Fix crash when WiFi is enabled late alongside ESP-NOW (#17239) 2026-06-29 20:30:24 +12:00
esphome[bot] 24d8e99c50 Bump bundled esphome-device-builder to 1.0.21 (#17257)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-06-29 20:30:24 +12:00
Jonathan Swoboda 14b6a0ede1 [espnow] Don't throttle ESP-NOW RX when deep_sleep is present (#17240) 2026-06-29 20:30:24 +12:00
Jonathan Swoboda 1793ca5eac [core] Suppress unactionable legacy-redaction warning for substitutions (#17242) 2026-06-29 20:30:24 +12:00
esphome[bot] 62e19bcb27 Bump bundled esphome-device-builder to 1.0.20 (#17244) 2026-06-29 20:30:24 +12:00
Franck Nijhof 84d1c34c28 [core] Fix area saved as null in storage.json (#17219)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-29 20:30:24 +12:00
esphome[bot] f78cbf9200 Bump bundled esphome-device-builder to 1.0.19 (#17217)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-06-29 20:30:23 +12:00
esphome[bot] eb711381d3 Bump bundled esphome-device-builder to 1.0.18 (#17212)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-06-29 20:30:23 +12:00
Jonathan Swoboda 9a1daa5247 [hbridge] Fix light stuck on one polarity (#17162) 2026-06-29 20:30:18 +12:00
Clyde Stubbs f3d61ca3e1 [mipi][mipi_spi] Swap native dimensions for swap_xy hardware transform (#17201)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 20:29:44 +12:00
Jonathan Swoboda 29dfd820c6 [wifi] Report STA IP, not SoftAP IP, in wifi_info on ESP8266 (#17185) 2026-06-29 20:29:44 +12:00
Jonathan Swoboda 8bc5b97298 [network] Set IPv4 type tag on all lwIP platforms, not just esp32 (#17200) 2026-06-29 20:29:44 +12:00
Jonathan Swoboda 7a64163c4f [esp32] Accept '#' as ESP-IDF source ref separator (#17193) 2026-06-29 20:29:44 +12:00
esphome[bot] dfe14f9c3a Bump bundled esphome-device-builder to 1.0.17 (#17199) 2026-06-29 20:29:44 +12:00
esphome[bot] 26cf373ae7 Bump bundled esphome-device-builder to 1.0.16 (#17182) 2026-06-29 20:29:44 +12:00
Geoffrey Frogeye 94ccddf176 [opentherm] Support power scaling disabled (#17183)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-06-29 20:29:44 +12:00
Clyde Stubbs 2ec24505d0 [mipi_spi] Suppress sequence errors when page selection used (#17176) 2026-06-29 20:29:44 +12:00
esphome[bot] 4f7faa7712 Bump bundled esphome-device-builder to 1.0.15 (#17170) 2026-06-29 20:29:44 +12:00
Clyde Stubbs b3dcaac262 [mipi_spi] Warn on MODE3 default for display without CS pin (#17153) 2026-06-29 20:29:44 +12:00
mnewton25 ee118d384a [esp32] Use POSIX path for secure-boot signing/verification keys Fixes #17164 (#17166)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-29 20:29:44 +12:00
Jonathan Swoboda 8d36167e11 [esp32_ble_server] Fix set_value action with by-reference triggers (#17156) 2026-06-29 20:29:44 +12:00
esphome[bot] 6d559a32df Bump bundled esphome-device-builder to 1.0.14 (#17139) 2026-06-29 20:29:37 +12:00
Jonathan Swoboda bf0d31b3ab [espidf] Don't fail framework check on broken unrelated PATH tools (#17053) 2026-06-29 20:23:23 +12:00
dependabot[bot] d8ffb732b7 Bump zeroconf from 0.149.16 to 0.150.0 (#17137)
Signed-off-by: dependabot[bot] <support@github.com>
2026-06-29 20:23:23 +12:00
Tom 9e8261056c [espnow] Fix espnow crash when send() is called without a callback (#17266) 2026-06-28 23:06:12 -04:00
Clyde Stubbs 5f311d281e [esphome] Warn when a YAML merge (<<:) drops a key (#17246)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-29 07:17:01 +10:00
J. Nick Koston a336ad6732 [mcp4725] Use constexpr bit shift instead of powf for full-scale value (#17261) 2026-06-28 17:07:51 -04:00
Bonne Eggleston 8434d54cc7 [modbus] Reinstate turnaround delay after broadcasts (Revert #17209) (#17263)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-28 17:07:25 -04:00
Jonathan Swoboda b62f7a41c9 [multiple] Single-precision float math, avoid double promotion (stragglers) (#17260) 2026-06-28 16:15:38 -04:00
Bonne Eggleston 4ebecf514a [modbus_server] Simplify server response handling (#12376)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-06-28 19:41:47 +00:00
Jonathan Swoboda 2f32c88ae5 [wifi] Fix crash when WiFi is enabled late alongside ESP-NOW (#17239) 2026-06-28 15:16:42 -04:00
Jonathan Swoboda 95449068e7 [multiple] Single-precision float math, avoid double promotion (batch 4/4) (#17256) 2026-06-28 14:18:41 -04:00
Jonathan Swoboda 556def78aa [multiple] Single-precision float math, avoid double promotion (batch 3/4) (#17255) 2026-06-28 14:18:30 -04:00
Jonathan Swoboda b7803cf9b5 [multiple] Single-precision float math, avoid double promotion (batch 2/4) (#17254) 2026-06-28 14:18:20 -04:00
Jonathan Swoboda 40820287f1 [multiple] Single-precision float math, avoid double promotion (batch 1/4) (#17253) 2026-06-28 14:18:13 -04:00
Jonathan Swoboda 6210dfb4d0 [core] Use single-precision float math to avoid double promotion (#17252) 2026-06-28 14:18:01 -04:00
esphome[bot] 45c712b17b Bump bundled esphome-device-builder to 1.0.21 (#17257)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-06-28 09:41:49 -07:00
alorente 8e23065b86 [it8951] Add IT8951 e-paper controller support to epaper_spi (#15346)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Citric Li <37475446+limengdu@users.noreply.github.com>
Co-authored-by: koosoli <koosoli@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 21:14:05 +10:00
Jonathan Swoboda d3892b8399 [platformio] Extract toolchain-agnostic PlatformIO library converter (#17243) 2026-06-28 07:03:09 -04:00
Jonathan Swoboda bda789052d [espnow] Don't throttle ESP-NOW RX when deep_sleep is present (#17240) 2026-06-27 22:17:09 +00:00
Jonathan Swoboda 0fb100f2d1 [core] Suppress unactionable legacy-redaction warning for substitutions (#17242) 2026-06-27 18:05:00 -04:00
esphome[bot] fd7fc6b8e8 Bump bundled esphome-device-builder to 1.0.20 (#17244) 2026-06-27 15:00:23 -07:00
tomaszduda23 690e8c3fb9 [nrf52] add upload for native build (#17100)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-27 15:50:28 -04:00
Ardumine a0742a9535 [api] Add nRF52 support (#17226)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-27 14:22:34 -04:00
Jonathan Swoboda ccc57475b7 [deep_sleep] Add ESP32-C5 support (#17237) 2026-06-27 12:04:45 -04:00
Franck Nijhof 24ec65e68e [esp32] Only warn about S3 PSRAM pins (GPIO33-37) in octal mode (#17222)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-27 11:26:47 -04:00
dependabot[bot] 436938b931 Bump actions/cache/restore from 6.0.0 to 6.1.0 (#17231)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-27 10:33:13 -04:00
dependabot[bot] 063c4371de Bump actions/cache from 6.0.0 to 6.1.0 (#17230)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-27 10:33:01 -04:00
dependabot[bot] 7ad4cbf46f Bump actions/cache/save from 6.0.0 to 6.1.0 (#17229)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-27 10:32:50 -04:00
dependabot[bot] 88875daf52 Bump actions/cache/restore from 6.0.0 to 6.1.0 in /.github/actions/restore-python (#17228)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-27 10:32:39 -04:00
Franck Nijhof 7811781a96 [es8388] Fix DAC unable to unmute once muted (#17221)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-27 10:21:36 -04:00
Franck Nijhof da5e11d196 [core] Fix area saved as null in storage.json (#17219)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-27 09:49:56 -04:00
Ardumine 75cdabee3d [socket] Add BSD socket support for nRF52 (#16699)
Co-authored-by: tomaszduda23 <tomaszduda23@gmail.com>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-26 09:30:07 -04:00
esphome[bot] f9f28a6a00 Bump bundled esphome-device-builder to 1.0.19 (#17217)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-06-26 07:48:52 +02:00
Ardumine be8523a73c [mdns] Add mDNS to Zephyr and nRF52 (#16924)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-25 16:33:38 -04:00
dependabot[bot] f49bed47de Bump ruff from 0.15.19 to 0.15.20 (#17216)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-25 16:07:55 -04:00
Franck Nijhof 239211e521 [time] Defer aioesphomeapi import to speed up config validation (#17214) 2026-06-25 15:34:44 -04:00
Franck Nijhof cc646b2213 [core] Defer requests import in framework_helpers to speed up config validation (#17215) 2026-06-25 15:34:33 -04:00
dependabot[bot] ddf075a2dd Bump aioesphomeapi from 45.3.1 to 45.5.2 (#17211)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-25 18:00:52 +00:00
esphome[bot] e304c318fb Bump bundled esphome-device-builder to 1.0.18 (#17212)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-06-25 19:47:24 +02:00
Jonathan Swoboda e27390bddb [hbridge] Fix light stuck on one polarity (#17162) 2026-06-25 12:36:10 -04:00
rwrozelle 6f36ce6429 [openthread] Provide action to control poll_period when device MTD (#11766)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 12:35:15 -04:00
Jonathan Swoboda 1d5490fd91 [modbus] Only apply turnaround delay after broadcasts (#17209) 2026-06-25 11:38:34 -04:00
Jonathan Swoboda 18c7f60410 [uart] Validate fixed UART settings at config time for fixed-baud components (#17207) 2026-06-25 10:19:05 -04:00
Clyde Stubbs 4f70f6b2a6 [mipi][mipi_spi] Swap native dimensions for swap_xy hardware transform (#17201)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 08:36:36 -04:00
Jesse Hills 29a6105730 [ms8607] Mark configurable classes as final (#17147) 2026-06-25 08:31:15 -04:00
Jesse Hills 46cf052ec5 [config_validation] Fix multicast typo in error message (#17206) 2026-06-25 08:28:43 -04:00
Jesse Hills e5d8c22b47 Mark configurable classes as final (13/21: pmsa003i-rc522) (#16964) 2026-06-25 13:20:11 +02:00
Jesse Hills 64acb358a5 Mark configurable classes as final (8/21: hm3301-integration) (#16959) 2026-06-25 13:20:00 +02:00
Jesse Hills d511f0614d Mark configurable classes as final (18/21: template-tx20) (#16969) 2026-06-25 13:19:51 +02:00
Jesse Hills eb9ca517e3 Mark configurable classes as final (14/21: rc522_i2c-scd4x) (#16965) 2026-06-25 13:19:39 +02:00
Jesse Hills f769457bb0 Mark configurable classes as final (15/21: script-slow_pwm) (#16966) 2026-06-25 13:19:30 +02:00
Tomasz Witke 0fcf512148 [image] Use LVGL 9 color formats (#16871) 2026-06-25 21:03:50 +10:00
Jesse Hills 8c68e95568 [config_validation] Add tests for 100% validator coverage (#17204) 2026-06-25 22:33:28 +12:00
Fae d8eee03556 [host] Fix handling of directory for preferences (#11160) 2026-06-25 10:20:15 +00:00
Julian Lunz 23933c1b58 [adc] Only call cyw43_thread_enter/exit for VSYS when WiFi is active on RP2040 (#17203) 2026-06-24 23:21:41 -07:00
Jonathan Swoboda 8c9f4fba8f [wifi] Report STA IP, not SoftAP IP, in wifi_info on ESP8266 (#17185) 2026-06-24 22:28:15 -04:00
Jonathan Swoboda 92554f4e67 [network] Set IPv4 type tag on all lwIP platforms, not just esp32 (#17200) 2026-06-24 22:28:06 -04:00
Jonathan Swoboda 1dfafce06a [i2c][spi] Wire ESP32-S31/H4/H21 bus capabilities (#17188) 2026-06-24 21:05:29 -04:00
Jonathan Swoboda abbcfd213f [tinyusb][usb_cdc_acm][usb_host][usb_uart] Support ESP32-S31/H4 (#17190) 2026-06-24 20:51:03 -04:00
Jonathan Swoboda 23aff5202b [wifi][openthread] Wire ESP32-S31/H4/H21 radio support (#17186) 2026-06-24 20:42:41 -04:00
Jonathan Swoboda 91e515ca7c [esp32] Accept '#' as ESP-IDF source ref separator (#17193) 2026-06-24 20:40:28 -04:00
Jonathan Swoboda 538f554bdb [psram] Support ESP32-S31/H4 (#17192) 2026-06-24 20:38:55 -04:00
Jonathan Swoboda e96717f6cd [waveshare_io_ch32v003] Pin i2c_id in test to avoid grouping conflict (#17191) 2026-06-24 20:16:53 -04:00
dependabot[bot] aff5e248ed Bump actions/setup-python from 6.2.0 to 6.3.0 in /.github/actions/restore-python (#17194)
Signed-off-by: dependabot[bot] <support@github.com>
2026-06-25 02:05:30 +02:00
dependabot[bot] 155439be74 Bump actions/setup-python from 6.2.0 to 6.3.0 (#17197)
Signed-off-by: dependabot[bot] <support@github.com>
2026-06-25 02:05:13 +02:00
dependabot[bot] fa34c67950 Bump CodSpeedHQ/action from 4.17.6 to 4.18.1 (#17198)
Signed-off-by: dependabot[bot] <support@github.com>
2026-06-25 02:04:52 +02:00
dependabot[bot] b688474444 Bump ruff from 0.15.18 to 0.15.19 (#17195)
Signed-off-by: dependabot[bot] <support@github.com>
2026-06-25 02:04:37 +02:00
esphome[bot] 72b663fc40 Bump bundled esphome-device-builder to 1.0.17 (#17199) 2026-06-25 02:04:22 +02:00
esphome[bot] f471329d60 Bump bundled esphome-device-builder to 1.0.16 (#17182) 2026-06-24 17:15:00 +02:00
Geoffrey Frogeye e8acd24fd9 [opentherm] Support power scaling disabled (#17183)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-06-24 09:29:57 -04:00
Clyde Stubbs 18f29f8d2b [mipi_spi] Suppress sequence errors when page selection used (#17176) 2026-06-24 08:05:23 -04:00
Anton Viktorov cbcf23426d [waveshare_io_ch32v003] Waveshare I/O Expander component (#10071) 2026-06-24 20:08:59 +10:00
Jesse Hills e6455c5b44 Mark configurable classes as final (12/21: msa3xx-pm2005) (#16963) 2026-06-23 18:27:07 -05:00
Jesse Hills 2b8916fc4e [ci] Exclude test changes from small-pr/medium-pr size labels (#17172) 2026-06-24 11:13:34 +12:00
esphome[bot] dae078fc56 Bump bundled esphome-device-builder to 1.0.15 (#17170) 2026-06-24 11:06:15 +12:00
Clyde Stubbs 72686bd4af [mipi_spi] Warn on MODE3 default for display without CS pin (#17153) 2026-06-24 08:43:39 +10:00
Jonathan Swoboda 344da7c4f4 [docker] Move build deps to base image, drop app apt step (#17167)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2026-06-23 17:35:07 -04:00
Jesse Hills 84de814e6f [config_validation] Make bind_key a sensitive dual-mode validator (#17146) 2026-06-24 09:05:39 +12:00
Jonathan Swoboda 1d32b6c9e0 [espidf] Enable ccache by default for ESP-IDF builds (#17163) 2026-06-23 16:41:13 -04:00
mnewton25 49536693b7 [esp32] Use POSIX path for secure-boot signing/verification keys Fixes #17164 (#17166)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-23 16:40:49 -04:00
dependabot[bot] a24a63e61b Bump actions/cache from 5.0.5 to 6.0.0 (#17168)
Signed-off-by: dependabot[bot] <support@github.com>
2026-06-23 15:03:28 -05:00
dependabot[bot] e3b644c2a0 Bump actions/cache from 5.0.5 to 6.0.0 in /.github/actions/restore-python (#17169)
Signed-off-by: dependabot[bot] <support@github.com>
2026-06-23 15:03:16 -05:00
Jonathan Swoboda 7763ce958d [tests] Disable Hypothesis deadline on IP validation property tests (#17138) 2026-06-23 15:21:40 -04:00
Jonathan Swoboda ff001b9e45 [esp32_ble_server] Fix set_value action with by-reference triggers (#17156) 2026-06-23 15:21:26 -04:00
Jonathan Swoboda c2d79c972c [docker] Install ccache in the image (#17157) 2026-06-23 15:04:42 -04:00
Jonathan Swoboda e0377bbbd3 [ci] Enable ccache for component batch builds (~7% faster) (#17136) 2026-06-23 15:04:23 -04:00
Berik Visschers eae65a6b88 [bme680_bsec][bme68x_bsec2][const] Move BME sensor constants to shared component consts (#17160) 2026-06-23 11:44:48 -04:00
Jesse Hills 225d426d95 [core] Use CORE.is_* platform helpers in __main__ (#17144) 2026-06-23 08:40:16 -04:00
Zach Isbach 9614bc20a0 [epaper_spi] Add support for Waveshare 2.13" V4 series B (R/B/W) (#16828) 2026-06-23 11:00:19 +00:00
arunderwood 41747c2de7 [epaper_spi] Add support for the Inkplate 2 (#16856) 2026-06-23 16:50:02 +10:00
Jesse Hills c70d56807f [motion] Make motion test configs mergeable in CI (#17149) 2026-06-23 16:44:58 +12:00
Jesse Hills 69d700727d [docker] Remove dead HA addon env exports (streamer_mode, relative_url) (#17140) 2026-06-23 11:25:24 +12:00
esphome[bot] 5fcf656806 Bump bundled esphome-device-builder to 1.0.14 (#17139) 2026-06-22 17:45:22 -05:00
Jonathan Swoboda 1ace836744 [espidf] Don't fail framework check on broken unrelated PATH tools (#17053) 2026-06-22 18:41:21 -04:00
Anunay Kulshrestha 3a4831bd7e [ble_nus] Atomic log-line framing (no partial ring-buffer writes) (#17105)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: tomaszduda23 <tomaszduda23@gmail.com>
2026-06-22 21:04:11 +00:00
dependabot[bot] 6c1724874b Bump zeroconf from 0.149.16 to 0.150.0 (#17137)
Signed-off-by: dependabot[bot] <support@github.com>
2026-06-22 15:04:22 -05:00
Joseph C. Lehner 2483576909 [sx126x] Add data whitening options (#17102)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-22 10:51:10 -04:00
esphome[bot] 0df1db6205 Bump bundled esphome-device-builder to 1.0.13 (#17132) 2026-06-22 00:01:22 -05:00
J. Nick Koston 614eae7a3b [dashboard_import] Store package_import_url in flash on ESP8266 (#17127) 2026-06-21 23:18:06 -05:00
Jesse Hills 2fe67a6eda [graphical_display_menu] Mark configurable classes as final (#17129) 2026-06-21 23:17:47 -05:00
Jesse Hills e88f69b5f8 Mark configurable classes as final (7/21: gcja5-hlw8032) (#16958) 2026-06-21 21:05:05 -05:00
Jesse Hills c69cfd44be Mark configurable classes as final (5/21) (#16956) 2026-06-21 21:04:55 -05:00
Jesse Hills 9f5ed6fdfd Mark configurable classes as final (6/21) (#16957) 2026-06-21 21:04:45 -05:00
Jesse Hills 0891473280 Mark configurable classes as final (10/21: matrix_keypad-micronova) (#16961) 2026-06-21 21:04:34 -05:00
Jesse Hills 2982d7c834 Mark configurable classes as final (9/21) (#16960) 2026-06-21 21:04:16 -05:00
Jesse Hills 026bac4cd1 [ld2420] Mark configurable classes as final (#17130) 2026-06-22 13:27:56 +12:00
J. Nick Koston 7fcc890e84 [rp2040] Bump arduino-pico framework to 5.6.1 (#17122) 2026-06-22 13:02:46 +12:00
J. Nick Koston 44c54b3a75 [json] Bump ArduinoJson to 7.4.3 (#17126) 2026-06-21 23:09:54 +00:00
Clyde Stubbs faabafad2b [mipi_rgb] Fix offsets for Wave 5 1024x600 (#17057) 2026-06-22 09:54:05 +12:00
Jesse Hills cce7cfff29 Mark configurable classes as final (3/21: ble_scanner-ch423) (#16954) 2026-06-22 09:52:32 +12:00
Kevin Ahrendt 77a91853be [i2s_audio] Narrow wider streams to the speaker's configured bit depth (#16821)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-22 09:52:05 +12:00
Jesse Hills 73dbc8214b Mark configurable classes as final (4/21: chsc6x-dfplayer) (#16955) 2026-06-22 09:51:31 +12:00
Jesse Hills 7d7cdb6c66 Mark configurable classes as final (21/21: zhlt01-zyaura) (#16972) 2026-06-22 09:51:11 +12:00
J. Nick Koston 0d7130c499 [docs] Remove leftover dashboard references after dashboard removal (#17125) 2026-06-22 09:48:21 +12:00
J. Nick Koston 1d5d581734 [esp8266] Drop stale esphome-docker-base reference (#17123) 2026-06-22 09:34:09 +12:00
Jesse Hills d0e3e98d55 [dashboard] Remove legacy web dashboard (#17124) 2026-06-22 09:33:27 +12:00
J. Nick Koston c4abc5476e [core] Remove deprecated std::string scheduler/timer overloads (#17111) 2026-06-21 16:22:08 -05:00
J. Nick Koston 6c10fc1272 [hub75] Remove deprecated scan_wiring name aliases (#17118) 2026-06-21 16:21:50 -05:00
Kevin Ahrendt 036768c399 [audio] Fix mono channel MP3 playback (#17106) 2026-06-21 15:19:13 -05:00
J. Nick Koston bac3de4f93 Merge remote-tracking branch 'origin/dev' into web-server-logs
# Conflicts:
#	esphome/__main__.py
#	tests/unit_tests/test_main.py
2026-06-21 15:03:00 -05:00
J. Nick Koston 921758f87d [core] Clarify resolve error when a device has no network log/OTA transport (#17107) 2026-06-21 15:00:55 -05:00
J. Nick Koston c6ead57a9e [packages] Remove deprecated single-package include syntax (#17119) 2026-06-22 08:00:46 +12:00
J. Nick Koston 78c6131bbf [web_server] Deprecate version 1 (#17109) 2026-06-21 15:00:36 -05:00
J. Nick Koston d8f883bd9d [core] Remove deprecated get_object_id() and get_compilation_time() (#17112) 2026-06-22 07:54:05 +12:00
J. Nick Koston d1d77fc217 [remote_base] Remove deprecated MideaData::to_string() (#17117) 2026-06-22 07:51:08 +12:00
J. Nick Koston f273221cf4 [core] Remove deprecated value_accuracy_to_string() (#17116) 2026-06-22 07:50:32 +12:00
J. Nick Koston 03121d2efe [core] Remove deprecated std::string GPIOPin::dump_summary() (#17115) 2026-06-22 07:49:27 +12:00
J. Nick Koston 7c2603d9bc [ethernet] Defer clk_mode removal to 2026.9.0 (#17114) 2026-06-22 07:47:15 +12:00
J. Nick Koston 21aee91e67 [web_server] Remove deprecated object ID URL matching (#17113) 2026-06-22 07:45:03 +12:00
J. Nick Koston 1bd937d89c [api] Remove pre-1.14 object_id backward-compat code (#17108) 2026-06-22 07:44:00 +12:00
Bonne Eggleston 63d8a344c5 [modbus] Fix parsing & split out server mode (#11969) 2026-06-21 13:32:35 -05:00
J. Nick Koston a97f9e7cda [core] Add esphome logs over web_server HTTP SSE
Stream device logs over the web_server /events Server-Sent Events feed so
'esphome logs' works on devices that have web_server: but no api:. This is
the logging counterpart to web_server OTA. Priority stays API, then MQTT,
then web_server. Reconnects automatically when the stream drops.

Factor the resolve-to-URLs step and the web_server port/auth lookup shared
with web_server OTA into a new web_server_helpers module (resolve_web_server_urls
and get_web_server_connection), with helpers.format_ip_url for IPv4/IPv6 URL
formatting, and broaden the missing-transport log error to suggest web_server:
alongside api:/MQTT/USB.
2026-06-21 10:50:49 -05:00
J. Nick Koston c826293efc [core] Clarify resolve error when a device has no network log/OTA transport
A device with a static IP, mDNS disabled, and no api: component failed
logs with "All specified devices ['OTA'] could not be resolved" and a
hint to set use_address; the hint is misleading since the static IP
already resolves, the real gap is that network logs ride the native API.

Name the missing transport instead: api: for logs, an ota: platform for
uploads. The generic "could not be resolved" message stays for a
genuinely unreachable address.
2026-06-21 09:48:27 -05:00
Jonathan Swoboda dbdf125ec8 Merge branch 'release' into dev 2026-06-20 14:18:09 -04:00
Jonathan Swoboda 9ab2a573ab Merge pull request #17093 from esphome/bump-2026.6.2
2026.6.2
2026-06-20 14:17:55 -04:00
Jonathan Swoboda 99d1c4eb69 Bump version to 2026.6.2 2026-06-20 13:33:41 -04:00
esphome[bot] b079be756f Bump bundled esphome-device-builder to 1.0.12 (#17091) 2026-06-20 13:33:41 -04:00
J. Nick Koston 039a1f063e [ha-addon] Expose the device-builder public port only when port 6052 is mapped (#17076) 2026-06-20 13:33:41 -04:00
esphome[bot] 2354165e41 Bump bundled esphome-device-builder to 1.0.11 (#17081) 2026-06-20 13:33:41 -04:00
Jonathan Swoboda f5697b0ae5 [packet_transport] Mark encryption key as cv.sensitive (#17066) 2026-06-20 13:33:41 -04:00
Jonathan Swoboda fe794a26e8 [fastled_base] Fix RMT5 intr_priority conflict (#17072) 2026-06-20 13:33:41 -04:00
Jonathan Swoboda 8d77051b9a [espidf] Resolve IDF tools path to avoid unnormalized path warning (#17055) 2026-06-20 13:33:41 -04:00
esphome[bot] 9609d370c0 Bump bundled esphome-device-builder to 1.0.12 (#17091) 2026-06-20 12:22:19 -05:00
J. Nick Koston 59711b8e6a Add THREAT_MODEL.md (#17089)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-20 11:51:42 -05:00
J. Nick Koston d77c0d2bc5 [ha-addon] Expose the device-builder public port only when port 6052 is mapped (#17076) 2026-06-20 11:33:54 -05:00
esphome[bot] 657d9bf4d0 Bump bundled esphome-device-builder to 1.0.11 (#17081) 2026-06-20 00:30:30 -05:00
dependabot[bot] 3fb250133f Bump pytest from 9.1.0 to 9.1.1 (#17069)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-19 23:20:37 -04:00
dependabot[bot] d8bd80ef38 Bump resvg-py from 0.3.2 to 0.3.3 (#17070)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-19 23:20:28 -04:00
dependabot[bot] db6bd36cf9 Bump py7zr from 1.1.0 to 1.1.3 (#17071)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-19 23:20:17 -04:00
Jonathan Swoboda f57d31374e [packet_transport] Mark encryption key as cv.sensitive (#17066) 2026-06-19 23:19:30 -04:00
Jonathan Swoboda 50994704a3 [fastled_base] Fix RMT5 intr_priority conflict (#17072) 2026-06-19 23:19:17 -04:00
Jonathan Swoboda 350e7bb763 [espidf] Resolve IDF tools path to avoid unnormalized path warning (#17055) 2026-06-19 17:17:06 +12:00
J. Nick Koston 4ae6dc355f [select] Remove deprecated state member (#17027) 2026-06-19 17:08:07 +12:00
dependabot[bot] 6a79dfb5c5 Bump ruff from 0.15.17 to 0.15.18 (#17046)
Signed-off-by: dependabot[bot] <support@github.com>
2026-06-19 17:07:32 +12:00
Big Mike 1dbd9af617 [sen6x] Remove codeowner (#17056) 2026-06-19 17:04:11 +12:00
Jesse Hills 995eba9178 Merge branch 'release' into dev 2026-06-19 11:35:46 +12:00
esphome[bot] a497174da2 Bump bundled esphome-device-builder to 1.0.10 (#17051) 2026-06-19 10:05:20 +12:00
J. Nick Koston b97182d302 [logger] Hold recursion guard while draining the task log buffer (#17044) 2026-06-19 09:16:14 +12:00
Jonathan Swoboda 8e7518fe9d [esp32] Don't overwrite PlatformIO's factory.bin (#17042) 2026-06-19 09:15:38 +12:00
Jonathan Swoboda a0f546e375 [ci] Smoke-test Arduino framework in esp32 PlatformIO job (#17034) 2026-06-18 16:08:22 -04:00
Jonathan Swoboda 53e85e07d4 [esp32] Support esphome idedata with the native ESP-IDF toolchain (#17040) 2026-06-18 15:56:21 -04:00
Jonathan Swoboda f6c78f7415 [uptime] Revert timestamp sensor device_class to timestamp (#17037) 2026-06-18 15:43:17 -04:00
Jonathan Swoboda 19cca9e177 [esp32] Remove framework migration notice (#17023) 2026-06-18 15:41:03 -04:00
Jonathan Swoboda 1a553018bf [build] Skip target-platform deps when populating host unit-test config (#17039) 2026-06-18 15:38:57 -04:00
dependabot[bot] a39505f5ef Bump CodSpeedHQ/action from 4.17.5 to 4.17.6 (#17047)
Signed-off-by: dependabot[bot] <support@github.com>
2026-06-18 19:33:01 +00:00
dependabot[bot] 14e89f3dae Bump actions/checkout from 6.0.3 to 7.0.0 (#17049)
Signed-off-by: dependabot[bot] <support@github.com>
2026-06-18 19:31:17 +00:00
Kevin Ahrendt bf12af4645 [wifi] Add runtime suppression of post-connect roaming scans (#17012)
Co-authored-by: J. Nick Koston <nick+github@koston.org>
2026-06-18 08:31:49 -05:00
Jonathan Swoboda 1753ccd811 [ci] Update component-test CI for ESP-IDF default toolchain (#16383)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2026-06-18 08:57:59 -04:00
J. Nick Koston 69f905f154 [ci] Revert "Bump awalsh128/cache-apt-pkgs-action from 1.6.0 to 1.6.1" (#17028) 2026-06-18 04:18:12 -05:00
Jesse Hills 92028e53b5 Mark configurable classes as final (2/21: as3935_i2c-ble_rssi) (#16953) 2026-06-18 15:35:12 +12:00
Jesse Hills 11deff2bed Mark configurable classes as final (1/21: a01nyub-aqi) (#16952) 2026-06-18 15:35:03 +12:00
Kevin Ahrendt 2b38e4b7e2 [audio] Bump microMP3 to v0.3.0 (#17009)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-18 03:23:18 +00:00
dependabot[bot] c63bed8c21 Bump pytest from 9.0.3 to 9.1.0 (#16981)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-17 23:16:04 -04:00
dependabot[bot] 3b2564bbf3 Bump cryptography from 48.0.1 to 49.0.0 (#16985)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-17 23:15:56 -04:00
dependabot[bot] 26c42af354 Bump awalsh128/cache-apt-pkgs-action from 1.6.0 to 1.6.1 (#16986)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-17 23:15:46 -04:00
dependabot[bot] 9ace0ffb26 Bump pylint from 4.0.5 to 4.0.6 (#16983)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-17 23:15:15 -04:00
Thomas A. d4b6426087 [esp32] Pin Names for Seeed XIAO C3 / C6 / S3 (#17002)
Co-authored-by: Thomas A <1294885+zeroflow@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-06-17 23:12:22 -04:00
J. Nick Koston bd9375117a [core] Honor transferred address cache in has_resolvable_address (#17025)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-17 22:11:24 -05:00
Keith Burzinski c2784c9fd8 [esp32] Consolidate network/coexistence sdkconfig into a single reconciler (#17008)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 03:09:39 +00:00
Jesse Hills 3a1a8a8955 [ci] Fail CI Status job when workflow is cancelled (#17024)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-18 15:02:22 +12:00
Jesse Hills b6763cfaed [ci] Smoke-test docker image by compiling each target toolchain (#16995)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-18 15:02:07 +12:00
Ardumine f76dfd579c [openthread] Add basic Openthread support to Zephyr/nRF52 platform (#16854)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: tomaszduda23 <tomaszduda23@gmail.com>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-18 01:58:51 +00:00
rwrozelle 4b8568e948 [socket] bugfix Set wake-request gate flag on LwIP socket receive event (#17010)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 13:54:29 +12:00
Jonathan Swoboda ac6a0f34ec [esp32] Make ESP-IDF the default toolchain (#16910) 2026-06-18 13:45:30 +12:00
Jesse Hills c214a8ce79 [core] Add generic component alias infrastructure (#16826) 2026-06-18 01:21:00 +00:00
tomaszduda23 e3f164fff2 [nrf52] add support for native builds (#16898)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-17 21:17:07 -04:00
Petter Ljungqvist c9095841ae [ufm01] Add UFM-01 ultrasonic flow meter component (#16582)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-17 21:16:28 -04:00
Jonathan Swoboda 7cb6cf2f2a [ci] Replace clang-tidy hash with direct config-file diff check (#17019) 2026-06-17 21:12:39 -04:00
Jesse Hills 34844da668 Merge branch 'release' into dev 2026-06-18 13:00:38 +12:00
esphome[bot] 77a99bceb2 Bump bundled esphome-device-builder to 1.0.9 (#17021)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-06-17 17:38:39 -05:00
esphome[bot] ae7c800de8 Bump bundled esphome-device-builder to 1.0.8 (#17020) 2026-06-17 17:32:37 -05:00
Jesse Hills 084398436d Merge branch 'beta' into dev 2026-06-18 10:08:51 +12:00
esphome[bot] 6d9490b5a3 Bump bundled esphome-device-builder to 1.0.7 (#17018) 2026-06-17 15:53:10 -05:00
dependabot[bot] 0f5defa67e Bump tzlocal from 5.3.1 to 5.4.3 (#17015)
Signed-off-by: dependabot[bot] <support@github.com>
2026-06-17 14:51:08 -05:00
esphome[bot] 900e0b8566 Bump bundled esphome-device-builder to 1.0.6 (#17016) 2026-06-17 14:50:56 -05:00
esphome[bot] 40d0cbee3f Bump bundled esphome-device-builder to 1.0.5 (#17014) 2026-06-17 14:07:36 -05:00
esphome[bot] 009c6dd995 Bump bundled esphome-device-builder to 1.0.4 (#17013) 2026-06-17 12:38:23 -05:00
esphome[bot] e80461eba9 Bump bundled esphome-device-builder to 1.0.3 (#17005) 2026-06-16 22:28:27 -05:00
Jonathan Swoboda 29e8949e3e [ota] Scale ESP-IDF OTA erase watchdog to image size (#16998) 2026-06-16 13:23:46 -04:00
Jonathan Swoboda ce11d38c9b [esp32_hosted] Bump esp_hosted to 2.12.9 (#16999) 2026-06-16 11:53:11 -04:00
Jesse Hills f62d804a60 Merge branch 'beta' into dev 2026-06-16 23:54:03 +12:00
Jesse Hills 930cf2b5b9 [docker] Bundle device-builder 1.0.1, make HA add-on builder-only (#16989)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-06-16 22:47:14 +12:00
Jesse Hills d8fa0e4140 [core] Stop parent git repos from breaking ESP-IDF/PlatformIO builds (#16994) 2026-06-16 20:24:26 +12:00
Jesse Hills b09a5f9e43 [ci] Push branch-tagged docker images to ghcr.io for local testing (#16992) 2026-06-16 13:37:31 +12:00
dependabot[bot] bb6cd97948 Bump clang-tidy from 22.1.0.1 to 22.1.7 (#16984)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-15 21:15:12 -04:00
Jesse Hills 73f839437e [docker] Remove alpine base, build only on debian (#16991) 2026-06-16 13:12:53 +12:00
Jonathan Swoboda a7a407c22c [openthread] Fix InstanceLock releasing the lock twice on try_acquire (#16980) 2026-06-16 09:05:50 +12:00
Kevin Ahrendt 7a2657cea1 [audio] Bump microMP3 to v0.2.3 (#16977) 2026-06-16 08:48:07 +12:00
J. Nick Koston 3420cff316 [core] Attribute "took a long time" blocking warning to the owning script (#16768) 2026-06-16 08:46:33 +12:00
Clyde Stubbs 963465a0a6 [mipi_dsi] Add SWRESET command to M5Stack Tab5-V2 init sequence (#16975) 2026-06-15 08:25:54 -04:00
Jesse Hills 1ee49720c7 [psram] Make schema extractable with per-variant options (#16949)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-06-15 19:55:21 +12:00
Jesse Hills c1a7a8ff55 Add PEP 572 walrus operator preference to coding conventions (#16951) 2026-06-15 16:01:44 +12:00
Clyde Stubbs f1fd5f2f49 [epaper_spi] Metadata, bug fixes, new model (#16950) 2026-06-15 12:10:58 +10:00
Jesse Hills 8d2c2e6adc Merge branch 'beta' into dev 2026-06-15 12:05:55 +12:00
Jonathan Swoboda efebea3296 [esp32] Add flash_mode and flash_frequency config options (#16920)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2026-06-15 10:56:18 +12:00
Jonathan Swoboda e191fc5d47 [core] Support platformio_options on the native ESP-IDF toolchain (#16917) 2026-06-15 10:56:03 +12:00
Jonathan Swoboda 1e5771a3fa [esp32] Fix idedata generation failing on unset ESPHOME_ARDUINO (#16925) 2026-06-15 09:48:43 +12:00
Clyde Stubbs 5b7f8cf90d [mipi_spi] Implement automatic mapping of offsets (#16722)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-15 09:36:38 +12:00
guillempages 35e5c7c7c3 [runtime_image] Improve error logging (#16943) 2026-06-14 07:40:49 +10:00
Clyde Stubbs 10ce6024bf [lvgl] Fix schema extraction (#16895)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 08:21:38 -04:00
dependabot[bot] bf6c8568d3 Bump CodSpeedHQ/action from 4.17.0 to 4.17.5 (#16919)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-12 14:33:28 -04:00
dependabot[bot] 88084f2ec7 Bump ruff from 0.15.16 to 0.15.17 (#16918)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-12 14:32:51 -04:00
Tobiasz Jakubowski 6ef35b6d3d [spi] Skip logging on begin_transaction() of an auto-releasing write-only SPI device (#16921)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-06-11 19:50:51 -04:00
Dan Drown 28dd935359 [xpt2046] touchscreen driver enhancement (#16414)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-11 12:35:44 -04:00
J. Nick Koston 750cf1995b [esp8266] Decode crash handler PC and backtrace in logs (#16911) 2026-06-11 08:47:50 -05:00
Jesse Hills a56b6d8993 Merge branch 'beta' into dev 2026-06-11 16:09:15 +12:00
Jesse Hills 6a527c7efc [tests] Mock target branch in memory-impact exclusion test (#16913) 2026-06-11 14:04:22 +12:00
Jesse Hills e0b0c1e8d3 Bump version to 2026.7.0-dev 2026-06-11 12:41:19 +12:00
1208 changed files with 22127 additions and 15563 deletions
-1
View File
@@ -1 +0,0 @@
72f02816e288b68ff4ef4b3d6fb66432c893b187a80ad3ebaa29afa443ff9ea6
+1 -1
View File
@@ -1,4 +1,4 @@
ARG BUILD_BASE_VERSION=2025.04.0
ARG BUILD_BASE_VERSION=2026.06.1
FROM ghcr.io/esphome/docker-base:debian-${BUILD_BASE_VERSION} AS base
+5 -2
View File
@@ -15,7 +15,6 @@
// uncomment and edit the path in order to pass through local USB serial to the container
// , "--device=/dev/ttyACM0"
],
"appPort": 6052,
// if you are using avahi in the host device, uncomment these to allow the
// devcontainer to find devices via mdns
//"mounts": [
@@ -41,7 +40,11 @@
],
"settings": {
"python.languageServer": "Pylance",
"python.pythonPath": "/usr/bin/python3",
// Use the container's pre-provisioned venv (built by the Dockerfile, outside the
// bind-mounted workspace) rather than a ./venv that may leak in from the host and
// mismatch the container's Python. See .devcontainer/Dockerfile (esphome-venv).
"python.defaultInterpreterPath": "/home/esphome/.local/esphome-venv/bin/python",
"python.terminal.activateEnvironment": true,
"pylint.args": [
"--rcfile=${workspaceFolder}/pyproject.toml"
],
+10 -4
View File
@@ -2,8 +2,8 @@ name: Cache ESP-IDF
description: >
Resolve the pinned ESP-IDF version and cache the native ESP-IDF install
(toolchains + source) at ~/.esphome-idf. Every job that installs ESP-IDF
natively (clang-tidy for IDF/Arduino and the native-IDF component build)
shares one cache, since the install is identical (ESPHOME_IDF_DEFAULT_TARGETS
natively (clang-tidy for IDF/Arduino and the component test batches) shares
one cache, since the install is identical (ESPHOME_IDF_DEFAULT_TARGETS
defaults to "all", so all toolchains are present regardless of the chip).
Callers must set env ESPHOME_ESP_IDF_PREFIX: ~/.esphome-idf and have the
Python venv already restored.
@@ -11,6 +11,12 @@ inputs:
framework:
description: 'Which pinned IDF version to key on: "espidf" (recommended) or "arduino".'
default: espidf
restore-only:
description: >
When "true", only restore -- never save the cache, even on dev. Use from
jobs that may not produce an ESP-IDF install (e.g. a component batch with
no esp32 target), so a partial/empty install is never written to the key.
default: "false"
runs:
using: composite
steps:
@@ -33,13 +39,13 @@ runs:
# PRs), and PRs are restore-only -- they never push multi-GB artifacts into
# their own scope / the repo quota (e.g. on a version-bump PR).
- name: Cache ESP-IDF install (write on dev)
if: github.ref == 'refs/heads/dev'
if: github.ref == 'refs/heads/dev' && inputs.restore-only != 'true'
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.esphome-idf
key: ${{ runner.os }}-esphome-idf-${{ steps.version.outputs.version }}
- name: Cache ESP-IDF install (restore-only off dev)
if: github.ref != 'refs/heads/dev'
if: github.ref != 'refs/heads/dev' || inputs.restore-only == 'true'
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.esphome-idf
+2 -2
View File
@@ -17,12 +17,12 @@ runs:
steps:
- name: Set up Python ${{ inputs.python-version }}
id: python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: ${{ inputs.python-version }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: venv
# yamllint disable-line rule:line-length
+19 -12
View File
@@ -147,19 +147,9 @@ async function detectCoreChanges(changedFiles) {
}
// Strategy: PR size detection
async function detectPRSize(prFiles, totalAdditions, totalDeletions, totalChanges, isMegaPR, SMALL_PR_THRESHOLD, MEDIUM_PR_THRESHOLD, TOO_BIG_THRESHOLD) {
async function detectPRSize(prFiles, totalAdditions, totalDeletions, isMegaPR, SMALL_PR_THRESHOLD, MEDIUM_PR_THRESHOLD, TOO_BIG_THRESHOLD) {
const labels = new Set();
if (totalChanges <= SMALL_PR_THRESHOLD) {
labels.add('small-pr');
return labels;
}
if (totalChanges <= MEDIUM_PR_THRESHOLD) {
labels.add('medium-pr');
return labels;
}
const testAdditions = prFiles
.filter(file => file.filename.startsWith('tests/'))
.reduce((sum, file) => sum + (file.additions || 0), 0);
@@ -167,7 +157,24 @@ async function detectPRSize(prFiles, totalAdditions, totalDeletions, totalChange
.filter(file => file.filename.startsWith('tests/'))
.reduce((sum, file) => sum + (file.deletions || 0), 0);
const nonTestChanges = (totalAdditions - testAdditions) - (totalDeletions - testDeletions);
const nonTestAdditions = totalAdditions - testAdditions;
const nonTestDeletions = totalDeletions - testDeletions;
// small/medium count churn (additions + deletions) so a balanced refactor isn't undersized.
const nonTestChurn = nonTestAdditions + nonTestDeletions;
if (nonTestChurn <= SMALL_PR_THRESHOLD) {
labels.add('small-pr');
return labels;
}
if (nonTestChurn <= MEDIUM_PR_THRESHOLD) {
labels.add('medium-pr');
return labels;
}
// too-big uses net line delta (additions - deletions), matching the review message in reviews.js.
const nonTestChanges = nonTestAdditions - nonTestDeletions;
// Don't add too-big if mega-pr label is already present
if (nonTestChanges > TOO_BIG_THRESHOLD && !isMegaPR) {
+1 -1
View File
@@ -123,7 +123,7 @@ module.exports = async ({ github, context }) => {
detectNewComponents(github, context, prFiles),
detectNewPlatforms(github, context, prFiles, apiData),
detectCoreChanges(changedFiles),
detectPRSize(prFiles, totalAdditions, totalDeletions, totalChanges, isMegaPR, SMALL_PR_THRESHOLD, MEDIUM_PR_THRESHOLD, TOO_BIG_THRESHOLD),
detectPRSize(prFiles, totalAdditions, totalDeletions, isMegaPR, SMALL_PR_THRESHOLD, MEDIUM_PR_THRESHOLD, TOO_BIG_THRESHOLD),
detectDashboardChanges(changedFiles),
detectGitHubActionsChanges(changedFiles),
detectCodeOwner(github, context, changedFiles),
@@ -1,6 +1,6 @@
const { describe, it } = require('node:test');
const assert = require('node:assert/strict');
const { detectNewPlatforms, detectNewComponents } = require('../detectors');
const { detectNewPlatforms, detectNewComponents, detectPRSize } = require('../detectors');
// Minimal GitHub API mock — only repos.getContent is called by detectNewPlatforms/detectNewComponents
// to check for CONFIG_SCHEMA in newly added files.
@@ -145,3 +145,79 @@ describe('detectNewComponents', () => {
assert.equal(result.labels.size, 0);
});
});
// ---------------------------------------------------------------------------
// detectPRSize
// ---------------------------------------------------------------------------
describe('detectPRSize', () => {
const SMALL = 30;
const MEDIUM = 100;
const TOO_BIG = 1000;
function size(prFiles, isMegaPR = false) {
const totalAdditions = prFiles.reduce((sum, file) => sum + (file.additions || 0), 0);
const totalDeletions = prFiles.reduce((sum, file) => sum + (file.deletions || 0), 0);
return detectPRSize(prFiles, totalAdditions, totalDeletions, isMegaPR, SMALL, MEDIUM, TOO_BIG);
}
it('counts only non-test changes toward small-pr', async () => {
// 10 source + 5000 test lines -> non-test churn of 10 is still small.
const labels = await size([
{ filename: 'esphome/components/foo/foo.cpp', additions: 10, deletions: 0 },
{ filename: 'tests/components/foo/test.esp32-idf.yaml', additions: 5000, deletions: 0 },
]);
assert.ok(labels.has('small-pr'));
assert.equal(labels.size, 1);
});
it('counts additions and deletions as churn (not net delta)', async () => {
// A balanced refactor (40 added, 40 removed) is 80 lines of churn -> medium, not small.
const labels = await size([
{ filename: 'esphome/components/foo/foo.cpp', additions: 40, deletions: 40 },
]);
assert.ok(labels.has('medium-pr'));
assert.equal(labels.size, 1);
});
it('labels medium-pr when non-test changes exceed small threshold', async () => {
const labels = await size([
{ filename: 'esphome/components/foo/foo.cpp', additions: 60, deletions: 0 },
{ filename: 'tests/components/foo/test.esp32-idf.yaml', additions: 5000, deletions: 0 },
]);
assert.ok(labels.has('medium-pr'));
assert.equal(labels.size, 1);
});
it('uses net delta (not churn) for too-big', async () => {
// 600 added + 600 removed: 1200 churn (above too-big) but 0 net delta -> not too-big.
const labels = await size([
{ filename: 'esphome/components/foo/foo.cpp', additions: 600, deletions: 600 },
]);
assert.equal(labels.size, 0);
});
it('labels too-big when non-test changes exceed the big threshold', async () => {
const labels = await size([
{ filename: 'esphome/components/foo/foo.cpp', additions: 2000, deletions: 0 },
{ filename: 'tests/components/foo/test.esp32-idf.yaml', additions: 5000, deletions: 0 },
]);
assert.ok(labels.has('too-big'));
assert.equal(labels.size, 1);
});
it('does not label too-big when mega-pr is set', async () => {
const labels = await size([
{ filename: 'esphome/components/foo/foo.cpp', additions: 2000, deletions: 0 },
], true);
assert.equal(labels.size, 0);
});
it('produces no size label for a large mega-pr in the gap above medium', async () => {
// Non-test changes land between MEDIUM and TOO_BIG: not small/medium, and mega-pr suppresses too-big.
const labels = await size([
{ filename: 'esphome/components/foo/foo.cpp', additions: 500, deletions: 0 },
], true);
assert.equal(labels.size, 0);
});
});
-1
View File
@@ -41,7 +41,6 @@ function hasCoreChanges(changedFiles) {
*/
function hasDashboardChanges(changedFiles) {
return changedFiles.some(file =>
file.startsWith('esphome/dashboard/') ||
file.startsWith('esphome/components/dashboard_import/')
);
}
+1 -1
View File
@@ -24,7 +24,7 @@ jobs:
if: github.event.pull_request.state == 'open' && (github.event.action != 'labeled' || github.event.sender.type != 'Bot')
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Generate a token
id: generate-token
+3 -3
View File
@@ -21,11 +21,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.11"
python-version: "3.12"
- name: Set up uv
# ``--system`` (below) installs into the setup-python interpreter;
# no venv is created or restored by this workflow.
-76
View File
@@ -1,76 +0,0 @@
name: Clang-tidy Hash CI
on:
pull_request:
paths:
- ".clang-tidy"
- "platformio.ini"
- "requirements_dev.txt"
- "sdkconfig.defaults"
- ".clang-tidy.hash"
- "script/clang_tidy_hash.py"
- ".github/workflows/ci-clang-tidy-hash.yml"
permissions:
contents: read # actions/checkout for the PR head
pull-requests: write # pulls.createReview / listReviews / dismissReview when the clang-tidy hash is out of date
jobs:
verify-hash:
name: Verify clang-tidy hash
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.11"
- name: Verify hash
run: |
python script/clang_tidy_hash.py --verify
- if: failure()
name: Show hash details
run: |
python script/clang_tidy_hash.py
echo "## Job Failed" | tee -a $GITHUB_STEP_SUMMARY
echo "You have modified clang-tidy configuration but have not updated the hash." | tee -a $GITHUB_STEP_SUMMARY
echo "Please run 'script/clang_tidy_hash.py --update' and commit the changes." | tee -a $GITHUB_STEP_SUMMARY
- if: failure() && github.event.pull_request.head.repo.full_name == github.repository
name: Request changes
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
await github.rest.pulls.createReview({
pull_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
event: 'REQUEST_CHANGES',
body: 'You have modified clang-tidy configuration but have not updated the hash.\nPlease run `script/clang_tidy_hash.py --update` and commit the changes.'
})
- if: success() && github.event.pull_request.head.repo.full_name == github.repository
name: Dismiss review
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
let reviews = await github.rest.pulls.listReviews({
pull_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo
});
for (let review of reviews.data) {
if (review.user.login === 'github-actions[bot]' && review.state === 'CHANGES_REQUESTED') {
await github.rest.pulls.dismissReview({
pull_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
review_id: review.id,
message: 'Clang-tidy hash now matches configuration.'
});
}
}
+97 -17
View File
@@ -1,25 +1,38 @@
---
name: CI for docker images
# Only run when docker paths change
# Only run on PRs that touch the docker image, its build inputs, or any code
# whose toolchain the compile smoke test exercises (core + target platforms).
on:
push:
branches: [dev, beta, release]
paths:
- "docker/**"
- ".github/workflows/ci-docker.yml"
- "requirements*.txt"
- "platformio.ini"
- "script/platformio_install_deps.py"
pull_request:
paths:
# Docker image and its build inputs.
- "docker/**"
- ".github/workflows/ci-docker.yml"
- "requirements*.txt"
- "pyproject.toml"
- "platformio.ini"
- "esphome/idf_component.yml"
- "script/platformio_install_deps.py"
# Core, build pipeline, toolchain, and target-platform changes can change
# how a toolchain is set up or built, so re-run the per-toolchain compile
# smoke test when they change.
- "esphome/core/**"
- "esphome/writer.py"
- "esphome/build_gen/**"
- "esphome/espidf/**"
- "esphome/platformio/**"
- "esphome/components/bk72xx/**"
- "esphome/components/esp32/**"
- "esphome/components/esp8266/**"
- "esphome/components/host/**"
- "esphome/components/libretiny/**"
- "esphome/components/ln882x/**"
- "esphome/components/nrf52/**"
- "esphome/components/rp2040/**"
- "esphome/components/rtl87xx/**"
- "esphome/components/zephyr/**"
permissions:
contents: read # actions/checkout only
@@ -48,11 +61,11 @@ jobs:
tag: ${{ steps.tag.outputs.tag }}
push: ${{ steps.tag.outputs.push }}
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.11"
python-version: "3.12"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
@@ -96,7 +109,26 @@ jobs:
--arch "${{ matrix.os == 'ubuntu-24.04-arm' && 'aarch64' || 'amd64' }}" \
--build-type "${{ matrix.build_type }}" \
--registry ghcr \
build ${{ steps.tag.outputs.push == 'true' && '--push --no-cache-to' || '' }}
build ${{ steps.tag.outputs.push == 'true' && '--push --no-cache-to' || '' }} ${{ (matrix.os == 'ubuntu-24.04' && matrix.build_type == 'docker') && '--load' || '' }}
# The amd64 "docker" image is also loaded locally (above) and handed to
# compile-test as an artifact, so the smoke test reuses this build instead
# of building the image a second time. Using an artifact (rather than the
# pushed image) keeps it working for fork PRs, which never push to ghcr.io.
- name: Export image for compile-test
if: matrix.os == 'ubuntu-24.04' && matrix.build_type == 'docker'
run: docker save "ghcr.io/esphome/esphome-amd64:${{ steps.tag.outputs.tag }}" | gzip > compile-test-image.tar.gz
- name: Upload compile-test image artifact
if: matrix.os == 'ubuntu-24.04' && matrix.build_type == 'docker'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
# The tar is already gzipped, so upload it as-is. archive: false skips
# the redundant zip and makes the file name the artifact name (the
# `name` input is ignored in that mode).
path: compile-test-image.tar.gz
retention-days: 1
archive: false
manifest:
name: Push ${{ matrix.build_type }} manifest to ghcr.io
@@ -113,11 +145,11 @@ jobs:
- "ha-addon"
- "docker"
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.11"
python-version: "3.12"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
@@ -135,3 +167,51 @@ jobs:
--build-type "${{ matrix.build_type }}" \
--registry ghcr \
manifest
# Smoke-test the built image by compiling one minimal config per target
# platform / toolchain. This catches missing system dependencies in the image
# that only surface when a given toolchain is downloaded and run. The image is
# the amd64 "docker" build produced by check-docker (shared as an artifact).
compile-test:
name: Compile ${{ matrix.id }}
needs: check-docker
runs-on: ubuntu-24.04
permissions:
contents: read # actions/checkout to load the test configs
strategy:
fail-fast: false
# Cap concurrency so this smoke test doesn't hog all the shared runners.
max-parallel: 2
matrix:
# One entry per distinct toolchain. ESP32 variants (c3/c6/s2/s3/p4)
# share a toolchain bundle, so esp32 is exercised on the base variant
# across the full framework x toolchain cross-product (arduino/esp-idf
# framework, each built with the platformio and native esp-idf
# toolchains) so both toolchains stay covered regardless of which one is
# the default.
id:
- esp8266-arduino
- esp32-arduino-platformio
- esp32-arduino-esp-idf
- esp32-idf-platformio
- esp32-idf-esp-idf
- rp2040-arduino
- bk72xx-arduino
- rtl87xx-arduino
- ln882x-arduino
- nrf52
- host
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Download image artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: compile-test-image.tar.gz
- name: Load image
run: docker load --input compile-test-image.tar.gz
- name: Compile ${{ matrix.id }}
run: |
docker run --rm \
-v "${{ github.workspace }}/docker/test_configs:/config" \
"ghcr.io/esphome/esphome-amd64:${{ needs.check-docker.outputs.tag }}" \
compile "${{ matrix.id }}.yaml"
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Run tests
working-directory: .github/scripts/auto-label-pr
@@ -49,7 +49,7 @@ jobs:
- name: Check out code from base repository
if: steps.pr.outputs.skip != 'true'
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
# Always check out from the base repository (esphome/esphome), never from forks
# Use the PR's target branch to ensure we run trusted code from the main repo
@@ -60,7 +60,7 @@ jobs:
if: steps.pr.outputs.skip != 'true'
uses: ./.github/actions/restore-python
with:
python-version: "3.11"
python-version: "3.12"
cache-key: ${{ hashFiles('.cache-key') }}
- name: Download memory analysis artifacts
+93 -135
View File
@@ -12,8 +12,8 @@ permissions:
contents: read # actions/checkout for all jobs; individual jobs add their own scopes when they need to write
env:
DEFAULT_PYTHON: "3.11"
PYUPGRADE_TARGET: "--py311-plus"
DEFAULT_PYTHON: "3.12"
PYUPGRADE_TARGET: "--py312-plus"
concurrency:
# yamllint disable-line rule:line-length
@@ -28,18 +28,18 @@ jobs:
cache-key: ${{ steps.cache-key.outputs.key }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Generate cache-key
id: cache-key
run: echo key="${{ hashFiles('requirements.txt', 'requirements_dev.txt', 'requirements_test.txt', '.pre-commit-config.yaml') }}" >> $GITHUB_OUTPUT
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: venv
# yamllint disable-line rule:line-length
@@ -74,7 +74,7 @@ jobs:
if: needs.determine-jobs.outputs.python-linters == 'true'
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python
uses: ./.github/actions/restore-python
with:
@@ -97,7 +97,7 @@ jobs:
if: needs.determine-jobs.outputs.core-ci == 'true'
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python
uses: ./.github/actions/restore-python
with:
@@ -124,7 +124,7 @@ jobs:
if: needs.determine-jobs.outputs.import-time == 'true'
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python
uses: ./.github/actions/restore-python
with:
@@ -152,17 +152,17 @@ jobs:
if: needs.determine-jobs.outputs.device-builder == 'true'
steps:
- name: Check out esphome (this PR)
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
path: esphome
- name: Check out esphome/device-builder
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: esphome/device-builder
ref: main
path: device-builder
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.13"
- name: Set up uv
@@ -203,7 +203,7 @@ jobs:
fail-fast: false
matrix:
python-version:
- "3.11"
- "3.12"
- "3.13"
- "3.14"
os:
@@ -225,7 +225,7 @@ jobs:
if: needs.determine-jobs.outputs.core-ci == 'true'
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python
id: restore-python
uses: ./.github/actions/restore-python
@@ -250,7 +250,7 @@ jobs:
token: ${{ secrets.CODECOV_TOKEN }}
- name: Save Python virtual environment cache
if: github.ref == 'refs/heads/dev'
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: venv
key: ${{ runner.os }}-${{ steps.restore-python.outputs.python-version }}-venv-${{ needs.common.outputs.cache-key }}
@@ -270,8 +270,8 @@ jobs:
python-linters: ${{ steps.determine.outputs.python-linters }}
import-time: ${{ steps.determine.outputs.import-time }}
device-builder: ${{ steps.determine.outputs.device-builder }}
native-idf: ${{ steps.determine.outputs.native-idf }}
native-idf-components: ${{ steps.determine.outputs.native-idf-components }}
esp32-platformio: ${{ steps.determine.outputs.esp32-platformio }}
esp32-platformio-components: ${{ steps.determine.outputs.esp32-platformio-components }}
changed-components: ${{ steps.determine.outputs.changed-components }}
changed-components-with-tests: ${{ steps.determine.outputs.changed-components-with-tests }}
directly-changed-components-with-tests: ${{ steps.determine.outputs.directly-changed-components-with-tests }}
@@ -285,7 +285,7 @@ jobs:
benchmarks: ${{ steps.determine.outputs.benchmarks }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
# Fetch enough history to find the merge base
fetch-depth: 2
@@ -295,7 +295,7 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Restore components graph cache
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: .temp/components_graph.json
key: components-graph-${{ hashFiles('esphome/components/**/*.py') }}
@@ -324,8 +324,8 @@ jobs:
echo "python-linters=$(echo "$output" | jq -r '.python_linters')" >> $GITHUB_OUTPUT
echo "import-time=$(echo "$output" | jq -r '.import_time')" >> $GITHUB_OUTPUT
echo "device-builder=$(echo "$output" | jq -r '.device_builder')" >> $GITHUB_OUTPUT
echo "native-idf=$(echo "$output" | jq -r '.native_idf')" >> $GITHUB_OUTPUT
echo "native-idf-components=$(echo "$output" | jq -r '.native_idf_components')" >> $GITHUB_OUTPUT
echo "esp32-platformio=$(echo "$output" | jq -r '.esp32_platformio')" >> $GITHUB_OUTPUT
echo "esp32-platformio-components=$(echo "$output" | jq -r '.esp32_platformio_components')" >> $GITHUB_OUTPUT
echo "changed-components=$(echo "$output" | jq -c '.changed_components')" >> $GITHUB_OUTPUT
echo "changed-components-with-tests=$(echo "$output" | jq -c '.changed_components_with_tests')" >> $GITHUB_OUTPUT
echo "directly-changed-components-with-tests=$(echo "$output" | jq -c '.directly_changed_components_with_tests')" >> $GITHUB_OUTPUT
@@ -339,7 +339,7 @@ jobs:
echo "benchmarks=$(echo "$output" | jq -r '.benchmarks')" >> $GITHUB_OUTPUT
- name: Save components graph cache
if: github.ref == 'refs/heads/dev'
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: .temp/components_graph.json
key: components-graph-${{ hashFiles('esphome/components/**/*.py') }}
@@ -357,15 +357,15 @@ jobs:
bucket: ${{ fromJson(needs.determine-jobs.outputs.integration-test-buckets) }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Python 3.13
id: python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.13"
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: venv
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{ needs.common.outputs.cache-key }}
@@ -409,7 +409,7 @@ jobs:
if: github.event_name == 'pull_request' && (needs.determine-jobs.outputs.cpp-unit-tests-run-all == 'true' || needs.determine-jobs.outputs.cpp-unit-tests-components != '[]')
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python
uses: ./.github/actions/restore-python
@@ -438,7 +438,7 @@ jobs:
(github.event_name == 'pull_request' && needs.determine-jobs.outputs.benchmarks == 'true')
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python
uses: ./.github/actions/restore-python
@@ -456,7 +456,7 @@ jobs:
echo "binary=$BINARY" >> $GITHUB_OUTPUT
- name: Run CodSpeed benchmarks
uses: CodSpeedHQ/action@9d332c4d90b43981c3e55ae8e38e68709996240f # v4.17.0
uses: CodSpeedHQ/action@a4a36bb07c0638b0b4ca52bf1f3dad1b4289e52f # v4.18.1
with:
run: |
. venv/bin/activate
@@ -496,7 +496,7 @@ jobs:
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
# Need history for HEAD~1 to work for checking changed files
fetch-depth: 2
@@ -509,20 +509,19 @@ jobs:
- name: Cache platformio
if: github.ref == 'refs/heads/dev' && matrix.pio_cache_key
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.platformio
key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
- name: Cache platformio
if: github.ref != 'refs/heads/dev' && matrix.pio_cache_key
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.platformio
key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
- name: Cache ESP-IDF install
# Shared with the IDF tidy + native-IDF build jobs (same install).
if: matrix.cache_idf
uses: ./.github/actions/cache-esp-idf
with:
@@ -537,15 +536,12 @@ jobs:
id: check_full_scan
run: |
. venv/bin/activate
# determine-jobs.clang-tidy-full-scan is true when core C++ changed
# OR the ci-run-all label forced --force-all. Independent of the
# hash check, both must produce a full scan in the job itself.
# determine-jobs.clang-tidy-full-scan is true when core C++ or a
# clang-tidy-relevant config file changed, or the ci-run-all label
# forced --force-all.
if [ "${{ needs.determine-jobs.outputs.clang-tidy-full-scan }}" = "true" ]; then
echo "full_scan=true" >> $GITHUB_OUTPUT
echo "reason=determine_jobs" >> $GITHUB_OUTPUT
elif python script/clang_tidy_hash.py --check; then
echo "full_scan=true" >> $GITHUB_OUTPUT
echo "reason=hash_changed" >> $GITHUB_OUTPUT
else
echo "full_scan=false" >> $GITHUB_OUTPUT
echo "reason=normal" >> $GITHUB_OUTPUT
@@ -583,7 +579,7 @@ jobs:
ESPHOME_ESP_IDF_PREFIX: ~/.esphome-idf
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
# Need history for HEAD~1 to work for checking changed files
fetch-depth: 2
@@ -595,7 +591,6 @@ jobs:
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Cache ESP-IDF install
# Shared with the Arduino tidy + native-IDF build jobs (same install).
uses: ./.github/actions/cache-esp-idf
- name: Register problem matchers
@@ -607,15 +602,12 @@ jobs:
id: check_full_scan
run: |
. venv/bin/activate
# determine-jobs.clang-tidy-full-scan is true when core C++ changed
# OR the ci-run-all label forced --force-all. Independent of the
# hash check, both must produce a full scan in the job itself.
# determine-jobs.clang-tidy-full-scan is true when core C++ or a
# clang-tidy-relevant config file changed, or the ci-run-all label
# forced --force-all.
if [ "${{ needs.determine-jobs.outputs.clang-tidy-full-scan }}" = "true" ]; then
echo "full_scan=true" >> $GITHUB_OUTPUT
echo "reason=determine_jobs" >> $GITHUB_OUTPUT
elif python script/clang_tidy_hash.py --check; then
echo "full_scan=true" >> $GITHUB_OUTPUT
echo "reason=hash_changed" >> $GITHUB_OUTPUT
else
echo "full_scan=false" >> $GITHUB_OUTPUT
echo "reason=normal" >> $GITHUB_OUTPUT
@@ -667,7 +659,7 @@ jobs:
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
# Need history for HEAD~1 to work for checking changed files
fetch-depth: 2
@@ -679,7 +671,6 @@ jobs:
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Cache ESP-IDF install
# Shared with the Arduino tidy + native-IDF build jobs (same install).
uses: ./.github/actions/cache-esp-idf
- name: Register problem matchers
@@ -691,15 +682,12 @@ jobs:
id: check_full_scan
run: |
. venv/bin/activate
# determine-jobs.clang-tidy-full-scan is true when core C++ changed
# OR the ci-run-all label forced --force-all. Independent of the
# hash check, both must produce a full scan in the job itself.
# determine-jobs.clang-tidy-full-scan is true when core C++ or a
# clang-tidy-relevant config file changed, or the ci-run-all label
# forced --force-all.
if [ "${{ needs.determine-jobs.outputs.clang-tidy-full-scan }}" = "true" ]; then
echo "full_scan=true" >> $GITHUB_OUTPUT
echo "reason=determine_jobs" >> $GITHUB_OUTPUT
elif python script/clang_tidy_hash.py --check; then
echo "full_scan=true" >> $GITHUB_OUTPUT
echo "reason=hash_changed" >> $GITHUB_OUTPUT
else
echo "full_scan=false" >> $GITHUB_OUTPUT
echo "reason=normal" >> $GITHUB_OUTPUT
@@ -755,7 +743,7 @@ jobs:
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
# Need history for HEAD~1 to work for checking changed files
fetch-depth: 2
@@ -767,7 +755,6 @@ jobs:
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Cache ESP-IDF install
# Shared with the IDF/Arduino clang-tidy jobs + native-IDF build (same install).
uses: ./.github/actions/cache-esp-idf
- name: Register problem matchers
@@ -779,15 +766,12 @@ jobs:
id: check_full_scan
run: |
. venv/bin/activate
# determine-jobs.clang-tidy-full-scan is true when core C++ changed
# OR the ci-run-all label forced --force-all. Independent of the
# hash check, both must produce a full scan in the job itself.
# determine-jobs.clang-tidy-full-scan is true when core C++ or a
# clang-tidy-relevant config file changed, or the ci-run-all label
# forced --force-all.
if [ "${{ needs.determine-jobs.outputs.clang-tidy-full-scan }}" = "true" ]; then
echo "full_scan=true" >> $GITHUB_OUTPUT
echo "reason=determine_jobs" >> $GITHUB_OUTPUT
elif python script/clang_tidy_hash.py --check; then
echo "full_scan=true" >> $GITHUB_OUTPUT
echo "reason=hash_changed" >> $GITHUB_OUTPUT
else
echo "full_scan=false" >> $GITHUB_OUTPUT
echo "reason=normal" >> $GITHUB_OUTPUT
@@ -817,6 +801,10 @@ jobs:
- common
- determine-jobs
if: github.event_name == 'pull_request' && fromJSON(needs.determine-jobs.outputs.component-test-count) > 0
env:
# esp32 component builds use the native ESP-IDF toolchain (default), so
# share the tidy jobs' install location -- the restore below lands here.
ESPHOME_ESP_IDF_PREFIX: ~/.esphome-idf
strategy:
fail-fast: false
max-parallel: ${{ (startsWith(github.base_ref, 'beta') || startsWith(github.base_ref, 'release')) && 8 || 4 }}
@@ -832,18 +820,24 @@ jobs:
run: echo ${{ matrix.components }}
- name: Cache apt packages
uses: awalsh128/cache-apt-pkgs-action@acb598e5ddbc6f68a970c5da0688d2f3a9f04d05 # v1.5.3
uses: awalsh128/cache-apt-pkgs-action@acb598e5ddbc6f68a970c5da0688d2f3a9f04d05 # v1.6.0
with:
packages: libsdl2-dev
version: 1.0
packages: libsdl2-dev ccache
version: 1.1
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python
uses: ./.github/actions/restore-python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Cache ESP-IDF install (restore-only)
# A batch may contain no esp32 build, so never save -- just reuse the
# shared install the dev tidy jobs already cached when present.
uses: ./.github/actions/cache-esp-idf
with:
restore-only: true
- name: Validate and compile components with intelligent grouping
run: |
. venv/bin/activate
@@ -947,23 +941,27 @@ jobs:
echo "All components in this batch are validate-only -- skipping compile stage."
fi
test-native-idf:
name: Test components with native ESP-IDF
- name: Print ccache statistics
# esphome stores the cache under the IDF tools path; expand the leading
# ~ in ESPHOME_ESP_IDF_PREFIX so ccache reads the dir the build used.
run: CCACHE_DIR="${ESPHOME_ESP_IDF_PREFIX/#\~/$HOME}/ccache" ccache -s
test-esp32-platformio:
name: Test esp32 components with PlatformIO
runs-on: ubuntu-24.04
needs:
- common
- determine-jobs
if: github.event_name == 'pull_request' && needs.determine-jobs.outputs.native-idf == 'true'
if: github.event_name == 'pull_request' && needs.determine-jobs.outputs.esp32-platformio == 'true'
env:
ESPHOME_ESP_IDF_PREFIX: ~/.esphome-idf
# Comma-joined subset of the native-IDF representative component list,
# computed by script/determine-jobs.py (native_idf_components_to_test).
# Comma-joined subset of the esp32 PlatformIO representative component list,
# computed by script/determine-jobs.py (esp32_platformio_components_to_test).
# Single source of truth -- the full list lives in
# script/determine-jobs.py::NATIVE_IDF_TEST_COMPONENTS.
TEST_COMPONENTS: ${{ needs.determine-jobs.outputs.native-idf-components }}
# script/determine-jobs.py::ESP32_PLATFORMIO_TEST_COMPONENTS.
TEST_COMPONENTS: ${{ needs.determine-jobs.outputs.esp32-platformio-components }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python
uses: ./.github/actions/restore-python
@@ -971,66 +969,23 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Prepare build storage on /mnt
# Bind-mount the larger /mnt disk over the IDF install + build dirs BEFORE
# restoring the cache, so the ~4.5GB restore lands on the roomier volume
# instead of being shadowed by a mount set up later in the run step.
run: |
root_avail=$(df -k / | awk 'NR==2 {print $4}')
mnt_avail=$(df -k /mnt 2>/dev/null | awk 'NR==2 {print $4}')
echo "Available space: / has ${root_avail}KB, /mnt has ${mnt_avail}KB"
if [ -n "$mnt_avail" ] && [ "$mnt_avail" -gt "$root_avail" ]; then
echo "Using /mnt for build files (more space available)"
sudo mkdir -p /mnt/esphome-idf
sudo chown $USER:$USER /mnt/esphome-idf
mkdir -p ~/.esphome-idf
sudo mount --bind /mnt/esphome-idf ~/.esphome-idf
sudo mkdir -p /mnt/test_build_components_build
sudo chown $USER:$USER /mnt/test_build_components_build
mkdir -p tests/test_build_components/build
sudo mount --bind /mnt/test_build_components_build tests/test_build_components/build
else
echo "Using / for build files (more space available than /mnt or /mnt unavailable)"
fi
- name: Cache ESP-IDF install
# Shared with the IDF/Arduino clang-tidy jobs (same install); restores
# into the /mnt bind-mount prepared above when present.
uses: ./.github/actions/cache-esp-idf
- name: Run native ESP-IDF compile test
- name: Run PlatformIO compile test
run: |
. venv/bin/activate
echo "Testing components: $TEST_COMPONENTS"
echo ""
# Show disk space before validation
echo "Disk space before config validation:"
df -h
echo ""
# Run config validation (auto-grouped by test_build_components.py)
python3 script/test_build_components.py -e config -t esp32-idf -c "$TEST_COMPONENTS" -f --toolchain esp-idf
# compile validates config first, so a separate config pass is
# redundant for this smoke test. ESP-IDF framework via PlatformIO:
python3 script/test_build_components.py -e compile -t esp32-idf -c "$TEST_COMPONENTS" -f --toolchain platformio
echo ""
echo "Config validation passed! Starting compilation..."
echo "ESP-IDF-via-PlatformIO build passed! Starting Arduino smoke test..."
echo ""
# Show disk space before compilation
echo "Disk space before compilation:"
df -h
echo ""
# Run compilation (auto-grouped by test_build_components.py)
python3 script/test_build_components.py -e compile -t esp32-idf -c "$TEST_COMPONENTS" -f --toolchain esp-idf
- name: Save ESPHome cache
if: github.ref == 'refs/heads/dev'
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.esphome-idf
key: ${{ runner.os }}-esphome-${{ needs.common.outputs.cache-key }}
# Arduino framework via PlatformIO (only components with an esp32-ard test are built):
python3 script/test_build_components.py -e compile -t esp32-ard -c "$TEST_COMPONENTS" -f --toolchain platformio
pre-commit-ci-lite:
name: pre-commit.ci lite
@@ -1041,7 +996,7 @@ jobs:
if: github.event_name == 'pull_request' && !startsWith(github.base_ref, 'beta') && !startsWith(github.base_ref, 'release') && needs.determine-jobs.outputs.core-ci == 'true'
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python
uses: ./.github/actions/restore-python
with:
@@ -1049,7 +1004,7 @@ jobs:
cache-key: ${{ needs.common.outputs.cache-key }}
- uses: esphome/pre-commit-action@43cd1109c09c544d97196f7730ee5b2e0cc6d81e # v3.0.1 fork with pinned actions/cache
env:
SKIP: pylint,clang-tidy-hash,ci-custom
SKIP: pylint,ci-custom
- uses: pre-commit-ci/lite-action@5d6cc0eb514c891a40562a58a8e71576c5c7fb43 # v1.1.0
if: always()
@@ -1067,7 +1022,7 @@ jobs:
skip: ${{ steps.check-script.outputs.skip || steps.check-tests.outputs.skip }}
steps:
- name: Check out target branch
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.base_ref }}
@@ -1143,7 +1098,7 @@ jobs:
- name: Restore cached memory analysis
id: cache-memory-analysis
if: steps.check-script.outputs.skip != 'true' && steps.check-tests.outputs.skip != 'true'
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: memory-analysis-target.json
key: ${{ steps.cache-key.outputs.cache-key }}
@@ -1167,7 +1122,7 @@ jobs:
- name: Cache platformio
if: steps.check-script.outputs.skip != 'true' && steps.check-tests.outputs.skip != 'true' && steps.cache-memory-analysis.outputs.cache-hit != 'true'
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.platformio
key: platformio-memory-${{ fromJSON(needs.determine-jobs.outputs.memory_impact).platform }}-${{ hashFiles('platformio.ini') }}
@@ -1209,7 +1164,7 @@ jobs:
- name: Save memory analysis to cache
if: steps.check-script.outputs.skip != 'true' && steps.check-tests.outputs.skip != 'true' && steps.cache-memory-analysis.outputs.cache-hit != 'true' && steps.build.outcome == 'success'
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: memory-analysis-target.json
key: ${{ steps.cache-key.outputs.cache-key }}
@@ -1249,14 +1204,14 @@ jobs:
flash_usage: ${{ steps.extract.outputs.flash_usage }}
steps:
- name: Check out PR branch
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python
uses: ./.github/actions/restore-python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Cache platformio
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.platformio
key: platformio-memory-${{ fromJSON(needs.determine-jobs.outputs.memory_impact).platform }}-${{ hashFiles('platformio.ini') }}
@@ -1318,7 +1273,7 @@ jobs:
GH_TOKEN: ${{ github.token }}
steps:
- name: Check out code
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python
uses: ./.github/actions/restore-python
with:
@@ -1365,7 +1320,7 @@ jobs:
- determine-jobs
- device-builder
- test-build-components-split
- test-native-idf
- test-esp32-platformio
- pre-commit-ci-lite
- memory-impact-target-branch
- memory-impact-pr-branch
@@ -1381,4 +1336,7 @@ jobs:
# 1. The target branch has a build issue independent of this PR
# 2. This PR fixes a build issue on the target branch
# In either case, we only care that the PR branch builds successfully.
echo "$NEEDS_JSON" | jq -e 'del(.["memory-impact-target-branch"]) | all(.result != "failure")'
# Every other job must have succeeded or been skipped; a "cancelled" or
# "failure" result fails this check so CI is not reported green when the
# workflow was cancelled.
echo "$NEEDS_JSON" | jq -e 'del(.["memory-impact-target-branch"]) | all(.result == "success" or .result == "skipped")'
@@ -26,7 +26,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout base branch
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.event.pull_request.base.sha }}
sparse-checkout: |
@@ -29,7 +29,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout base branch
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.event.pull_request.base.sha }}
+1 -1
View File
@@ -52,7 +52,7 @@ jobs:
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
@@ -1,119 +0,0 @@
name: Add Dashboard Deprecation Comment
on:
pull_request_target:
types: [opened, synchronize]
# All API calls (pulls.listFiles + issues.{list,create,update}Comment) are performed with
# the App token minted below, so the workflow's GITHUB_TOKEN does not need any scopes.
permissions: {}
jobs:
dashboard-deprecation-comment:
name: Dashboard deprecation comment
runs-on: ubuntu-latest
# Release-bump PRs (bump-X.Y.Z -> beta, beta -> release) inevitably
# roll up everything merged into dev since the last cut, which can
# include dashboard changes that have already been reviewed once.
# The bot's purpose is to warn new contributors before they invest
# time -- that only applies to PRs entering dev.
if: github.event.pull_request.base.ref == 'dev'
steps:
- name: Generate a token
id: generate-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
client-id: ${{ vars.ESPHOME_GITHUB_APP_CLIENT_ID }}
private-key: ${{ secrets.ESPHOME_GITHUB_APP_PRIVATE_KEY }}
# pulls.listFiles + issues.{list,create,update}Comment on PRs. For PR resources
# the issues.*Comment APIs require the pull-requests scope, not issues.
permission-pull-requests: write
- name: Add dashboard deprecation comment
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
github-token: ${{ steps.generate-token.outputs.token }}
script: |
const commentMarker = "<!-- This comment was generated automatically by the dashboard-deprecation-comment workflow. -->";
const commentBody = `Thanks for opening this PR!
Heads up: the legacy ESPHome dashboard (\`esphome/dashboard/\` and \`tests/dashboard/\`) is **deprecated** and is being replaced by [ESPHome Device Builder](https://github.com/esphome/device-builder). We are not adding new features to the legacy dashboard and it will eventually be removed from this repository.
What this means for your PR:
- **New features / enhancements**: please port the change to [esphome/device-builder](https://github.com/esphome/device-builder) instead. We are unlikely to review or merge new dashboard features here.
- **Bug fixes**: small fixes may still be considered, but please check first whether the same issue exists in Device Builder, where the fix will have a longer life.
- **Security issues**: please do not file a public PR. Report privately via [GitHub security advisories](https://github.com/esphome/esphome/security/advisories/new) so we can coordinate a fix.
We appreciate the contribution and apologize for the friction; flagging this early so your time isn't spent on a change that may not land.
---
(Added by the PR bot)
${commentMarker}`;
async function getDashboardChanges(github, owner, repo, prNumber) {
const changedFiles = await github.paginate(
github.rest.pulls.listFiles,
{
owner: owner,
repo: repo,
pull_number: prNumber,
per_page: 100,
}
);
return changedFiles.filter(file =>
file.filename.startsWith('esphome/dashboard/') ||
file.filename.startsWith('tests/dashboard/')
);
}
async function findBotComment(github, owner, repo, prNumber) {
const comments = await github.paginate(
github.rest.issues.listComments,
{
owner: owner,
repo: repo,
issue_number: prNumber,
per_page: 100,
}
);
return comments.find(comment =>
comment.body.includes(commentMarker) && comment.user.type === "Bot"
);
}
const prNumber = context.payload.pull_request.number;
const { owner, repo } = context.repo;
const dashboardChanges = await getDashboardChanges(github, owner, repo, prNumber);
const existingComment = await findBotComment(github, owner, repo, prNumber);
if (dashboardChanges.length === 0) {
// PR doesn't (or no longer) touches the legacy dashboard. If we previously
// commented (e.g. files were removed in a later push), leave the comment in
// place for history rather than thrash on edit/delete.
return;
}
if (existingComment) {
if (existingComment.body === commentBody) {
return;
}
await github.rest.issues.updateComment({
owner: owner,
repo: repo,
comment_id: existingComment.id,
body: commentBody,
});
} else {
await github.rest.issues.createComment({
owner: owner,
repo: repo,
issue_number: prNumber,
body: commentBody,
});
}
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
name: Validate PR title
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
+7 -7
View File
@@ -20,7 +20,7 @@ jobs:
branch_build: ${{ steps.tag.outputs.branch_build }}
deploy_env: ${{ steps.tag.outputs.deploy_env }}
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Get tag
id: tag
# yamllint disable rule:line-length
@@ -60,9 +60,9 @@ jobs:
contents: read # actions/checkout to build the sdist/wheel
id-token: write # OIDC token for PyPI Trusted Publishing (pypa/gh-action-pypi-publish)
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.x"
- name: Build
@@ -92,11 +92,11 @@ jobs:
os: "ubuntu-24.04-arm"
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.11"
python-version: "3.12"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
@@ -168,7 +168,7 @@ jobs:
- ghcr
- dockerhub
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Download digests
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+3 -3
View File
@@ -28,16 +28,16 @@ jobs:
permission-pull-requests: write # pulls.create / pulls.update to open or refresh the sync PR
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Checkout Home Assistant
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: home-assistant/core
path: lib/home-assistant
- name: Setup Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.14"
+2 -9
View File
@@ -6,7 +6,7 @@ ci:
autoupdate_commit_msg: 'pre-commit: autoupdate'
autoupdate_schedule: off # Disabled until ruff versions are synced between deps and pre-commit
# Skip hooks that have issues in pre-commit CI environment
skip: [pylint, clang-tidy-hash]
skip: [pylint]
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
@@ -40,7 +40,7 @@ repos:
rev: v3.21.2
hooks:
- id: pyupgrade
args: [--py311-plus]
args: [--py312-plus]
- repo: https://github.com/adrienverge/yamllint.git
rev: v1.37.1
hooks:
@@ -59,13 +59,6 @@ repos:
language: system
types: [python]
files: ^esphome/.+\.py$
- id: clang-tidy-hash
name: Update clang-tidy hash
entry: python script/clang_tidy_hash.py --update-if-changed
language: python
files: ^(\.clang-tidy|platformio\.ini|requirements_dev\.txt|sdkconfig\.defaults|esphome/idf_component\.yml)$
pass_filenames: false
additional_dependencies: []
- id: ci-custom
name: ci-custom
entry: python script/run-in-env.py script/ci-custom.py
+22 -5
View File
@@ -9,12 +9,12 @@ This document provides essential context for AI models interacting with this pro
## 2. Core Technologies & Stack
* **Languages:** Python (>=3.11), C++ (gnu++20)
* **Languages:** Python (>=3.12), C++ (gnu++20)
* **Frameworks & Runtimes:** PlatformIO, Arduino, ESP-IDF.
* **Build Systems:** PlatformIO is the primary build system. CMake is used as an alternative.
* **Configuration:** YAML.
* **Key Libraries/Dependencies:**
* **Python:** `voluptuous` (for configuration validation), `PyYAML` (for parsing configuration files), `paho-mqtt` (for MQTT communication), `tornado` (for the web server), `aioesphomeapi` (for the native API).
* **Python:** `voluptuous` (for configuration validation), `PyYAML` (for parsing configuration files), `paho-mqtt` (for MQTT communication), `aioesphomeapi` (for the native API).
* **C++:** `ArduinoJson` (for JSON serialization/deserialization), `AsyncMqttClient-esphome` (for MQTT), `ESPAsyncWebServer` (for the web server).
* **Package Manager(s):** `pip` (for Python dependencies), `platformio` (for C++/PlatformIO dependencies).
* **Communication Protocols:** Protobuf (for native API), MQTT, HTTP.
@@ -35,7 +35,6 @@ This document provides essential context for AI models interacting with this pro
2. **Code Generation** (`esphome/codegen.py`, `esphome/cpp_generator.py`): Manages Python to C++ code generation, template processing, and build flag management.
3. **Component System** (`esphome/components/`): Contains modular hardware and software components with platform-specific implementations and dependency management.
4. **Core Framework** (`esphome/core/`): Manages the application lifecycle, hardware abstraction, and component registration.
5. **Dashboard** (`esphome/dashboard/`): A web-based interface for device configuration, management, and OTA updates.
* **Platform Support:**
1. **ESP32** (`components/esp32/`): Espressif ESP32 family. Supports multiple variants (Original, C2, C3, C5, C6, H2, P4, S2, S3) with ESP-IDF framework. Arduino framework supports only a subset of the variants (Original, C3, S2, S3).
@@ -59,6 +58,19 @@ This document provides essential context for AI models interacting with this pro
- Protected/private fields: `lower_snake_case_with_trailing_underscore_`
- Favor descriptive names over abbreviations
* **Python Idioms:**
* **Assignment expressions (PEP 572):** Prefer the walrus operator (`:=`) wherever it removes a redundant lookup or a throwaway temporary. The most common case in component code is presence-checking a config key and then indexing it separately — fetch once with `.get()` and bind in the condition instead:
```python
# Bad - looks up CONF_BLAH twice
if CONF_BLAH in config:
cg.add(var.set_blah(config[CONF_BLAH]))
# Good - single lookup, value bound inline
if (blah := config.get(CONF_BLAH)) is not None:
cg.add(var.set_blah(blah))
```
The same applies to `while` loops and comprehensions where it avoids recomputing a value. Don't contort code to use it — reach for `:=` only when it genuinely cuts repetition or an extra assignment line.
* **C++ Field Visibility:**
* **Prefer `protected`:** Use `protected` for most class fields to enable extensibility and testing. Fields should be `lower_snake_case_with_trailing_underscore_`.
* **Use `private` for safety-critical cases:** Use `private` visibility when direct field access could introduce bugs or violate invariants:
@@ -443,7 +455,6 @@ This document provides essential context for AI models interacting with this pro
* **Debug Tools:**
- `esphome config <file>.yaml` to validate configuration.
- `esphome compile <file>.yaml` to compile without uploading.
- Check the Dashboard for real-time logs.
- Use component-specific debug logging.
* **Common Issues:**
- **Import Errors**: Check component dependencies and `PYTHONPATH`.
@@ -645,7 +656,7 @@ This document provides essential context for AI models interacting with this pro
If you need a real-world example, search for components that use `@dataclass` with `CORE.data` in the codebase. Note: Some components may use `TypedDict` for dictionary-based storage; both patterns are acceptable depending on your needs.
**Why this matters:**
- Module-level globals persist between compilation runs if the dashboard doesn't fork/exec
- Module-level globals persist between compilation runs if the host process (e.g. device-builder) doesn't fork/exec
- `CORE.data` automatically clears between runs
- Namespacing under `DOMAIN` prevents key collisions between components
- `@dataclass` provides type safety and cleaner attribute access
@@ -698,3 +709,9 @@ This document provides essential context for AI models interacting with this pro
_LOGGER.warning(f"'{CONF_OLD_KEY}' deprecated, use '{CONF_NEW_KEY}'. Removed in 2026.6.0")
config[CONF_NEW_KEY] = config.pop(CONF_OLD_KEY) # Auto-migrate
```
## 9. English Language
The project uses English for non-code content. When drafting documentation, code comments, commit messages,
PR descriptions, and similar text, avoid technical jargon. Instead, express concepts in plain English,
using standard technical terms only when required. Ensure the text is readily comprehensible to a wide
audience, including non-native English speakers.
+7 -1
View File
@@ -123,6 +123,7 @@ esphome/components/cs5460a/* @balrog-kun
esphome/components/cse7761/* @berfenger
esphome/components/cst226/* @clydebarrow
esphome/components/cst816/* @clydebarrow
esphome/components/cst9220/* @clydebarrow
esphome/components/ct_clamp/* @jesserockz
esphome/components/current_based/* @djwmarcx
esphome/components/dac7678/* @NickB1
@@ -266,6 +267,7 @@ esphome/components/integration/* @OttoWinter
esphome/components/internal_temperature/* @Mat931
esphome/components/interval/* @esphome/core
esphome/components/ir_rf_proxy/* @kbx81
esphome/components/it8951/* @koosoli @limengdu @Passific
esphome/components/jsn_sr04t/* @Mafus1
esphome/components/json/* @esphome/core
esphome/components/kamstrup_kmp/* @cfeenstra1024
@@ -385,6 +387,7 @@ esphome/components/pcm5122/* @remcom
esphome/components/pi4ioe5v6408/* @jesserockz
esphome/components/pid/* @OttoWinter
esphome/components/pipsolar/* @andreashergert1984
esphome/components/pixoo/* @jesserockz
esphome/components/pm1006/* @habbie
esphome/components/pm2005/* @andrewjswan
esphome/components/pmsa003i/* @sjtrny
@@ -404,6 +407,7 @@ esphome/components/psram/* @esphome/core
esphome/components/pulse_meter/* @cstaahl @stevebaxter @TrentHouliston
esphome/components/pvvx_mithermometer/* @pasiz
esphome/components/pylontech/* @functionpointer
esphome/components/qmi8658/* @clydebarrow
esphome/components/qmp6988/* @andrewpc
esphome/components/qr_code/* @wjtje
esphome/components/qspi_dbi/* @clydebarrow
@@ -445,7 +449,7 @@ esphome/components/select/* @esphome/core
esphome/components/sen0321/* @notjj
esphome/components/sen21231/* @shreyaskarnik
esphome/components/sen5x/* @martgras
esphome/components/sen6x/* @martgras @mebner86 @mikelawrence @tuct
esphome/components/sen6x/* @martgras @mebner86 @tuct
esphome/components/sendspin/* @kahrendt
esphome/components/sendspin/media_player/* @kahrendt
esphome/components/sendspin/media_source/* @kahrendt
@@ -561,6 +565,7 @@ esphome/components/uart/packet_transport/* @clydebarrow
esphome/components/udp/* @clydebarrow
esphome/components/ufire_ec/* @pvizeli
esphome/components/ufire_ise/* @pvizeli
esphome/components/ufm01/* @ljungqvist
esphome/components/ultrasonic/* @ssieb @swoboda1337
esphome/components/update/* @jesserockz
esphome/components/uponor_smatrix/* @kroimon
@@ -577,6 +582,7 @@ esphome/components/wake_on_lan/* @clydebarrow @willwill2will54
esphome/components/watchdog/* @oarcher
esphome/components/water_heater/* @dhoeben
esphome/components/waveshare_epaper/* @clydebarrow
esphome/components/waveshare_io_ch32v003/* @latonita
esphome/components/web_server/ota/* @esphome/core
esphome/components/web_server_base/* @esphome/core
esphome/components/web_server_idf/* @dentra
+1 -1
View File
@@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 2026.6.1
PROJECT_NUMBER = 2026.7.0-dev
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
+102
View File
@@ -0,0 +1,102 @@
# ESPHome Threat Model
This document defines the trust boundary for the **ESPHome** repository — the
Python compiler/CLI and the device firmware it generates — so that real security
bugs can be told apart from defense-in-depth improvements. It gives contributors,
reviewers, and security researchers a clear answer to one question:
**does this issue let an _unauthenticated_ attacker do something they shouldn't?**
Related documents:
- Deployment guidance for operators:
https://esphome.io/guides/security_best_practices/
- The **Device Builder dashboard** (the web UI, its authentication, ingress,
Origin/Host gates, and peer-link pairing) lives in a separate repository and
has its own threat model. If your report concerns any of that, please read and
report there instead:
https://github.com/esphome/device-builder/blob/main/docs/THREAT_MODEL.md
## The trust boundary
For this repository there are two trusted inputs by design:
1. **The configuration.** Anyone who can supply or edit a YAML config is trusted
(see below).
2. **Authenticated peers of a running device** — clients holding the device's
API encryption key / password, OTA password, or web server credentials.
The security boundary is therefore **unauthenticated network traffic vs. those
trusted inputs.** A bug that lets an unauthenticated attacker cross it is a
security bug.
## Config authors are host-equivalent by design
Anyone who can supply or edit a configuration is **trusted with full code
execution on the host that runs `esphome`**, on purpose. This is what the product
does, not a flaw. A config author can already, through fully supported features:
- Run arbitrary **Python** at validation/compile time via `external_components:`
(and other component-import mechanisms) — ESPHome imports those packages as
ordinary Python.
- Run arbitrary **shell** commands through the compile/validate/flash toolchain
that ESPHome invokes as subprocesses.
- Read and write arbitrary files reachable by the process (e.g. via `!include`,
`packages:`, `dashboard_import:`, and generated build output).
Because of this, a malicious config author is equivalent to shell access on the
host running the build.
## What is *not* a security vulnerability
If exploiting an issue requires the ability to supply or edit configuration, it
is **not** a vulnerability in ESPHome, because that ability already grants host
code execution. This explicitly includes, among others:
- Template / expression injection in substitutions or any YAML string value
(e.g. Jinja `${...}` evaluation reaching Python internals). This grants no
capability a config author lacks.
- `!include` / `packages:` / `dashboard_import:` reading or fetching content
from surprising or remote locations.
- The validator or compiler crashing or behaving unexpectedly on adversarial
YAML.
- ESPHome running as root in the official container — that is the documented
deployment posture, reachable by the same caller through the features above.
These do not warrant a CVE or coordinated disclosure. Hardening in these areas
(for example, sandboxing template evaluation as least-surprise defense-in-depth)
is welcome as a normal enhancement PR, framed as cleanliness rather than a
security fix — not as a vulnerability remediation.
## What we do defend
These *are* security bugs in this repo, and we want to hear about them privately:
- Memory-safety or protocol bugs in the generated **device firmware** that are
remotely triggerable over the network (native API, web server, OTA, BLE,
captive portal, etc.) **without** valid credentials.
- Authentication or encryption bypass on the device — reaching API calls, OTA
updates, or the web server without the configured key/password.
- Flaws that weaken the device's API encryption (Noise), OTA, or web server auth
below their documented guarantees.
## Explicitly out of scope
- Local attackers who already have shell access on the host that runs `esphome`.
- Supply-chain attacks against ESPHome or its dependencies.
- Operator-supplied hostile YAML (covered above — config authoring is trusted).
- Attacks that require an already-authenticated device peer (someone who already
holds the API key / OTA / web credentials).
- Anything in the dashboard / device-builder — report that in its own repository
(linked at the top).
- Deployments where the operator removed protections or exposed credentials. See
the security best practices guide:
https://esphome.io/guides/security_best_practices/
## Reporting a vulnerability
If you believe you've found an issue that crosses the unauthenticated boundary
above, please report it privately via GitHub Security Advisories rather than a
public issue. For issues that require config-write access, please review this
document first — they are very likely out of scope by design. For dashboard /
device-builder issues, report against that repository and consult its threat
model (linked at the top).
+2 -12
View File
@@ -1,5 +1,5 @@
ARG BUILD_VERSION=dev
ARG BUILD_BASE_VERSION=2026.06.0
ARG BUILD_BASE_VERSION=2026.06.1
ARG BUILD_TYPE=docker
FROM ghcr.io/esphome/docker-base:debian-${BUILD_BASE_VERSION} AS base-source-docker
@@ -11,16 +11,6 @@ FROM base-source-${BUILD_TYPE} AS base
RUN git config --system --add safe.directory "*" \
&& git config --system advice.detachedHead false
# Install build tools for Python packages that require compilation
# (e.g., ruamel.yaml.clib used by ESP-IDF's idf-component-manager).
# Also install libusb-1.0 at runtime so the ESP-IDF tools installer can
# validate openocd-esp32 (it dynamically links libusb-1.0.so.0); without
# it idf_tools.py rejects the openocd install with exit 127 and aborts
# the whole framework setup.
RUN apt-get update \
&& apt-get install -y --no-install-recommends build-essential libusb-1.0-0 \
&& rm -rf /var/lib/apt/lists/*
ENV PIP_DISABLE_PIP_VERSION_CHECK=1
RUN pip install --no-cache-dir -U pip uv==0.10.1
@@ -32,7 +22,7 @@ RUN \
-r /requirements.txt
# Install the ESPHome Device Builder dashboard.
RUN uv pip install --no-cache-dir esphome-device-builder==1.0.10
RUN uv pip install --no-cache-dir esphome-device-builder==1.0.22
RUN \
platformio settings set enable_telemetry No \
+4
View File
@@ -21,6 +21,10 @@ export PLATFORMIO_PLATFORMS_DIR="${pio_cache_base}/platforms"
export PLATFORMIO_PACKAGES_DIR="${pio_cache_base}/packages"
export PLATFORMIO_CACHE_DIR="${pio_cache_base}/cache"
# Keep the native ESP-IDF install on the persistent cache root, not the
# container's ephemeral user cache dir (re-downloaded on every restart).
export ESPHOME_ESP_IDF_PREFIX="$(dirname "${pio_cache_base}")/idf"
# If /build is mounted, use that as the build path
# otherwise use path in /config (so that builds aren't lost on container restart)
if [[ -d /build ]]; then
@@ -15,18 +15,14 @@ export PLATFORMIO_PLATFORMS_DIR="${pio_cache_base}/platforms"
export PLATFORMIO_PACKAGES_DIR="${pio_cache_base}/packages"
export PLATFORMIO_CACHE_DIR="${pio_cache_base}/cache"
# Keep the native ESP-IDF install on the persistent /data volume, not the
# container's ephemeral user cache dir (wiped on every add-on update/restart).
export ESPHOME_ESP_IDF_PREFIX=/data/cache/idf
if bashio::config.true 'leave_front_door_open'; then
export DISABLE_HA_AUTHENTICATION=true
fi
if bashio::config.true 'streamer_mode'; then
export ESPHOME_STREAMER_MODE=true
fi
if bashio::config.has_value 'relative_url'; then
export ESPHOME_DASHBOARD_RELATIVE_URL=$(bashio::config 'relative_url')
fi
if bashio::config.has_value 'default_compile_process_limit'; then
export ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT=$(bashio::config 'default_compile_process_limit')
else
@@ -49,7 +45,21 @@ if bashio::fs.directory_exists '/config/esphome/.esphome'; then
rm -rf /config/esphome/.esphome
fi
# Only signal device-builder to expose the public LAN port when the operator
# mapped port 6052, matching the legacy dashboard where nginx listened on the
# fixed port 6052 only when it was configured. We use the mapping purely as a
# presence check and don't forward the published value; device-builder binds
# its default port 6052 (the fixed container port, as the legacy
# "listen 6052" did). --ha-addon-allow-public is inert on its own: the no-auth
# gate is the DISABLE_HA_AUTHENTICATION env var set above, so both opt-ins are
# required to bind 6052 unauthenticated; either alone stays ingress-only.
set --
if bashio::var.has_value "$(bashio::addon.port 6052)"; then
set -- --ha-addon-allow-public
fi
bashio::log.info "Starting ESPHome Device Builder..."
exec esphome-device-builder /config/esphome \
--ha-addon \
--ingress-port "$(bashio::addon.ingress_port)"
--ingress-port "$(bashio::addon.ingress_port)" \
"$@"
+7
View File
@@ -0,0 +1,7 @@
esphome:
name: docker-test-bk72xx-arduino
bk72xx:
board: generic-bk7231n-qfn32-tuya
logger:
@@ -0,0 +1,10 @@
esphome:
name: docker-test-esp32-ard-idf
esp32:
variant: esp32
framework:
type: arduino
toolchain: esp-idf
logger:
@@ -0,0 +1,10 @@
esphome:
name: docker-test-esp32-ard-pio
esp32:
variant: esp32
framework:
type: arduino
toolchain: platformio
logger:
@@ -0,0 +1,10 @@
esphome:
name: docker-test-esp32-idf-idf
esp32:
variant: esp32
framework:
type: esp-idf
toolchain: esp-idf
logger:
@@ -0,0 +1,10 @@
esphome:
name: docker-test-esp32-idf-pio
esp32:
variant: esp32
framework:
type: esp-idf
toolchain: platformio
logger:
+7
View File
@@ -0,0 +1,7 @@
esphome:
name: docker-test-esp8266-arduino
esp8266:
board: d1_mini
logger:
+6
View File
@@ -0,0 +1,6 @@
esphome:
name: docker-test-host
host:
logger:
+7
View File
@@ -0,0 +1,7 @@
esphome:
name: docker-test-ln882x-arduino
ln882x:
board: generic-ln882h
logger:
+8
View File
@@ -0,0 +1,8 @@
esphome:
name: docker-test-nrf52
nrf52:
board: adafruit_itsybitsy_nrf52840
bootloader: adafruit_nrf52_sd140_v6
logger:
+7
View File
@@ -0,0 +1,7 @@
esphome:
name: docker-test-rp2040-arduino
rp2040:
variant: rp2040
logger:
+7
View File
@@ -0,0 +1,7 @@
esphome:
name: docker-test-rtl87xx-arduino
rtl87xx:
board: generic-rtl8710bn-2mb-788k
logger:
+130 -90
View File
@@ -28,13 +28,13 @@ from esphome.const import (
ALLOWED_NAME_CHARS,
ARGUMENT_HELP_DEVICE,
CONF_API,
CONF_AUTH,
CONF_BAUD_RATE,
CONF_BROKER,
CONF_DEASSERT_RTS_DTR,
CONF_DISABLED,
CONF_ESPHOME,
CONF_LEVEL,
CONF_LOG,
CONF_LOG_TOPIC,
CONF_LOGGER,
CONF_MDNS,
@@ -48,15 +48,10 @@ from esphome.const import (
CONF_PORT,
CONF_SUBSTITUTIONS,
CONF_TOPIC,
CONF_USERNAME,
CONF_VERSION,
CONF_WEB_SERVER,
CONF_WIFI,
ENV_NOGITIGNORE,
KEY_CORE,
KEY_TARGET_PLATFORM,
PLATFORM_ESP32,
PLATFORM_ESP8266,
PLATFORM_RP2040,
SECRETS_FILES,
Toolchain,
)
@@ -268,6 +263,36 @@ def _ota_hostnames_for_default(purpose: Purpose) -> list[str]:
return _resolve_with_cache(CORE.address, purpose)
def _unresolved_default_error(purpose: Purpose, defaults: list[str]) -> str:
"""Build the error when a default device target produced no usable host.
When the OTA default was requested and the address resolves but the config
lacks the transport the purpose needs (``api:`` for logs, an ``ota:``
platform for uploads), name that gap instead of the misleading
"could not be resolved" / set-use_address hint.
"""
if "OTA" in defaults and has_resolvable_address():
if purpose == Purpose.LOGGING and not has_api():
return (
"Cannot view logs over the network: no 'api:' component is "
"configured. Add an 'api:' component, enable MQTT logging, add a "
"'web_server:' component, or view logs over USB."
)
if purpose == Purpose.UPLOADING and not has_ota():
return (
"Cannot upload over the network: no 'ota:' platform is "
"configured. Add an 'ota:' platform, or upload over USB."
)
if CORE.dashboard:
hint = "If you know the IP, set 'use_address' in your network config."
else:
hint = "If you know the IP, try --device <IP>"
return (
f"All specified devices {defaults} could not be resolved. "
f"Is the device connected to the network? {hint}"
)
def choose_upload_log_host(
default: list[str] | str | None,
check_default: str | None,
@@ -291,9 +316,12 @@ def choose_upload_log_host(
]
resolved.append(choose_prompt(options, purpose=purpose))
elif device == "OTA":
# Logs can stream over a network transport via the native API
# or the web_server HTTP SSE feed.
network_logging = has_api() or has_web_server_logging()
# ensure IP adresses are used first
if is_ip_address(CORE.address) and (
(purpose == Purpose.LOGGING and has_api())
(purpose == Purpose.LOGGING and network_logging)
or (purpose == Purpose.UPLOADING and has_ota())
):
resolved.extend(_resolve_with_cache(CORE.address, purpose))
@@ -305,7 +333,11 @@ def choose_upload_log_host(
if has_mqtt_logging():
resolved.append("MQTT")
if has_api() and has_non_ip_address() and has_resolvable_address():
if (
network_logging
and has_non_ip_address()
and has_resolvable_address()
):
resolved.extend(_ota_hostnames_for_default(purpose))
elif purpose == Purpose.UPLOADING:
@@ -317,14 +349,7 @@ def choose_upload_log_host(
else:
resolved.append(device)
if not resolved:
if CORE.dashboard:
hint = "If you know the IP, set 'use_address' in your network config."
else:
hint = "If you know the IP, try --device <IP>"
raise EsphomeError(
f"All specified devices {defaults} could not be resolved. "
f"Is the device connected to the network? {hint}"
)
raise EsphomeError(_unresolved_default_error(purpose, defaults))
return resolved
# No devices specified, show interactive chooser
@@ -336,7 +361,7 @@ def choose_upload_log_host(
bootsel_permission_error = False
if (
purpose == Purpose.UPLOADING
and CORE.data.get(KEY_CORE, {}).get(KEY_TARGET_PLATFORM) == PLATFORM_RP2040
and CORE.is_rp2040
and (picotool := _find_picotool()) is not None
):
bootsel = detect_rp2040_bootsel(picotool)
@@ -374,7 +399,7 @@ def choose_upload_log_host(
mqtt_config = CORE.config[CONF_MQTT]
options.append((f"MQTT ({mqtt_config[CONF_BROKER]})", "MQTT"))
if has_api():
if has_api() or has_web_server_logging():
add_ota_options()
elif purpose == Purpose.UPLOADING and has_ota():
@@ -383,7 +408,7 @@ def choose_upload_log_host(
# Show helpful BOOTSEL instructions for RP2040 when no BOOTSEL device is found
if (
purpose == Purpose.UPLOADING
and CORE.data.get(KEY_CORE, {}).get(KEY_TARGET_PLATFORM) == PLATFORM_RP2040
and CORE.is_rp2040
and not any(get_port_type(opt[1]) == PortType.BOOTSEL for opt in options)
):
if bootsel_permission_error:
@@ -467,6 +492,21 @@ def has_web_server_ota() -> bool:
)
def has_web_server_logging() -> bool:
"""Check if logs can be streamed over the web_server HTTP SSE endpoint.
The ``web_server`` component exposes a ``/events`` Server-Sent Events
stream that carries ``event: log`` frames. This requires version 2+ (the
v1 UI has no ``/events`` endpoint) and the ``log`` option enabled (default).
"""
web_conf = CORE.config.get(CONF_WEB_SERVER)
if web_conf is None:
return False
if web_conf.get(CONF_VERSION, 2) == 1:
return False
return web_conf.get(CONF_LOG, True)
def has_mqtt_ip_lookup() -> bool:
"""Check if MQTT is available and IP lookup is supported."""
from esphome.components.mqtt import CONF_DISCOVER_IP
@@ -504,7 +544,7 @@ def has_resolvable_address() -> bool:
if has_ip_address():
return True
# The dashboard pre-resolves the device and passes the IPs via
# device-builder pre-resolves the device and passes the IPs via
# --mdns-address-cache/--dns-address-cache; honor a cached address even when the
# device has mDNS disabled (e.g. a .local host found via ping).
if CORE.address_cache and CORE.address_cache.get_addresses(CORE.address):
@@ -961,7 +1001,7 @@ def upload_using_platformio(config: ConfigType, port: str) -> int:
# RP2040 platform-raspberrypi build recipe expects firmware.bin.signed for
# the upload target, but 'nobuild' skips the build phase that creates it.
# Create it here so the upload doesn't fail.
if CORE.data.get(KEY_CORE, {}).get(KEY_TARGET_PLATFORM) == PLATFORM_RP2040:
if CORE.is_rp2040:
idedata = toolchain.get_idedata(config)
build_dir = Path(idedata.firmware_elf_path).parent
firmware_bin = build_dir / "firmware.bin"
@@ -1146,10 +1186,10 @@ def upload_program(
check_permissions(host)
exit_code = 1
if CORE.target_platform in (PLATFORM_ESP32, PLATFORM_ESP8266):
if CORE.is_esp32 or CORE.is_esp8266:
file = getattr(args, "file", None)
exit_code = upload_using_esptool(config, host, file, args.upload_speed)
elif CORE.target_platform == PLATFORM_RP2040 or CORE.is_libretiny:
elif CORE.is_rp2040 or CORE.is_libretiny:
exit_code = upload_using_platformio(config, host)
# else: Unknown target platform, exit_code remains 1
@@ -1267,25 +1307,23 @@ def _upload_via_native_api(
def _upload_via_web_server(
config: ConfigType, network_devices: list[str], binary: Path
) -> tuple[int, str | None]:
web_conf = config.get(CONF_WEB_SERVER)
if not web_conf:
raise EsphomeError(
f"Cannot upload via web_server OTA: the {CONF_WEB_SERVER} component "
f"is not configured."
)
remote_port = int(web_conf[CONF_PORT])
auth = web_conf.get(CONF_AUTH) or {}
username = auth.get(CONF_USERNAME)
password = auth.get(CONF_PASSWORD)
from esphome import web_server_ota
from esphome.web_server_helpers import get_web_server_connection
remote_port, username, password = get_web_server_connection(config)
return web_server_ota.run_ota(
network_devices, remote_port, username, password, binary
)
def _show_logs_via_web_server(config: ConfigType, network_devices: list[str]) -> int:
from esphome import web_server_logs
from esphome.web_server_helpers import get_web_server_connection
port, username, password = get_web_server_connection(config)
return web_server_logs.run_logs(network_devices, port, username, password)
# Layout of esp_partition_info_t on flash. Each entry is 32 bytes, leading with a
# 16-bit little-endian magic. ESP-IDF defines ESP_PARTITION_MAGIC = 0x50AA (stored as
# bytes 0xAA, 0x50) for partition entries and ESP_PARTITION_MAGIC_MD5 = 0xEBEB for the
@@ -1414,6 +1452,13 @@ def show_logs(config: ConfigType, args: ArgsProtocol, devices: list[str]) -> int
config, args.topic, args.username, args.password, args.client_id
)
# Fall back to the web_server HTTP SSE log stream for devices that have
# web_server: but no api: (the logging counterpart to web_server OTA).
if has_web_server_logging() and (
network_devices := _resolve_network_devices(devices, config, args)
):
return _show_logs_via_web_server(config, network_devices)
raise EsphomeError("No remote or local logging method configured (api/mqtt/logger)")
@@ -1470,12 +1515,29 @@ _LEGACY_REDACTION_REMOVAL = "2026.12.0"
def _redact_with_legacy_fallback(output: str) -> str:
unmarked: set[str] = set()
# Track the top-level ``substitutions:`` block. Its keys are arbitrary
# user-chosen names with no schema validator, so the ``cv.sensitive(...)``
# migration named in the warning can't be applied to them. Their values are
# still redacted, but emitting the (unactionable) deprecation warning would
# only confuse users.
in_substitutions = False
def _replace(m: re.Match[str]) -> str:
unmarked.add(m.group("key"))
return f"{m.group('key')}: \\033[8m{m.group('val')}\\033[28m"
output = _LEGACY_REDACTION_RE.sub(_replace, output)
lines = output.split("\n")
for i, line in enumerate(lines):
# A non-indented, non-blank line is a top-level key that opens or
# closes the substitutions block.
if line and not line[0].isspace():
in_substitutions = line.startswith(f"{CONF_SUBSTITUTIONS}:")
m = _LEGACY_REDACTION_RE.search(line)
if m is None:
continue
if not in_substitutions:
unmarked.add(m.group("key"))
lines[i] = (
f"{line[: m.start()]}{m.group('key')}: "
f"\\033[8m{m.group('val')}\\033[28m{line[m.end() :]}"
)
output = "\n".join(lines)
for key in sorted(unmarked):
_LOGGER.warning(
"Field '%s' is being redacted by a legacy substring heuristic. "
@@ -1606,10 +1668,7 @@ def command_run(args: ArgsProtocol, config: ConfigType) -> int | None:
# After BOOTSEL upload, wait for a new serial port to appear
# so it shows up in the log chooser
if (
successful_device is None
and CORE.data.get(KEY_CORE, {}).get(KEY_TARGET_PLATFORM) == PLATFORM_RP2040
):
if successful_device is None and CORE.is_rp2040:
_wait_for_serial_port(known_ports=pre_upload_ports)
# If exactly one new serial port appeared, use it directly
serial_ports = get_serial_ports()
@@ -1692,9 +1751,13 @@ def command_bundle(args: ArgsProtocol, config: ConfigType) -> int | None:
def command_dashboard(args: ArgsProtocol) -> int | None:
from esphome.dashboard import dashboard
return dashboard.start_dashboard(args)
raise EsphomeError(
"The built-in dashboard has been removed from ESPHome. "
"Install and run ESPHome Device Builder instead:\n"
" pip install esphome-device-builder\n"
" esphome-device-builder\n"
"See https://github.com/esphome/device-builder for more information."
)
def run_multiple_configs(
@@ -2350,50 +2413,31 @@ def parse_args(argv):
)
parser_clean_all = subparsers.add_parser(
"clean-all", help="Clean all build and platform files."
"clean-all",
help="Clean all build and platform files, including machine-global "
"toolchain caches shared by all configurations, so other projects will "
"re-download them on next build.",
)
parser_clean_all.add_argument(
"configuration", help="Your YAML file or configuration directory.", nargs="*"
)
parser_dashboard = subparsers.add_parser(
"dashboard", help="Create a simple web server for a dashboard."
# The dashboard moved to ESPHome Device Builder; the command is kept only to
# print a redirect (see command_dashboard). Accept and ignore the old flags
# so legacy invocations reach that message instead of failing on argparse
# "unrecognized arguments".
parser_dashboard = subparsers.add_parser("dashboard")
parser_dashboard.add_argument("configuration", nargs="?", help=argparse.SUPPRESS)
parser_dashboard.add_argument("--port", help=argparse.SUPPRESS)
parser_dashboard.add_argument("--address", help=argparse.SUPPRESS)
parser_dashboard.add_argument("--username", help=argparse.SUPPRESS)
parser_dashboard.add_argument("--password", help=argparse.SUPPRESS)
parser_dashboard.add_argument("--socket", help=argparse.SUPPRESS)
parser_dashboard.add_argument(
"--open-ui", action="store_true", help=argparse.SUPPRESS
)
parser_dashboard.add_argument(
"configuration", help="Your YAML configuration file directory."
)
parser_dashboard.add_argument(
"--port",
help="The HTTP port to open connections on. Defaults to 6052.",
type=int,
default=6052,
)
parser_dashboard.add_argument(
"--address",
help="The address to bind to.",
type=str,
default="0.0.0.0",
)
parser_dashboard.add_argument(
"--username",
help="The optional username to require for authentication.",
type=str,
default="",
)
parser_dashboard.add_argument(
"--password",
help="The optional password to require for authentication.",
type=str,
default="",
)
parser_dashboard.add_argument(
"--open-ui", help="Open the dashboard UI in a browser.", action="store_true"
)
parser_dashboard.add_argument(
"--ha-addon", help=argparse.SUPPRESS, action="store_true"
)
parser_dashboard.add_argument(
"--socket", help="Make the dashboard serve under a unix socket", type=str
"--ha-addon", action="store_true", help=argparse.SUPPRESS
)
parser_vscode = subparsers.add_parser("vscode")
@@ -2488,11 +2532,7 @@ def run_esphome(argv):
elif args.quiet:
args.log_level = "CRITICAL"
setup_log(
log_level=args.log_level,
# Show timestamp for dashboard access logs
include_timestamp=args.command == "dashboard",
)
setup_log(log_level=args.log_level)
if args.command in PRE_CONFIG_ACTIONS:
try:
+3 -6
View File
@@ -12,12 +12,9 @@ from __future__ import annotations
import asyncio
from collections.abc import Awaitable, Callable
import threading
from typing import Generic, TypeVar
_T = TypeVar("_T")
class AsyncThreadRunner(threading.Thread, Generic[_T]):
class AsyncThreadRunner[T](threading.Thread):
"""Run an async coroutine in a daemon thread and expose its result.
The runner catches all exceptions from the coroutine and stores them in
@@ -35,10 +32,10 @@ class AsyncThreadRunner(threading.Thread, Generic[_T]):
result = runner.result
"""
def __init__(self, coro_factory: Callable[[], Awaitable[_T]]) -> None:
def __init__(self, coro_factory: Callable[[], Awaitable[T]]) -> None:
super().__init__(daemon=True)
self._coro_factory = coro_factory
self.result: _T | None = None
self.result: T | None = None
self.exception: BaseException | None = None
self.event = threading.Event()
+4 -8
View File
@@ -6,6 +6,7 @@ from pathlib import Path
from esphome.components.esp32 import get_esp32_variant, idf_version
import esphome.config_validation as cv
from esphome.core import CORE
from esphome.framework_helpers import get_project_compile_flags, get_project_link_flags
from esphome.helpers import mkdir_p, write_file_if_changed
# Replaces the IDF default C++ standard (-std=gnu++2b appended to
@@ -84,12 +85,7 @@ def get_project_cmakelists(minimal: bool = False) -> str:
# esphome__micro-mp3) rather than just src/. Required so suppressions
# like ``-Wno-error=maybe-uninitialized`` actually silence warnings in
# third-party components we don't author.
project_compile_opts = [
flag
for flag in sorted(CORE.build_flags)
if flag.startswith("-D")
or (flag.startswith("-W") and not flag.startswith("-Wl,"))
]
project_compile_opts = get_project_compile_flags()
extra_compile_options = "\n".join(
f'idf_build_set_property(COMPILE_OPTIONS "{flag}" APPEND)'
for flag in project_compile_opts
@@ -188,8 +184,8 @@ def get_component_cmakelists() -> str:
# Extract linker options (-Wl, flags). Compile flags (-D, -W) are
# emitted project-wide via idf_build_set_property in
# get_project_cmakelists so they reach every component, not just src/.
link_opts = [flag for flag in CORE.build_flags if flag.startswith("-Wl,")]
link_opts_str = "\n ".join(sorted(link_opts)) if link_opts else ""
link_opts = get_project_link_flags()
link_opts_str = "\n ".join(link_opts) if link_opts else ""
return f"""\
# Auto-generated by ESPHome
+1 -1
View File
@@ -25,7 +25,7 @@ void A01nyubComponent::check_buffer_() {
if (this->buffer_[3] == checksum) {
float distance = (this->buffer_[1] << 8) + this->buffer_[2];
if (distance > 280) {
float meters = distance / 1000.0;
float meters = distance / 1000.0f;
ESP_LOGV(TAG, "Distance from sensor: %f mm, %f m", distance, meters);
this->publish_state(meters);
} else {
+1 -1
View File
@@ -8,7 +8,7 @@
namespace esphome::a01nyub {
class A01nyubComponent : public sensor::Sensor, public Component, public uart::UARTDevice {
class A01nyubComponent final : public sensor::Sensor, public Component, public uart::UARTDevice {
public:
// Nothing really public.
+1 -1
View File
@@ -8,7 +8,7 @@
namespace esphome::a02yyuw {
class A02yyuwComponent : public sensor::Sensor, public Component, public uart::UARTDevice {
class A02yyuwComponent final : public sensor::Sensor, public Component, public uart::UARTDevice {
public:
// Nothing really public.
+1 -1
View File
@@ -6,7 +6,7 @@
namespace esphome::a4988 {
class A4988 : public stepper::Stepper, public Component {
class A4988 final : public stepper::Stepper, public Component {
public:
void set_step_pin(GPIOPin *step_pin) { step_pin_ = step_pin; }
void set_dir_pin(GPIOPin *dir_pin) { dir_pin_ = dir_pin; }
@@ -13,7 +13,7 @@ enum SaturationVaporPressureEquation {
};
/// This class implements calculation of absolute humidity from temperature and relative humidity.
class AbsoluteHumidityComponent : public sensor::Sensor, public Component {
class AbsoluteHumidityComponent final : public sensor::Sensor, public Component {
public:
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { this->temperature_sensor_ = temperature_sensor; }
void set_humidity_sensor(sensor::Sensor *humidity_sensor) { this->humidity_sensor_ = humidity_sensor; }
+1 -1
View File
@@ -216,7 +216,7 @@ void AcDimmer::setup() {
}
void AcDimmer::write_state(float state) {
state = std::acos(1 - (2 * state)) / std::numbers::pi; // RMS power compensation
state = std::acos(1 - (2 * state)) / std::numbers::pi_v<float>; // RMS power compensation
auto new_value = static_cast<uint16_t>(roundf(state * 65535));
if (new_value != 0 && this->store_.value == 0)
this->store_.init_cycle = this->init_with_half_cycle_;
+1 -1
View File
@@ -41,7 +41,7 @@ struct AcDimmerDataStore {
#endif
};
class AcDimmer : public output::FloatOutput, public Component {
class AcDimmer final : public output::FloatOutput, public Component {
public:
void setup() override;
+1 -1
View File
@@ -54,7 +54,7 @@ template<typename T> class Aggregator {
SamplingMode mode_{SamplingMode::AVG};
};
class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler {
class ADCSensor final : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler {
public:
/// Update the sensor's state by reading the current ADC value.
/// This method is called periodically based on the update interval.
+8 -5
View File
@@ -66,15 +66,18 @@ float ADCSensor::sample() {
}
uint8_t pin = this->pin_->get_pin();
#ifdef CYW43_USES_VSYS_PIN
#if defined(CYW43_USES_VSYS_PIN) && defined(USE_WIFI)
if (pin == PICO_VSYS_PIN) {
// Measuring VSYS on Raspberry Pico W needs to be wrapped with
// `cyw43_thread_enter()`/`cyw43_thread_exit()` as discussed in
// https://github.com/raspberrypi/pico-sdk/issues/1222, since Wifi chip and
// VSYS ADC both share GPIO29
// VSYS ADC both share GPIO29.
// The USE_WIFI guard is required because CYW43_USES_VSYS_PIN can be defined
// transitively (e.g. via lwip_wrap.h) even on non-WiFi boards where the CYW43
// driver is never initialized; calling cyw43_thread_enter() there hard-faults.
cyw43_thread_enter();
}
#endif // CYW43_USES_VSYS_PIN
#endif // defined(CYW43_USES_VSYS_PIN) && defined(USE_WIFI)
adc_gpio_init(pin);
adc_select_input(pin - 26);
@@ -84,11 +87,11 @@ float ADCSensor::sample() {
aggr.add_sample(raw);
}
#ifdef CYW43_USES_VSYS_PIN
#if defined(CYW43_USES_VSYS_PIN) && defined(USE_WIFI)
if (pin == PICO_VSYS_PIN) {
cyw43_thread_exit();
}
#endif // CYW43_USES_VSYS_PIN
#endif // defined(CYW43_USES_VSYS_PIN) && defined(USE_WIFI)
if (this->output_raw_) {
return aggr.aggregate();
+3 -3
View File
@@ -6,9 +6,9 @@
namespace esphome::adc128s102 {
class ADC128S102 : public Component,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING,
spi::DATA_RATE_10MHZ> {
class ADC128S102 final : public Component,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_10MHZ> {
public:
ADC128S102() = default;
@@ -9,10 +9,10 @@
namespace esphome::adc128s102 {
class ADC128S102Sensor : public PollingComponent,
public Parented<ADC128S102>,
public sensor::Sensor,
public voltage_sampler::VoltageSampler {
class ADC128S102Sensor final : public PollingComponent,
public Parented<ADC128S102>,
public sensor::Sensor,
public voltage_sampler::VoltageSampler {
public:
ADC128S102Sensor(uint8_t channel);
@@ -9,7 +9,7 @@
namespace esphome::addressable_light {
class AddressableLightDisplay : public display::DisplayBuffer {
class AddressableLightDisplay final : public display::DisplayBuffer {
public:
light::AddressableLight *get_light() const { return this->light_; }
+1 -1
View File
@@ -65,7 +65,7 @@ struct ADE7880Store {
static void gpio_intr(ADE7880Store *arg);
};
class ADE7880 : public i2c::I2CDevice, public PollingComponent {
class ADE7880 final : public i2c::I2CDevice, public PollingComponent {
public:
void set_irq0_pin(InternalGPIOPin *pin) { this->irq0_pin_ = pin; }
void set_irq1_pin(InternalGPIOPin *pin) { this->irq1_pin_ = pin; }
+1 -1
View File
@@ -10,7 +10,7 @@
namespace esphome::ade7953_i2c {
class AdE7953I2c : public ade7953_base::ADE7953, public i2c::I2CDevice {
class AdE7953I2c final : public ade7953_base::ADE7953, public i2c::I2CDevice {
public:
void dump_config() override;
+1 -1
View File
@@ -43,7 +43,7 @@ enum ADS1115Samplerate {
ADS1115_860SPS = 0b111
};
class ADS1115Component : public Component, public i2c::I2CDevice {
class ADS1115Component final : public Component, public i2c::I2CDevice {
public:
void setup() override;
void dump_config() override;
@@ -11,10 +11,10 @@
namespace esphome::ads1115 {
/// Internal holder class that is in instance of Sensor so that the hub can create individual sensors.
class ADS1115Sensor : public sensor::Sensor,
public PollingComponent,
public voltage_sampler::VoltageSampler,
public Parented<ADS1115Component> {
class ADS1115Sensor final : public sensor::Sensor,
public PollingComponent,
public voltage_sampler::VoltageSampler,
public Parented<ADS1115Component> {
public:
void update() override;
void set_multiplexer(ADS1115Multiplexer multiplexer) { this->multiplexer_ = multiplexer; }
+3 -3
View File
@@ -26,9 +26,9 @@ enum ADS1118Gain {
ADS1118_GAIN_0P256 = 0b101,
};
class ADS1118 : public Component,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_TRAILING,
spi::DATA_RATE_1MHZ> {
class ADS1118 final : public Component,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
spi::CLOCK_PHASE_TRAILING, spi::DATA_RATE_1MHZ> {
public:
ADS1118() = default;
void setup() override;
@@ -10,10 +10,10 @@
namespace esphome::ads1118 {
class ADS1118Sensor : public PollingComponent,
public sensor::Sensor,
public voltage_sampler::VoltageSampler,
public Parented<ADS1118> {
class ADS1118Sensor final : public PollingComponent,
public sensor::Sensor,
public voltage_sampler::VoltageSampler,
public Parented<ADS1118> {
public:
void update() override;
+3 -3
View File
@@ -7,7 +7,7 @@
namespace esphome::ags10 {
class AGS10Component : public PollingComponent, public i2c::I2CDevice {
class AGS10Component final : public PollingComponent, public i2c::I2CDevice {
public:
/**
* Sets TVOC sensor.
@@ -100,7 +100,7 @@ class AGS10Component : public PollingComponent, public i2c::I2CDevice {
template<size_t N> optional<std::array<uint8_t, N>> read_and_check_(uint8_t a_register);
};
template<typename... Ts> class AGS10NewI2cAddressAction : public Action<Ts...>, public Parented<AGS10Component> {
template<typename... Ts> class AGS10NewI2cAddressAction final : public Action<Ts...>, public Parented<AGS10Component> {
public:
TEMPLATABLE_VALUE(uint8_t, new_address)
@@ -116,7 +116,7 @@ enum AGS10SetZeroPointActionMode {
CUSTOM_VALUE,
};
template<typename... Ts> class AGS10SetZeroPointAction : public Action<Ts...>, public Parented<AGS10Component> {
template<typename... Ts> class AGS10SetZeroPointAction final : public Action<Ts...>, public Parented<AGS10Component> {
public:
TEMPLATABLE_VALUE(uint16_t, value)
TEMPLATABLE_VALUE(AGS10SetZeroPointActionMode, mode)
+1 -1
View File
@@ -10,7 +10,7 @@ namespace esphome::aht10 {
enum AHT10Variant { AHT10, AHT20 };
class AHT10Component : public PollingComponent, public i2c::I2CDevice {
class AHT10Component final : public PollingComponent, public i2c::I2CDevice {
public:
void setup() override;
void update() override;
+1 -1
View File
@@ -61,7 +61,7 @@ static const uint8_t AIC3204_ADC_PTM = 0x3D; // Register 61 - ADC Power Tu
static const uint8_t AIC3204_AN_IN_CHRG = 0x47; // Register 71 - Analog Input Quick Charging Config
static const uint8_t AIC3204_REF_STARTUP = 0x7B; // Register 123 - Reference Power Up Config
class AIC3204 : public audio_dac::AudioDac, public Component, public i2c::I2CDevice {
class AIC3204 final : public audio_dac::AudioDac, public Component, public i2c::I2CDevice {
public:
void setup() override;
void dump_config() override;
+1 -1
View File
@@ -6,7 +6,7 @@
namespace esphome::aic3204 {
template<typename... Ts> class SetAutoMuteAction : public Action<Ts...> {
template<typename... Ts> class SetAutoMuteAction final : public Action<Ts...> {
public:
explicit SetAutoMuteAction(AIC3204 *aic3204) : aic3204_(aic3204) {}
@@ -7,7 +7,7 @@
namespace esphome::airthings_ble {
class AirthingsListener : public esp32_ble_tracker::ESPBTDeviceListener {
class AirthingsListener final : public esp32_ble_tracker::ESPBTDeviceListener {
public:
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
};
@@ -12,7 +12,7 @@ static const char *const SERVICE_UUID = "b42e3882-ade7-11e4-89d3-123b93f75cba";
static const char *const CHARACTERISTIC_UUID = "b42e3b98-ade7-11e4-89d3-123b93f75cba";
static const char *const ACCESS_CONTROL_POINT_CHARACTERISTIC_UUID = "b42e3ef4-ade7-11e4-89d3-123b93f75cba";
class AirthingsWaveMini : public airthings_wave_base::AirthingsWaveBase {
class AirthingsWaveMini final : public airthings_wave_base::AirthingsWaveBase {
public:
AirthingsWaveMini();
@@ -19,7 +19,7 @@ static const char *const CHARACTERISTIC_UUID_WAVE_RADON_GEN2 = "b42e4dcc-ade7-11
static const char *const ACCESS_CONTROL_POINT_CHARACTERISTIC_UUID_WAVE_RADON_GEN2 =
"b42e50d8-ade7-11e4-89d3-123b93f75cba";
class AirthingsWavePlus : public airthings_wave_base::AirthingsWaveBase {
class AirthingsWavePlus final : public airthings_wave_base::AirthingsWaveBase {
public:
void setup() override;
@@ -27,7 +27,7 @@ static_assert(std::is_trivially_copyable_v<StateAnyForwarder>);
static_assert(sizeof(StateEnterForwarder<ACP_STATE_TRIGGERED>) <= sizeof(void *));
static_assert(std::is_trivially_copyable_v<StateEnterForwarder<ACP_STATE_TRIGGERED>>);
template<typename... Ts> class ArmAwayAction : public Action<Ts...> {
template<typename... Ts> class ArmAwayAction final : public Action<Ts...> {
public:
explicit ArmAwayAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
@@ -39,7 +39,7 @@ template<typename... Ts> class ArmAwayAction : public Action<Ts...> {
AlarmControlPanel *alarm_control_panel_;
};
template<typename... Ts> class ArmHomeAction : public Action<Ts...> {
template<typename... Ts> class ArmHomeAction final : public Action<Ts...> {
public:
explicit ArmHomeAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
@@ -51,7 +51,7 @@ template<typename... Ts> class ArmHomeAction : public Action<Ts...> {
AlarmControlPanel *alarm_control_panel_;
};
template<typename... Ts> class ArmNightAction : public Action<Ts...> {
template<typename... Ts> class ArmNightAction final : public Action<Ts...> {
public:
explicit ArmNightAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
@@ -63,7 +63,7 @@ template<typename... Ts> class ArmNightAction : public Action<Ts...> {
AlarmControlPanel *alarm_control_panel_;
};
template<typename... Ts> class DisarmAction : public Action<Ts...> {
template<typename... Ts> class DisarmAction final : public Action<Ts...> {
public:
explicit DisarmAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
@@ -75,7 +75,7 @@ template<typename... Ts> class DisarmAction : public Action<Ts...> {
AlarmControlPanel *alarm_control_panel_;
};
template<typename... Ts> class PendingAction : public Action<Ts...> {
template<typename... Ts> class PendingAction final : public Action<Ts...> {
public:
explicit PendingAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
@@ -85,7 +85,7 @@ template<typename... Ts> class PendingAction : public Action<Ts...> {
AlarmControlPanel *alarm_control_panel_;
};
template<typename... Ts> class TriggeredAction : public Action<Ts...> {
template<typename... Ts> class TriggeredAction final : public Action<Ts...> {
public:
explicit TriggeredAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
@@ -95,7 +95,7 @@ template<typename... Ts> class TriggeredAction : public Action<Ts...> {
AlarmControlPanel *alarm_control_panel_;
};
template<typename... Ts> class AlarmControlPanelCondition : public Condition<Ts...> {
template<typename... Ts> class AlarmControlPanelCondition final : public Condition<Ts...> {
public:
AlarmControlPanelCondition(AlarmControlPanel *parent) : parent_(parent) {}
bool check(const Ts &...x) override {
+1 -1
View File
@@ -31,7 +31,7 @@ static const int16_t GENI_RESPONSE_POWER_OFFSET = 12;
static const int16_t GENI_RESPONSE_MOTOR_POWER_OFFSET = 16; // not sure
static const int16_t GENI_RESPONSE_MOTOR_SPEED_OFFSET = 20;
class Alpha3 : public esphome::ble_client::BLEClientNode, public PollingComponent {
class Alpha3 final : public esphome::ble_client::BLEClientNode, public PollingComponent {
public:
void setup() override;
void update() override;
+1 -1
View File
@@ -27,7 +27,7 @@
namespace esphome::am2315c {
class AM2315C : public PollingComponent, public i2c::I2CDevice {
class AM2315C final : public PollingComponent, public i2c::I2CDevice {
public:
void dump_config() override;
void update() override;
+1 -1
View File
@@ -6,7 +6,7 @@
namespace esphome::am2320 {
class AM2320Component : public PollingComponent, public i2c::I2CDevice {
class AM2320Component final : public PollingComponent, public i2c::I2CDevice {
public:
void setup() override;
void dump_config() override;
+5 -5
View File
@@ -114,13 +114,13 @@ void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
this->decoder_->decode(param->notify.value, param->notify.value_len);
if (this->decoder_->has_position()) {
this->position = ((float) this->decoder_->position_ / 100.0);
this->position = ((float) this->decoder_->position_ / 100.0f);
if (!this->invert_position_)
this->position = 1 - this->position;
if (this->position > 0.97)
this->position = 1.0;
if (this->position < 0.02)
this->position = 0.0;
if (this->position > 0.97f)
this->position = 1.0f;
if (this->position < 0.02f)
this->position = 0.0f;
this->publish_state();
}
+1 -1
View File
@@ -14,7 +14,7 @@ namespace esphome::am43 {
namespace espbt = esphome::esp32_ble_tracker;
class Am43Component : public cover::Cover, public esphome::ble_client::BLEClientNode, public Component {
class Am43Component final : public cover::Cover, public esphome::ble_client::BLEClientNode, public Component {
public:
void setup() override;
void loop() override;
+1 -1
View File
@@ -14,7 +14,7 @@ namespace esphome::am43 {
namespace espbt = esphome::esp32_ble_tracker;
class Am43 : public esphome::ble_client::BLEClientNode, public PollingComponent {
class Am43 final : public esphome::ble_client::BLEClientNode, public PollingComponent {
public:
void setup() override;
void update() override;
@@ -7,7 +7,7 @@
namespace esphome::analog_threshold {
class AnalogThresholdBinarySensor : public Component, public binary_sensor::BinarySensor {
class AnalogThresholdBinarySensor final : public Component, public binary_sensor::BinarySensor {
public:
void dump_config() override;
void setup() override;
+4 -4
View File
@@ -5,7 +5,7 @@
namespace esphome::animation {
class Animation : public image::Image {
class Animation final : public image::Image {
public:
Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, image::ImageType type,
image::Transparency transparent);
@@ -35,7 +35,7 @@ class Animation : public image::Image {
int loop_current_iteration_;
};
template<typename... Ts> class AnimationNextFrameAction : public Action<Ts...> {
template<typename... Ts> class AnimationNextFrameAction final : public Action<Ts...> {
public:
AnimationNextFrameAction(Animation *parent) : parent_(parent) {}
void play(const Ts &...x) override { this->parent_->next_frame(); }
@@ -44,7 +44,7 @@ template<typename... Ts> class AnimationNextFrameAction : public Action<Ts...> {
Animation *parent_;
};
template<typename... Ts> class AnimationPrevFrameAction : public Action<Ts...> {
template<typename... Ts> class AnimationPrevFrameAction final : public Action<Ts...> {
public:
AnimationPrevFrameAction(Animation *parent) : parent_(parent) {}
void play(const Ts &...x) override { this->parent_->prev_frame(); }
@@ -53,7 +53,7 @@ template<typename... Ts> class AnimationPrevFrameAction : public Action<Ts...> {
Animation *parent_;
};
template<typename... Ts> class AnimationSetFrameAction : public Action<Ts...> {
template<typename... Ts> class AnimationSetFrameAction final : public Action<Ts...> {
public:
AnimationSetFrameAction(Animation *parent) : parent_(parent) {}
TEMPLATABLE_VALUE(uint16_t, frame)
+1 -1
View File
@@ -17,7 +17,7 @@ namespace espbt = esphome::esp32_ble_tracker;
static const uint16_t ANOVA_SERVICE_UUID = 0xFFE0;
static const uint16_t ANOVA_CHARACTERISTIC_UUID = 0xFFE1;
class Anova : public climate::Climate, public esphome::ble_client::BLEClientNode, public PollingComponent {
class Anova final : public climate::Climate, public esphome::ble_client::BLEClientNode, public PollingComponent {
public:
void setup() override;
void loop() override;
+2 -2
View File
@@ -6,9 +6,9 @@
namespace esphome::anova {
float ftoc(float f) { return (f - 32.0) * (5.0f / 9.0f); }
float ftoc(float f) { return (f - 32.0f) * (5.0f / 9.0f); }
float ctof(float c) { return (c * 9.0f / 5.0f) + 32.0; }
float ctof(float c) { return (c * 9.0f / 5.0f) + 32.0f; }
AnovaPacket *AnovaCodec::clean_packet_() {
this->packet_.length = strlen((char *) this->packet_.data);
+1 -1
View File
@@ -39,7 +39,7 @@ enum AmbientLightGain : uint8_t {
};
static const uint8_t AMBIENT_LIGHT_GAIN_VALUES[] = {1, 3, 6, 9, 18};
class APDS9306 : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice {
class APDS9306 final : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice {
public:
void setup() override;
float get_setup_priority() const override { return setup_priority::BUS; }
+1 -1
View File
@@ -12,7 +12,7 @@
namespace esphome::apds9960 {
class APDS9960 : public PollingComponent, public i2c::I2CDevice {
class APDS9960 final : public PollingComponent, public i2c::I2CDevice {
#ifdef USE_SENSOR
SUB_SENSOR(red)
SUB_SENSOR(green)
+2
View File
@@ -305,6 +305,7 @@ CONFIG_SCHEMA = cv.All(
rtl87xx=4, # Moderate RAM, BSD-style sockets
host=4, # Abundant resources
ln882x=4, # Moderate RAM
nrf52=4, # ~256KB RAM, BSD sockets
): cv.int_range(min=1, max=10),
cv.SplitDefault(
CONF_MAX_CONNECTIONS,
@@ -315,6 +316,7 @@ CONFIG_SCHEMA = cv.All(
rtl87xx=5, # Moderate RAM
host=8, # Abundant resources
ln882x=5, # Moderate RAM
nrf52=4, # ~256KB RAM, BSD sockets, Thread (single HA controller)
): cv.int_range(min=1, max=20),
# Maximum queued send buffers per connection before dropping connection
# Each buffer uses ~8-12 bytes overhead plus actual message size
+1 -11
View File
@@ -375,7 +375,7 @@ void APIConnection::finalize_iterator_sync_() {
void APIConnection::process_iterator_batch_(ComponentIterator &iterator) {
size_t initial_size = this->deferred_batch_.size();
size_t max_batch = this->get_max_batch_size_();
size_t max_batch = MAX_INITIAL_PER_BATCH;
while (!iterator.completed() && (this->deferred_batch_.size() - initial_size) < max_batch) {
iterator.advance();
}
@@ -418,16 +418,6 @@ uint16_t APIConnection::fill_and_encode_entity_info(EntityBase *entity, InfoResp
// Set common fields that are shared by all entity types
msg.key = entity->get_object_id_hash();
// API 1.14+ clients compute object_id client-side from the entity name
// For older clients, we must send object_id for backward compatibility
// See: https://github.com/esphome/backlog/issues/76
// TODO: Remove this backward compat code before 2026.7.0 - all clients should support API 1.14 by then
// Buffer must remain in scope until encode_to_buffer is called
char object_id_buf[OBJECT_ID_MAX_LEN];
if (!conn->client_supports_api_version(1, 14)) {
msg.object_id = entity->get_object_id_to(object_id_buf);
}
if (entity->has_own_name()) {
msg.name = entity->get_name();
}
+1 -11
View File
@@ -43,10 +43,7 @@ class APIServer;
// Keepalive timeout in milliseconds
static constexpr uint32_t KEEPALIVE_TIMEOUT_MS = 60000;
// Maximum number of entities to process in a single batch during initial state/info sending
// API 1.14+ clients compute object_id client-side, so messages are smaller and we can fit more per batch
// TODO: Remove MAX_INITIAL_PER_BATCH_LEGACY before 2026.7.0 - all clients should support API 1.14 by then
static constexpr size_t MAX_INITIAL_PER_BATCH_LEGACY = 24; // For clients < API 1.14 (includes object_id)
static constexpr size_t MAX_INITIAL_PER_BATCH = 34; // For clients >= API 1.14 (no object_id)
static constexpr size_t MAX_INITIAL_PER_BATCH = 34;
// Verify MAX_MESSAGES_PER_BATCH (defined in api_frame_helper.h) can hold the initial batch
static_assert(MAX_MESSAGES_PER_BATCH >= MAX_INITIAL_PER_BATCH,
"MAX_MESSAGES_PER_BATCH must be >= MAX_INITIAL_PER_BATCH");
@@ -481,13 +478,6 @@ class APIConnection final : public APIServerConnectionBase {
inline bool check_voice_assistant_api_connection_() const;
#endif
// Get the max batch size based on client API version
// API 1.14+ clients don't receive object_id, so messages are smaller and more fit per batch
// TODO: Remove this method before 2026.7.0 and use MAX_INITIAL_PER_BATCH directly
size_t get_max_batch_size_() const {
return this->client_supports_api_version(1, 14) ? MAX_INITIAL_PER_BATCH : MAX_INITIAL_PER_BATCH_LEGACY;
}
// Send keepalive ping or disconnect unresponsive client.
// Cold path — extracted from loop() to reduce instruction cache pressure.
void __attribute__((noinline)) check_keepalive_(uint32_t now);
@@ -31,6 +31,13 @@
#include <vector>
#include <string>
#if defined(LOG_LEVEL_NONE)
// Zephyr defines LOG_LEVEL_NONE as a logging macro that collides with the LogLevel enum value of
// the same name in the generated api_pb2.h. Undefine it for the rest of this translation unit so
// the enum parses; nothing below needs Zephyr's logging macro.
#undef LOG_LEVEL_NONE
#endif
namespace esphome::api {
// This file only provides includes, no actual code
+1 -1
View File
@@ -342,7 +342,7 @@ class APIServer final : public Component,
extern APIServer *global_api_server; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
template<typename... Ts> class APIConnectedCondition : public Condition<Ts...> {
template<typename... Ts> class APIConnectedCondition final : public Condition<Ts...> {
TEMPLATABLE_VALUE(bool, state_subscription_only)
public:
bool check(const Ts &...x) override {
@@ -104,7 +104,7 @@ class ActionResponse {
template<typename... Ts> using ActionResponseCallback = std::function<void(const ActionResponse &, Ts...)>;
#endif
template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts...> {
template<typename... Ts> class HomeAssistantServiceCallAction final : public Action<Ts...> {
public:
explicit HomeAssistantServiceCallAction(APIServer *parent, bool is_event) : parent_(parent) {
this->flags_.is_event = is_event;
+10 -9
View File
@@ -164,7 +164,8 @@ template<enums::SupportsResponseType Mode, typename... Ts> class UserServiceTrig
// Specialization for NONE - no extra trigger arguments
template<typename... Ts>
class UserServiceTrigger<enums::SUPPORTS_RESPONSE_NONE, Ts...> : public UserServiceBase<Ts...>, public Trigger<Ts...> {
class UserServiceTrigger<enums::SUPPORTS_RESPONSE_NONE, Ts...> final : public UserServiceBase<Ts...>,
public Trigger<Ts...> {
public:
UserServiceTrigger(const char *name, const std::array<const char *, sizeof...(Ts)> &arg_names)
: UserServiceBase<Ts...>(name, arg_names, enums::SUPPORTS_RESPONSE_NONE) {}
@@ -175,8 +176,8 @@ class UserServiceTrigger<enums::SUPPORTS_RESPONSE_NONE, Ts...> : public UserServ
// Specialization for OPTIONAL - call_id and return_response trigger arguments
template<typename... Ts>
class UserServiceTrigger<enums::SUPPORTS_RESPONSE_OPTIONAL, Ts...> : public UserServiceBase<Ts...>,
public Trigger<uint32_t, bool, Ts...> {
class UserServiceTrigger<enums::SUPPORTS_RESPONSE_OPTIONAL, Ts...> final : public UserServiceBase<Ts...>,
public Trigger<uint32_t, bool, Ts...> {
public:
UserServiceTrigger(const char *name, const std::array<const char *, sizeof...(Ts)> &arg_names)
: UserServiceBase<Ts...>(name, arg_names, enums::SUPPORTS_RESPONSE_OPTIONAL) {}
@@ -189,8 +190,8 @@ class UserServiceTrigger<enums::SUPPORTS_RESPONSE_OPTIONAL, Ts...> : public User
// Specialization for ONLY - just call_id trigger argument
template<typename... Ts>
class UserServiceTrigger<enums::SUPPORTS_RESPONSE_ONLY, Ts...> : public UserServiceBase<Ts...>,
public Trigger<uint32_t, Ts...> {
class UserServiceTrigger<enums::SUPPORTS_RESPONSE_ONLY, Ts...> final : public UserServiceBase<Ts...>,
public Trigger<uint32_t, Ts...> {
public:
UserServiceTrigger(const char *name, const std::array<const char *, sizeof...(Ts)> &arg_names)
: UserServiceBase<Ts...>(name, arg_names, enums::SUPPORTS_RESPONSE_ONLY) {}
@@ -201,8 +202,8 @@ class UserServiceTrigger<enums::SUPPORTS_RESPONSE_ONLY, Ts...> : public UserServ
// Specialization for STATUS - just call_id trigger argument (reports success/error without data)
template<typename... Ts>
class UserServiceTrigger<enums::SUPPORTS_RESPONSE_STATUS, Ts...> : public UserServiceBase<Ts...>,
public Trigger<uint32_t, Ts...> {
class UserServiceTrigger<enums::SUPPORTS_RESPONSE_STATUS, Ts...> final : public UserServiceBase<Ts...>,
public Trigger<uint32_t, Ts...> {
public:
UserServiceTrigger(const char *name, const std::array<const char *, sizeof...(Ts)> &arg_names)
: UserServiceBase<Ts...>(name, arg_names, enums::SUPPORTS_RESPONSE_STATUS) {}
@@ -221,7 +222,7 @@ class UserServiceTrigger<enums::SUPPORTS_RESPONSE_STATUS, Ts...> : public UserSe
namespace esphome::api {
template<typename... Ts> class APIRespondAction : public Action<Ts...> {
template<typename... Ts> class APIRespondAction final : public Action<Ts...> {
public:
explicit APIRespondAction(APIServer *parent) : parent_(parent) {}
@@ -286,7 +287,7 @@ template<typename... Ts> class APIRespondAction : public Action<Ts...> {
// Action to unregister a service call after execution completes
// Automatically appended to the end of action lists for non-none response modes
template<typename... Ts> class APIUnregisterServiceCallAction : public Action<Ts...> {
template<typename... Ts> class APIUnregisterServiceCallAction final : public Action<Ts...> {
public:
explicit APIUnregisterServiceCallAction(APIServer *parent) : parent_(parent) {}
+1 -1
View File
@@ -6,7 +6,7 @@
namespace esphome::aqi {
class AQISensor : public sensor::Sensor, public Component {
class AQISensor final : public sensor::Sensor, public Component {
public:
void setup() override;
void dump_config() override;
+1 -1
View File
@@ -5,7 +5,7 @@
namespace esphome::as3935_i2c {
class I2CAS3935Component : public as3935::AS3935Component, public i2c::I2CDevice {
class I2CAS3935Component final : public as3935::AS3935Component, public i2c::I2CDevice {
public:
void dump_config() override;
+3 -3
View File
@@ -8,9 +8,9 @@ namespace esphome::as3935_spi {
enum AS3935RegisterMasks { SPI_READ_M = 0x40 };
class SPIAS3935Component : public as3935::AS3935Component,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_2MHZ> {
class SPIAS3935Component final : public as3935::AS3935Component,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_2MHZ> {
public:
void setup() override;
void dump_config() override;
+1 -1
View File
@@ -43,7 +43,7 @@ enum AS5600MagnetStatus : uint8_t {
MAGNET_WEAK = 6, // 0b110 / magnet too weak
};
class AS5600Component : public Component, public i2c::I2CDevice {
class AS5600Component final : public Component, public i2c::I2CDevice {
public:
/// Set up the internal sensor array.
void setup() override;
@@ -9,7 +9,7 @@
namespace esphome::as5600 {
class AS5600Sensor : public PollingComponent, public Parented<AS5600Component>, public sensor::Sensor {
class AS5600Sensor final : public PollingComponent, public Parented<AS5600Component>, public sensor::Sensor {
public:
void update() override;
void dump_config() override;
+1 -1
View File
@@ -73,7 +73,7 @@ enum AS7341Gain {
AS7341_GAIN_512X,
};
class AS7341Component : public PollingComponent, public i2c::I2CDevice {
class AS7341Component final : public PollingComponent, public i2c::I2CDevice {
public:
void setup() override;
void dump_config() override;
+1 -1
View File
@@ -12,7 +12,7 @@
namespace esphome::at581x {
class AT581XComponent : public Component, public i2c::I2CDevice {
class AT581XComponent final : public Component, public i2c::I2CDevice {
public:
#ifdef USE_SWITCH
void set_rf_power_switch(switch_::Switch *s) {
+2 -2
View File
@@ -7,12 +7,12 @@
namespace esphome::at581x {
template<typename... Ts> class AT581XResetAction : public Action<Ts...>, public Parented<AT581XComponent> {
template<typename... Ts> class AT581XResetAction final : public Action<Ts...>, public Parented<AT581XComponent> {
public:
void play(const Ts &...x) { this->parent_->reset_hardware_frontend(); }
};
template<typename... Ts> class AT581XSettingsAction : public Action<Ts...>, public Parented<AT581XComponent> {
template<typename... Ts> class AT581XSettingsAction final : public Action<Ts...>, public Parented<AT581XComponent> {
public:
TEMPLATABLE_VALUE(int8_t, hw_frontend_reset)
TEMPLATABLE_VALUE(int, frequency)
+1 -1
View File
@@ -5,7 +5,7 @@
namespace esphome::at581x {
class RFSwitch : public switch_::Switch, public Parented<AT581XComponent> {
class RFSwitch final : public switch_::Switch, public Parented<AT581XComponent> {
protected:
void write_state(bool state) override;
};
@@ -18,7 +18,7 @@ struct ParseResult {
int raw_offset;
};
class ATCMiThermometer : public Component, public esp32_ble_tracker::ESPBTDeviceListener {
class ATCMiThermometer final : public Component, public esp32_ble_tracker::ESPBTDeviceListener {
public:
void set_address(uint64_t address) { address_ = address; };

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