Compare commits

...

513 Commits

Author SHA1 Message Date
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
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
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
Jesse Hills
9534ab2a19 Merge pull request #17052 from esphome/bump-2026.6.1
2026.6.1
2026-06-19 11:35:03 +12:00
Jesse Hills
1b1c8d767d Bump version to 2026.6.1 2026-06-19 10:06:13 +12:00
esphome[bot]
e3d68deef9 Bump bundled esphome-device-builder to 1.0.10 (#17051) 2026-06-19 10:06:13 +12:00
J. Nick Koston
20cd6a1771 [logger] Hold recursion guard while draining the task log buffer (#17044) 2026-06-19 10:06:13 +12:00
Jonathan Swoboda
d27229a1c7 [esp32] Don't overwrite PlatformIO's factory.bin (#17042) 2026-06-19 10:06:13 +12:00
Jonathan Swoboda
129aebe8f4 [esp32] Support esphome idedata with the native ESP-IDF toolchain (#17040) 2026-06-19 10:06:13 +12:00
Jonathan Swoboda
a84ad7b1f8 [uptime] Revert timestamp sensor device_class to timestamp (#17037) 2026-06-19 10:06:13 +12:00
Jonathan Swoboda
86096b96f5 [build] Skip target-platform deps when populating host unit-test config (#17039) 2026-06-19 10:06:13 +12:00
J. Nick Koston
ac5a28301a [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-19 10:06:13 +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
Jesse Hills
e2157a3d26 Merge pull request #17022 from esphome/bump-2026.6.0
2026.6.0
2026-06-18 12:59:50 +12:00
esphome[bot]
d934fb3910 Bump bundled esphome-device-builder to 1.0.9 (#17021)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-06-18 10:46:22 +12:00
esphome[bot]
c4076ec8a9 Bump bundled esphome-device-builder to 1.0.8 (#17020) 2026-06-18 10:46:12 +12:00
esphome[bot]
9ac22f9244 Bump bundled esphome-device-builder to 1.0.7 (#17018) 2026-06-18 10:46:06 +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
9e7b3e0330 Bump version to 2026.6.0 2026-06-18 10:18:37 +12:00
Jesse Hills
084398436d Merge branch 'beta' into dev 2026-06-18 10:08:51 +12:00
Jesse Hills
2abe272867 Merge pull request #17017 from esphome/bump-2026.6.0b4
2026.6.0b4
2026-06-18 10:08:15 +12:00
esphome[bot]
6d9490b5a3 Bump bundled esphome-device-builder to 1.0.7 (#17018) 2026-06-17 15:53:10 -05:00
Jesse Hills
db6b9166f4 Bump version to 2026.6.0b4 2026-06-18 08:20:15 +12:00
esphome[bot]
7ab95ddcb1 Bump bundled esphome-device-builder to 1.0.6 (#17016) 2026-06-18 08:20:02 +12:00
esphome[bot]
cdd2bfbc60 Bump bundled esphome-device-builder to 1.0.4 (#17013) 2026-06-18 08:19:05 +12:00
esphome[bot]
41f7f8cccb Bump bundled esphome-device-builder to 1.0.3 (#17005) 2026-06-18 08:19:05 +12:00
Jonathan Swoboda
045de436ba [ota] Scale ESP-IDF OTA erase watchdog to image size (#16998) 2026-06-18 08:19:05 +12:00
Jonathan Swoboda
24e276c3f9 [esp32_hosted] Bump esp_hosted to 2.12.9 (#16999) 2026-06-18 08:18:25 +12: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
9e768bb510 Merge pull request #16997 from esphome/bump-2026.6.0b3
2026.6.0b3
2026-06-16 23:52:22 +12:00
Jesse Hills
53fd99578a Bump version to 2026.6.0b3 2026-06-16 23:02:55 +12:00
Jesse Hills
310baab524 [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 23:02:55 +12:00
Jesse Hills
0422b581cb [core] Stop parent git repos from breaking ESP-IDF/PlatformIO builds (#16994) 2026-06-16 23:02:55 +12:00
Jesse Hills
0ce89c17ab [ci] Push branch-tagged docker images to ghcr.io for local testing (#16992) 2026-06-16 23:02:55 +12:00
Jesse Hills
66be793cd8 [docker] Remove alpine base, build only on debian (#16991) 2026-06-16 23:02:54 +12:00
Jonathan Swoboda
1d38498ca7 [openthread] Fix InstanceLock releasing the lock twice on try_acquire (#16980) 2026-06-16 23:02:54 +12:00
Kevin Ahrendt
aef9b5b72f [audio] Bump microMP3 to v0.2.3 (#16977) 2026-06-16 23:02:54 +12:00
J. Nick Koston
9bf35ab8fb [core] Attribute "took a long time" blocking warning to the owning script (#16768) 2026-06-16 23:02:54 +12:00
Clyde Stubbs
33ace9d698 [mipi_dsi] Add SWRESET command to M5Stack Tab5-V2 init sequence (#16975) 2026-06-16 23:02:54 +12:00
Jesse Hills
32ab3abd7c [psram] Make schema extractable with per-variant options (#16949)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-06-16 23:02:54 +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
Jesse Hills
94b248527d Merge pull request #16948 from esphome/bump-2026.6.0b2
2026.6.0b2
2026-06-15 12:05:19 +12:00
Jesse Hills
a46aa594b3 Bump version to 2026.6.0b2 2026-06-15 11:04:46 +12:00
Jonathan Swoboda
99425e3a97 [esp32] Add flash_mode and flash_frequency config options (#16920)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2026-06-15 11:04:46 +12:00
Jonathan Swoboda
f83e3ad6a6 [core] Support platformio_options on the native ESP-IDF toolchain (#16917) 2026-06-15 11:04:46 +12:00
Jonathan Swoboda
c768e2eabc [esp32] Fix idedata generation failing on unset ESPHOME_ARDUINO (#16925) 2026-06-15 11:04:46 +12:00
Clyde Stubbs
9ffd350095 [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 11:04:46 +12:00
Clyde Stubbs
26ccaf70db [lvgl] Fix schema extraction (#16895)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 11:04:46 +12:00
Tobiasz Jakubowski
20925b3220 [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-15 11:04:46 +12:00
J. Nick Koston
83504d2de2 [esp8266] Decode crash handler PC and backtrace in logs (#16911) 2026-06-15 11:04:46 +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
acbb662316 Merge pull request #16912 from esphome/bump-2026.6.0b1
2026.6.0b1
2026-06-11 16:06:59 +12:00
Jesse Hills
abf6212a5a [tests] Mock target branch in memory-impact exclusion test (#16913) 2026-06-11 14:05:10 +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
4dbc5ce920 Bump version to 2026.6.0b1 2026-06-11 12:41:19 +12:00
Jesse Hills
e0b0c1e8d3 Bump version to 2026.7.0-dev 2026-06-11 12:41:19 +12:00
Keith Burzinski
92c82f3d25 [improv_serial] Report stopped state when Wi-Fi is disabled (#16904)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 10:26:50 +12:00
Kevin Ahrendt
77009cfafe [resampler] Allow resampler to passthrough bits per sample instead of converting (#16892)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-11 10:21:04 +12:00
dependabot[bot]
cd7e54dbf2 Bump cryptography from 48.0.0 to 48.0.1 (#16909)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-10 16:32:17 -04:00
Jonathan Swoboda
29a79b1373 [core] Make set_cpp_standard work on the native IDF toolchain (#16907) 2026-06-11 07:25:03 +12:00
Clyde Stubbs
dafc3560dd [tests] Isolate ESPHOME_LOG_STATES in main logs-states tests (#16905)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 07:44:58 -04:00
Clyde Stubbs
a25ac28ae5 [lsm6ds] Add motion platform for STMicro LSM6DS IMU (#16232)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2026-06-10 01:22:44 +00:00
Jonathan Swoboda
6809af3de0 [espidf] Warn when the install path is too long for Windows MAX_PATH (#16896) 2026-06-10 13:12:28 +12:00
Jonathan Swoboda
e16a877745 [platformio] De-duplicate non-ESP32 lib_deps into common:idf-component-libs (#16893) 2026-06-10 13:11:11 +12:00
Jonathan Swoboda
4963ddcb95 [espidf] Fix idedata generation on Windows (#16894) 2026-06-10 13:09:50 +12:00
Clyde Stubbs
4f62bb7171 [bmi270] Support Bosch BMI270 IMU (#16202)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-06-10 11:04:28 +10:00
Ricky Tsai
eb6d6eac7d [xdb401] XDB401 Pressure Sensor (#15108)
Co-authored-by: Ricky Tsai <ricky@rtnztech.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>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-06-09 19:58:02 -04:00
dependabot[bot]
7533835e04 Bump py7zr from 0.22.0 to 1.1.0 (#16901)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-09 16:03:31 -04:00
Oliver Kleinecke
2310b9e3fe [usb_uart] Add Prolific PL2303 USB-serial driver (#16885) 2026-06-10 04:27:37 +10:00
Tomáš Lohynský
8206df6e4e [dlms_meter] dlms_parser library (#15458)
Co-authored-by: PolarGoose <35307286+PolarGoose@users.noreply.github.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@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: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-09 08:57:13 -04:00
tomaszduda23
5faed9d5f5 [nrf52] native build - download toolchain and sdk in venv (#16388)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
Co-authored-by: Jonathan Swoboda <swoboda1337@users.noreply.github.com>
2026-06-09 07:04:51 -04:00
PolarGoose
25d656d468 [dsmr] Update dsmr_parser library to 1.9.0 (#16881) 2026-06-09 07:04:10 -04:00
Remco van Essen
cdc63f0fed [pcm5122] Add PCM5122 audio DAC component (#15709)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: kbx81 <kbx81x@gmail.com>
2026-06-09 01:33:15 -05:00
Clyde Stubbs
ddd21ba442 [mipi_spi] add WAVESHARE-ESP32-S3-TOUCH-AMOLED-2.16 (#16887) 2026-06-09 13:06:13 +10:00
Jonathan Swoboda
a32817207c [ade7880] Fix reverse active energy reading from reserved register (#16822) 2026-06-08 20:30:03 -04:00
Jonathan Swoboda
6e01f3fccd [heatpumpir] Bump tonia/HeatpumpIR to 1.0.42 (#16880) 2026-06-08 20:29:35 -04:00
dependabot[bot]
e0072ef4c5 Bump tornado from 6.5.6 to 6.5.7 (#16883)
Signed-off-by: dependabot[bot] <support@github.com>
2026-06-08 19:22:48 -05:00
dependabot[bot]
b21a69f07a Bump codecov/codecov-action from 6.0.1 to 7.0.0 (#16884)
Signed-off-by: dependabot[bot] <support@github.com>
2026-06-09 08:08:39 +12:00
J. Nick Koston
36e043debb [tests] Fail component test merge on conflicting duplicate IDs (#16849) 2026-06-08 12:49:25 -05:00
Kevin P. Fleming
54c73bf1bc [ade7880][airthings_wave_base] Remove kpfleming from CODEOWNERS (#16858) 2026-06-08 09:04:09 -04:00
J. Nick Koston
cbc3770b11 Include model-driven display schemas in the language schema dump (#16872) 2026-06-07 17:30:43 -05:00
Jonathan Swoboda
64fc09646c [esp32] Fix clang-tidy on ESP-IDF 6 (#16850) 2026-06-06 20:00:42 -04:00
Jonathan Swoboda
8400bab926 [esp32] Make no-default-board variant test explicit about platformio toolchain (#16847) 2026-06-06 19:58:25 -04:00
Clyde Stubbs
745db9f705 [motion] Implement hub component for IMUs (#16226)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-06 18:10:08 +10:00
Jonathan Swoboda
6996b7ed1c [ci] Add ESP32 Variants clang-tidy run (S3/P4/C6) (#16825) 2026-06-05 22:03:08 -04:00
Jonathan Swoboda
8aa4157574 [fastled_base] Use FastLED IDF component on ESP32 (#16804) 2026-06-05 21:01:29 -04:00
J. Nick Koston
2a4913713a Revert "[tests] Fail component test merge on conflicting duplicate IDs" (#16848) 2026-06-05 19:37:19 -05:00
J. Nick Koston
8f8a70b2be Exit nginx bypass placeholder cleanly on SIGTERM (#16845) 2026-06-05 17:58:50 -05:00
J. Nick Koston
70d9ab25f3 [tests] Fail component test merge on conflicting duplicate IDs (#16795) 2026-06-05 17:57:42 -05:00
Jonathan Swoboda
f18cf954ba [improv_serial] Fix build on ESP32-C5/P4 and simplify variant guards (#16833) 2026-06-05 17:30:26 -05:00
Jonathan Swoboda
85fd83288d [esp32_camera] Bump esp32-camera to 2.1.7 (#16846) 2026-06-05 17:29:33 -05:00
Clyde Stubbs
93334d4e60 [scripts] Fix build_language_schema (#16816)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-05 21:44:31 +00:00
i-am-no-magic
913b9f5ca4 [tuya] Fixed hysteresis bug for Tuya climate (#16832)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-06-05 17:37:40 -04:00
Kevin Ahrendt
b63e327ae3 [audio] Deprecate unused scale_audio_samples helper (#16831) 2026-06-05 17:22:03 -04:00
Jonathan Swoboda
77f644f576 [ci] Share a cached native ESP-IDF install across clang-tidy and build jobs (#16841) 2026-06-05 16:42:51 -04:00
Ross Tyler
4cb6f2c046 [qmp6988] fix publishing bogus zero values on i2c error (#16840) 2026-06-05 14:35:33 -04:00
Ross Tyler
2ab4399ae5 [qmp6988] fix false report of software reset error (#16843) 2026-06-05 14:31:17 -04:00
Jonathan Swoboda
aa11ddb333 [zigbee][openthread][esp32_hosted] Fix clang-tidy findings (#16838) 2026-06-05 14:22:55 -04:00
Jonathan Swoboda
2b581ecd3c [esp32] Bump platform to 55.03.39, Arduino to 3.3.9 (#16803) 2026-06-05 14:22:20 -04:00
Jonathan Swoboda
42cf421f5c [usb_uart] Fix clang-tidy findings (#16835) 2026-06-05 12:03:22 -04:00
Jonathan Swoboda
351b986896 [ci] Make ESP32 IDF the comprehensive clang-tidy pass (#16823)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-06-05 12:01:53 -04:00
Jonathan Swoboda
b0e1b94c45 [mipi_dsi][mipi_rgb][st7701s][rpi_dpi_rgb] Fix clang-tidy findings (#16837) 2026-06-05 11:56:48 -04:00
Jonathan Swoboda
80c84d6665 [usb_host][usb_cdc_acm][tinyusb] Fix clang-tidy findings (#16836) 2026-06-05 11:55:39 -04:00
J. Nick Koston
61bb1805b1 [api] Fix nullptr deref when client teardown reenters state dispatch (#16834) 2026-06-05 10:47:32 -05:00
Jesse Hills
cbd3aaa1e0 [ci] Add codecov.yml to enforce 100% patch coverage on PRs (#16827) 2026-06-05 19:40:18 +12:00
Oliver Kleinecke
e209a3fa91 [usb_uart] Add FTDI FT23XX USB UART driver (#14587)
Co-authored-by: Oliver Kleinecke <kleinecke.oliver@googlemail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
2026-06-05 13:57:05 +10:00
Jesse Hills
d72f119dd2 Merge branch 'release' into dev 2026-06-05 14:31:50 +12:00
Jesse Hills
0cd3734148 Merge pull request #16817 from esphome/bump-2026.5.3
2026.5.3
2026-06-05 14:31:04 +12:00
Jonathan Swoboda
ea3ac1ee96 [audio] Bump esp-audio-libs to v3.2.1 (#16818) 2026-06-04 20:02:22 -04:00
Jesse Hills
7f3feec3a3 Bump version to 2026.5.3 2026-06-05 11:11:36 +12:00
Jonathan Swoboda
bcf5606b31 [esp32_ble_server] Fix duplicate Device Information Service with string UUIDs (#16784) 2026-06-05 11:11:36 +12:00
Jonathan Swoboda
5662e1b7cd [rp2040] Fix lwipopts template load on Windows extended-length paths (#16783) 2026-06-05 11:11:35 +12:00
Jonathan Swoboda
375ecdfb2c [esp32][core] Restore ESP-IDF version on logs/upload fast path and clean build on framework change (#16770) 2026-06-05 11:11:25 +12:00
Jonathan Swoboda
a5b4a7cd51 [remote_base] Fix RC5 decoding at either receive polarity (#16767) 2026-06-05 11:07:33 +12:00
dependabot[bot]
772cae445f Bump github/codeql-action from 4.36.1 to 4.36.2 (#16808)
Signed-off-by: dependabot[bot] <support@github.com>
2026-06-04 17:30:29 -05:00
dependabot[bot]
ef64d27ed4 Bump ruff from 0.15.15 to 0.15.16 (#16807)
Signed-off-by: dependabot[bot] <support@github.com>
2026-06-04 17:30:18 -05:00
Jesse Hills
a8032054ea [light] Pass light reference into lambda light effect (#16815) 2026-06-05 10:28:50 +12:00
J. Nick Koston
9fbd4c38ae [i2s_audio] Move test bus into a shared package and give fixtures unique ids (#16793) 2026-06-04 17:28:32 -05:00
Jonathan Swoboda
82efa45187 [multiple] Avoid float-to-double promotion in math calls (#16812) 2026-06-04 18:16:18 -04:00
Jonathan Swoboda
d2c388f893 [ota][logger][esp32][internal_temperature] Fix clang-tidy findings surfaced by RISC-V analysis (#16811) 2026-06-04 18:01:00 -04:00
Jonathan Swoboda
5288767abf [clang-tidy] Add --exclude-grep to skip files by content (#16813) 2026-06-04 17:58:22 -04:00
Jonathan Swoboda
e2459a3923 [clang-tidy] Support RISC-V targets natively (#16809) 2026-06-04 17:57:56 -04:00
Kevin Ahrendt
419bde18b0 [audio] Bump esp-audio-libs to v3.2.0 (#16806) 2026-06-04 16:24:47 -04:00
Jonathan Swoboda
148a5ba68e [esp32] Run clang-tidy via the native ESP-IDF toolchain (#16748) 2026-06-04 11:47:42 -04:00
J. Nick Koston
c765e22622 [ci] Exclude device-builder slow e2e tests from downstream CI (#16801) 2026-06-04 10:39:54 -05:00
Clyde Stubbs
1734dc85d2 [const][animation][dfplayer] Extract CONF_LOOP to const (#16797) 2026-06-04 07:42:01 -04:00
Jonathan Swoboda
891ec33c94 [esp32] Deduplicate PlatformIO library conversion by resolving the batch together (#16756) 2026-06-04 07:39:17 -04:00
Jonathan Swoboda
ffaa31febc [clang-tidy] Hash idf_component.yml and trigger hash hook on more inputs (#16753)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-06-03 23:48:34 -04:00
Jonathan Swoboda
53d685f242 [mixer] Give mixer test its own speaker id to avoid CI grouping collision (#16792) 2026-06-03 23:37:29 -04:00
J. Nick Koston
3e562b9267 [ci] Fix memory impact build selecting unbuildable platform (#16788) 2026-06-03 22:30:36 -05:00
dependabot[bot]
d47f6b896e Bump astral-sh/setup-uv from 8.1.0 to 8.2.0 in /.github/actions/restore-python (#16790)
Signed-off-by: dependabot[bot] <support@github.com>
2026-06-03 22:06:59 -05:00
dependabot[bot]
a02b9c3796 Bump astral-sh/setup-uv from 8.1.0 to 8.2.0 (#16791)
Signed-off-by: dependabot[bot] <support@github.com>
2026-06-03 22:06:45 -05:00
Jesse Hills
93f25258ee [config] Add --no-defaults flag to config command (#16718) 2026-06-04 14:08:36 +12:00
Jonathan Swoboda
0d7d091e71 [esp32_ble_server] Fix duplicate Device Information Service with string UUIDs (#16784) 2026-06-04 09:46:17 +12:00
Jonathan Swoboda
0fcfd1e3d6 [rp2040] Fix lwipopts template load on Windows extended-length paths (#16783) 2026-06-04 09:46:08 +12:00
Jonathan Swoboda
74a1ff9fc7 [esp32][core] Restore ESP-IDF version on logs/upload fast path and clean build on framework change (#16770) 2026-06-04 09:46:01 +12:00
Jonathan Swoboda
78d8a93fff [remote_base] Fix RC5 decoding at either receive polarity (#16767) 2026-06-04 09:45:56 +12:00
Jon Little
92819d8658 [logger] Fix USB JTAG VFS symbols linked when logging is disabled (#15721)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-03 16:54:34 -04:00
Clyde Stubbs
7b8cbe2de1 [sdl] Add option to choose display screen (#16363) 2026-06-03 14:18:16 -04:00
Leonardo Rivera
3b0f669f47 [gree] Fix HEAT_COOL advertised when supports_heat is false; restrict YAN swing to vertical (#16199) 2026-06-03 13:38:00 -04:00
dependabot[bot]
87735d71a0 Bump actions/checkout from 6.0.2 to 6.0.3 (#16776)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-03 11:46:22 -04:00
dependabot[bot]
eba70dc193 Bump github/codeql-action from 4.36.0 to 4.36.1 (#16775)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-03 11:46:07 -04:00
dependabot[bot]
712ef2ec0e Bump esptool from 5.2.0 to 5.3.0 (#16774)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-03 11:45:52 -04:00
Clyde Stubbs
2009f6cc5f [lvgl] Fix indicator updates (#16780) 2026-06-03 11:43:48 -04:00
PolarGoose
89ddd34cb9 [dmsr] [breaking] Fix decryption that uses custom auth key. Add CRC to telegram sensor. Automatic hex string detection in equipment_id fields. Support EON Hungary smart meters (#16561) 2026-06-03 10:55:51 -04:00
Jonathan Swoboda
e4980713d1 [core] esphome clean wipes the whole build directory (#16772) 2026-06-03 07:35:23 -04:00
Clyde Stubbs
997ab11687 [lvgl][mipi_spi][mipi_rgb][mipi_dsi][display] Metadata (#16702) 2026-06-03 15:21:33 +10:00
Jesse Hills
792e1ff304 [i2c] Add basic host platform support (#14489)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-03 07:12:50 +12:00
Kevin Ahrendt
063770bcf4 [i2s_audio] Fix speaker DMA buffer sizing and validate bit depth at compile time (#16672) 2026-06-02 09:32:27 -04:00
Jesse Hills
6197282f1a Merge branch 'release' into dev 2026-06-02 15:40:28 +12:00
Jesse Hills
9c0ffee020 Merge pull request #16760 from esphome/bump-2026.5.2
2026.5.2
2026-06-02 15:39:40 +12:00
Bonne Eggleston
1740e54105 [ci] Fix auto label platform restructure false positive (#16734)
Co-authored-by: Claude <noreply@anthropic.com>
2026-06-01 23:20:18 -04:00
Jesse Hills
070c14b04a Bump version to 2026.5.2 2026-06-02 14:33:41 +12:00
J. Nick Koston
559cfd1555 [api] Fix crash loop on VoiceAssistantConfigurationRequest (#16757) 2026-06-02 14:33:41 +12:00
Jonathan Swoboda
571a12ffe5 [core] Clean build when the toolchain changes (#16744) 2026-06-02 14:33:41 +12:00
J. Nick Koston
a4d247fa0a [core] Persist esphome.area in StorageJSON (#16710) 2026-06-02 14:33:41 +12:00
Fyleo
8e57894af7 [sx126x] fix a typo in image calibration on 863 - 870 Mhz frequency (#16731) 2026-06-02 14:33:41 +12:00
J. Nick Koston
f9aba18f8e [libretiny] Fix RTL8710B IRAM_ATTR section being dropped from flashed image (#16616) 2026-06-02 14:33:41 +12:00
Jesse Hills
a04f6da814 [packages] Resolve git symlinks on Windows when materialized as text (#16657) 2026-06-02 14:33:41 +12:00
Jonathan Swoboda
3f57117efd [esp32] Decode crash PCs via IDF toolchain on IDF builds (#16626) 2026-06-02 14:33:41 +12:00
J. Nick Koston
d7f809181a [writer] Mark storage_should_clean as public API for device-builder (#16443) 2026-06-02 14:33:41 +12:00
Clyde Stubbs
d7d20f4f6b [cli] Allow state reporting control via env (#16746) 2026-06-02 07:04:35 +10:00
J. Nick Koston
ab46f8bd74 [api] Fix crash loop on VoiceAssistantConfigurationRequest (#16757) 2026-06-01 20:32:23 +00:00
Keith Burzinski
2454ad1645 [ethernet] Add enable_on_boot lifecycle + lazy-init to reclaim DMA-capable SRAM (#16607)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-01 15:30:07 -05:00
Keith Burzinski
4e48682468 [wifi] Defer esp_wifi_init() to lazy-init so enable_on_boot: false actually saves RAM (#16606)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-01 14:18:29 -05:00
Clyde Stubbs
805aa252d5 [const] Move CONF_SHA256 to common code (#16751) 2026-06-01 13:30:05 -04:00
Jonathan Swoboda
6116d10ab1 [espidf] Derive idedata from the native ESP-IDF compile_commands.json (#16742) 2026-05-31 17:44:12 -04:00
Jonathan Swoboda
48844a68ba [core] Clean build when the toolchain changes (#16744) 2026-05-31 16:29:16 -04:00
Jonathan Swoboda
7865dc33bc [ethernet] Bump espressif/dm9051 to 1.1.0 (#16735) 2026-05-31 09:50:17 -05:00
Jonathan Swoboda
bf62124032 [esp32] Refine ESP-IDF framework version suffix handling (#16726) 2026-05-30 07:43:21 -04:00
dependabot[bot]
95397948b9 Bump CodSpeedHQ/action from 4.15.1 to 4.17.0 (#16730)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-30 00:09:27 -05:00
J. Nick Koston
f0202155b3 [core] Persist esphome.area in StorageJSON (#16710) 2026-05-30 00:09:07 -05:00
Fyleo
07a57d7557 [sx126x] fix a typo in image calibration on 863 - 870 Mhz frequency (#16731) 2026-05-29 23:03:42 -04:00
Jonathan Swoboda
091a05ccde [esp32_camera] Enable PicolibC Newlib compatibility on IDF 6.0+ (#16703) 2026-05-29 05:16:55 +00:00
Jonathan Swoboda
dd961156d0 [ledc] Adapt to LEDC LL API changes in ESP-IDF 6.1 (#16697) 2026-05-29 00:54:14 -04:00
Jonathan Swoboda
10abb0647c [esp32] Add ESP32-S31, ESP32-H4 and ESP32-H21 variant scaffolding (#16700) 2026-05-29 00:30:52 -04:00
Jonathan Swoboda
a85f8ad935 [core] Use esp_rom_crc.h public API instead of legacy rom/crc.h (#16698) 2026-05-29 00:28:08 -04:00
dependabot[bot]
8945550c6c Bump ruff from 0.15.14 to 0.15.15 (#16712)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-29 03:35:37 +00:00
dependabot[bot]
4b8e06b5bc Bump tornado from 6.5.5 to 6.5.6 (#16704)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-28 09:12:35 -04:00
Mischa Siekmann
f41866a9b8 [gpio][binary_sensor] Fix pin validation for external GPIO pins (#16528) 2026-05-28 09:11:48 -04:00
tomaszduda23
5732d7135f [network] move ipv6 enforcement to validation step (#16701) 2026-05-28 08:39:11 -04:00
Jesse Hills
ec597bfc03 [docs] Update esphome-docs references to esphome.io after repo rename (#16705) 2026-05-28 14:54:42 +12:00
rwrozelle
9a6157b469 [tests] Sandbox PlatformIO paths in test_writer to fix xdist race (#16619)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-05-27 15:50:43 -04:00
GuzTech
ac29fad120 [growatt_solar] Replace hard coded register addresses with constexpr (#16581) 2026-05-27 14:21:50 -04:00
SoCuul
e87190edb4 [midea] fix casing of custom fan modes (#16419) 2026-05-27 14:20:00 -04:00
Elvin Luff
911e330c09 [core] Add Codeberg as a supported git url (#16501) 2026-05-27 14:13:03 -04:00
Jonathan Swoboda
e64b6bc398 [esp32] Stub arduino-esp32 with INTERFACE re-export to framework (#16695) 2026-05-27 11:00:51 -04:00
J. Nick Koston
21e548f1d7 [core] Sensitive redaction via yaml_util representer (#16690) 2026-05-27 09:20:50 -05:00
J. Nick Koston
3cc875c40b [core] Enable ruff BLE (flake8-blind-except) lint family (#16659) 2026-05-27 20:09:57 +12:00
Ardumine
7463a15c7e [network] Add Zephyr IPv6 networking support for nRF52 (#16336)
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: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: tomaszduda23 <tomaszduda23@gmail.com>
2026-05-27 07:43:38 +00:00
dependabot[bot]
8d19c55be2 Bump pytest-asyncio from 1.3.0 to 1.4.0 (#16687)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-26 19:58:13 -05:00
dependabot[bot]
87d0e24d19 Bump aioesphomeapi from 45.2.2 to 45.3.1 (#16688)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-26 19:57:29 -05:00
J. Nick Koston
91ead4ff54 [core] Mark canonical sensitive fields with cv.sensitive (#16677) 2026-05-27 00:16:47 +00:00
Jonathan Swoboda
a6ef67aa65 [text_sensor] Remove deprecated public raw_state member (#16683) 2026-05-26 23:34:52 +00:00
Jonathan Swoboda
e174c44b28 [neopixelbus] Deprecate on ESP32 (#16676) 2026-05-26 19:15:25 -04:00
Jonathan Swoboda
f728cb4373 [core] Remove deprecated seq/gens templates (#16685) 2026-05-26 18:50:20 -04:00
Jonathan Swoboda
6c4a8a3245 [dsmr] Force BearSSL on ESP8266 to avoid mbedtls link failure (#16686) 2026-05-26 22:49:44 +00:00
Jonathan Swoboda
eb1196c6b2 [nfc] Remove deprecated heap-allocating format helpers (#16684) 2026-05-26 18:48:17 -04:00
Jonathan Swoboda
fb0b73980b [wifi] Default ESP8266 min_auth_mode to WPA2 (#16682) 2026-05-26 18:47:40 -04:00
Jonathan Swoboda
171ded35a5 [core] Remove cv.only_with_esp_idf and CORE.using_esp_idf (#16681) 2026-05-26 18:47:16 -04:00
Jonathan Swoboda
b71d445e79 [core] Remove deprecated const char* mark_failed/status_set_error (#16680) 2026-05-26 18:46:45 -04:00
Jonathan Swoboda
4d908798bc [core] Remove deprecated custom_components folder loading (#16679) 2026-05-26 18:45:50 -04:00
Clyde Stubbs
62b3b1cc75 [lvgl] Support rounded property for meter arcs (#16669) 2026-05-26 15:30:08 -05:00
J. Nick Koston
52ead52ef2 [core] Enable ruff PGH (pygrep-hooks) lint family (#16651) 2026-05-26 15:29:54 -05:00
J. Nick Koston
96816e2491 [core] Enable ruff DTZ (flake8-datetimez) lint family (#16660) 2026-05-26 15:29:38 -05:00
J. Nick Koston
bac62cb7de [core] Add cv.sensitive marker for schema-level sensitive fields (#16673) 2026-05-26 15:29:06 -05:00
Kevin Ahrendt
722cbfe843 [voice_assistant] Never send zero-length audio to Home Assistant (#16634) 2026-05-26 14:05:57 -04:00
J. Nick Koston
88b12a1c45 [lvgl] Build automation_schema event validators lazily (#16633) 2026-05-26 08:41:54 -05:00
J. Nick Koston
ceb9d406e1 [core] Enable ruff PIE (flake8-pie) lint family (#16658) 2026-05-26 07:46:44 -04:00
J. Nick Koston
8b62cfded7 [libretiny] Fix RTL8710B IRAM_ATTR section being dropped from flashed image (#16616) 2026-05-26 19:57:44 +12:00
Jesse Hills
423b60c90c [packages] Resolve git symlinks on Windows when materialized as text (#16657) 2026-05-26 19:56:44 +12:00
J. Nick Koston
ae74920b81 [core] Enable ruff PTH (flake8-use-pathlib) lint family (#16661) 2026-05-26 05:14:42 +00:00
J. Nick Koston
ae814cff5c [core] Enable ruff B (flake8-bugbear) lint family (#16655) 2026-05-26 02:28:14 +00:00
J. Nick Koston
489cf483d0 [core] Enable ruff PYI (flake8-pyi) lint family (#16654) 2026-05-25 20:55:35 -05:00
J. Nick Koston
dd0028c1b5 [core] Enable ruff G (flake8-logging-format) lint family (#16650) 2026-05-26 01:36:49 +00:00
J. Nick Koston
e492f8f8b6 [tests] Disable hypothesis deadline on flaky IP address test (#16652) 2026-05-25 20:14:36 -05:00
J. Nick Koston
b39b34bfe1 [core] Enable ruff C4 (flake8-comprehensions) lint family (#16653) 2026-05-25 20:14:26 -05:00
J. Nick Koston
bbc24ab546 [core] Enable ruff RSE (flake8-raise) lint family (#16649) 2026-05-25 20:06:34 -05:00
J. Nick Koston
f1839489dd [core] Enable ruff ISC (flake8-implicit-str-concat) lint family (#16646) 2026-05-25 20:06:18 -05:00
J. Nick Koston
5172227931 [core] Enable ruff SLOT (flake8-slots) lint family (#16647) 2026-05-25 20:06:01 -05:00
J. Nick Koston
97267105e1 [core] Enable ruff EXE (flake8-executable) lint family (#16648) 2026-05-25 20:05:51 -05:00
J. Nick Koston
8645f3672d [core] Enable additional zero-violation ruff lint families (#16645) 2026-05-25 18:11:40 -05:00
Boris Krivonog
a257edba62 [mitsubishi_cn105] Add basic swing support (#15653)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-25 16:46:33 -05:00
Jonathan Swoboda
61e8830a3c [espidf] Keep cmake output filter working when IDF writes raw bytes (#16642) 2026-05-25 17:40:38 -04:00
Jonathan Swoboda
fc0a4e2201 [espidf] Support github:// and https://github.com/.../.git framework sources (#16639) 2026-05-25 17:07:35 -04:00
Clyde Stubbs
0b780f1fd2 [time][homeassistant] Fix timezone handling (#16583) 2026-05-26 06:51:15 +10:00
Kevin Ahrendt
dcc30f8651 [router] Share a single I2S bus in test (#16637) 2026-05-25 15:39:54 -04:00
Kevin Ahrendt
892e116680 [router] Add a router speaker component to runtime choose output speaker (#16592) 2026-05-25 12:42:49 -04:00
Kevin Ahrendt
1c7ae96e42 [micro_wake_word] Allow task stack to be allocated in PSRAM (#16632) 2026-05-25 11:04:26 -04:00
Jonathan Swoboda
684bce8b9a [esp32] Decode crash PCs via IDF toolchain on IDF builds (#16626) 2026-05-25 14:36:41 +00:00
Kevin Ahrendt
7c494fd3ef [psram] Consolidate task stack in PSRAM handling (#16628) 2026-05-25 10:15:51 -04:00
Jonathan Swoboda
cf1fabe6d4 [esp32_hosted] Bump esp_hosted to 2.12.8 and add use_psram option (#16627) 2026-05-25 10:11:31 -04:00
J. Nick Koston
cde52ef75e [lvgl] Merge dict-extend chains to speed up schema construction (#16614) 2026-05-25 09:09:54 -05:00
Jonathan Swoboda
98e7213387 [espidf] Warn instead of skipping libraries with framework mismatch (#16630) 2026-05-25 14:08:16 +00:00
Jonathan Swoboda
e7ab78366d [core] Add esphome.build_flags option for IDF + PlatformIO (#16629) 2026-05-25 10:03:38 -04:00
J. Nick Koston
e0167e9bdf [lvgl] Memoize obj_schema by widget_type (#16615) 2026-05-24 20:17:51 -05:00
Jesse Hills
62b0a93e5e [rp2040] Add variant config option for RP2040/RP2350 (#16602) 2026-05-25 10:43:39 +12:00
Jesse Hills
1fb8c26704 Merge branch 'release' into dev 2026-05-25 10:43:04 +12:00
Jesse Hills
3d1a614e55 Merge pull request #16610 from esphome/bump-2026.5.1
2026.5.1
2026-05-25 10:42:20 +12:00
dependabot[bot]
917ffc3797 Bump aioesphomeapi from 45.0.4 to 45.2.2 (#16611)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-24 21:49:52 +00:00
J. Nick Koston
090f5a486a Lift dependabot pip open PR limit (#16609) 2026-05-24 16:32:47 -05:00
Jesse Hills
03e2eb4b4a Bump version to 2026.5.1 2026-05-25 09:28:49 +12:00
Jonathan Swoboda
ddd353d105 [esp32] Disable IDF's COMPILER_DISABLE_DEFAULT_ERRORS so -Wno-error actually undoes -Werror (#16604) 2026-05-25 09:28:49 +12:00
Jonathan Swoboda
9a34a6aabb [esp32] Replace per-class -Wno-error=X demotes with blanket -Wno-error for ESP-IDF toolchain (#16599) 2026-05-25 09:28:49 +12:00
J. Nick Koston
0babc52472 [bluetooth_proxy] Recover slot stuck in DISCONNECTING when CLOSE_EVT is dropped (#16588) 2026-05-25 09:28:49 +12:00
Jonathan Swoboda
adde7681e8 [esp32] Demote IDF #warning deprecations from error under ESP-IDF toolchain (#16584) 2026-05-25 09:28:49 +12:00
J. Nick Koston
8f6ea62628 [uart] Wake main loop on ESP8266 software serial RX (#16562) 2026-05-25 09:28:49 +12:00
J. Nick Koston
4e7bc92061 [esp8266] Use os_timer-based esp_delay() in delay() (#16563) 2026-05-25 09:28:49 +12:00
Edvard Filistovič
1f4a061572 [libretiny] Fix LN882H IRAM_ATTR injection point in patch_linker.py (#16570) 2026-05-25 09:28:49 +12:00
J. Nick Koston
59db9a4673 [dashboard] Fix flaky test_websocket_refresh_command on Windows CI (#16565) 2026-05-25 09:28:49 +12:00
Kevin Ahrendt
7ae5566472 [sendspin] Bump sendspin-cpp to v0.6.1 (#16553) 2026-05-25 09:28:49 +12:00
J. Nick Koston
f247def4ac [core] Refresh compiled config cache after upload/logs fallback (#16548) 2026-05-25 09:28:49 +12:00
Jonathan Swoboda
27d53ec117 [sx126x] Assert NSS before wait_busy so commands wake the chip from sleep (#16546) 2026-05-25 09:28:49 +12:00
J. Nick Koston
0c94a173b6 [api] Break api_connection/api_server include cycle to drop custom unique_ptr deleter (#16542) 2026-05-25 09:28:49 +12:00
Jonathan Swoboda
ae2e372762 [tuya] Restore null guard on status_pin lost in #16353 (#16539) 2026-05-25 09:28:48 +12:00
Jonathan Swoboda
e6ed275746 [esp32] Defer esp_panic_handler wrap so arduino-esp32 IDF component skips it (#16538) 2026-05-25 09:28:48 +12:00
Jonathan Swoboda
878027ff50 [espidf] Honor the dict shorthand for library.json dependencies (#16537) 2026-05-25 09:28:48 +12:00
Jonathan Swoboda
858cfd5b94 [espidf] Default to remote HEAD when cg.add_library URL has no #ref (#16535) 2026-05-25 09:28:48 +12:00
Jonathan Swoboda
5225416347 [espidf] Backport ninja linux-arm64 entry into tools.json on aarch64 hosts (#16527) 2026-05-25 09:28:48 +12:00
Jonathan Swoboda
615d5aa827 [core] Persist & restore CORE.toolchain through StorageJSON (#16531) 2026-05-25 09:28:48 +12:00
Jonathan Swoboda
e92a4c9472 [espidf] Write version.txt after extract so bootloader shows the real version (#16532) 2026-05-25 09:28:48 +12:00
Jonathan Swoboda
32fa856bf0 [espidf] Fix tarfile extract crashing on Python 3.11 with None mode (#16530) 2026-05-25 09:28:48 +12:00
Jonathan Swoboda
cc88456ce7 [espidf] Filter noisy 'git rev-parse' errors when .git is stripped (#16521) 2026-05-25 09:28:48 +12:00
dependabot[bot]
79539cb85d Bump zeroconf from 0.149.13 to 0.149.16 (#16533)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-25 09:28:48 +12:00
dependabot[bot]
16b6509a03 Bump zeroconf from 0.149.12 to 0.149.13 (#16520)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-25 09:28:48 +12:00
Kevin Ahrendt
9fcb638f33 [micro_wake_word] Use RingBufferAudioSource (#16595)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-24 15:34:51 -04:00
Kevin Ahrendt
747787ae98 [audio] Use RingBufferAudioSource for resampling (#16560) 2026-05-24 15:34:15 -04:00
Kevin Ahrendt
5cb7e62241 [audio] Use RingBufferAudioSource for decoding (#16564) 2026-05-24 15:33:32 -04:00
Kevin Ahrendt
c17c4478ac [mixer] Support any bit depth audio (#16524) 2026-05-24 15:32:43 -04:00
Kevin Ahrendt
750d52741a [voice_assistant] Use RingBufferAudioSource (#16597) 2026-05-24 08:36:53 -04:00
Rodrigo Martín
a37f27ee7f [espnow, ethernet, network, openthread, wifi] centralize network initialization for ESP32 (#14012)
Co-authored-by: kbx81 <kbx81x@gmail.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick+github@koston.org>
2026-05-24 05:27:31 +00:00
Jonathan Swoboda
5f860ff5bd [esp32] Disable IDF's COMPILER_DISABLE_DEFAULT_ERRORS so -Wno-error actually undoes -Werror (#16604) 2026-05-24 04:19:07 +00:00
Keith Burzinski
c951881eea [api] Fix uint32_t/int32_t format strings for stricter GCC toolchain (#16603) 2026-05-24 04:05:18 +00:00
Kevin Ahrendt
5cb145a8c3 [ethernet] Offload W5500 bulk SPI transfers from the busy-wait path (#16596) 2026-05-23 17:53:53 -04:00
Kevin Ahrendt
74001ccf05 [wifi] Wake main loop when requesting high performance mode (#16598) 2026-05-23 17:39:20 -04:00
Kevin Ahrendt
58931f2610 [audio] Add clear_buffered_data method to RingBufferAudioSource (#16594) 2026-05-23 17:37:59 -04:00
Jonathan Swoboda
f616103621 [esp32] Replace per-class -Wno-error=X demotes with blanket -Wno-error for ESP-IDF toolchain (#16599) 2026-05-23 15:44:25 -04:00
J. Nick Koston
188ff7ebfd [bluetooth_proxy] Recover slot stuck in DISCONNECTING when CLOSE_EVT is dropped (#16588) 2026-05-23 14:30:12 -05:00
Clyde Stubbs
be99553fd4 [ci] Fix flash memory overflow on tests (#16587) 2026-05-23 14:26:53 +10:00
Jonathan Swoboda
b0dc688c14 [esp32] Demote IDF #warning deprecations from error under ESP-IDF toolchain (#16584) 2026-05-22 20:30:25 -04:00
J. Nick Koston
2b422cbd99 [lvgl] Build widget update action schemas lazily (#16569)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
2026-05-23 00:20:39 +00:00
dependabot[bot]
9930b3c216 Bump github/codeql-action from 4.35.5 to 4.36.0 (#16579)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-22 19:18:31 -05:00
dependabot[bot]
55f4e5cb75 Bump the docker-actions group across 1 directory with 2 updates (#16578)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-22 19:18:20 -05:00
J. Nick Koston
71550bb3be [lvgl] Memoize and lazily build container_schema (#16567) 2026-05-22 18:39:25 -05:00
J. Nick Koston
a58b4edb6a [ci] Gate unconditional CI jobs on a single determine-jobs output instead of a path filter (#16580) 2026-05-22 18:39:06 -05:00
Clyde Stubbs
f85fdb475a [homeassistant] Reduce log spam for sensors (#16555) 2026-05-23 08:07:51 +10:00
dependabot[bot]
4a78c8d45a Bump pytest-codspeed from 5.0.2 to 5.0.3 (#16575)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-22 15:55:49 -05:00
Kevin Ahrendt
c3bef24389 [i2s_audio] Reset dout GPIO when stopping speaker driver (#16573) 2026-05-22 14:43:50 -05:00
J. Nick Koston
7182b1a8ae [uart] Wake main loop on ESP8266 software serial RX (#16562) 2026-05-22 14:30:43 -05:00
J. Nick Koston
64e32ebe04 [esp8266] Use os_timer-based esp_delay() in delay() (#16563) 2026-05-22 14:30:28 -05:00
Edvard Filistovič
94b10981e1 [libretiny] Fix LN882H IRAM_ATTR injection point in patch_linker.py (#16570) 2026-05-22 14:09:32 -05:00
J. Nick Koston
680c9fc9c0 [dashboard] Fix flaky test_websocket_refresh_command on Windows CI (#16565) 2026-05-22 08:49:03 -05:00
dependabot[bot]
99de741f99 Bump docker/build-push-action from 7.1.0 to 7.2.0 in /.github/actions/build-image (#16545)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-22 08:08:29 -05:00
dependabot[bot]
ac530c33b0 Bump actions/stale from 10.2.0 to 10.3.0 (#16544)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-22 08:08:14 -05:00
Kevin Ahrendt
0b5e7ae8fa [sendspin] Bump sendspin-cpp to v0.6.1 (#16553) 2026-05-22 06:43:08 -04:00
Jesse Hills
0b2eb6481f [light] Add light.effect.next / light.effect.previous actions (#16491) 2026-05-22 13:42:50 +12:00
Jesse Hills
1d3eea098e [core] Support YAML frontmatter for arbitrary user metadata (#16552) 2026-05-22 13:00:22 +12:00
dependabot[bot]
4ff8eb4b15 Bump ruff from 0.15.13 to 0.15.14 (#16543)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-21 22:08:56 +00:00
J. Nick Koston
aea1e4d136 [core] Refresh compiled config cache after upload/logs fallback (#16548) 2026-05-21 17:05:17 -05:00
Jonathan Swoboda
38b8b41ccc [sx126x] Assert NSS before wait_busy so commands wake the chip from sleep (#16546) 2026-05-21 18:03:07 -04:00
J. Nick Koston
96eced0378 [api] Break api_connection/api_server include cycle to drop custom unique_ptr deleter (#16542) 2026-05-21 15:42:57 -05:00
Jonathan Swoboda
1ea95264bd [tuya] Restore null guard on status_pin lost in #16353 (#16539) 2026-05-21 18:08:09 +00:00
Jonathan Swoboda
d2bda0a402 [esp32] Defer esp_panic_handler wrap so arduino-esp32 IDF component skips it (#16538) 2026-05-21 14:03:55 -04:00
Jonathan Swoboda
56fd77e4c8 [espidf] Honor the dict shorthand for library.json dependencies (#16537) 2026-05-21 13:01:54 -05:00
Jonathan Swoboda
3719ea740a [espidf] Default to remote HEAD when cg.add_library URL has no #ref (#16535) 2026-05-21 13:01:19 -05:00
Jonathan Swoboda
750ae56778 [espidf] Backport ninja linux-arm64 entry into tools.json on aarch64 hosts (#16527) 2026-05-21 12:05:27 -04:00
Kevin Ahrendt
01494f7431 [audio] Bump esp-audio-libs to v3.1.0 (#16519) 2026-05-21 11:57:32 -04:00
J. Nick Koston
233a60f106 [ci] Pin uv version in setup-uv to fix Windows manifest fetch flake (#16534) 2026-05-21 10:53:34 -05:00
Jonathan Swoboda
e0076cb1a8 [core] Persist & restore CORE.toolchain through StorageJSON (#16531) 2026-05-21 10:37:46 -05:00
Jonathan Swoboda
b619e3e8c7 [espidf] Write version.txt after extract so bootloader shows the real version (#16532) 2026-05-21 10:37:10 -05:00
Jonathan Swoboda
f2bfe5cd17 [espidf] Fix tarfile extract crashing on Python 3.11 with None mode (#16530) 2026-05-21 10:36:27 -05:00
Jonathan Swoboda
90715373f2 [espidf] Filter noisy 'git rev-parse' errors when .git is stripped (#16521) 2026-05-21 10:35:51 -05:00
Jonathan Swoboda
52e7d3ccfb [esp32] Use new sdkconfig key names that replaced deprecated ones (#16522) 2026-05-21 10:35:25 -05:00
dependabot[bot]
a70e358cea Bump zeroconf from 0.149.13 to 0.149.16 (#16533)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-21 09:55:28 -05:00
dependabot[bot]
43a1c2067e Bump zeroconf from 0.149.12 to 0.149.13 (#16520)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-21 08:28:09 -05:00
Jesse Hills
11760307f7 Merge branch 'release' into dev 2026-05-21 13:49:17 +12:00
Jesse Hills
15c546b809 Merge pull request #16523 from esphome/bump-2026.5.0
2026.5.0
2026-05-21 13:48:28 +12:00
Jesse Hills
104c8bed41 Bump version to 2026.5.0 2026-05-21 11:16:58 +12:00
Jesse Hills
49bfa12eb7 Merge branch 'beta' into dev 2026-05-21 10:14:14 +12:00
Jesse Hills
ca859de212 Merge pull request #16518 from esphome/bump-2026.5.0b4
2026.5.0b4
2026-05-21 10:13:39 +12:00
Jesse Hills
de783e72d5 Bump version to 2026.5.0b4 2026-05-21 09:10:52 +12:00
Jonathan Swoboda
cd7e2d79c4 [esp32] Decouple esp-idf toolchain version check from PIO, honor framework source: override (#16516) 2026-05-21 09:10:52 +12:00
Jonathan Swoboda
ecf823b871 [espidf] Drop version field from generated idf_component.yml (#16511) 2026-05-21 09:10:52 +12:00
dependabot[bot]
9fdad68138 Bump aioesphomeapi from 45.0.3 to 45.0.4 (#16513)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-21 09:10:51 +12:00
dependabot[bot]
b79a306d02 Bump zeroconf from 0.149.7 to 0.149.12 (#16510)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-21 09:10:51 +12:00
Jonathan Swoboda
870f628637 [esp32] Decouple esp-idf toolchain version check from PIO, honor framework source: override (#16516) 2026-05-20 20:40:59 +00:00
Jonathan Swoboda
52c9a2d07b [espidf] Drop version field from generated idf_component.yml (#16511) 2026-05-20 14:31:58 -04:00
Jonathan Swoboda
60afad442c [esp32] Fix sdkconfig int values silently clamped to default (#16515) 2026-05-20 13:36:18 -04:00
dependabot[bot]
fbe212944b Bump aioesphomeapi from 45.0.3 to 45.0.4 (#16513)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-20 10:51:53 -05:00
dependabot[bot]
8927ade789 Bump zeroconf from 0.149.7 to 0.149.12 (#16510)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-20 15:40:20 +00:00
Jesse Hills
63fe977adb Merge branch 'beta' into dev 2026-05-20 14:34:33 +12:00
Jesse Hills
94badfcb19 Merge pull request #16507 from esphome/bump-2026.5.0b3
2026.5.0b3
2026-05-20 14:33:57 +12:00
Jesse Hills
19c4da2aa5 Bump version to 2026.5.0b3 2026-05-20 12:53:26 +12:00
Kevin Ahrendt
e4c8d1f430 [sendspin] Bump sendspin to v0.6.0 (#16496) 2026-05-20 12:53:26 +12:00
Kevin Ahrendt
302938f875 [i2s_audio] Optimize SPDIF encoder and suport higher bit depth audio (#16504)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2026-05-20 12:53:26 +12:00
Jonathan Swoboda
65e1e210de [espidf] Print RAM summary on ESP32-S3 / unified-DIRAM variants (#16494) 2026-05-20 12:53:26 +12:00
luar123
43cc9fc879 [zigbee] don't allow zigbee + thread or access point (#16499) 2026-05-20 12:53:25 +12:00
Kevin Ahrendt
41ad2ba763 [i2s_audio] Compute ring buffer size with SPDIF sample count (#16400) 2026-05-20 12:53:25 +12:00
Brandon Harvey
25739091da [sen6x] Remove incorrect AQI device class from VOC and NOx Index sensors (#16465) 2026-05-20 12:53:25 +12:00
Brandon Harvey
bbf5fe8450 [sgp4x] Remove incorrect AQI device class from VOC and NOx Index sensors (#16464) 2026-05-20 12:53:25 +12:00
Brandon Harvey
e9ef58d99d [sen5x] Remove incorrect AQI device class from VOC and NOx Index sensors (#16463) 2026-05-20 12:53:25 +12:00
dependabot[bot]
e1793a1eff Bump zeroconf from 0.149.3 to 0.149.7 (#16492)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-20 12:53:25 +12:00
Jesse Hills
9bb70d568d [ci] Move ha-addon and schema release triggers to version-notifier (#16490) 2026-05-20 12:53:25 +12:00
Kevin Ahrendt
0912122634 [sendspin] Bump sendspin to v0.6.0 (#16496) 2026-05-19 15:16:00 -04:00
Kevin Ahrendt
9924d998f1 [i2s_audio] Optimize SPDIF encoder and suport higher bit depth audio (#16504)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2026-05-19 13:37:41 -05:00
dependabot[bot]
e979d461f0 Bump codecov/codecov-action from 6.0.0 to 6.0.1 (#16500)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-19 12:15:50 -05:00
J. Nick Koston
863af482ec [esp32_ble_server] Honor client offset and MTU in long reads (#16458) 2026-05-19 12:14:15 -05:00
J. Nick Koston
80ed541032 [core] Add progmem_memcpy HAL helper (#16470) 2026-05-19 12:13:58 -05:00
Jonathan Swoboda
1d0ddfac5d [espidf] Print RAM summary on ESP32-S3 / unified-DIRAM variants (#16494) 2026-05-19 12:57:18 -04:00
luar123
c0e71fc713 [zigbee] don't allow zigbee + thread or access point (#16499) 2026-05-19 12:53:36 -04:00
Kevin Ahrendt
7ecfe4b5c9 [i2s_audio] Compute ring buffer size with SPDIF sample count (#16400) 2026-05-18 21:53:19 -05:00
Brandon Harvey
36fc36071d [sen6x] Remove incorrect AQI device class from VOC and NOx Index sensors (#16465) 2026-05-19 11:20:08 +12:00
Brandon Harvey
cb581271ed [sgp4x] Remove incorrect AQI device class from VOC and NOx Index sensors (#16464) 2026-05-19 11:19:51 +12:00
Brandon Harvey
b0af4a9f0d [sen5x] Remove incorrect AQI device class from VOC and NOx Index sensors (#16463) 2026-05-19 11:19:48 +12:00
dependabot[bot]
edb59476b1 Bump zeroconf from 0.149.3 to 0.149.7 (#16492)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-17 23:29:03 -07:00
Jesse Hills
9c696f5de1 [ci] Move ha-addon and schema release triggers to version-notifier (#16490) 2026-05-18 15:56:44 +12:00
Jesse Hills
6804965bd8 Merge branch 'beta' into dev 2026-05-18 15:31:01 +12:00
Jesse Hills
213df0412d Merge pull request #16488 from esphome/bump-2026.5.0b2
2026.5.0b2
2026-05-18 15:28:53 +12:00
Jesse Hills
cdf74c180e Bump version to 2026.5.0b2 2026-05-18 11:11:54 +12:00
Jonathan Swoboda
df31c72e4e [espidf] Switch direct framework downloader to esphome-libs/esp-idf tarballs (#16484) 2026-05-18 11:11:54 +12:00
dependabot[bot]
4f188bf9bb Bump zeroconf from 0.148.0 to 0.149.3 (#16480)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-18 11:11:54 +12:00
dependabot[bot]
20f92ad5e9 Bump aioesphomeapi from 45.0.2 to 45.0.3 (#16479)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-18 11:11:54 +12:00
J. Nick Koston
f301e90fd9 [ci] Use larger app partition for esp32-s3-idf component test grouping (#16430) 2026-05-18 11:11:54 +12:00
dependabot[bot]
2dbaaf1efd Bump aioesphomeapi from 45.0.1 to 45.0.2 (#16469)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-18 11:11:54 +12:00
Clyde Stubbs
da237b5070 [lvgl] Fix image define (#16468) 2026-05-18 11:11:54 +12:00
Clyde Stubbs
6a8f24b951 [ft5x06] Fix setting calibration values (#16446) 2026-05-18 11:11:54 +12:00
dependabot[bot]
26907f17f5 Bump aioesphomeapi from 45.0.0 to 45.0.1 (#16467)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-18 11:11:54 +12:00
Jonathan Swoboda
c6a74222f1 [esp32_hosted][fingerprint_grow] Fix two remaining ESP32 toolchain warnings (#16442) 2026-05-18 11:11:54 +12:00
J. Nick Koston
5ec0879a10 [core] Fix KeyError: 'esp32' on upload when validated-config cache is used (#16457) 2026-05-18 11:11:54 +12:00
J. Nick Koston
50495c7085 [wifi] Refuse to compile when wifi_ssid is the device-builder placeholder (#16444) 2026-05-18 11:11:54 +12:00
Kevin Ahrendt
25dbef83de [sound_level] Use RingBufferAudioSource (#16436) 2026-05-18 11:11:54 +12:00
Kevin Ahrendt
4f895425ca [audio] Bump microMP3 to v0.2.1 (#16429) 2026-05-18 11:11:54 +12:00
Jonathan Swoboda
c037058c19 [esp32_hosted] Bump esp_hosted to 2.12.7 (#16440) 2026-05-18 11:11:54 +12:00
Jonathan Swoboda
ecac6b64ec [espidf] Gate esp_idf_size --ng on IDF version (#16441) 2026-05-18 11:11:54 +12:00
Jonathan Swoboda
3831aa809f [multiple] Fix -Wformat= mismatches in component .cpp sources (#16433) 2026-05-18 11:11:54 +12:00
Jonathan Swoboda
da8286f554 [docker] Install libusb-1.0 so ESP-IDF tools can validate openocd (#16424)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-18 11:11:54 +12:00
Jonathan Swoboda
d5c6efb2fe [tests] Fix -Wformat= mismatches in test YAML lambdas/logger.log (#16435) 2026-05-18 11:11:54 +12:00
Jonathan Swoboda
dd1818661c [esp32] Sweep ESP-IDF toolchain warnings + bump deprecated mark_failed (#16432) 2026-05-18 11:11:53 +12:00
Keith Burzinski
fb659f9ac4 [tinyusb] Reject logger.hardware_uart: USB_CDC (#16417)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 11:11:53 +12:00
Keith Burzinski
ab273a1f8f [tinyusb] Reject tinyusb: configured without a USB class companion (#16413)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 11:11:53 +12:00
Jonathan Swoboda
84b5931299 [espidf] Trim has_outdated_files watch list; embed IDF version in sdkconfig (#16416) 2026-05-18 11:11:53 +12:00
Jonathan Swoboda
c863d58999 [espidf] Stop perpetual reconfigure loop on native ESP-IDF builds (#16415) 2026-05-18 11:11:53 +12:00
Jesse Hills
42ad2a6272 [espidf] Accept list input in _str_to_lst_of_str helper (#16485) 2026-05-18 10:49:03 +12:00
Jonathan Swoboda
6690725860 [espidf] Switch direct framework downloader to esphome-libs/esp-idf tarballs (#16484) 2026-05-18 07:29:38 +12:00
dependabot[bot]
155232875a Bump zeroconf from 0.148.0 to 0.149.3 (#16480)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-16 19:08:19 -07:00
dependabot[bot]
01c0d3163e Bump aioesphomeapi from 45.0.2 to 45.0.3 (#16479)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-16 18:41:09 -07:00
J. Nick Koston
7c5d5f75dc [ci] Use larger app partition for esp32-s3-idf component test grouping (#16430) 2026-05-15 22:16:52 -07:00
dependabot[bot]
fb0bfea1c8 Bump aioesphomeapi from 45.0.1 to 45.0.2 (#16469)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-15 22:06:33 -07:00
J. Nick Koston
48d17571c8 [tests] Mock determine_cpp_unit_tests in clang_tidy_mode tests (#16456) 2026-05-15 15:50:58 -07:00
Clyde Stubbs
df100681e0 [lvgl] Fix image define (#16468) 2026-05-15 22:36:53 +00:00
Clyde Stubbs
1a287bf785 [ft5x06] Fix setting calibration values (#16446) 2026-05-16 10:30:04 +12:00
dependabot[bot]
ff34e1061b Bump resvg-py from 0.3.1 to 0.3.2 (#16466)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-15 14:43:08 -07:00
dependabot[bot]
b78b78cbbb Bump aioesphomeapi from 45.0.0 to 45.0.1 (#16467)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-15 14:42:51 -07:00
dependabot[bot]
4c090c6b85 Bump github/codeql-action from 4.35.4 to 4.35.5 (#16461)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-15 12:37:56 -07:00
dependabot[bot]
ec6669fa67 Bump requests from 2.34.1 to 2.34.2 (#16460)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-15 12:37:41 -07:00
dependabot[bot]
59f8c1019f Bump pytest-codspeed from 5.0.1 to 5.0.2 (#16459)
Signed-off-by: dependabot[bot] <support@github.com>
2026-05-15 12:37:28 -07:00
david-collett
fb70095ba1 [esp32_ble_server] Fix incorrect BLECharacteristic read truncation (#16420) (#16422)
Co-authored-by: Dave <dave@morty>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-05-15 10:47:26 -07:00
Jonathan Swoboda
65d6bb18ed [esp32_hosted][fingerprint_grow] Fix two remaining ESP32 toolchain warnings (#16442) 2026-05-15 17:32:51 +00:00
J. Nick Koston
47eb2adbf2 [core] Fix KeyError: 'esp32' on upload when validated-config cache is used (#16457) 2026-05-15 10:29:15 -07:00
J. Nick Koston
35631be260 [writer] Mark storage_should_clean as public API for device-builder (#16443) 2026-05-15 10:20:31 -07:00
J. Nick Koston
96106d25bc [wifi] Refuse to compile when wifi_ssid is the device-builder placeholder (#16444) 2026-05-15 10:20:15 -07:00
J. Nick Koston
1674ed9744 [ci] Use uv for pip installs across CI workflows (#16451) 2026-05-15 10:18:27 -07:00
J. Nick Koston
46be0f4f62 [ci] Log top 30 pytest durations (#16455) 2026-05-15 10:18:07 -07:00
J. Nick Koston
ec1826a6ed [yaml_util] Promote include-discovery helper, share it with bundle (#16447) 2026-05-15 10:17:50 -07:00
Simone Chemelli
8b3bc47547 [uptime] Update device_class for Uptime sensor (#16434)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-05-15 17:16:01 +00:00
J. Nick Koston
4381a8baaa [ci] pr-title-check: skip all bot authors, not just dependabot (#16453) 2026-05-15 09:59:35 -07:00
esphome[bot]
4189979391 Synchronise Device Classes from Home Assistant (#16452)
Co-authored-by: esphomebot <esphome@openhomefoundation.org>
2026-05-15 09:57:49 -07:00
J. Nick Koston
1b1e21d470 [ci] sync-device-classes: drop branch-switch hack, skip no-commit-to-branch instead (#16450) 2026-05-15 09:54:01 -07:00
1026 changed files with 31351 additions and 16164 deletions

View File

@@ -1 +0,0 @@
27aaab4e0ebfc10491720345aa746fc2dffa6a3985f73ec111b12dd99078d46f

View File

@@ -29,7 +29,7 @@ Required fields:
- **What does this implement/fix?**: Brief description of changes - **What does this implement/fix?**: Brief description of changes
- **Types of changes**: Check ONE appropriate box (Bugfix, New feature, Breaking change, etc.) - **Types of changes**: Check ONE appropriate box (Bugfix, New feature, Breaking change, etc.)
- **Related issue**: Use `fixes <link>` syntax if applicable - **Related issue**: Use `fixes <link>` syntax if applicable
- **Pull request in esphome-docs**: Link if docs are needed - **Pull request in esphome.io**: Link if docs are needed
- **Test Environment**: Check platforms you tested on - **Test Environment**: Check platforms you tested on
- **Example config.yaml**: Include working example YAML - **Example config.yaml**: Include working example YAML
- **Checklist**: Verify code is tested and tests added - **Checklist**: Verify code is tested and tests added
@@ -54,9 +54,9 @@ Required fields:
- fixes https://github.com/esphome/esphome/issues/XXX - fixes https://github.com/esphome/esphome/issues/XXX
**Pull request in [esphome-docs](https://github.com/esphome/esphome-docs) with documentation (if applicable):** **Pull request in [esphome.io](https://github.com/esphome/esphome.io) with documentation (if applicable):**
- esphome/esphome-docs#XXX - esphome/esphome.io#XXX
## Test Environment ## Test Environment
@@ -83,7 +83,7 @@ component_name:
- [x] Tests have been added to verify that the new code works (under `tests/` folder). - [x] Tests have been added to verify that the new code works (under `tests/` folder).
If user exposed functionality or configuration variables are added/changed: If user exposed functionality or configuration variables are added/changed:
- [ ] Documentation added/updated in [esphome-docs](https://github.com/esphome/esphome-docs). - [ ] Documentation added/updated in [esphome.io](https://github.com/esphome/esphome.io).
``` ```
## 5. Push and Create PR ## 5. Push and Create PR

View File

@@ -2,7 +2,7 @@
blank_issues_enabled: false blank_issues_enabled: false
contact_links: contact_links:
- name: Report an issue with the ESPHome documentation - name: Report an issue with the ESPHome documentation
url: https://github.com/esphome/esphome-docs/issues/new/choose url: https://github.com/esphome/esphome.io/issues/new/choose
about: Report an issue with the ESPHome documentation. about: Report an issue with the ESPHome documentation.
- name: Report an issue with the ESPHome web server - name: Report an issue with the ESPHome web server
url: https://github.com/esphome/esphome-webserver/issues/new/choose url: https://github.com/esphome/esphome-webserver/issues/new/choose

View File

@@ -16,9 +16,9 @@
- fixes <link to issue> - fixes <link to issue>
**Pull request in [esphome-docs](https://github.com/esphome/esphome-docs) with documentation (if applicable):** **Pull request in [esphome.io](https://github.com/esphome/esphome.io) with documentation (if applicable):**
- esphome/esphome-docs#<esphome-docs PR number goes here> - esphome/esphome.io#<esphome.io PR number goes here>
## Test Environment ## Test Environment
@@ -43,4 +43,4 @@
- [ ] Tests have been added to verify that the new code works (under `tests/` folder). - [ ] Tests have been added to verify that the new code works (under `tests/` folder).
If user exposed functionality or configuration variables are added/changed: If user exposed functionality or configuration variables are added/changed:
- [ ] Documentation added/updated in [esphome-docs](https://github.com/esphome/esphome-docs). - [ ] Documentation added/updated in [esphome.io](https://github.com/esphome/esphome.io).

View File

@@ -15,11 +15,6 @@ inputs:
description: "Version to build" description: "Version to build"
required: true required: true
example: "2023.12.0" example: "2023.12.0"
base_os:
description: "Base OS to use"
required: false
default: "debian"
example: "debian"
runs: runs:
using: "composite" using: "composite"
steps: steps:
@@ -47,7 +42,7 @@ runs:
- name: Build and push to ghcr by digest - name: Build and push to ghcr by digest
id: build-ghcr id: build-ghcr
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
env: env:
DOCKER_BUILD_SUMMARY: false DOCKER_BUILD_SUMMARY: false
DOCKER_BUILD_RECORD_UPLOAD: false DOCKER_BUILD_RECORD_UPLOAD: false
@@ -60,7 +55,6 @@ runs:
build-args: | build-args: |
BUILD_TYPE=${{ inputs.build_type }} BUILD_TYPE=${{ inputs.build_type }}
BUILD_VERSION=${{ inputs.version }} BUILD_VERSION=${{ inputs.version }}
BUILD_OS=${{ inputs.base_os }}
outputs: | outputs: |
type=image,name=ghcr.io/${{ steps.tags.outputs.image_name }},push-by-digest=true,name-canonical=true,push=true type=image,name=ghcr.io/${{ steps.tags.outputs.image_name }},push-by-digest=true,name-canonical=true,push=true
@@ -73,7 +67,7 @@ runs:
- name: Build and push to dockerhub by digest - name: Build and push to dockerhub by digest
id: build-dockerhub id: build-dockerhub
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7.1.0 uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
env: env:
DOCKER_BUILD_SUMMARY: false DOCKER_BUILD_SUMMARY: false
DOCKER_BUILD_RECORD_UPLOAD: false DOCKER_BUILD_RECORD_UPLOAD: false
@@ -86,7 +80,6 @@ runs:
build-args: | build-args: |
BUILD_TYPE=${{ inputs.build_type }} BUILD_TYPE=${{ inputs.build_type }}
BUILD_VERSION=${{ inputs.version }} BUILD_VERSION=${{ inputs.version }}
BUILD_OS=${{ inputs.base_os }}
outputs: | outputs: |
type=image,name=docker.io/${{ steps.tags.outputs.image_name }},push-by-digest=true,name-canonical=true,push=true type=image,name=docker.io/${{ steps.tags.outputs.image_name }},push-by-digest=true,name-canonical=true,push=true

View File

@@ -0,0 +1,52 @@
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 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.
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:
- name: Resolve ESP-IDF version for cache key
# The native-IDF version is pinned in code, not in any file that feeds the
# other cache keys, so resolve it explicitly. Keying on it means the cache
# invalidates on a version bump (actions/cache never overwrites a key).
id: version
shell: bash
run: |
. venv/bin/activate
if [ "${{ inputs.framework }}" = "arduino" ]; then
version=$(python -c 'from esphome.components.esp32 import ARDUINO_FRAMEWORK_VERSION_LOOKUP as A, ARDUINO_IDF_VERSION_LOOKUP as L; print(L[A["recommended"]])')
else
version=$(python -c 'from esphome.components.esp32 import ESP_IDF_FRAMEWORK_VERSION_LOOKUP as L; print(L["recommended"])')
fi
echo "version=$version" >> "$GITHUB_OUTPUT"
# Mirror the adjacent PlatformIO cache: only dev-branch runs write the
# shared cache (so it lives in the default-branch scope readable by all
# 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' && 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' || inputs.restore-only == 'true'
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.esphome-idf
key: ${{ runner.os }}-esphome-idf-${{ steps.version.outputs.version }}

View File

@@ -27,6 +27,18 @@ runs:
path: venv path: venv
# yamllint disable-line rule:line-length # yamllint disable-line rule:line-length
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{ inputs.cache-key }} key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{ inputs.cache-key }}
- name: Set up uv
# Only needed on cache miss to populate the venv. ``uv pip install``
# detects the activated venv via ``VIRTUAL_ENV`` so the venv layout
# downstream jobs rely on is preserved.
if: steps.cache-venv.outputs.cache-hit != 'true'
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
with:
enable-cache: true
# Pin uv version so the action does not have to fetch the
# manifest from raw.githubusercontent.com on every cache
# miss; that fetch flakes on Windows runners.
version: "0.11.15"
- name: Create Python virtual environment - name: Create Python virtual environment
if: steps.cache-venv.outputs.cache-hit != 'true' && runner.os != 'Windows' if: steps.cache-venv.outputs.cache-hit != 'true' && runner.os != 'Windows'
shell: bash shell: bash
@@ -34,8 +46,8 @@ runs:
python -m venv venv python -m venv venv
source venv/bin/activate source venv/bin/activate
python --version python --version
pip install -r requirements.txt -r requirements_test.txt uv pip install -r requirements.txt -r requirements_test.txt
pip install -e . uv pip install -e .
- name: Create Python virtual environment - name: Create Python virtual environment
if: steps.cache-venv.outputs.cache-hit != 'true' && runner.os == 'Windows' if: steps.cache-venv.outputs.cache-hit != 'true' && runner.os == 'Windows'
shell: bash shell: bash
@@ -43,5 +55,5 @@ runs:
python -m venv venv python -m venv venv
source ./venv/Scripts/activate source ./venv/Scripts/activate
python --version python --version
pip install -r requirements.txt -r requirements_test.txt uv pip install -r requirements.txt -r requirements_test.txt
pip install -e . uv pip install -e .

View File

@@ -5,6 +5,7 @@ updates:
directory: "/" directory: "/"
schedule: schedule:
interval: daily interval: daily
open-pull-requests-limit: 10
ignore: ignore:
# Hypotehsis is only used for testing and is updated quite often # Hypotehsis is only used for testing and is updated quite often
- dependency-name: hypothesis - dependency-name: hypothesis

View File

@@ -35,6 +35,9 @@ module.exports = {
], ],
DOCS_PR_PATTERNS: [ DOCS_PR_PATTERNS: [
/https:\/\/github\.com\/esphome\/esphome\.io\/pull\/\d+/,
/esphome\/esphome\.io#\d+/,
// Keep matching the old esphome-docs name during the transition period
/https:\/\/github\.com\/esphome\/esphome-docs\/pull\/\d+/, /https:\/\/github\.com\/esphome\/esphome-docs\/pull\/\d+/,
/esphome\/esphome-docs#\d+/ /esphome\/esphome-docs#\d+/
] ]

View File

@@ -107,6 +107,8 @@ async function detectNewPlatforms(github, context, prFiles, apiData) {
/^esphome\/components\/([^\/]+)\/([^\/]+)\/__init__\.py$/, /^esphome\/components\/([^\/]+)\/([^\/]+)\/__init__\.py$/,
]; ];
const removedFiles = new Set(prFiles.filter(file => file.status === 'removed').map(file => file.filename));
for (const file of addedFiles) { for (const file of addedFiles) {
for (const re of platformPathPatterns) { for (const re of platformPathPatterns) {
const match = file.match(re); const match = file.match(re);
@@ -114,6 +116,12 @@ async function detectNewPlatforms(github, context, prFiles, apiData) {
const platform = match[2]; const platform = match[2];
if (!apiData.platformComponents.includes(platform)) break; if (!apiData.platformComponents.includes(platform)) break;
// Skip if this is a restructure between flat and subdirectory forms (either direction):
// <component>/<platform>.py <-> <component>/<platform>/__init__.py
const flatEquivalent = `esphome/components/${match[1]}/${platform}.py`;
const subdirEquivalent = `esphome/components/${match[1]}/${platform}/__init__.py`;
if (removedFiles.has(flatEquivalent) || removedFiles.has(subdirEquivalent)) break;
labels.add('new-platform'); labels.add('new-platform');
const content = await fetchPrFileContent(github, context, file); const content = await fetchPrFileContent(github, context, file);
if (content === null) { if (content === null) {

View File

@@ -0,0 +1,7 @@
{
"name": "auto-label-pr",
"private": true,
"scripts": {
"test": "node --test tests/*.test.js"
}
}

View File

@@ -0,0 +1,147 @@
const { describe, it } = require('node:test');
const assert = require('node:assert/strict');
const { detectNewPlatforms, detectNewComponents } = require('../detectors');
// Minimal GitHub API mock — only repos.getContent is called by detectNewPlatforms/detectNewComponents
// to check for CONFIG_SCHEMA in newly added files.
function makeGithub(content = '') {
return {
rest: {
repos: {
getContent: async () => ({
data: { content: Buffer.from(content).toString('base64') }
})
}
}
};
}
const CONTEXT = {
repo: { owner: 'esphome', repo: 'esphome' },
payload: { pull_request: { head: { sha: 'abc123' }, base: { ref: 'dev' } } }
};
const API_DATA = {
targetPlatforms: ['esp32', 'esp8266', 'rp2040'],
platformComponents: ['cover', 'sensor', 'binary_sensor', 'switch', 'light', 'fan', 'climate', 'valve']
};
const WITH_SCHEMA = 'CONFIG_SCHEMA = cv.Schema({})';
const WITHOUT_SCHEMA = 'CODEOWNERS = ["@esphome/core"]';
// ---------------------------------------------------------------------------
// detectNewPlatforms
// ---------------------------------------------------------------------------
describe('detectNewPlatforms', () => {
describe('restructure detection (no false positives)', () => {
it('flat .py -> subdir __init__.py is not a new platform', async () => {
const prFiles = [
{ filename: 'esphome/components/endstop/cover.py', status: 'removed' },
{ filename: 'esphome/components/endstop/cover/__init__.py', status: 'added' },
];
const result = await detectNewPlatforms(makeGithub(WITH_SCHEMA), CONTEXT, prFiles, API_DATA);
assert.equal(result.labels.size, 0);
assert.equal(result.hasYamlLoadable, false);
});
it('subdir __init__.py -> flat .py is not a new platform', async () => {
const prFiles = [
{ filename: 'esphome/components/endstop/cover/__init__.py', status: 'removed' },
{ filename: 'esphome/components/endstop/cover.py', status: 'added' },
];
const result = await detectNewPlatforms(makeGithub(WITH_SCHEMA), CONTEXT, prFiles, API_DATA);
assert.equal(result.labels.size, 0);
assert.equal(result.hasYamlLoadable, false);
});
});
describe('genuine new platforms', () => {
it('new subdir platform with CONFIG_SCHEMA sets new-platform and hasYamlLoadable', async () => {
const prFiles = [
{ filename: 'esphome/components/my_sensor/cover/__init__.py', status: 'added' },
];
const result = await detectNewPlatforms(makeGithub(WITH_SCHEMA), CONTEXT, prFiles, API_DATA);
assert.ok(result.labels.has('new-platform'));
assert.equal(result.hasYamlLoadable, true);
});
it('new flat platform with CONFIG_SCHEMA sets new-platform and hasYamlLoadable', async () => {
const prFiles = [
{ filename: 'esphome/components/my_sensor/cover.py', status: 'added' },
];
const result = await detectNewPlatforms(makeGithub(WITH_SCHEMA), CONTEXT, prFiles, API_DATA);
assert.ok(result.labels.has('new-platform'));
assert.equal(result.hasYamlLoadable, true);
});
it('new platform without CONFIG_SCHEMA sets new-platform but not hasYamlLoadable', async () => {
const prFiles = [
{ filename: 'esphome/components/my_sensor/cover.py', status: 'added' },
];
const result = await detectNewPlatforms(makeGithub(WITHOUT_SCHEMA), CONTEXT, prFiles, API_DATA);
assert.ok(result.labels.has('new-platform'));
assert.equal(result.hasYamlLoadable, false);
});
it('non-platform file addition produces no labels', async () => {
const prFiles = [
{ filename: 'esphome/components/my_sensor/sensor.py', status: 'added' },
];
// Override platformComponents so 'sensor' is not a recognized platform -> no label expected.
const nonPlatformApiData = { ...API_DATA, platformComponents: ['cover'] };
const result = await detectNewPlatforms(makeGithub(WITH_SCHEMA), CONTEXT, prFiles, nonPlatformApiData);
assert.equal(result.labels.size, 0);
assert.equal(result.hasYamlLoadable, false);
});
});
});
// ---------------------------------------------------------------------------
// detectNewComponents
// ---------------------------------------------------------------------------
describe('detectNewComponents', () => {
it('new top-level __init__.py sets new-component', async () => {
const prFiles = [
{ filename: 'esphome/components/actuator/__init__.py', status: 'added', },
];
const result = await detectNewComponents(makeGithub(WITHOUT_SCHEMA), CONTEXT, prFiles);
assert.ok(result.labels.has('new-component'));
assert.equal(result.hasYamlLoadable, false);
});
it('new top-level __init__.py with CONFIG_SCHEMA sets hasYamlLoadable', async () => {
const prFiles = [
{ filename: 'esphome/components/my_component/__init__.py', status: 'added' },
];
const result = await detectNewComponents(makeGithub(WITH_SCHEMA), CONTEXT, prFiles);
assert.ok(result.labels.has('new-component'));
assert.equal(result.hasYamlLoadable, true);
});
it('new top-level __init__.py with IS_TARGET_PLATFORM sets new-target-platform', async () => {
const prFiles = [
{ filename: 'esphome/components/my_platform/__init__.py', status: 'added' },
];
const result = await detectNewComponents(makeGithub('IS_TARGET_PLATFORM = True'), CONTEXT, prFiles);
assert.ok(result.labels.has('new-component'));
assert.ok(result.labels.has('new-target-platform'));
});
it('modified __init__.py does not set new-component', async () => {
const prFiles = [
{ filename: 'esphome/components/existing/__init__.py', status: 'modified' },
];
const result = await detectNewComponents(makeGithub(WITH_SCHEMA), CONTEXT, prFiles);
assert.equal(result.labels.size, 0);
});
it('nested __init__.py does not set new-component', async () => {
const prFiles = [
{ filename: 'esphome/components/endstop/cover/__init__.py', status: 'added' },
];
const result = await detectNewComponents(makeGithub(WITH_SCHEMA), CONTEXT, prFiles);
assert.equal(result.labels.size, 0);
});
});

View File

@@ -41,7 +41,6 @@ function hasCoreChanges(changedFiles) {
*/ */
function hasDashboardChanges(changedFiles) { function hasDashboardChanges(changedFiles) {
return changedFiles.some(file => return changedFiles.some(file =>
file.startsWith('esphome/dashboard/') ||
file.startsWith('esphome/components/dashboard_import/') file.startsWith('esphome/components/dashboard_import/')
); );
} }

View File

@@ -24,7 +24,7 @@ jobs:
if: github.event.pull_request.state == 'open' && (github.event.action != 'labeled' || github.event.sender.type != 'Bot') if: github.event.pull_request.state == 'open' && (github.event.action != 'labeled' || github.event.sender.type != 'Bot')
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Generate a token - name: Generate a token
id: generate-token id: generate-token

View File

@@ -21,11 +21,21 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Python - name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with: with:
python-version: "3.11" python-version: "3.11"
- name: Set up uv
# ``--system`` (below) installs into the setup-python interpreter;
# no venv is created or restored by this workflow.
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
with:
enable-cache: true
# Pin uv version so the action does not have to fetch the
# manifest from raw.githubusercontent.com on every cache
# miss; that fetch flakes on Windows runners.
version: "0.11.15"
- name: Install apt dependencies - name: Install apt dependencies
run: | run: |
@@ -34,7 +44,7 @@ jobs:
sudo apt install -y protobuf-compiler sudo apt install -y protobuf-compiler
protoc --version protoc --version
- name: Install python dependencies - name: Install python dependencies
run: pip install aioesphomeapi -c requirements.txt -r requirements_dev.txt run: uv pip install --system aioesphomeapi -c requirements.txt -r requirements_dev.txt
- name: Generate files - name: Generate files
run: script/api_protobuf/api_protobuf.py run: script/api_protobuf/api_protobuf.py
- name: Check for changes - name: Check for changes

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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- 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.'
});
}
}

View File

@@ -1,28 +1,41 @@
--- ---
name: CI for docker images 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: on:
push:
branches: [dev, beta, release]
paths:
- "docker/**"
- ".github/workflows/ci-docker.yml"
- "requirements*.txt"
- "platformio.ini"
- "script/platformio_install_deps.py"
pull_request: pull_request:
paths: paths:
# Docker image and its build inputs.
- "docker/**" - "docker/**"
- ".github/workflows/ci-docker.yml" - ".github/workflows/ci-docker.yml"
- "requirements*.txt" - "requirements*.txt"
- "pyproject.toml"
- "platformio.ini" - "platformio.ini"
- "esphome/idf_component.yml"
- "script/platformio_install_deps.py" - "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: permissions:
contents: read # actions/checkout only; the build does not push images contents: read # actions/checkout only
concurrency: concurrency:
# yamllint disable-line rule:line-length # yamllint disable-line rule:line-length
@@ -33,6 +46,9 @@ jobs:
check-docker: check-docker:
name: Build docker containers name: Build docker containers
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
permissions:
contents: read # actions/checkout to load Dockerfile and build context
packages: write # push branch-tagged images to ghcr.io for local testing
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@@ -41,23 +57,161 @@ jobs:
- "ha-addon" - "ha-addon"
- "docker" - "docker"
# - "lint" # - "lint"
outputs:
tag: ${{ steps.tag.outputs.tag }}
push: ${{ steps.tag.outputs.push }}
steps: steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Python - name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with: with:
python-version: "3.11" python-version: "3.11"
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
- name: Set TAG - name: Determine tag and whether to push
id: tag
run: | run: |
echo "TAG=check" >> $GITHUB_ENV # Sanitize the branch name into a valid docker tag: replace invalid
# characters, ensure the first character is valid (tags must start
# with [A-Za-z0-9_]), and cap the length at 128 characters.
branch="${{ github.head_ref || github.ref_name }}"
tag="${branch//[^a-zA-Z0-9_.-]/-}"
case "$tag" in
[a-zA-Z0-9_]*) ;;
*) tag="pr-${tag}" ;;
esac
tag="${tag:0:128}"
echo "tag=${tag}" >> "$GITHUB_OUTPUT"
# Only push branch images for same-repo pull requests. Push events
# only fire for dev/beta/release, whose images are owned by the
# release pipeline -- never overwrite those from here.
if [ "${{ github.event_name }}" = "pull_request" ] \
&& [ "${{ github.repository }}" = "esphome/esphome" ] \
&& [ "${{ github.event.pull_request.head.repo.full_name }}" = "esphome/esphome" ]; then
echo "push=true" >> "$GITHUB_OUTPUT"
else
echo "push=false" >> "$GITHUB_OUTPUT"
fi
- name: Log in to the GitHub container registry
if: steps.tag.outputs.push == 'true'
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Run build - name: Run build
run: | run: |
docker/build.py \ docker/build.py \
--tag "${TAG}" \ --tag "${{ steps.tag.outputs.tag }}" \
--arch "${{ matrix.os == 'ubuntu-24.04-arm' && 'aarch64' || 'amd64' }}" \ --arch "${{ matrix.os == 'ubuntu-24.04-arm' && 'aarch64' || 'amd64' }}" \
--build-type "${{ matrix.build_type }}" \ --build-type "${{ matrix.build_type }}" \
build --registry ghcr \
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
needs: [check-docker]
if: needs.check-docker.outputs.push == 'true'
runs-on: ubuntu-24.04
permissions:
contents: read # actions/checkout to run docker/build.py
packages: write # buildx imagetools writes the multi-arch tag to ghcr.io
strategy:
fail-fast: false
matrix:
build_type:
- "ha-addon"
- "docker"
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.11"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
- name: Log in to the GitHub container registry
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Create and push manifest
run: |
docker/build.py \
--tag "${{ needs.check-docker.outputs.tag }}" \
--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"

27
.github/workflows/ci-github-scripts.yml vendored Normal file
View File

@@ -0,0 +1,27 @@
name: CI - GitHub Scripts
on:
push:
branches: [dev, beta, release]
paths:
- ".github/scripts/**"
- ".github/workflows/ci-github-scripts.yml"
pull_request:
paths:
- ".github/scripts/**"
- ".github/workflows/ci-github-scripts.yml"
permissions:
contents: read
jobs:
test-auto-label-pr:
name: Test auto-label-pr scripts
runs-on: ubuntu-latest
steps:
- name: Check out code from GitHub
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Run tests
working-directory: .github/scripts/auto-label-pr
run: npm test

View File

@@ -49,7 +49,7 @@ jobs:
- name: Check out code from base repository - name: Check out code from base repository
if: steps.pr.outputs.skip != 'true' if: steps.pr.outputs.skip != 'true'
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
# Always check out from the base repository (esphome/esphome), never from forks # 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 # Use the PR's target branch to ensure we run trusted code from the main repo

View File

@@ -6,14 +6,6 @@ on:
branches: [dev, beta, release] branches: [dev, beta, release]
pull_request: pull_request:
paths:
- "**"
- "!.github/workflows/*.yml"
- "!.github/actions/build-image/*"
- ".github/workflows/ci.yml"
- "!.yamllint"
- "!.github/dependabot.yml"
- "!docker/**"
merge_group: merge_group:
permissions: permissions:
@@ -36,7 +28,7 @@ jobs:
cache-key: ${{ steps.cache-key.outputs.key }} cache-key: ${{ steps.cache-key.outputs.key }}
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Generate cache-key - name: Generate cache-key
id: cache-key id: cache-key
run: echo key="${{ hashFiles('requirements.txt', 'requirements_dev.txt', 'requirements_test.txt', '.pre-commit-config.yaml') }}" >> $GITHUB_OUTPUT run: echo key="${{ hashFiles('requirements.txt', 'requirements_dev.txt', 'requirements_test.txt', '.pre-commit-config.yaml') }}" >> $GITHUB_OUTPUT
@@ -52,14 +44,26 @@ jobs:
path: venv path: venv
# yamllint disable-line rule:line-length # yamllint disable-line rule:line-length
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{ steps.cache-key.outputs.key }} key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{ steps.cache-key.outputs.key }}
- name: Set up uv
# Only needed on cache miss to populate the venv. ``uv pip install``
# detects the activated venv via ``VIRTUAL_ENV`` so downstream jobs
# that ``. venv/bin/activate`` see an identical layout.
if: steps.cache-venv.outputs.cache-hit != 'true'
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
with:
enable-cache: true
# Pin uv version so the action does not have to fetch the
# manifest from raw.githubusercontent.com on every cache
# miss; that fetch flakes on Windows runners.
version: "0.11.15"
- name: Create Python virtual environment - name: Create Python virtual environment
if: steps.cache-venv.outputs.cache-hit != 'true' if: steps.cache-venv.outputs.cache-hit != 'true'
run: | run: |
python -m venv venv python -m venv venv
. venv/bin/activate . venv/bin/activate
python --version python --version
pip install -r requirements.txt -r requirements_dev.txt -r requirements_test.txt pre-commit uv pip install -r requirements.txt -r requirements_dev.txt -r requirements_test.txt pre-commit
pip install -e . uv pip install -e .
pylint: pylint:
name: Check pylint name: Check pylint
@@ -70,7 +74,7 @@ jobs:
if: needs.determine-jobs.outputs.python-linters == 'true' if: needs.determine-jobs.outputs.python-linters == 'true'
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@@ -89,9 +93,11 @@ jobs:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
needs: needs:
- common - common
- determine-jobs
if: needs.determine-jobs.outputs.core-ci == 'true'
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@@ -107,6 +113,7 @@ jobs:
script/build_language_schema.py --check script/build_language_schema.py --check
script/generate-esp32-boards.py --check script/generate-esp32-boards.py --check
script/generate-rp2040-boards.py --check script/generate-rp2040-boards.py --check
script/ci_check_duplicate_test_ids.py
import-time: import-time:
name: Check import esphome.__main__ time name: Check import esphome.__main__ time
@@ -117,7 +124,7 @@ jobs:
if: needs.determine-jobs.outputs.import-time == 'true' if: needs.determine-jobs.outputs.import-time == 'true'
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@@ -145,11 +152,11 @@ jobs:
if: needs.determine-jobs.outputs.device-builder == 'true' if: needs.determine-jobs.outputs.device-builder == 'true'
steps: steps:
- name: Check out esphome (this PR) - name: Check out esphome (this PR)
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
path: esphome path: esphome
- name: Check out esphome/device-builder - name: Check out esphome/device-builder
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
repository: esphome/device-builder repository: esphome/device-builder
ref: main ref: main
@@ -164,9 +171,13 @@ jobs:
# install step (order-of-magnitude faster on cold boots, # install step (order-of-magnitude faster on cold boots,
# with its own wheel cache). actions/setup-python still # with its own wheel cache). actions/setup-python still
# provides the interpreter. # provides the interpreter.
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
with: with:
enable-cache: true enable-cache: true
# Pin uv version so the action does not have to fetch the
# manifest from raw.githubusercontent.com on every cache
# miss; that fetch flakes on Windows runners.
version: "0.11.15"
- name: Install device-builder + esphome from PR - name: Install device-builder + esphome from PR
# Install device-builder with its esphome + test extras # Install device-builder with its esphome + test extras
# first so its pinned versions of pytest/etc. land, then # first so its pinned versions of pytest/etc. land, then
@@ -179,9 +190,12 @@ jobs:
- name: Run device-builder pytest - name: Run device-builder pytest
# ``-n auto`` runs under pytest-xdist (matches device-builder's # ``-n auto`` runs under pytest-xdist (matches device-builder's
# own CI). No ``--cov`` here -- this is purely a downstream # own CI). No ``--cov`` here -- this is purely a downstream
# smoke check against this PR's esphome code. # smoke check against this PR's esphome code. ``tests/e2e/slow``
# is excluded: those are real multi-minute toolchain compiles
# (LibreTiny SDK clone, native ESP-IDF install) that device-builder
# runs in its own dedicated jobs, not this smoke check.
working-directory: device-builder working-directory: device-builder
run: pytest -q -n auto --maxfail=5 --durations=10 --no-cov --ignore=tests/benchmarks run: pytest -q -n auto --maxfail=5 --durations=30 --no-cov --ignore=tests/benchmarks --ignore=tests/e2e/slow
pytest: pytest:
name: Run pytest name: Run pytest
@@ -207,9 +221,11 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
needs: needs:
- common - common
- determine-jobs
if: needs.determine-jobs.outputs.core-ci == 'true'
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python - name: Restore Python
id: restore-python id: restore-python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
@@ -222,14 +238,14 @@ jobs:
if: matrix.os == 'windows-latest' if: matrix.os == 'windows-latest'
run: | run: |
. ./venv/Scripts/activate.ps1 . ./venv/Scripts/activate.ps1
pytest -vv --cov-report=xml --tb=native -n auto tests --ignore=tests/integration/ pytest -vv --cov-report=xml --tb=native --durations=30 -n auto tests --ignore=tests/integration/
- name: Run pytest - name: Run pytest
if: matrix.os == 'ubuntu-latest' || matrix.os == 'macOS-latest' if: matrix.os == 'ubuntu-latest' || matrix.os == 'macOS-latest'
run: | run: |
. venv/bin/activate . venv/bin/activate
pytest -vv --cov-report=xml --tb=native -n auto tests --ignore=tests/integration/ pytest -vv --cov-report=xml --tb=native --durations=30 -n auto tests --ignore=tests/integration/
- name: Upload coverage to Codecov - name: Upload coverage to Codecov
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0 uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
with: with:
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}
- name: Save Python virtual environment cache - name: Save Python virtual environment cache
@@ -245,6 +261,7 @@ jobs:
needs: needs:
- common - common
outputs: outputs:
core-ci: ${{ steps.determine.outputs.core-ci }}
integration-tests: ${{ steps.determine.outputs.integration-tests }} integration-tests: ${{ steps.determine.outputs.integration-tests }}
integration-test-buckets: ${{ steps.determine.outputs.integration-test-buckets }} integration-test-buckets: ${{ steps.determine.outputs.integration-test-buckets }}
clang-tidy: ${{ steps.determine.outputs.clang-tidy }} clang-tidy: ${{ steps.determine.outputs.clang-tidy }}
@@ -253,8 +270,8 @@ jobs:
python-linters: ${{ steps.determine.outputs.python-linters }} python-linters: ${{ steps.determine.outputs.python-linters }}
import-time: ${{ steps.determine.outputs.import-time }} import-time: ${{ steps.determine.outputs.import-time }}
device-builder: ${{ steps.determine.outputs.device-builder }} device-builder: ${{ steps.determine.outputs.device-builder }}
native-idf: ${{ steps.determine.outputs.native-idf }} esp32-platformio: ${{ steps.determine.outputs.esp32-platformio }}
native-idf-components: ${{ steps.determine.outputs.native-idf-components }} esp32-platformio-components: ${{ steps.determine.outputs.esp32-platformio-components }}
changed-components: ${{ steps.determine.outputs.changed-components }} changed-components: ${{ steps.determine.outputs.changed-components }}
changed-components-with-tests: ${{ steps.determine.outputs.changed-components-with-tests }} changed-components-with-tests: ${{ steps.determine.outputs.changed-components-with-tests }}
directly-changed-components-with-tests: ${{ steps.determine.outputs.directly-changed-components-with-tests }} directly-changed-components-with-tests: ${{ steps.determine.outputs.directly-changed-components-with-tests }}
@@ -268,7 +285,7 @@ jobs:
benchmarks: ${{ steps.determine.outputs.benchmarks }} benchmarks: ${{ steps.determine.outputs.benchmarks }}
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
# Fetch enough history to find the merge base # Fetch enough history to find the merge base
fetch-depth: 2 fetch-depth: 2
@@ -298,6 +315,7 @@ jobs:
echo "$output" | jq echo "$output" | jq
# Extract individual fields # Extract individual fields
echo "core-ci=$(echo "$output" | jq -r '.core_ci')" >> $GITHUB_OUTPUT
echo "integration-tests=$(echo "$output" | jq -r '.integration_tests')" >> $GITHUB_OUTPUT echo "integration-tests=$(echo "$output" | jq -r '.integration_tests')" >> $GITHUB_OUTPUT
echo "integration-test-buckets=$(echo "$output" | jq -c '.integration_test_buckets')" >> $GITHUB_OUTPUT echo "integration-test-buckets=$(echo "$output" | jq -c '.integration_test_buckets')" >> $GITHUB_OUTPUT
echo "clang-tidy=$(echo "$output" | jq -r '.clang_tidy')" >> $GITHUB_OUTPUT echo "clang-tidy=$(echo "$output" | jq -r '.clang_tidy')" >> $GITHUB_OUTPUT
@@ -306,8 +324,8 @@ jobs:
echo "python-linters=$(echo "$output" | jq -r '.python_linters')" >> $GITHUB_OUTPUT echo "python-linters=$(echo "$output" | jq -r '.python_linters')" >> $GITHUB_OUTPUT
echo "import-time=$(echo "$output" | jq -r '.import_time')" >> $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 "device-builder=$(echo "$output" | jq -r '.device_builder')" >> $GITHUB_OUTPUT
echo "native-idf=$(echo "$output" | jq -r '.native_idf')" >> $GITHUB_OUTPUT echo "esp32-platformio=$(echo "$output" | jq -r '.esp32_platformio')" >> $GITHUB_OUTPUT
echo "native-idf-components=$(echo "$output" | jq -r '.native_idf_components')" >> $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=$(echo "$output" | jq -c '.changed_components')" >> $GITHUB_OUTPUT
echo "changed-components-with-tests=$(echo "$output" | jq -c '.changed_components_with_tests')" >> $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 echo "directly-changed-components-with-tests=$(echo "$output" | jq -c '.directly_changed_components_with_tests')" >> $GITHUB_OUTPUT
@@ -339,7 +357,7 @@ jobs:
bucket: ${{ fromJson(needs.determine-jobs.outputs.integration-test-buckets) }} bucket: ${{ fromJson(needs.determine-jobs.outputs.integration-test-buckets) }}
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Python 3.13 - name: Set up Python 3.13
id: python id: python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
@@ -351,14 +369,24 @@ jobs:
with: with:
path: venv path: venv
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{ needs.common.outputs.cache-key }} key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{ needs.common.outputs.cache-key }}
- name: Set up uv
# Only needed on cache miss to populate the venv.
if: steps.cache-venv.outputs.cache-hit != 'true'
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
with:
enable-cache: true
# Pin uv version so the action does not have to fetch the
# manifest from raw.githubusercontent.com on every cache
# miss; that fetch flakes on Windows runners.
version: "0.11.15"
- name: Create Python virtual environment - name: Create Python virtual environment
if: steps.cache-venv.outputs.cache-hit != 'true' if: steps.cache-venv.outputs.cache-hit != 'true'
run: | run: |
python -m venv venv python -m venv venv
. venv/bin/activate . venv/bin/activate
python --version python --version
pip install -r requirements.txt -r requirements_test.txt uv pip install -r requirements.txt -r requirements_test.txt
pip install -e . uv pip install -e .
- name: Register matcher - name: Register matcher
run: echo "::add-matcher::.github/workflows/matchers/pytest.json" run: echo "::add-matcher::.github/workflows/matchers/pytest.json"
- name: Run integration tests - name: Run integration tests
@@ -370,7 +398,7 @@ jobs:
. venv/bin/activate . venv/bin/activate
mapfile -t test_files < <(echo "$BUCKET_TESTS" | jq -r '.[]') mapfile -t test_files < <(echo "$BUCKET_TESTS" | jq -r '.[]')
echo "Bucket ${{ matrix.bucket.name }}: running ${#test_files[@]} integration tests" echo "Bucket ${{ matrix.bucket.name }}: running ${#test_files[@]} integration tests"
pytest -vv --no-cov --tb=native -n auto "${test_files[@]}" pytest -vv --no-cov --tb=native --durations=30 -n auto "${test_files[@]}"
cpp-unit-tests: cpp-unit-tests:
name: Run C++ unit tests name: Run C++ unit tests
@@ -381,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 != '[]') 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: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
@@ -410,7 +438,7 @@ jobs:
(github.event_name == 'pull_request' && needs.determine-jobs.outputs.benchmarks == 'true') (github.event_name == 'pull_request' && needs.determine-jobs.outputs.benchmarks == 'true')
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
@@ -428,7 +456,7 @@ jobs:
echo "binary=$BINARY" >> $GITHUB_OUTPUT echo "binary=$BINARY" >> $GITHUB_OUTPUT
- name: Run CodSpeed benchmarks - name: Run CodSpeed benchmarks
uses: CodSpeedHQ/action@3194d9a39c4d46684cb44bf7207fc56626aad8fd # v4.15.1 uses: CodSpeedHQ/action@63f3e98b61959fe67f146a3ff022e4136fe9bb9c # v4.17.6
with: with:
run: | run: |
. venv/bin/activate . venv/bin/activate
@@ -445,6 +473,8 @@ jobs:
if: needs.determine-jobs.outputs.clang-tidy == 'true' if: needs.determine-jobs.outputs.clang-tidy == 'true'
env: env:
GH_TOKEN: ${{ github.token }} GH_TOKEN: ${{ github.token }}
# esp32-arduino-tidy installs ESP-IDF natively; share the native IDF cache.
ESPHOME_ESP_IDF_PREFIX: ~/.esphome-idf
strategy: strategy:
fail-fast: false fail-fast: false
max-parallel: 2 max-parallel: 2
@@ -455,9 +485,9 @@ jobs:
options: --environment esp8266-arduino-tidy --grep USE_ESP8266 options: --environment esp8266-arduino-tidy --grep USE_ESP8266
pio_cache_key: tidyesp8266 pio_cache_key: tidyesp8266
- id: clang-tidy - id: clang-tidy
name: Run script/clang-tidy for ESP32 IDF name: Run script/clang-tidy for ESP32 Arduino
options: --environment esp32-idf-tidy --grep USE_ESP_IDF options: --environment esp32-arduino-tidy --grep USE_ARDUINO
pio_cache_key: tidyesp32-idf cache_idf: true
- id: clang-tidy - id: clang-tidy
name: Run script/clang-tidy for ZEPHYR name: Run script/clang-tidy for ZEPHYR
options: --environment nrf52-tidy --grep USE_ZEPHYR --grep USE_NRF52 options: --environment nrf52-tidy --grep USE_ZEPHYR --grep USE_NRF52
@@ -466,7 +496,7 @@ jobs:
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
# Need history for HEAD~1 to work for checking changed files # Need history for HEAD~1 to work for checking changed files
fetch-depth: 2 fetch-depth: 2
@@ -478,44 +508,40 @@ jobs:
cache-key: ${{ needs.common.outputs.cache-key }} cache-key: ${{ needs.common.outputs.cache-key }}
- name: Cache platformio - name: Cache platformio
if: github.ref == 'refs/heads/dev' if: github.ref == 'refs/heads/dev' && matrix.pio_cache_key
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with: with:
path: ~/.platformio path: ~/.platformio
key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }} key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
- name: Cache platformio - name: Cache platformio
if: github.ref != 'refs/heads/dev' if: github.ref != 'refs/heads/dev' && matrix.pio_cache_key
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with: with:
path: ~/.platformio path: ~/.platformio
key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }} key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
- name: Cache ESP-IDF install
if: matrix.cache_idf
uses: ./.github/actions/cache-esp-idf
with:
framework: arduino
- name: Register problem matchers - name: Register problem matchers
run: | run: |
echo "::add-matcher::.github/workflows/matchers/gcc.json" echo "::add-matcher::.github/workflows/matchers/gcc.json"
echo "::add-matcher::.github/workflows/matchers/clang-tidy.json" echo "::add-matcher::.github/workflows/matchers/clang-tidy.json"
- name: Run 'pio run --list-targets -e esp32-idf-tidy'
if: matrix.name == 'Run script/clang-tidy for ESP32 IDF'
run: |
. venv/bin/activate
mkdir -p .temp
pio run --list-targets -e esp32-idf-tidy
- name: Check if full clang-tidy scan needed - name: Check if full clang-tidy scan needed
id: check_full_scan id: check_full_scan
run: | run: |
. venv/bin/activate . venv/bin/activate
# determine-jobs.clang-tidy-full-scan is true when core C++ changed # determine-jobs.clang-tidy-full-scan is true when core C++ or a
# OR the ci-run-all label forced --force-all. Independent of the # clang-tidy-relevant config file changed, or the ci-run-all label
# hash check, both must produce a full scan in the job itself. # forced --force-all.
if [ "${{ needs.determine-jobs.outputs.clang-tidy-full-scan }}" = "true" ]; then if [ "${{ needs.determine-jobs.outputs.clang-tidy-full-scan }}" = "true" ]; then
echo "full_scan=true" >> $GITHUB_OUTPUT echo "full_scan=true" >> $GITHUB_OUTPUT
echo "reason=determine_jobs" >> $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 else
echo "full_scan=false" >> $GITHUB_OUTPUT echo "full_scan=false" >> $GITHUB_OUTPUT
echo "reason=normal" >> $GITHUB_OUTPUT echo "reason=normal" >> $GITHUB_OUTPUT
@@ -541,7 +567,7 @@ jobs:
if: always() if: always()
clang-tidy-nosplit: clang-tidy-nosplit:
name: Run script/clang-tidy for ESP32 Arduino name: Run script/clang-tidy for ESP32 IDF
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
needs: needs:
- common - common
@@ -549,9 +575,11 @@ jobs:
if: needs.determine-jobs.outputs.clang-tidy-mode == 'nosplit' if: needs.determine-jobs.outputs.clang-tidy-mode == 'nosplit'
env: env:
GH_TOKEN: ${{ github.token }} GH_TOKEN: ${{ github.token }}
# esp32-idf-tidy installs ESP-IDF natively; share the native IDF cache.
ESPHOME_ESP_IDF_PREFIX: ~/.esphome-idf
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
# Need history for HEAD~1 to work for checking changed files # Need history for HEAD~1 to work for checking changed files
fetch-depth: 2 fetch-depth: 2
@@ -562,19 +590,8 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }} cache-key: ${{ needs.common.outputs.cache-key }}
- name: Cache platformio - name: Cache ESP-IDF install
if: github.ref == 'refs/heads/dev' uses: ./.github/actions/cache-esp-idf
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.platformio
key: platformio-tidyesp32-${{ hashFiles('platformio.ini') }}
- name: Cache platformio
if: github.ref != 'refs/heads/dev'
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.platformio
key: platformio-tidyesp32-${{ hashFiles('platformio.ini') }}
- name: Register problem matchers - name: Register problem matchers
run: | run: |
@@ -585,15 +602,12 @@ jobs:
id: check_full_scan id: check_full_scan
run: | run: |
. venv/bin/activate . venv/bin/activate
# determine-jobs.clang-tidy-full-scan is true when core C++ changed # determine-jobs.clang-tidy-full-scan is true when core C++ or a
# OR the ci-run-all label forced --force-all. Independent of the # clang-tidy-relevant config file changed, or the ci-run-all label
# hash check, both must produce a full scan in the job itself. # forced --force-all.
if [ "${{ needs.determine-jobs.outputs.clang-tidy-full-scan }}" = "true" ]; then if [ "${{ needs.determine-jobs.outputs.clang-tidy-full-scan }}" = "true" ]; then
echo "full_scan=true" >> $GITHUB_OUTPUT echo "full_scan=true" >> $GITHUB_OUTPUT
echo "reason=determine_jobs" >> $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 else
echo "full_scan=false" >> $GITHUB_OUTPUT echo "full_scan=false" >> $GITHUB_OUTPUT
echo "reason=normal" >> $GITHUB_OUTPUT echo "reason=normal" >> $GITHUB_OUTPUT
@@ -604,10 +618,10 @@ jobs:
. venv/bin/activate . venv/bin/activate
if [ "${{ steps.check_full_scan.outputs.full_scan }}" = "true" ]; then if [ "${{ steps.check_full_scan.outputs.full_scan }}" = "true" ]; then
echo "Running FULL clang-tidy scan (reason: ${{ steps.check_full_scan.outputs.reason }})" echo "Running FULL clang-tidy scan (reason: ${{ steps.check_full_scan.outputs.reason }})"
script/clang-tidy --all-headers --fix --environment esp32-arduino-tidy script/clang-tidy --all-headers --fix --environment esp32-idf-tidy
else else
echo "Running clang-tidy on changed files only" echo "Running clang-tidy on changed files only"
script/clang-tidy --all-headers --fix --changed --environment esp32-arduino-tidy script/clang-tidy --all-headers --fix --changed --environment esp32-idf-tidy
fi fi
env: env:
# Also cache libdeps, store them in a ~/.platformio subfolder # Also cache libdeps, store them in a ~/.platformio subfolder
@@ -626,27 +640,26 @@ jobs:
if: needs.determine-jobs.outputs.clang-tidy-mode == 'split' if: needs.determine-jobs.outputs.clang-tidy-mode == 'split'
env: env:
GH_TOKEN: ${{ github.token }} GH_TOKEN: ${{ github.token }}
# esp32-idf-tidy installs ESP-IDF natively; share the native IDF cache.
ESPHOME_ESP_IDF_PREFIX: ~/.esphome-idf
strategy: strategy:
fail-fast: false fail-fast: false
max-parallel: 2 max-parallel: 3
matrix: matrix:
include: include:
- id: clang-tidy - id: clang-tidy
name: Run script/clang-tidy for ESP32 Arduino 1/4 name: Run script/clang-tidy for ESP32 IDF 1/3
options: --environment esp32-arduino-tidy --split-num 4 --split-at 1 options: --environment esp32-idf-tidy --split-num 3 --split-at 1
- id: clang-tidy - id: clang-tidy
name: Run script/clang-tidy for ESP32 Arduino 2/4 name: Run script/clang-tidy for ESP32 IDF 2/3
options: --environment esp32-arduino-tidy --split-num 4 --split-at 2 options: --environment esp32-idf-tidy --split-num 3 --split-at 2
- id: clang-tidy - id: clang-tidy
name: Run script/clang-tidy for ESP32 Arduino 3/4 name: Run script/clang-tidy for ESP32 IDF 3/3
options: --environment esp32-arduino-tidy --split-num 4 --split-at 3 options: --environment esp32-idf-tidy --split-num 3 --split-at 3
- id: clang-tidy
name: Run script/clang-tidy for ESP32 Arduino 4/4
options: --environment esp32-arduino-tidy --split-num 4 --split-at 4
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
# Need history for HEAD~1 to work for checking changed files # Need history for HEAD~1 to work for checking changed files
fetch-depth: 2 fetch-depth: 2
@@ -657,19 +670,8 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }} cache-key: ${{ needs.common.outputs.cache-key }}
- name: Cache platformio - name: Cache ESP-IDF install
if: github.ref == 'refs/heads/dev' uses: ./.github/actions/cache-esp-idf
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.platformio
key: platformio-tidyesp32-${{ hashFiles('platformio.ini') }}
- name: Cache platformio
if: github.ref != 'refs/heads/dev'
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.platformio
key: platformio-tidyesp32-${{ hashFiles('platformio.ini') }}
- name: Register problem matchers - name: Register problem matchers
run: | run: |
@@ -680,15 +682,12 @@ jobs:
id: check_full_scan id: check_full_scan
run: | run: |
. venv/bin/activate . venv/bin/activate
# determine-jobs.clang-tidy-full-scan is true when core C++ changed # determine-jobs.clang-tidy-full-scan is true when core C++ or a
# OR the ci-run-all label forced --force-all. Independent of the # clang-tidy-relevant config file changed, or the ci-run-all label
# hash check, both must produce a full scan in the job itself. # forced --force-all.
if [ "${{ needs.determine-jobs.outputs.clang-tidy-full-scan }}" = "true" ]; then if [ "${{ needs.determine-jobs.outputs.clang-tidy-full-scan }}" = "true" ]; then
echo "full_scan=true" >> $GITHUB_OUTPUT echo "full_scan=true" >> $GITHUB_OUTPUT
echo "reason=determine_jobs" >> $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 else
echo "full_scan=false" >> $GITHUB_OUTPUT echo "full_scan=false" >> $GITHUB_OUTPUT
echo "reason=normal" >> $GITHUB_OUTPUT echo "reason=normal" >> $GITHUB_OUTPUT
@@ -712,6 +711,89 @@ jobs:
run: script/ci-suggest-changes run: script/ci-suggest-changes
if: always() if: always()
clang-tidy-esp32-variants:
name: ${{ matrix.name }}
runs-on: ubuntu-24.04
needs:
- common
- determine-jobs
if: needs.determine-jobs.outputs.clang-tidy == 'true'
env:
GH_TOKEN: ${{ github.token }}
# The variant tidy envs install ESP-IDF natively; share the native IDF cache.
ESPHOME_ESP_IDF_PREFIX: ~/.esphome-idf
strategy:
fail-fast: false
max-parallel: 3
matrix:
include:
- id: clang-tidy
name: Run script/clang-tidy for ESP32 S3
options: --environment esp32s3-idf-tidy --grep USE_ESP32_VARIANT_ESP32S3
- id: clang-tidy
name: Run script/clang-tidy for ESP32 P4
# P4 has no native Wi-Fi/BLE; those run over the hosted co-processor,
# so their code paths differ -- lint them under the P4 build too.
# yamllint disable-line rule:line-length
options: --environment esp32p4-idf-tidy --grep USE_ESP32_VARIANT_ESP32P4 --grep USE_ESP32_HOSTED --grep USE_WIFI --grep USE_BLE
- id: clang-tidy
name: Run script/clang-tidy for ESP32 C6
# yamllint disable-line rule:line-length
options: --environment esp32c6-idf-tidy --grep USE_ESP32_VARIANT_ESP32C6 --grep USE_OPENTHREAD --grep USE_ZIGBEE
steps:
- name: Check out code from GitHub
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
# Need history for HEAD~1 to work for checking changed files
fetch-depth: 2
- 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
uses: ./.github/actions/cache-esp-idf
- name: Register problem matchers
run: |
echo "::add-matcher::.github/workflows/matchers/gcc.json"
echo "::add-matcher::.github/workflows/matchers/clang-tidy.json"
- name: Check if full clang-tidy scan needed
id: check_full_scan
run: |
. venv/bin/activate
# 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
else
echo "full_scan=false" >> $GITHUB_OUTPUT
echo "reason=normal" >> $GITHUB_OUTPUT
fi
- name: Run clang-tidy
# Limited variant scan: only the files carrying that variant's code paths
# (no --all-headers; the comprehensive esp32-idf pass covers the shared tree).
run: |
. venv/bin/activate
if [ "${{ steps.check_full_scan.outputs.full_scan }}" = "true" ]; then
echo "Running FULL clang-tidy scan (reason: ${{ steps.check_full_scan.outputs.reason }})"
script/clang-tidy --fix ${{ matrix.options }}
else
echo "Running clang-tidy on changed files only"
script/clang-tidy --fix --changed ${{ matrix.options }}
fi
- name: Suggested changes
run: script/ci-suggest-changes
if: always()
test-build-components-split: test-build-components-split:
name: Test components batch (${{ matrix.components }}) name: Test components batch (${{ matrix.components }})
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
@@ -719,6 +801,10 @@ jobs:
- common - common
- determine-jobs - determine-jobs
if: github.event_name == 'pull_request' && fromJSON(needs.determine-jobs.outputs.component-test-count) > 0 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: strategy:
fail-fast: false fail-fast: false
max-parallel: ${{ (startsWith(github.base_ref, 'beta') || startsWith(github.base_ref, 'release')) && 8 || 4 }} max-parallel: ${{ (startsWith(github.base_ref, 'beta') || startsWith(github.base_ref, 'release')) && 8 || 4 }}
@@ -740,12 +826,18 @@ jobs:
version: 1.0 version: 1.0
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }} cache-key: ${{ needs.common.outputs.cache-key }}
- name: 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 - name: Validate and compile components with intelligent grouping
run: | run: |
. venv/bin/activate . venv/bin/activate
@@ -797,7 +889,7 @@ jobs:
fi fi
echo "" echo ""
# Show disk space before validation (after bind mounts setup) # Show disk space before validation
echo "Disk space before config validation:" echo "Disk space before config validation:"
df -h df -h
echo "" echo ""
@@ -849,23 +941,22 @@ jobs:
echo "All components in this batch are validate-only -- skipping compile stage." echo "All components in this batch are validate-only -- skipping compile stage."
fi fi
test-native-idf: test-esp32-platformio:
name: Test components with native ESP-IDF name: Test esp32 components with PlatformIO
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
needs: needs:
- common - common
- determine-jobs - 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: env:
ESPHOME_ESP_IDF_PREFIX: ~/.esphome-idf # Comma-joined subset of the esp32 PlatformIO representative component list,
# Comma-joined subset of the native-IDF representative component list, # computed by script/determine-jobs.py (esp32_platformio_components_to_test).
# computed by script/determine-jobs.py (native_idf_components_to_test).
# Single source of truth -- the full list lives in # Single source of truth -- the full list lives in
# script/determine-jobs.py::NATIVE_IDF_TEST_COMPONENTS. # script/determine-jobs.py::ESP32_PLATFORMIO_TEST_COMPONENTS.
TEST_COMPONENTS: ${{ needs.determine-jobs.outputs.native-idf-components }} TEST_COMPONENTS: ${{ needs.determine-jobs.outputs.esp32-platformio-components }}
steps: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
@@ -873,80 +964,34 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }} cache-key: ${{ needs.common.outputs.cache-key }}
- name: Cache ESPHome - name: Run PlatformIO compile test
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.esphome-idf
key: ${{ runner.os }}-esphome-${{ needs.common.outputs.cache-key }}
- name: Run native ESP-IDF compile test
run: | run: |
. venv/bin/activate . venv/bin/activate
# Check if /mnt has more free space than / before bind mounting
# Extract available space in KB for comparison
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"
# Only use /mnt if it has more space than /
if [ -n "$mnt_avail" ] && [ "$mnt_avail" -gt "$root_avail" ]; then
echo "Using /mnt for build files (more space available)"
# Bind mount PlatformIO directory to /mnt (tools, packages, build cache all go there)
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
# Bind mount test build directory to /mnt
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
echo "Testing components: $TEST_COMPONENTS" echo "Testing components: $TEST_COMPONENTS"
echo "" echo ""
# Show disk space before validation (after bind mounts setup) # compile validates config first, so a separate config pass is
echo "Disk space before config validation:" # redundant for this smoke test. ESP-IDF framework via PlatformIO:
df -h python3 script/test_build_components.py -e compile -t esp32-idf -c "$TEST_COMPONENTS" -f --toolchain platformio
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
echo "" echo ""
echo "Config validation passed! Starting compilation..." echo "ESP-IDF-via-PlatformIO build passed! Starting Arduino smoke test..."
echo "" echo ""
# Show disk space before compilation # Arduino framework via PlatformIO (only components with an esp32-ard test are built):
echo "Disk space before compilation:" python3 script/test_build_components.py -e compile -t esp32-ard -c "$TEST_COMPONENTS" -f --toolchain platformio
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 }}
pre-commit-ci-lite: pre-commit-ci-lite:
name: pre-commit.ci lite name: pre-commit.ci lite
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: needs:
- common - common
if: github.event_name == 'pull_request' && !startsWith(github.base_ref, 'beta') && !startsWith(github.base_ref, 'release') - determine-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: steps:
- name: Check out code from GitHub - name: Check out code from GitHub
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@@ -954,7 +999,7 @@ jobs:
cache-key: ${{ needs.common.outputs.cache-key }} cache-key: ${{ needs.common.outputs.cache-key }}
- uses: esphome/pre-commit-action@43cd1109c09c544d97196f7730ee5b2e0cc6d81e # v3.0.1 fork with pinned actions/cache - uses: esphome/pre-commit-action@43cd1109c09c544d97196f7730ee5b2e0cc6d81e # v3.0.1 fork with pinned actions/cache
env: env:
SKIP: pylint,clang-tidy-hash,ci-custom SKIP: pylint,ci-custom
- uses: pre-commit-ci/lite-action@5d6cc0eb514c891a40562a58a8e71576c5c7fb43 # v1.1.0 - uses: pre-commit-ci/lite-action@5d6cc0eb514c891a40562a58a8e71576c5c7fb43 # v1.1.0
if: always() if: always()
@@ -972,7 +1017,7 @@ jobs:
skip: ${{ steps.check-script.outputs.skip || steps.check-tests.outputs.skip }} skip: ${{ steps.check-script.outputs.skip || steps.check-tests.outputs.skip }}
steps: steps:
- name: Check out target branch - name: Check out target branch
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
ref: ${{ github.base_ref }} ref: ${{ github.base_ref }}
@@ -1154,7 +1199,7 @@ jobs:
flash_usage: ${{ steps.extract.outputs.flash_usage }} flash_usage: ${{ steps.extract.outputs.flash_usage }}
steps: steps:
- name: Check out PR branch - name: Check out PR branch
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@@ -1223,7 +1268,7 @@ jobs:
GH_TOKEN: ${{ github.token }} GH_TOKEN: ${{ github.token }}
steps: steps:
- name: Check out code - name: Check out code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python - name: Restore Python
uses: ./.github/actions/restore-python uses: ./.github/actions/restore-python
with: with:
@@ -1266,10 +1311,11 @@ jobs:
- clang-tidy-single - clang-tidy-single
- clang-tidy-nosplit - clang-tidy-nosplit
- clang-tidy-split - clang-tidy-split
- clang-tidy-esp32-variants
- determine-jobs - determine-jobs
- device-builder - device-builder
- test-build-components-split - test-build-components-split
- test-native-idf - test-esp32-platformio
- pre-commit-ci-lite - pre-commit-ci-lite
- memory-impact-target-branch - memory-impact-target-branch
- memory-impact-pr-branch - memory-impact-pr-branch
@@ -1285,4 +1331,7 @@ jobs:
# 1. The target branch has a build issue independent of this PR # 1. The target branch has a build issue independent of this PR
# 2. This PR fixes a build issue on the target branch # 2. This PR fixes a build issue on the target branch
# In either case, we only care that the PR branch builds successfully. # 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")'

View File

@@ -26,7 +26,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout base branch - name: Checkout base branch
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
ref: ${{ github.event.pull_request.base.sha }} ref: ${{ github.event.pull_request.base.sha }}
sparse-checkout: | sparse-checkout: |

View File

@@ -29,7 +29,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout base branch - name: Checkout base branch
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
ref: ${{ github.event.pull_request.base.sha }} ref: ${{ github.event.pull_request.base.sha }}

View File

@@ -52,11 +52,11 @@ 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 # 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: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }} build-mode: ${{ matrix.build-mode }}
@@ -84,6 +84,6 @@ jobs:
exit 1 exit 1
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4 uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
with: with:
category: "/language:${{matrix.language}}" category: "/language:${{matrix.language}}"

View File

@@ -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,
});
}

View File

@@ -16,7 +16,7 @@ jobs:
name: Validate PR title name: Validate PR title
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with: with:
@@ -29,10 +29,11 @@ jobs:
} = require('./.github/scripts/detect-tags.js'); } = require('./.github/scripts/detect-tags.js');
const title = context.payload.pull_request.title; const title = context.payload.pull_request.title;
const author = context.payload.pull_request.user.login; const user = context.payload.pull_request.user;
// Skip bot PRs (e.g. dependabot) - they have their own title format // Skip bot PRs (e.g. dependabot, esphome[bot] device-class sync) -
if (author === 'dependabot[bot]') { // they have their own title formats.
if (user.type === 'Bot') {
return; return;
} }

View File

@@ -20,7 +20,7 @@ jobs:
branch_build: ${{ steps.tag.outputs.branch_build }} branch_build: ${{ steps.tag.outputs.branch_build }}
deploy_env: ${{ steps.tag.outputs.deploy_env }} deploy_env: ${{ steps.tag.outputs.deploy_env }}
steps: steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Get tag - name: Get tag
id: tag id: tag
# yamllint disable rule:line-length # yamllint disable rule:line-length
@@ -60,7 +60,7 @@ jobs:
contents: read # actions/checkout to build the sdist/wheel contents: read # actions/checkout to build the sdist/wheel
id-token: write # OIDC token for PyPI Trusted Publishing (pypa/gh-action-pypi-publish) id-token: write # OIDC token for PyPI Trusted Publishing (pypa/gh-action-pypi-publish)
steps: steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Python - name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with: with:
@@ -92,22 +92,22 @@ jobs:
os: "ubuntu-24.04-arm" os: "ubuntu-24.04-arm"
steps: steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Python - name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with: with:
python-version: "3.11" python-version: "3.11"
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
- name: Log in to docker hub - name: Log in to docker hub
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with: with:
username: ${{ secrets.DOCKER_USER }} username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASSWORD }} password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to the GitHub container registry - name: Log in to the GitHub container registry
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
@@ -168,7 +168,7 @@ jobs:
- ghcr - ghcr
- dockerhub - dockerhub
steps: steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Download digests - name: Download digests
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
@@ -178,17 +178,17 @@ jobs:
merge-multiple: true merge-multiple: true
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
- name: Log in to docker hub - name: Log in to docker hub
if: matrix.registry == 'dockerhub' if: matrix.registry == 'dockerhub'
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with: with:
username: ${{ secrets.DOCKER_USER }} username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASSWORD }} password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to the GitHub container registry - name: Log in to the GitHub container registry
if: matrix.registry == 'ghcr' if: matrix.registry == 'ghcr'
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
@@ -212,74 +212,6 @@ jobs:
docker buildx imagetools create $(jq -Rcnr 'inputs | . / "," | map("-t " + .) | join(" ")' <<< "${{ steps.tags.outputs.tags}}") \ docker buildx imagetools create $(jq -Rcnr 'inputs | . / "," | map("-t " + .) | join(" ")' <<< "${{ steps.tags.outputs.tags}}") \
$(printf '${{ steps.tags.outputs.image }}@sha256:%s ' *) $(printf '${{ steps.tags.outputs.image }}@sha256:%s ' *)
deploy-ha-addon-repo:
if: github.repository == 'esphome/esphome' && needs.init.outputs.branch_build == 'false'
runs-on: ubuntu-latest
needs:
- init
- deploy-manifest
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 }}
owner: esphome
repositories: home-assistant-addon
permission-actions: write # actions.createWorkflowDispatch on the target repo (only API call made with this token)
- name: Trigger Workflow
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
github-token: ${{ steps.generate-token.outputs.token }}
script: |
let description = "ESPHome";
if (context.eventName == "release") {
description = ${{ toJSON(github.event.release.body) }};
}
github.rest.actions.createWorkflowDispatch({
owner: "esphome",
repo: "home-assistant-addon",
workflow_id: "bump-version.yml",
ref: "main",
inputs: {
version: "${{ needs.init.outputs.tag }}",
content: description
}
})
deploy-esphome-schema:
if: github.repository == 'esphome/esphome' && needs.init.outputs.branch_build == 'false'
runs-on: ubuntu-latest
needs: [init]
environment: ${{ needs.init.outputs.deploy_env }}
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 }}
owner: esphome
repositories: esphome-schema
permission-actions: write # actions.createWorkflowDispatch on the target repo (only API call made with this token)
- name: Trigger Workflow
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
github-token: ${{ steps.generate-token.outputs.token }}
script: |
github.rest.actions.createWorkflowDispatch({
owner: "esphome",
repo: "esphome-schema",
workflow_id: "generate-schemas.yml",
ref: "main",
inputs: {
version: "${{ needs.init.outputs.tag }}",
}
})
version-notifier: version-notifier:
if: github.repository == 'esphome/esphome' && needs.init.outputs.branch_build == 'false' if: github.repository == 'esphome/esphome' && needs.init.outputs.branch_build == 'false'
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -302,7 +234,7 @@ jobs:
with: with:
github-token: ${{ steps.generate-token.outputs.token }} github-token: ${{ steps.generate-token.outputs.token }}
script: | script: |
github.rest.actions.createWorkflowDispatch({ await github.rest.actions.createWorkflowDispatch({
owner: "esphome", owner: "esphome",
repo: "version-notifier", repo: "version-notifier",
workflow_id: "notify.yml", workflow_id: "notify.yml",

View File

@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Stale - name: Stale
uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0 uses: actions/stale@eb5cf3af3ac0a1aa4c9c45633dd1ae542a27a899 # v10.3.0
with: with:
debug-only: ${{ github.ref != 'refs/heads/dev' }} # Dry-run when not run on dev branch debug-only: ${{ github.ref != 'refs/heads/dev' }} # Dry-run when not run on dev branch
remove-stale-when-updated: true remove-stale-when-updated: true

View File

@@ -28,15 +28,10 @@ jobs:
permission-pull-requests: write # pulls.create / pulls.update to open or refresh the sync PR permission-pull-requests: write # pulls.create / pulls.update to open or refresh the sync PR
- name: Checkout - name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Create sync branch
# Switch off dev before pre-commit runs so the
# no-commit-to-branch hook passes (it blocks dev/release/beta).
run: git checkout -B sync/device-classes
- name: Checkout Home Assistant - name: Checkout Home Assistant
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with: with:
repository: home-assistant/core repository: home-assistant/core
path: lib/home-assistant path: lib/home-assistant
@@ -52,9 +47,13 @@ jobs:
# setup-python interpreter so subsequent ``pre-commit`` / # setup-python interpreter so subsequent ``pre-commit`` /
# ``script/run-in-env.py`` steps find the deps without a # ``script/run-in-env.py`` steps find the deps without a
# ``uv run`` prefix. # ``uv run`` prefix.
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
with: with:
enable-cache: true enable-cache: true
# Pin uv version so the action does not have to fetch the
# manifest from raw.githubusercontent.com on every cache
# miss; that fetch flakes on Windows runners.
version: "0.11.15"
- name: Install Home Assistant - name: Install Home Assistant
run: | run: |
@@ -71,22 +70,26 @@ jobs:
# which would otherwise abort the workflow before the auto-fixes # which would otherwise abort the workflow before the auto-fixes
# can flow into the sync PR. # can flow into the sync PR.
# #
# pylint is skipped: this workflow only installs a subset of the # SKIP:
# runtime deps (HA + requirements*.txt), so pylint surfaces # - no-commit-to-branch is a local guard against committing on
# import-error / relative-beyond-top-level noise that the main CI # dev/release/beta; CI runs on dev by definition, and
# already gates on real PRs. # peter-evans/create-pull-request creates the branch itself.
# - pylint surfaces import-error / relative-beyond-top-level
# noise here because this workflow installs only a subset of
# the runtime deps (HA + requirements*.txt); main CI already
# gates pylint on real PRs.
env: env:
SKIP: pylint SKIP: pylint,no-commit-to-branch
run: python script/run-in-env.py pre-commit run --all-files || true run: python script/run-in-env.py pre-commit run --all-files || true
- name: Verify pre-commit clean - name: Verify pre-commit clean
# Second pass: re-run all hooks against the now-fixed tree. # Second pass: re-run all hooks against the now-fixed tree.
# Auto-fixers exit 0 (nothing to change); any remaining failure # Auto-fixers exit 0 (nothing to change); any remaining failure
# from a check-only hook (flake8 / yamllint / ci-custom) is a # from a check-only hook (flake8 / yamllint / ci-custom) is a
# real issue and fails the workflow loudly. pylint stays skipped # real issue and fails the workflow loudly. Same SKIP list as
# for the same reason as above. # above for the same reasons.
env: env:
SKIP: pylint SKIP: pylint,no-commit-to-branch
run: python script/run-in-env.py pre-commit run --all-files run: python script/run-in-env.py pre-commit run --all-files
- name: Commit changes - name: Commit changes

1
.gitignore vendored
View File

@@ -141,6 +141,7 @@ tests/.esphome/
sdkconfig.* sdkconfig.*
!sdkconfig.defaults !sdkconfig.defaults
!sdkconfig.defaults.*
.tests/ .tests/

View File

@@ -6,12 +6,12 @@ ci:
autoupdate_commit_msg: 'pre-commit: autoupdate' autoupdate_commit_msg: 'pre-commit: autoupdate'
autoupdate_schedule: off # Disabled until ruff versions are synced between deps and pre-commit 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 hooks that have issues in pre-commit CI environment
skip: [pylint, clang-tidy-hash] skip: [pylint]
repos: repos:
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version. # Ruff version.
rev: v0.15.12 rev: v0.15.15
hooks: hooks:
# Run the linter. # Run the linter.
- id: ruff - id: ruff
@@ -59,13 +59,6 @@ repos:
language: system language: system
types: [python] types: [python]
files: ^esphome/.+\.py$ 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)$
pass_filenames: false
additional_dependencies: []
- id: ci-custom - id: ci-custom
name: ci-custom name: ci-custom
entry: python script/run-in-env.py script/ci-custom.py entry: python script/run-in-env.py script/ci-custom.py

View File

@@ -14,7 +14,7 @@ This document provides essential context for AI models interacting with this pro
* **Build Systems:** PlatformIO is the primary build system. CMake is used as an alternative. * **Build Systems:** PlatformIO is the primary build system. CMake is used as an alternative.
* **Configuration:** YAML. * **Configuration:** YAML.
* **Key Libraries/Dependencies:** * **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). * **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). * **Package Manager(s):** `pip` (for Python dependencies), `platformio` (for C++/PlatformIO dependencies).
* **Communication Protocols:** Protobuf (for native API), MQTT, HTTP. * **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. 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. 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. 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:** * **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). 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_` - Protected/private fields: `lower_snake_case_with_trailing_underscore_`
- Favor descriptive names over abbreviations - 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:** * **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_`. * **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: * **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:** * **Debug Tools:**
- `esphome config <file>.yaml` to validate configuration. - `esphome config <file>.yaml` to validate configuration.
- `esphome compile <file>.yaml` to compile without uploading. - `esphome compile <file>.yaml` to compile without uploading.
- Check the Dashboard for real-time logs.
- Use component-specific debug logging. - Use component-specific debug logging.
* **Common Issues:** * **Common Issues:**
- **Import Errors**: Check component dependencies and `PYTHONPATH`. - **Import Errors**: Check component dependencies and `PYTHONPATH`.
@@ -462,7 +473,7 @@ This document provides essential context for AI models interacting with this pro
6. **Pull Request:** Submit a PR against the `dev` branch. The Pull Request title should have a prefix of the component being worked on (e.g., `[display] Fix bug`, `[abc123] Add new component`). Update documentation, examples, and add `CODEOWNERS` entries as needed. Pull requests should always be made using the `.github/PULL_REQUEST_TEMPLATE.md` template - fill out all sections completely without removing any parts of the template. 6. **Pull Request:** Submit a PR against the `dev` branch. The Pull Request title should have a prefix of the component being worked on (e.g., `[display] Fix bug`, `[abc123] Add new component`). Update documentation, examples, and add `CODEOWNERS` entries as needed. Pull requests should always be made using the `.github/PULL_REQUEST_TEMPLATE.md` template - fill out all sections completely without removing any parts of the template.
* **Documentation Contributions:** * **Documentation Contributions:**
* Documentation is hosted in the separate `esphome/esphome-docs` repository. * Documentation is hosted in the separate `esphome/esphome.io` repository.
* The contribution workflow is the same as for the codebase. * The contribution workflow is the same as for the codebase.
* When editing a component's documentation page, also update the corresponding component index page to ensure both pages remain in sync. * When editing a component's documentation page, also update the corresponding component index page to ensure both pages remain in sync.
@@ -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. 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:** **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 - `CORE.data` automatically clears between runs
- Namespacing under `DOMAIN` prevents key collisions between components - Namespacing under `DOMAIN` prevents key collisions between components
- `@dataclass` provides type safety and cleaner attribute access - `@dataclass` provides type safety and cleaner attribute access
@@ -681,7 +692,7 @@ This document provides essential context for AI models interacting with this pro
- [ ] Explored non-breaking alternatives - [ ] Explored non-breaking alternatives
- [ ] Added deprecation warnings if possible (use `ESPDEPRECATED` macro for C++) - [ ] Added deprecation warnings if possible (use `ESPDEPRECATED` macro for C++)
- [ ] Documented migration path in PR description with before/after examples - [ ] Documented migration path in PR description with before/after examples
- [ ] Updated all internal usage and esphome-docs - [ ] Updated all internal usage and esphome.io
- [ ] Tested backward compatibility during deprecation period - [ ] Tested backward compatibility during deprecation period
* **Deprecation Pattern (C++):** * **Deprecation Pattern (C++):**

View File

@@ -19,7 +19,6 @@ esphome/components/ac_dimmer/* @glmnet
esphome/components/adc/* @esphome/core esphome/components/adc/* @esphome/core
esphome/components/adc128s102/* @DeerMaximum esphome/components/adc128s102/* @DeerMaximum
esphome/components/addressable_light/* @justfalter esphome/components/addressable_light/* @justfalter
esphome/components/ade7880/* @kpfleming
esphome/components/ade7953/* @angelnu esphome/components/ade7953/* @angelnu
esphome/components/ade7953_base/* @angelnu esphome/components/ade7953_base/* @angelnu
esphome/components/ade7953_i2c/* @angelnu esphome/components/ade7953_i2c/* @angelnu
@@ -28,7 +27,7 @@ esphome/components/ads1118/* @solomondg1
esphome/components/ags10/* @mak-42 esphome/components/ags10/* @mak-42
esphome/components/aic3204/* @kbx81 esphome/components/aic3204/* @kbx81
esphome/components/airthings_ble/* @jeromelaban esphome/components/airthings_ble/* @jeromelaban
esphome/components/airthings_wave_base/* @jeromelaban @kpfleming @ncareau esphome/components/airthings_wave_base/* @jeromelaban @ncareau
esphome/components/airthings_wave_mini/* @ncareau esphome/components/airthings_wave_mini/* @ncareau
esphome/components/airthings_wave_plus/* @jeromelaban @precurse esphome/components/airthings_wave_plus/* @jeromelaban @precurse
esphome/components/alarm_control_panel/* @grahambrown11 @hwstar esphome/components/alarm_control_panel/* @grahambrown11 @hwstar
@@ -84,6 +83,7 @@ esphome/components/bme680_bsec/* @trvrnrth
esphome/components/bme68x_bsec2/* @kbx81 @neffs esphome/components/bme68x_bsec2/* @kbx81 @neffs
esphome/components/bme68x_bsec2_i2c/* @kbx81 @neffs esphome/components/bme68x_bsec2_i2c/* @kbx81 @neffs
esphome/components/bmi160/* @flaviut esphome/components/bmi160/* @flaviut
esphome/components/bmi270/* @clydebarrow
esphome/components/bmp280_base/* @ademuri esphome/components/bmp280_base/* @ademuri
esphome/components/bmp280_i2c/* @ademuri esphome/components/bmp280_i2c/* @ademuri
esphome/components/bmp280_spi/* @ademuri esphome/components/bmp280_spi/* @ademuri
@@ -139,7 +139,7 @@ esphome/components/dfplayer/* @glmnet
esphome/components/dfrobot_sen0395/* @niklasweber esphome/components/dfrobot_sen0395/* @niklasweber
esphome/components/dht/* @OttoWinter esphome/components/dht/* @OttoWinter
esphome/components/display_menu_base/* @numo68 esphome/components/display_menu_base/* @numo68
esphome/components/dlms_meter/* @SimonFischer04 esphome/components/dlms_meter/* @latonita @PolarGoose @SimonFischer04 @Tomer27cz
esphome/components/dps310/* @kbx81 esphome/components/dps310/* @kbx81
esphome/components/ds1307/* @badbadc0ffee esphome/components/ds1307/* @badbadc0ffee
esphome/components/ds2484/* @mrk-its esphome/components/ds2484/* @mrk-its
@@ -291,6 +291,7 @@ esphome/components/lock/* @esphome/core
esphome/components/logger/* @esphome/core esphome/components/logger/* @esphome/core
esphome/components/logger/select/* @clydebarrow esphome/components/logger/select/* @clydebarrow
esphome/components/lps22/* @nagisa esphome/components/lps22/* @nagisa
esphome/components/lsm6ds/* @clydebarrow
esphome/components/ltr390/* @latonita @sjtrny esphome/components/ltr390/* @latonita @sjtrny
esphome/components/ltr501/* @latonita esphome/components/ltr501/* @latonita
esphome/components/ltr_als_ps/* @latonita esphome/components/ltr_als_ps/* @latonita
@@ -351,6 +352,7 @@ esphome/components/modbus_server/* @exciton
esphome/components/mopeka_ble/* @Fabian-Schmidt @spbrogan esphome/components/mopeka_ble/* @Fabian-Schmidt @spbrogan
esphome/components/mopeka_pro_check/* @spbrogan esphome/components/mopeka_pro_check/* @spbrogan
esphome/components/mopeka_std_check/* @Fabian-Schmidt esphome/components/mopeka_std_check/* @Fabian-Schmidt
esphome/components/motion/* @esphome/core
esphome/components/mpl3115a2/* @kbickar esphome/components/mpl3115a2/* @kbickar
esphome/components/mpu6886/* @fabaff esphome/components/mpu6886/* @fabaff
esphome/components/ms8607/* @e28eta esphome/components/ms8607/* @e28eta
@@ -379,6 +381,7 @@ esphome/components/pca6416a/* @Mat931
esphome/components/pca9554/* @bdraco @clydebarrow @hwstar esphome/components/pca9554/* @bdraco @clydebarrow @hwstar
esphome/components/pcf85063/* @brogon esphome/components/pcf85063/* @brogon
esphome/components/pcf8563/* @KoenBreeman esphome/components/pcf8563/* @KoenBreeman
esphome/components/pcm5122/* @remcom
esphome/components/pi4ioe5v6408/* @jesserockz esphome/components/pi4ioe5v6408/* @jesserockz
esphome/components/pid/* @OttoWinter esphome/components/pid/* @OttoWinter
esphome/components/pipsolar/* @andreashergert1984 esphome/components/pipsolar/* @andreashergert1984
@@ -417,6 +420,7 @@ esphome/components/restart/* @esphome/core
esphome/components/rf_bridge/* @jesserockz esphome/components/rf_bridge/* @jesserockz
esphome/components/rgbct/* @jesserockz esphome/components/rgbct/* @jesserockz
esphome/components/ring_buffer/* @kahrendt esphome/components/ring_buffer/* @kahrendt
esphome/components/router/speaker/* @kahrendt
esphome/components/rp2040/* @jesserockz esphome/components/rp2040/* @jesserockz
esphome/components/rp2040_ble/* @bdraco esphome/components/rp2040_ble/* @bdraco
esphome/components/rp2040_pio_led_strip/* @Papa-DMan esphome/components/rp2040_pio_led_strip/* @Papa-DMan
@@ -441,7 +445,7 @@ esphome/components/select/* @esphome/core
esphome/components/sen0321/* @notjj esphome/components/sen0321/* @notjj
esphome/components/sen21231/* @shreyaskarnik esphome/components/sen21231/* @shreyaskarnik
esphome/components/sen5x/* @martgras esphome/components/sen5x/* @martgras
esphome/components/sen6x/* @martgras @mebner86 @mikelawrence @tuct esphome/components/sen6x/* @martgras @mebner86 @tuct
esphome/components/sendspin/* @kahrendt esphome/components/sendspin/* @kahrendt
esphome/components/sendspin/media_player/* @kahrendt esphome/components/sendspin/media_player/* @kahrendt
esphome/components/sendspin/media_source/* @kahrendt esphome/components/sendspin/media_source/* @kahrendt
@@ -557,6 +561,7 @@ esphome/components/uart/packet_transport/* @clydebarrow
esphome/components/udp/* @clydebarrow esphome/components/udp/* @clydebarrow
esphome/components/ufire_ec/* @pvizeli esphome/components/ufire_ec/* @pvizeli
esphome/components/ufire_ise/* @pvizeli esphome/components/ufire_ise/* @pvizeli
esphome/components/ufm01/* @ljungqvist
esphome/components/ultrasonic/* @ssieb @swoboda1337 esphome/components/ultrasonic/* @ssieb @swoboda1337
esphome/components/update/* @jesserockz esphome/components/update/* @jesserockz
esphome/components/uponor_smatrix/* @kroimon esphome/components/uponor_smatrix/* @kroimon
@@ -594,6 +599,7 @@ esphome/components/wk2212_spi/* @DrCoolZic
esphome/components/wl_134/* @hobbypunk90 esphome/components/wl_134/* @hobbypunk90
esphome/components/wts01/* @alepee esphome/components/wts01/* @alepee
esphome/components/x9c/* @EtienneMD esphome/components/x9c/* @EtienneMD
esphome/components/xdb401/* @RT530
esphome/components/xgzp68xx/* @gcormier esphome/components/xgzp68xx/* @gcormier
esphome/components/xiaomi_hhccjcy10/* @fariouche esphome/components/xiaomi_hhccjcy10/* @fariouche
esphome/components/xiaomi_lywsd02mmc/* @juanluss31 esphome/components/xiaomi_lywsd02mmc/* @juanluss31

View File

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

102
THREAT_MODEL.md Normal file
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).

18
codecov.yml Normal file
View File

@@ -0,0 +1,18 @@
coverage:
status:
patch:
default:
target: 100%
threshold: 0%
project:
default:
informational: true
ignore:
- "esphome/components/**/*"
- "esphome/analyze_memory/**/*"
- "tests/integration/**/*"
comment:
layout: "reach, diff, flags, files"
require_changes: true

View File

@@ -1,10 +1,9 @@
ARG BUILD_VERSION=dev ARG BUILD_VERSION=dev
ARG BUILD_OS=alpine ARG BUILD_BASE_VERSION=2026.06.0
ARG BUILD_BASE_VERSION=2025.04.0
ARG BUILD_TYPE=docker ARG BUILD_TYPE=docker
FROM ghcr.io/esphome/docker-base:${BUILD_OS}-${BUILD_BASE_VERSION} AS base-source-docker FROM ghcr.io/esphome/docker-base:debian-${BUILD_BASE_VERSION} AS base-source-docker
FROM ghcr.io/esphome/docker-base:${BUILD_OS}-ha-addon-${BUILD_BASE_VERSION} AS base-source-ha-addon FROM ghcr.io/esphome/docker-base:debian-ha-addon-${BUILD_BASE_VERSION} AS base-source-ha-addon
ARG BUILD_TYPE ARG BUILD_TYPE
FROM base-source-${BUILD_TYPE} AS base FROM base-source-${BUILD_TYPE} AS base
@@ -18,13 +17,9 @@ RUN git config --system --add safe.directory "*" \
# validate openocd-esp32 (it dynamically links libusb-1.0.so.0); without # 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 # it idf_tools.py rejects the openocd install with exit 127 and aborts
# the whole framework setup. # the whole framework setup.
RUN if command -v apk > /dev/null; then \ RUN apt-get update \
apk add --no-cache build-base libusb; \ && apt-get install -y --no-install-recommends build-essential libusb-1.0-0 \
else \ && rm -rf /var/lib/apt/lists/*
apt-get update \
&& apt-get install -y --no-install-recommends build-essential libusb-1.0-0 \
&& rm -rf /var/lib/apt/lists/*; \
fi
ENV PIP_DISABLE_PIP_VERSION_CHECK=1 ENV PIP_DISABLE_PIP_VERSION_CHECK=1
@@ -36,6 +31,9 @@ RUN \
uv pip install --no-cache-dir \ uv pip install --no-cache-dir \
-r /requirements.txt -r /requirements.txt
# Install the ESPHome Device Builder dashboard.
RUN uv pip install --no-cache-dir esphome-device-builder==1.0.12
RUN \ RUN \
platformio settings set enable_telemetry No \ platformio settings set enable_telemetry No \
&& platformio settings set check_platformio_interval 1000000 \ && platformio settings set check_platformio_interval 1000000 \

View File

@@ -20,6 +20,10 @@ TYPE_HA_ADDON = "ha-addon"
TYPE_LINT = "lint" TYPE_LINT = "lint"
TYPES = [TYPE_DOCKER, TYPE_HA_ADDON, TYPE_LINT] TYPES = [TYPE_DOCKER, TYPE_HA_ADDON, TYPE_LINT]
REGISTRY_GHCR = "ghcr"
REGISTRY_DOCKERHUB = "dockerhub"
REGISTRIES = [REGISTRY_GHCR, REGISTRY_DOCKERHUB]
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument( parser.add_argument(
@@ -34,6 +38,12 @@ parser.add_argument(
parser.add_argument( parser.add_argument(
"--build-type", choices=TYPES, required=True, help="The type of build to run" "--build-type", choices=TYPES, required=True, help="The type of build to run"
) )
parser.add_argument(
"--registry",
choices=REGISTRIES,
action="append",
help="Restrict to specific registries (default: all). May be passed multiple times.",
)
parser.add_argument( parser.add_argument(
"--dry-run", action="store_true", help="Don't run any commands, just print them" "--dry-run", action="store_true", help="Don't run any commands, just print them"
) )
@@ -45,6 +55,11 @@ build_parser.add_argument("--push", help="Also push the images", action="store_t
build_parser.add_argument( build_parser.add_argument(
"--load", help="Load the docker image locally", action="store_true" "--load", help="Load the docker image locally", action="store_true"
) )
build_parser.add_argument(
"--no-cache-to",
help="Don't write the build cache (avoids polluting the shared cache)",
action="store_true",
)
manifest_parser = subparsers.add_parser( manifest_parser = subparsers.add_parser(
"manifest", help="Create a manifest from already pushed images" "manifest", help="Create a manifest from already pushed images"
) )
@@ -95,11 +110,14 @@ def main():
print("Command failed") print("Command failed")
sys.exit(1) sys.exit(1)
registries = args.registry or REGISTRIES
# detect channel from tag # detect channel from tag
match = re.match(r"^(\d+\.\d+)(?:\.\d+)?(b\d+)?$", args.tag) match = re.match(r"^(\d+\.\d+)(?:\.\d+)?(b\d+)?$", args.tag)
major_minor_version = None major_minor_version = None
if match is None: if match is None:
channel = CHANNEL_DEV # Custom tag (e.g. a branch name) -- push only the tag itself
channel = None
elif match.group(2) is None: elif match.group(2) is None:
major_minor_version = match.group(1) major_minor_version = match.group(1)
channel = CHANNEL_RELEASE channel = CHANNEL_RELEASE
@@ -128,11 +146,18 @@ def main():
CHANNEL_DEV: "cache-dev", CHANNEL_DEV: "cache-dev",
CHANNEL_BETA: "cache-beta", CHANNEL_BETA: "cache-beta",
CHANNEL_RELEASE: "cache-latest", CHANNEL_RELEASE: "cache-latest",
}[channel] }.get(channel, "cache-dev")
cache_img = f"ghcr.io/{params.build_to}:{cache_tag}" # Cache images live alongside the pushed images; prefer GHCR when it is
# one of the selected registries, otherwise fall back to Docker Hub so a
# registry-restricted build doesn't need GHCR auth.
cache_prefix = "ghcr.io/" if REGISTRY_GHCR in registries else ""
cache_img = f"{cache_prefix}{params.build_to}:{cache_tag}"
imgs = [f"{params.build_to}:{tag}" for tag in tags_to_push] imgs = []
imgs += [f"ghcr.io/{params.build_to}:{tag}" for tag in tags_to_push] if REGISTRY_DOCKERHUB in registries:
imgs += [f"{params.build_to}:{tag}" for tag in tags_to_push]
if REGISTRY_GHCR in registries:
imgs += [f"ghcr.io/{params.build_to}:{tag}" for tag in tags_to_push]
# 3. build # 3. build
cmd = [ cmd = [
@@ -155,7 +180,9 @@ def main():
for img in imgs: for img in imgs:
cmd += ["--tag", img] cmd += ["--tag", img]
if args.push: if args.push:
cmd += ["--push", "--cache-to", f"type=registry,ref={cache_img},mode=max"] cmd += ["--push"]
if not args.no_cache_to:
cmd += ["--cache-to", f"type=registry,ref={cache_img},mode=max"]
if args.load: if args.load:
cmd += ["--load"] cmd += ["--load"]
@@ -163,20 +190,22 @@ def main():
elif args.command == "manifest": elif args.command == "manifest":
manifest = DockerParams.for_type_arch(args.build_type, ARCH_AMD64).manifest_to manifest = DockerParams.for_type_arch(args.build_type, ARCH_AMD64).manifest_to
targets = [f"{manifest}:{tag}" for tag in tags_to_push] targets = []
targets += [f"ghcr.io/{manifest}:{tag}" for tag in tags_to_push] if REGISTRY_DOCKERHUB in registries:
# 1. Create manifests targets += [f"{manifest}:{tag}" for tag in tags_to_push]
if REGISTRY_GHCR in registries:
targets += [f"ghcr.io/{manifest}:{tag}" for tag in tags_to_push]
# Use buildx imagetools (not `docker manifest`) so the per-arch sources,
# which buildx pushes as single-platform manifest lists, are combined
# and pushed correctly in one step.
for target in targets: for target in targets:
cmd = ["docker", "manifest", "create", target] cmd = ["docker", "buildx", "imagetools", "create", "--tag", target]
for arch in ARCHS: for arch in ARCHS:
src = f"{DockerParams.for_type_arch(args.build_type, arch).build_to}:{args.tag}" src = f"{DockerParams.for_type_arch(args.build_type, arch).build_to}:{args.tag}"
if target.startswith("ghcr.io"): if target.startswith("ghcr.io"):
src = f"ghcr.io/{src}" src = f"ghcr.io/{src}"
cmd.append(src) cmd.append(src)
run_command(*cmd) run_command(*cmd)
# 2. Push manifests
for target in targets:
run_command("docker", "manifest", "push", target)
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -27,4 +27,12 @@ if [[ -d /build ]]; then
export ESPHOME_BUILD_PATH=/build export ESPHOME_BUILD_PATH=/build
fi fi
# The default CMD is "dashboard /config". Route the dashboard to the new
# Device Builder, but pass every other subcommand (compile, run, config,
# logs, ...) straight through to the esphome CLI so direct CLI use keeps working.
if [[ "$1" == "dashboard" ]]; then
shift
exec esphome-device-builder "$@"
fi
exec esphome "$@" exec esphome "$@"

View File

@@ -1,22 +0,0 @@
#!/usr/bin/with-contenv bashio
# ==============================================================================
# Installs the latest prerelease of esphome-device-builder when the
# `use_new_device_builder` config option is enabled.
# This is a temporary install-on-boot step until esphome-device-builder
# becomes a direct dependency of esphome.
# ==============================================================================
if ! bashio::config.true 'use_new_device_builder'; then
exit 0
fi
bashio::log.info "Installing latest prerelease of esphome-device-builder..."
if command -v uv > /dev/null; then
uv pip install --system --no-cache-dir --prerelease=allow --upgrade \
esphome-device-builder ||
bashio::exit.nok "Failed installing esphome-device-builder."
else
pip install --no-cache-dir --pre --upgrade esphome-device-builder ||
bashio::exit.nok "Failed installing esphome-device-builder."
fi
bashio::log.info "Installed esphome-device-builder."

View File

@@ -1,96 +0,0 @@
types {
text/html html htm shtml;
text/css css;
text/xml xml;
image/gif gif;
image/jpeg jpeg jpg;
application/javascript js;
application/atom+xml atom;
application/rss+xml rss;
text/mathml mml;
text/plain txt;
text/vnd.sun.j2me.app-descriptor jad;
text/vnd.wap.wml wml;
text/x-component htc;
image/png png;
image/svg+xml svg svgz;
image/tiff tif tiff;
image/vnd.wap.wbmp wbmp;
image/webp webp;
image/x-icon ico;
image/x-jng jng;
image/x-ms-bmp bmp;
font/woff woff;
font/woff2 woff2;
application/java-archive jar war ear;
application/json json;
application/mac-binhex40 hqx;
application/msword doc;
application/pdf pdf;
application/postscript ps eps ai;
application/rtf rtf;
application/vnd.apple.mpegurl m3u8;
application/vnd.google-earth.kml+xml kml;
application/vnd.google-earth.kmz kmz;
application/vnd.ms-excel xls;
application/vnd.ms-fontobject eot;
application/vnd.ms-powerpoint ppt;
application/vnd.oasis.opendocument.graphics odg;
application/vnd.oasis.opendocument.presentation odp;
application/vnd.oasis.opendocument.spreadsheet ods;
application/vnd.oasis.opendocument.text odt;
application/vnd.openxmlformats-officedocument.presentationml.presentation
pptx;
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
xlsx;
application/vnd.openxmlformats-officedocument.wordprocessingml.document
docx;
application/vnd.wap.wmlc wmlc;
application/x-7z-compressed 7z;
application/x-cocoa cco;
application/x-java-archive-diff jardiff;
application/x-java-jnlp-file jnlp;
application/x-makeself run;
application/x-perl pl pm;
application/x-pilot prc pdb;
application/x-rar-compressed rar;
application/x-redhat-package-manager rpm;
application/x-sea sea;
application/x-shockwave-flash swf;
application/x-stuffit sit;
application/x-tcl tcl tk;
application/x-x509-ca-cert der pem crt;
application/x-xpinstall xpi;
application/xhtml+xml xhtml;
application/xspf+xml xspf;
application/zip zip;
application/octet-stream bin exe dll;
application/octet-stream deb;
application/octet-stream dmg;
application/octet-stream iso img;
application/octet-stream msi msp msm;
audio/midi mid midi kar;
audio/mpeg mp3;
audio/ogg ogg;
audio/x-m4a m4a;
audio/x-realaudio ra;
video/3gpp 3gpp 3gp;
video/mp2t ts;
video/mp4 mp4;
video/mpeg mpeg mpg;
video/quicktime mov;
video/webm webm;
video/x-flv flv;
video/x-m4v m4v;
video/x-mng mng;
video/x-ms-asf asx asf;
video/x-ms-wmv wmv;
video/x-msvideo avi;
}

View File

@@ -1,16 +0,0 @@
proxy_http_version 1.1;
proxy_ignore_client_abort off;
proxy_read_timeout 86400s;
proxy_redirect off;
proxy_send_timeout 86400s;
proxy_max_temp_file_size 0;
proxy_set_header Accept-Encoding "";
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-NginX-Proxy true;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Authorization "";

View File

@@ -1,8 +0,0 @@
root /dev/null;
server_name $hostname;
client_max_body_size 512m;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header X-Robots-Tag none;

View File

@@ -1,8 +0,0 @@
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;

View File

@@ -1,3 +0,0 @@
upstream esphome {
server unix:/var/run/esphome.sock;
}

View File

@@ -1,30 +0,0 @@
daemon off;
user root;
pid /var/run/nginx.pid;
worker_processes 1;
error_log /proc/1/fd/1 error;
events {
worker_connections 1024;
}
http {
include /etc/nginx/includes/mime.types;
access_log off;
default_type application/octet-stream;
gzip on;
keepalive_timeout 65;
sendfile on;
server_tokens off;
tcp_nodelay on;
tcp_nopush on;
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
include /etc/nginx/includes/upstream.conf;
include /etc/nginx/servers/*.conf;
}

View File

@@ -1 +0,0 @@
Without requirements or design, programming is the art of adding bugs to an empty text file. (Louis Srygley)

View File

@@ -1,28 +0,0 @@
server {
{{ if not .ssl }}
listen 6052 default_server;
{{ else }}
listen 6052 default_server ssl http2;
{{ end }}
include /etc/nginx/includes/server_params.conf;
include /etc/nginx/includes/proxy_params.conf;
{{ if .ssl }}
include /etc/nginx/includes/ssl_params.conf;
ssl_certificate /ssl/{{ .certfile }};
ssl_certificate_key /ssl/{{ .keyfile }};
# Redirect http requests to https on the same port.
# https://rageagainstshell.com/2016/11/redirect-http-to-https-on-the-same-port-in-nginx/
error_page 497 https://$http_host$request_uri;
{{ end }}
# Clear Home Assistant Ingress header
proxy_set_header X-HA-Ingress "";
location / {
proxy_pass http://esphome;
}
}

View File

@@ -1,18 +0,0 @@
server {
listen 127.0.0.1:{{ .port }} default_server;
listen {{ .interface }}:{{ .port }} default_server;
include /etc/nginx/includes/server_params.conf;
include /etc/nginx/includes/proxy_params.conf;
# Set Home Assistant Ingress header
proxy_set_header X-HA-Ingress "YES";
location / {
allow 172.30.32.2;
allow 127.0.0.1;
deny all;
proxy_pass http://esphome;
}
}

View File

@@ -16,7 +16,7 @@ fi
port=$(bashio::addon.ingress_port) port=$(bashio::addon.ingress_port)
# Wait for NGINX to become available # Wait for the ESPHome Device Builder to become available
bashio::net.wait_for "${port}" "127.0.0.1" 300 bashio::net.wait_for "${port}" "127.0.0.1" 300
config=$(\ config=$(\

View File

@@ -2,7 +2,7 @@
# shellcheck shell=bash # shellcheck shell=bash
# ============================================================================== # ==============================================================================
# Home Assistant Community Add-on: ESPHome # Home Assistant Community Add-on: ESPHome
# Take down the S6 supervision tree when ESPHome dashboard fails # Take down the S6 supervision tree when ESPHome Device Builder fails
# ============================================================================== # ==============================================================================
declare exit_code declare exit_code
readonly exit_code_container=$(</run/s6-linux-init-container-results/exitcode) readonly exit_code_container=$(</run/s6-linux-init-container-results/exitcode)
@@ -10,7 +10,7 @@ readonly exit_code_service="${1}"
readonly exit_code_signal="${2}" readonly exit_code_signal="${2}"
bashio::log.info \ bashio::log.info \
"Service ESPHome dashboard exited with code ${exit_code_service}" \ "Service ESPHome Device Builder exited with code ${exit_code_service}" \
"(by signal ${exit_code_signal})" "(by signal ${exit_code_signal})"
if [[ "${exit_code_service}" -eq 256 ]]; then if [[ "${exit_code_service}" -eq 256 ]]; then

View File

@@ -2,7 +2,7 @@
# shellcheck shell=bash # shellcheck shell=bash
# ============================================================================== # ==============================================================================
# Community Hass.io Add-ons: ESPHome # Community Hass.io Add-ons: ESPHome
# Runs the ESPHome dashboard # Runs the ESPHome Device Builder
# ============================================================================== # ==============================================================================
readonly pio_cache_base=/data/cache/platformio readonly pio_cache_base=/data/cache/platformio
@@ -49,12 +49,21 @@ if bashio::fs.directory_exists '/config/esphome/.esphome'; then
rm -rf /config/esphome/.esphome rm -rf /config/esphome/.esphome
fi fi
if bashio::config.true 'use_new_device_builder'; then # Only signal device-builder to expose the public LAN port when the operator
bashio::log.info "Starting ESPHome Device Builder..." # mapped port 6052, matching the legacy dashboard where nginx listened on the
exec esphome-device-builder /config/esphome \ # fixed port 6052 only when it was configured. We use the mapping purely as a
--ha-addon \ # presence check and don't forward the published value; device-builder binds
--ingress-port "$(bashio::addon.ingress_port)" # 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 fi
bashio::log.info "Starting ESPHome dashboard..." bashio::log.info "Starting ESPHome Device Builder..."
exec esphome dashboard /config/esphome --socket /var/run/esphome.sock --ha-addon exec esphome-device-builder /config/esphome \
--ha-addon \
--ingress-port "$(bashio::addon.ingress_port)" \
"$@"

View File

@@ -1,35 +0,0 @@
#!/command/with-contenv bashio
# shellcheck shell=bash
# ==============================================================================
# Community Hass.io Add-ons: ESPHome
# Configures NGINX for use with ESPHome
# ==============================================================================
# When the new device builder is enabled it serves HA ingress directly,
# so nginx is not used at all -- skip configuration.
if bashio::config.true 'use_new_device_builder'; then
bashio::log.info "Skipping NGINX setup: new device builder serves ingress directly."
bashio::exit.ok
fi
mkdir -p /var/log/nginx
# Generate Ingress configuration
bashio::var.json \
interface "$(bashio::addon.ip_address)" \
port "^$(bashio::addon.ingress_port)" \
| tempio \
-template /etc/nginx/templates/ingress.gtpl \
-out /etc/nginx/servers/ingress.conf
# Generate direct access configuration, if enabled.
if bashio::var.has_value "$(bashio::addon.port 6052)"; then
bashio::config.require.ssl
bashio::var.json \
certfile "$(bashio::config 'certfile')" \
keyfile "$(bashio::config 'keyfile')" \
ssl "^$(bashio::config 'ssl')" \
| tempio \
-template /etc/nginx/templates/direct.gtpl \
-out /etc/nginx/servers/direct.conf
fi

View File

@@ -1 +0,0 @@
/etc/s6-overlay/s6-rc.d/init-nginx/run

View File

@@ -1,25 +0,0 @@
#!/command/with-contenv bashio
# ==============================================================================
# Community Hass.io Add-ons: ESPHome
# Take down the S6 supervision tree when NGINX fails
# ==============================================================================
declare exit_code
readonly exit_code_container=$(</run/s6-linux-init-container-results/exitcode)
readonly exit_code_service="${1}"
readonly exit_code_signal="${2}"
bashio::log.info \
"Service NGINX exited with code ${exit_code_service}" \
"(by signal ${exit_code_signal})"
if [[ "${exit_code_service}" -eq 256 ]]; then
if [[ "${exit_code_container}" -eq 0 ]]; then
echo $((128 + $exit_code_signal)) > /run/s6-linux-init-container-results/exitcode
fi
[[ "${exit_code_signal}" -eq 15 ]] && exec /run/s6/basedir/bin/halt
elif [[ "${exit_code_service}" -ne 0 ]]; then
if [[ "${exit_code_container}" -eq 0 ]]; then
echo "${exit_code_service}" > /run/s6-linux-init-container-results/exitcode
fi
exec /run/s6/basedir/bin/halt
fi

View File

@@ -1,23 +0,0 @@
#!/command/with-contenv bashio
# shellcheck shell=bash
# ==============================================================================
# Community Hass.io Add-ons: ESPHome
# Runs the NGINX proxy
# ==============================================================================
# The new device builder handles HA ingress itself, so nginx is bypassed.
# Block the longrun forever so s6 keeps the dependency satisfied and does
# not respawn it.
if bashio::config.true 'use_new_device_builder'; then
bashio::log.info "NGINX bypassed: new device builder serves ingress directly."
exec sleep infinity
fi
bashio::log.info "Waiting for ESPHome dashboard to come up..."
while [[ ! -S /var/run/esphome.sock ]]; do
sleep 0.5
done
bashio::log.info "Starting NGINX..."
exec nginx

View File

@@ -0,0 +1,7 @@
esphome:
name: docker-test-bk72xx-arduino
bk72xx:
board: generic-bk7231n-qfn32-tuya
logger:

View File

@@ -0,0 +1,10 @@
esphome:
name: docker-test-esp32-ard-idf
esp32:
variant: esp32
framework:
type: arduino
toolchain: esp-idf
logger:

View File

@@ -0,0 +1,10 @@
esphome:
name: docker-test-esp32-ard-pio
esp32:
variant: esp32
framework:
type: arduino
toolchain: platformio
logger:

View File

@@ -0,0 +1,10 @@
esphome:
name: docker-test-esp32-idf-idf
esp32:
variant: esp32
framework:
type: esp-idf
toolchain: esp-idf
logger:

View File

@@ -0,0 +1,10 @@
esphome:
name: docker-test-esp32-idf-pio
esp32:
variant: esp32
framework:
type: esp-idf
toolchain: platformio
logger:

View File

@@ -0,0 +1,7 @@
esphome:
name: docker-test-esp8266-arduino
esp8266:
board: d1_mini
logger:

View File

@@ -0,0 +1,6 @@
esphome:
name: docker-test-host
host:
logger:

View File

@@ -0,0 +1,7 @@
esphome:
name: docker-test-ln882x-arduino
ln882x:
board: generic-ln882hki
logger:

View File

@@ -0,0 +1,8 @@
esphome:
name: docker-test-nrf52
nrf52:
board: adafruit_itsybitsy_nrf52840
bootloader: adafruit_nrf52_sd140_v6
logger:

View File

@@ -0,0 +1,7 @@
esphome:
name: docker-test-rp2040-arduino
rp2040:
variant: rp2040
logger:

View File

@@ -0,0 +1,7 @@
esphome:
name: docker-test-rtl87xx-arduino
rtl87xx:
board: generic-rtl8710bn-2mb-788k
logger:

View File

@@ -50,6 +50,7 @@ from esphome.const import (
CONF_TOPIC, CONF_TOPIC,
CONF_USERNAME, CONF_USERNAME,
CONF_WEB_SERVER, CONF_WEB_SERVER,
CONF_WIFI,
ENV_NOGITIGNORE, ENV_NOGITIGNORE,
KEY_CORE, KEY_CORE,
KEY_TARGET_PLATFORM, KEY_TARGET_PLATFORM,
@@ -267,6 +268,36 @@ def _ota_hostnames_for_default(purpose: Purpose) -> list[str]:
return _resolve_with_cache(CORE.address, purpose) 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. Network log streaming requires the native API; add "
"an 'api:' component, enable MQTT logging, 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( def choose_upload_log_host(
default: list[str] | str | None, default: list[str] | str | None,
check_default: str | None, check_default: str | None,
@@ -316,14 +347,7 @@ def choose_upload_log_host(
else: else:
resolved.append(device) resolved.append(device)
if not resolved: if not resolved:
if CORE.dashboard: raise EsphomeError(_unresolved_default_error(purpose, defaults))
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}"
)
return resolved return resolved
# No devices specified, show interactive chooser # No devices specified, show interactive chooser
@@ -503,6 +527,12 @@ def has_resolvable_address() -> bool:
if has_ip_address(): if has_ip_address():
return True return True
# 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):
return True
if has_mdns(): if has_mdns():
return True return True
@@ -607,7 +637,7 @@ def run_miniterm(config: ConfigType, port: str, args) -> int:
try: try:
module = importlib.import_module("esphome.components." + CORE.target_platform) module = importlib.import_module("esphome.components." + CORE.target_platform)
process_stacktrace = getattr(module, "process_stacktrace") process_stacktrace = module.process_stacktrace
except (AttributeError, ImportError): except (AttributeError, ImportError):
_LOGGER.info( _LOGGER.info(
'Stacktrace analysis is unavailable: no compatible analyzer found for target platform "%s".', 'Stacktrace analysis is unavailable: no compatible analyzer found for target platform "%s".',
@@ -638,7 +668,7 @@ def run_miniterm(config: ConfigType, port: str, args) -> int:
chunk = ser.read(ser.in_waiting or 1) chunk = ser.read(ser.in_waiting or 1)
if not chunk: if not chunk:
continue continue
time_ = datetime.now() time_ = datetime.now().astimezone()
milliseconds = time_.microsecond // 1000 milliseconds = time_.microsecond // 1000
time_str = f"[{time_.hour:02}:{time_.minute:02}:{time_.second:02}.{milliseconds:03}]" time_str = f"[{time_.hour:02}:{time_.minute:02}:{time_.second:02}.{milliseconds:03}]"
@@ -694,6 +724,11 @@ def _wrap_to_code(name, comp, yaml_util):
def write_cpp(config: ConfigType) -> int: def write_cpp(config: ConfigType) -> int:
from esphome import writer from esphome import writer
# Refresh the storage sidecar and clean an incompatible previous build
# before regenerating any sources. This may full-wipe the build dir, so it
# has to run before write_cpp_file writes src/.
writer.update_storage_json()
if not get_bool_env(ENV_NOGITIGNORE): if not get_bool_env(ENV_NOGITIGNORE):
writer.write_gitignore() writer.write_gitignore()
@@ -733,6 +768,13 @@ def write_cpp_file() -> int:
def compile_program(args: ArgsProtocol, config: ConfigType) -> int: def compile_program(args: ArgsProtocol, config: ConfigType) -> int:
# Keep this gate here, NOT in config validation: device-builder needs
# `esphome config` to keep succeeding with placeholders so onboarding can run.
if CONF_WIFI in config:
from esphome.components.wifi import check_placeholder_credentials
check_placeholder_credentials(config)
# NOTE: "Build path:" format is parsed by script/ci_memory_impact_extract.py # NOTE: "Build path:" format is parsed by script/ci_memory_impact_extract.py
# If you change this format, update the regex in that script as well # If you change this format, update the regex in that script as well
_LOGGER.info("Compiling app... Build path: %s", CORE.build_path) _LOGGER.info("Compiling app... Build path: %s", CORE.build_path)
@@ -752,6 +794,7 @@ def compile_program(args: ArgsProtocol, config: ConfigType) -> int:
toolchain.create_factory_bin() toolchain.create_factory_bin()
toolchain.create_ota_bin() toolchain.create_ota_bin()
toolchain.create_elf_copy() toolchain.create_elf_copy()
toolchain.get_idedata()
else: else:
from esphome.platformio import toolchain from esphome.platformio import toolchain
@@ -786,7 +829,7 @@ def _check_and_emit_build_info() -> None:
# Read build_info from JSON # Read build_info from JSON
try: try:
with open(build_info_json_path, encoding="utf-8") as f: with build_info_json_path.open(encoding="utf-8") as f:
build_info = json.load(f) build_info = json.load(f)
except (OSError, json.JSONDecodeError) as e: except (OSError, json.JSONDecodeError) as e:
_LOGGER.debug("Failed to read build_info: %s", e) _LOGGER.debug("Failed to read build_info: %s", e)
@@ -1048,7 +1091,7 @@ def _wait_for_serial_port(
def _port_found() -> bool: def _port_found() -> bool:
if port is not None: if port is not None:
if os.name == "posix": if os.name == "posix":
return os.path.exists(port) return Path(port).exists()
return any(p.path == port for p in get_serial_ports()) return any(p.path == port for p in get_serial_ports())
ports = get_serial_ports() ports = get_serial_ports()
if known_ports is not None: if known_ports is not None:
@@ -1093,7 +1136,7 @@ def upload_program(
host = devices[0] host = devices[0]
try: try:
module = importlib.import_module("esphome.components." + CORE.target_platform) module = importlib.import_module("esphome.components." + CORE.target_platform)
if getattr(module, "upload_program")(config, args, host): if module.upload_program(config, args, host):
return 0, host return 0, host
except AttributeError: except AttributeError:
pass pass
@@ -1342,10 +1385,23 @@ def _validate_bootloader_binary(binary: Path) -> None:
) )
def _should_subscribe_states(args: ArgsProtocol) -> bool:
"""Determine whether entity state changes should be shown in log output.
The ``--states``/``--no-states`` command line flags take precedence. When
neither is given, the ``ESPHOME_LOG_STATES`` environment variable controls
the behavior, defaulting to showing states.
"""
states = getattr(args, "states", None)
if states is not None:
return states
return get_bool_env("ESPHOME_LOG_STATES", True)
def show_logs(config: ConfigType, args: ArgsProtocol, devices: list[str]) -> int | None: def show_logs(config: ConfigType, args: ArgsProtocol, devices: list[str]) -> int | None:
try: try:
module = importlib.import_module("esphome.components." + CORE.target_platform) module = importlib.import_module("esphome.components." + CORE.target_platform)
if getattr(module, "show_logs")(config, args, devices): if module.show_logs(config, args, devices):
return 0 return 0
except AttributeError: except AttributeError:
pass pass
@@ -1371,7 +1427,7 @@ def show_logs(config: ConfigType, args: ArgsProtocol, devices: list[str]) -> int
return run_logs( return run_logs(
config, config,
network_devices, network_devices,
subscribe_states=not getattr(args, "no_states", False), subscribe_states=_should_subscribe_states(args),
) )
if port_type in (PortType.NETWORK, PortType.MQTT) and has_mqtt_logging(): if port_type in (PortType.NETWORK, PortType.MQTT) and has_mqtt_logging():
@@ -1401,20 +1457,59 @@ def command_wizard(args: ArgsProtocol) -> int | None:
def command_config(args: ArgsProtocol, config: ConfigType) -> int | None: def command_config(args: ArgsProtocol, config: ConfigType) -> int | None:
from esphome import yaml_util from esphome import yaml_util
if not CORE.verbose: if getattr(args, "no_defaults", False):
user_config = getattr(config, "user_config", None)
if user_config is None:
_LOGGER.warning(
"--no-defaults requested but the user-only config snapshot is "
"unavailable; falling back to the validated configuration."
)
else:
config = user_config
elif not CORE.verbose:
config = strip_default_ids(config) config = strip_default_ids(config)
output = yaml_util.dump(config, args.show_secrets) output = yaml_util.dump(config, args.show_secrets)
# add the console decoration so the front-end can hide the secrets
if not args.show_secrets: if not args.show_secrets:
output = re.sub( output = _redact_with_legacy_fallback(output)
r"(password|key|psk|ssid)\: (.+)", r"\1: \\033[8m\2\\033[28m", output
)
if not CORE.quiet: if not CORE.quiet:
safe_print(output) safe_print(output)
_LOGGER.info("Configuration is valid!") _LOGGER.info("Configuration is valid!")
return 0 return 0
# Legacy substring redaction fallback for unmigrated schemas; removed in
# 2026.12.0 once canonical sensitive fields are tagged. The lookahead skips
# values that already render themselves: ``\033[8m`` (SensitiveStr wrap),
# ``!secret`` (preserves the user-friendly tag), ``!lambda`` (multi-line
# block; first line is structural). The fragment must either start the
# field name or follow ``_`` so the warning names a real field; this avoids
# false positives like ``monkey:`` matching the ``key`` fragment.
_LEGACY_REDACTION_RE = re.compile(
r"(?P<key>\b(?:\w+_)?(?:password|key|psk|ssid))\: "
r"(?!\\033\[8m|!secret\b|!lambda\b)(?P<val>.+)"
)
_LEGACY_REDACTION_REMOVAL = "2026.12.0"
def _redact_with_legacy_fallback(output: str) -> str:
unmarked: set[str] = set()
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)
for key in sorted(unmarked):
_LOGGER.warning(
"Field '%s' is being redacted by a legacy substring heuristic. "
"Mark this field's schema validator with cv.sensitive(...) for "
"deterministic redaction; the heuristic will be removed in %s.",
key,
_LEGACY_REDACTION_REMOVAL,
)
return output
def command_config_hash(args: ArgsProtocol, config: ConfigType) -> int | None: def command_config_hash(args: ArgsProtocol, config: ConfigType) -> int | None:
# generating code might modify config, so it must be done in order to generate # generating code might modify config, so it must be done in order to generate
# a hash that will match what was generated when compiling and then running # a hash that will match what was generated when compiling and then running
@@ -1579,7 +1674,7 @@ def command_clean(args: ArgsProtocol, config: ConfigType) -> int | None:
from esphome import writer from esphome import writer
try: try:
writer.clean_build() writer.clean_build(full=True)
except OSError as err: except OSError as err:
_LOGGER.error("Error deleting build files: %s", err) _LOGGER.error("Error deleting build files: %s", err)
return 1 return 1
@@ -1620,9 +1715,13 @@ def command_bundle(args: ArgsProtocol, config: ConfigType) -> int | None:
def command_dashboard(args: ArgsProtocol) -> int | None: def command_dashboard(args: ArgsProtocol) -> int | None:
from esphome.dashboard import dashboard raise EsphomeError(
"The built-in dashboard has been removed from ESPHome. "
return dashboard.start_dashboard(args) "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( def run_multiple_configs(
@@ -1699,6 +1798,21 @@ def command_update_all(args: ArgsProtocol) -> int | None:
def command_idedata(args: ArgsProtocol, config: ConfigType) -> int: def command_idedata(args: ArgsProtocol, config: ConfigType) -> int:
import json import json
if CORE.using_toolchain_esp_idf:
# Native ESP-IDF derives idedata from the build's compile_commands.json,
# so the configuration must already be compiled.
from esphome.espidf import toolchain as espidf_toolchain
idedata = espidf_toolchain.get_idedata()
if idedata is None:
_LOGGER.error(
"No idedata available; compile the configuration first",
)
return 1
print(json.dumps(idedata, indent=2) + "\n")
return 0
if not CORE.using_toolchain_platformio: if not CORE.using_toolchain_platformio:
_LOGGER.error( _LOGGER.error(
"The idedata command is not compatible with %s toolchain", "The idedata command is not compatible with %s toolchain",
@@ -1792,7 +1906,7 @@ def command_analyze_memory(args: ArgsProtocol, config: ConfigType) -> int:
ram_report = ram_analyzer.generate_report() ram_report = ram_analyzer.generate_report()
print() print()
print(ram_report) print(ram_report)
except Exception as e: # pylint: disable=broad-except except Exception as e: # noqa: BLE001 # pylint: disable=broad-except
_LOGGER.warning("RAM strings analysis failed: %s", e) _LOGGER.warning("RAM strings analysis failed: %s", e)
return 0 return 0
@@ -1980,6 +2094,29 @@ SIMPLE_CONFIG_ACTIONS = [
] ]
def _add_states_args(parser: argparse.ArgumentParser) -> None:
"""Add mutually exclusive ``--states``/``--no-states`` flags to a parser.
When neither flag is given, the ``ESPHOME_LOG_STATES`` environment variable
controls whether entity state changes are shown (defaulting to showing them).
"""
states_group = parser.add_mutually_exclusive_group()
states_group.add_argument(
"--states",
dest="states",
action="store_true",
default=None,
help="Show entity state changes in log output (overrides ESPHOME_LOG_STATES).",
)
states_group.add_argument(
"--no-states",
dest="states",
action="store_false",
default=None,
help="Do not show entity state changes in log output.",
)
def parse_args(argv): def parse_args(argv):
options_parser = argparse.ArgumentParser(add_help=False) options_parser = argparse.ArgumentParser(add_help=False)
options_parser.add_argument( options_parser.add_argument(
@@ -2072,6 +2209,12 @@ def parse_args(argv):
parser_config.add_argument( parser_config.add_argument(
"--show-secrets", help="Show secrets in output.", action="store_true" "--show-secrets", help="Show secrets in output.", action="store_true"
) )
parser_config.add_argument(
"--no-defaults",
help="Only output the user-supplied configuration without "
"schema defaults applied.",
action="store_true",
)
parser_config_hash = subparsers.add_parser( parser_config_hash = subparsers.add_parser(
"config-hash", help="Calculate the hash of the configuration." "config-hash", help="Calculate the hash of the configuration."
@@ -2156,11 +2299,7 @@ def parse_args(argv):
help="Reset the device before starting serial logs.", help="Reset the device before starting serial logs.",
default=os.getenv("ESPHOME_SERIAL_LOGGING_RESET"), default=os.getenv("ESPHOME_SERIAL_LOGGING_RESET"),
) )
parser_logs.add_argument( _add_states_args(parser_logs)
"--no-states",
action="store_true",
help="Do not show entity state changes in log output.",
)
parser_discover = subparsers.add_parser( parser_discover = subparsers.add_parser(
"discover", "discover",
@@ -2192,11 +2331,7 @@ def parse_args(argv):
"--no-logs", help="Disable starting logs.", action="store_true" "--no-logs", help="Disable starting logs.", action="store_true"
) )
parser_run.add_argument( _add_states_args(parser_run)
"--no-states",
action="store_true",
help="Do not show entity state changes in log output.",
)
parser_run.add_argument( parser_run.add_argument(
"--reset", "--reset",
@@ -2248,44 +2383,22 @@ def parse_args(argv):
"configuration", help="Your YAML file or configuration directory.", nargs="*" "configuration", help="Your YAML file or configuration directory.", nargs="*"
) )
parser_dashboard = subparsers.add_parser( # The dashboard moved to ESPHome Device Builder; the command is kept only to
"dashboard", help="Create a simple web server for a dashboard." # 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( parser_dashboard.add_argument(
"configuration", help="Your YAML configuration file directory." "--ha-addon", action="store_true", help=argparse.SUPPRESS
)
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
) )
parser_vscode = subparsers.add_parser("vscode") parser_vscode = subparsers.add_parser("vscode")
@@ -2380,11 +2493,7 @@ def run_esphome(argv):
elif args.quiet: elif args.quiet:
args.log_level = "CRITICAL" args.log_level = "CRITICAL"
setup_log( setup_log(log_level=args.log_level)
log_level=args.log_level,
# Show timestamp for dashboard access logs
include_timestamp=args.command == "dashboard",
)
if args.command in PRE_CONFIG_ACTIONS: if args.command in PRE_CONFIG_ACTIONS:
try: try:
@@ -2441,7 +2550,10 @@ def run_esphome(argv):
# Skipped when -s overrides are passed, since the cache was written # Skipped when -s overrides are passed, since the cache was written
# against the previous substitution set. # against the previous substitution set.
config: ConfigType | None = None config: ConfigType | None = None
if args.command in ("upload", "logs") and not command_line_substitutions: cache_eligible = (
args.command in ("upload", "logs") and not command_line_substitutions
)
if cache_eligible:
from esphome.compiled_config import load_compiled_config from esphome.compiled_config import load_compiled_config
config = load_compiled_config(conf_path) config = load_compiled_config(conf_path)
@@ -2456,6 +2568,16 @@ def run_esphome(argv):
command_line_substitutions, command_line_substitutions,
skip_external_update=skip_external, skip_external_update=skip_external,
) )
# Refresh the cache so the next upload/logs hits the fast path
# instead of re-running read_config. Skip when the storage
# sidecar is absent (no compile has run): the cache would
# never be loaded back, so writing secrets to disk is wasted.
if cache_eligible and config is not None:
from esphome.compiled_config import save_compiled_config
from esphome.storage_json import ext_storage_path
if ext_storage_path(conf_path.name).exists():
save_compiled_config(config)
if config is None: if config is None:
return 2 return 2
CORE.config = config CORE.config = config

View File

@@ -6,6 +6,7 @@ from collections import defaultdict
from collections.abc import Callable from collections.abc import Callable
import heapq import heapq
from operator import itemgetter from operator import itemgetter
from pathlib import Path
import sys import sys
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
@@ -509,7 +510,7 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
lines.append( lines.append(
f"{_COMPONENT_CORE} Symbols > {self.SYMBOL_SIZE_THRESHOLD} B ({len(large_core_symbols)} symbols):" f"{_COMPONENT_CORE} Symbols > {self.SYMBOL_SIZE_THRESHOLD} B ({len(large_core_symbols)} symbols):"
) )
for i, (symbol, demangled, size) in enumerate(large_core_symbols): for i, (_symbol, demangled, size) in enumerate(large_core_symbols):
# Core symbols only track (symbol, demangled, size) without section info, # Core symbols only track (symbol, demangled, size) without section info,
# so we don't show section labels here # so we don't show section labels here
lines.append( lines.append(
@@ -601,7 +602,7 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
lines.append( lines.append(
f"{comp_name} Symbols > {self.SYMBOL_SIZE_THRESHOLD} B & storage ({len(large_symbols)} symbols):" f"{comp_name} Symbols > {self.SYMBOL_SIZE_THRESHOLD} B & storage ({len(large_symbols)} symbols):"
) )
for i, (symbol, demangled, size, section) in enumerate(large_symbols): for i, (_symbol, demangled, size, section) in enumerate(large_symbols):
lines.append( lines.append(
f"{i + 1}. {self._format_symbol_with_section(demangled, size, section)}" f"{i + 1}. {self._format_symbol_with_section(demangled, size, section)}"
) )
@@ -640,7 +641,7 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
lines.append( lines.append(
f" Symbols > {self.RAM_SYMBOL_SIZE_THRESHOLD} B ({len(large_ram_syms)}):" f" Symbols > {self.RAM_SYMBOL_SIZE_THRESHOLD} B ({len(large_ram_syms)}):"
) )
for symbol, demangled, size, section in large_ram_syms[:10]: for _symbol, demangled, size, section in large_ram_syms[:10]:
# Format section label consistently by stripping leading dot # Format section label consistently by stripping leading dot
section_label = section.lstrip(".") if section else "" section_label = section.lstrip(".") if section else ""
display_name = _format_pstorage_name(demangled) display_name = _format_pstorage_name(demangled)
@@ -699,7 +700,7 @@ class MemoryAnalyzerCLI(MemoryAnalyzer):
content = "\n".join(lines) content = "\n".join(lines)
if output_file: if output_file:
with open(output_file, "w", encoding="utf-8") as f: with Path(output_file).open("w", encoding="utf-8") as f:
f.write(content) f.write(content)
else: else:
print(content) print(content)
@@ -737,7 +738,6 @@ def main():
# Load build directory # Load build directory
import json import json
from pathlib import Path
from esphome.platformio.toolchain import IDEData from esphome.platformio.toolchain import IDEData
@@ -785,7 +785,7 @@ def main():
if not idedata_path.exists(): if not idedata_path.exists():
continue continue
try: try:
with open(idedata_path, encoding="utf-8") as f: with idedata_path.open(encoding="utf-8") as f:
raw_data = json.load(f) raw_data = json.load(f)
idedata = IDEData(raw_data) idedata = IDEData(raw_data)
print(f"Loaded idedata from: {idedata_path}", file=sys.stderr) print(f"Loaded idedata from: {idedata_path}", file=sys.stderr)

View File

@@ -154,7 +154,7 @@ def batch_demangle(
failed_count = 0 failed_count = 0
for original, stripped, prefix, demangled in zip( for original, stripped, prefix, demangled in zip(
symbols, symbols_stripped, symbols_prefixes, demangled_lines symbols, symbols_stripped, symbols_prefixes, demangled_lines, strict=True
): ):
# Add back any prefix that was removed # Add back any prefix that was removed
demangled = _restore_symbol_prefix(prefix, stripped, demangled) demangled = _restore_symbol_prefix(prefix, stripped, demangled)

View File

@@ -3,7 +3,6 @@
from __future__ import annotations from __future__ import annotations
import logging import logging
import os
from pathlib import Path from pathlib import Path
import subprocess import subprocess
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
@@ -37,7 +36,7 @@ def _find_in_platformio_packages(tool_name: str) -> str | None:
Full path to the tool or None if not found Full path to the tool or None if not found
""" """
# Get PlatformIO packages directory # Get PlatformIO packages directory
platformio_home = Path(os.path.expanduser("~/.platformio/packages")) platformio_home = Path("~/.platformio/packages").expanduser()
if not platformio_home.exists(): if not platformio_home.exists():
return None return None

View File

@@ -45,7 +45,7 @@ class AsyncThreadRunner(threading.Thread, Generic[_T]):
async def _runner(self) -> None: async def _runner(self) -> None:
try: try:
self.result = await self._coro_factory() self.result = await self._coro_factory()
except Exception as exc: # pylint: disable=broad-except except Exception as exc: # noqa: BLE001 # pylint: disable=broad-except
# Capture all exceptions so ``event`` is always set — otherwise a # Capture all exceptions so ``event`` is always set — otherwise a
# crash would hang the waiter forever. # crash would hang the waiter forever.
self.exception = exc self.exception = exc

View File

@@ -6,8 +6,19 @@ from pathlib import Path
from esphome.components.esp32 import get_esp32_variant, idf_version from esphome.components.esp32 import get_esp32_variant, idf_version
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.core import CORE 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 from esphome.helpers import mkdir_p, write_file_if_changed
from esphome.writer import update_storage_json
# Replaces the IDF default C++ standard (-std=gnu++2b appended to
# CXX_COMPILE_OPTIONS by project.cmake's __build_init) with the one set via
# cg.set_cpp_standard(). Emitted between include(project.cmake) and project(),
# i.e. after IDF appends its default and before the options are consumed, and
# applies project-wide like PlatformIO build_unflags.
CPP_STANDARD_TEMPLATE = """\
idf_build_get_property(esphome_cxx_compile_options CXX_COMPILE_OPTIONS)
list(FILTER esphome_cxx_compile_options EXCLUDE REGEX "^-std=")
list(APPEND esphome_cxx_compile_options "-std={standard}")
idf_build_set_property(CXX_COMPILE_OPTIONS "${{esphome_cxx_compile_options}}")"""
def get_available_components() -> list[str] | None: def get_available_components() -> list[str] | None:
@@ -24,7 +35,7 @@ def get_available_components() -> list[str] | None:
return None return None
try: try:
with open(project_desc, encoding="utf-8") as f: with project_desc.open(encoding="utf-8") as f:
data = json.load(f) data = json.load(f)
component_info = data.get("build_component_info", {}) component_info = data.get("build_component_info", {})
@@ -74,17 +85,18 @@ def get_project_cmakelists(minimal: bool = False) -> str:
# esphome__micro-mp3) rather than just src/. Required so suppressions # esphome__micro-mp3) rather than just src/. Required so suppressions
# like ``-Wno-error=maybe-uninitialized`` actually silence warnings in # like ``-Wno-error=maybe-uninitialized`` actually silence warnings in
# third-party components we don't author. # third-party components we don't author.
project_compile_opts = [ project_compile_opts = get_project_compile_flags()
flag
for flag in sorted(CORE.build_flags)
if flag.startswith("-D")
or (flag.startswith("-W") and not flag.startswith("-Wl,"))
]
extra_compile_options = "\n".join( extra_compile_options = "\n".join(
f'idf_build_set_property(COMPILE_OPTIONS "{flag}" APPEND)' f'idf_build_set_property(COMPILE_OPTIONS "{flag}" APPEND)'
for flag in project_compile_opts for flag in project_compile_opts
) )
cpp_standard_options = (
CPP_STANDARD_TEMPLATE.format(standard=CORE.cpp_standard)
if CORE.cpp_standard
else ""
)
# Per-project list exposed as a CMake variable so converted PIO libs # Per-project list exposed as a CMake variable so converted PIO libs
# can reference ${ESPHOME_PROJECT_MANAGED_COMPONENTS} without baking # can reference ${ESPHOME_PROJECT_MANAGED_COMPONENTS} without baking
# project-specific names into their cached CMakeLists. # project-specific names into their cached CMakeLists.
@@ -141,6 +153,8 @@ set(EXTRA_COMPONENT_DIRS ${{CMAKE_SOURCE_DIR}}/src)
include($ENV{{IDF_PATH}}/tools/cmake/project.cmake) include($ENV{{IDF_PATH}}/tools/cmake/project.cmake)
{cpp_standard_options}
{extra_compile_options} {extra_compile_options}
{managed_components_property} {managed_components_property}
@@ -170,8 +184,8 @@ def get_component_cmakelists() -> str:
# Extract linker options (-Wl, flags). Compile flags (-D, -W) are # Extract linker options (-Wl, flags). Compile flags (-D, -W) are
# emitted project-wide via idf_build_set_property in # emitted project-wide via idf_build_set_property in
# get_project_cmakelists so they reach every component, not just src/. # 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 = get_project_link_flags()
link_opts_str = "\n ".join(sorted(link_opts)) if link_opts else "" link_opts_str = "\n ".join(link_opts) if link_opts else ""
return f"""\ return f"""\
# Auto-generated by ESPHome # Auto-generated by ESPHome
@@ -201,9 +215,6 @@ idf_component_register(
REQUIRES ${{ESPHOME_PROJECT_BUILTIN_COMPONENTS}} REQUIRES ${{ESPHOME_PROJECT_BUILTIN_COMPONENTS}}
) )
# Apply C++ standard
target_compile_features(${{COMPONENT_LIB}} PUBLIC cxx_std_20)
# ESPHome linker options # ESPHome linker options
target_link_options(${{COMPONENT_LIB}} PUBLIC target_link_options(${{COMPONENT_LIB}} PUBLIC
{link_opts_str} {link_opts_str}
@@ -213,11 +224,6 @@ target_link_options(${{COMPONENT_LIB}} PUBLIC
def write_project(minimal: bool = False) -> None: def write_project(minimal: bool = False) -> None:
"""Write ESP-IDF project files.""" """Write ESP-IDF project files."""
# Refresh <data_dir>/storage/<name>.yaml.json so the dashboard's
# /info and /downloads endpoints can locate the build (they 404
# otherwise). This mirrors the PlatformIO build-gen path's call
# in build_gen/platformio.py:write_ini().
update_storage_json()
mkdir_p(CORE.build_path) mkdir_p(CORE.build_path)
mkdir_p(CORE.relative_src_path()) mkdir_p(CORE.relative_src_path())

View File

@@ -1,7 +1,7 @@
from esphome.const import __version__ from esphome.const import __version__
from esphome.core import CORE from esphome.core import CORE
from esphome.helpers import mkdir_p, read_file, write_file_if_changed from esphome.helpers import mkdir_p, read_file, write_file_if_changed
from esphome.writer import find_begin_end, update_storage_json from esphome.writer import find_begin_end
INI_AUTO_GENERATE_BEGIN = "; ========== AUTO GENERATED CODE BEGIN ===========" INI_AUTO_GENERATE_BEGIN = "; ========== AUTO GENERATED CODE BEGIN ==========="
INI_AUTO_GENERATE_END = "; =========== AUTO GENERATED CODE END ============" INI_AUTO_GENERATE_END = "; =========== AUTO GENERATED CODE END ============"
@@ -33,12 +33,27 @@ def format_ini(data: dict[str, str | list[str]]) -> str:
return content return content
# All -std= variants a platform/framework may set by default, in both the GNU
# and strict dialects; unflagged so the cg.set_cpp_standard() value is the
# only standard left in the build.
CPP_STD_VARIANTS = [
f"{prefix}{year}"
for year in ("11", "14", "17", "20", "23", "26", "2a", "2b", "2c")
for prefix in ("gnu++", "c++")
]
def get_ini_content(): def get_ini_content():
CORE.add_platformio_option( CORE.add_platformio_option(
"lib_deps", "lib_deps",
[x.as_lib_dep for x in CORE.platformio_libraries.values()] [x.as_lib_dep for x in CORE.platformio_libraries.values()]
+ ["${common.lib_deps}"], + ["${common.lib_deps}"],
) )
if CORE.cpp_standard:
for variant in CPP_STD_VARIANTS:
if variant != CORE.cpp_standard:
CORE.add_build_unflag(f"-std={variant}")
CORE.add_build_flag(f"-std={CORE.cpp_standard}")
# Sort to avoid changing build flags order # Sort to avoid changing build flags order
CORE.add_platformio_option("build_flags", sorted(CORE.build_flags)) CORE.add_platformio_option("build_flags", sorted(CORE.build_flags))
@@ -58,7 +73,6 @@ def get_ini_content():
def write_ini(content): def write_ini(content):
update_storage_json()
path = CORE.relative_build_path("platformio.ini") path = CORE.relative_build_path("platformio.ini")
if path.is_file(): if path.is_file():

View File

@@ -260,42 +260,20 @@ class ConfigBundleCreator:
def _discover_yaml_includes(self) -> None: def _discover_yaml_includes(self) -> None:
"""Discover YAML files loaded during config parsing. """Discover YAML files loaded during config parsing.
Deliberately uses a fresh re-parse and force-loads every deferred Delegates to :func:`yaml_util.discover_user_yaml_files`, which does a
``IncludeFile`` to include *all* potentially-reachable includes, fresh re-parse and force-loads every deferred ``IncludeFile`` so that
even branches not selected by the local substitutions. Bundles are *all* potentially-reachable includes are captured (even branches not
meant to be compiled on another system where command-line selected by local substitutions). Bundles are meant to be compiled on
substitution overrides may choose a different branch — e.g. another system where command-line substitution overrides may choose a
``!include network/${eth_model}/config.yaml`` must ship every different branch — e.g. ``!include network/${eth_model}/config.yaml``
candidate so the remote build can pick any one. must ship every candidate so the remote build can pick any one.
Entries with unresolved substitution variables in the filename
path are skipped with a warning (they cannot be resolved without
the substitution pass).
Secrets files are tracked separately so we can filter them to
only include the keys this config actually references.
""" """
# Must be a fresh parse: IncludeFile.load() caches its result in discovered = yaml_util.discover_user_yaml_files(self._config_path)
# _content, and we discover files by listening for loader calls. On self._secrets_paths.update(discovered.secrets)
# an already-parsed tree the cache is populated, .load() returns config_resolved = self._config_path.resolve()
# without calling the loader, the listener never fires, and the for fpath in discovered.files:
# referenced files would be silently dropped from the bundle. if fpath == config_resolved:
with yaml_util.track_yaml_loads() as loaded_files:
try:
data = yaml_util.load_yaml(self._config_path)
except EsphomeError:
_LOGGER.debug(
"Bundle: re-loading YAML for include discovery failed, "
"proceeding with partial file list"
)
else:
_force_load_include_files(data)
for fpath in loaded_files:
if fpath == self._config_path.resolve():
continue # Already added as config continue # Already added as config
if fpath.name in const.SECRETS_FILES:
self._secrets_paths.add(fpath)
self._add_file(fpath) self._add_file(fpath)
def _discover_component_files(self) -> None: def _discover_component_files(self) -> None:
@@ -434,7 +412,7 @@ class ConfigBundleCreator:
@staticmethod @staticmethod
def _add_to_tar(tar: tarfile.TarFile, bf: BundleFile) -> None: def _add_to_tar(tar: tarfile.TarFile, bf: BundleFile) -> None:
"""Add a BundleFile to the tar archive with deterministic metadata.""" """Add a BundleFile to the tar archive with deterministic metadata."""
with open(bf.source, "rb") as f: with bf.source.open("rb") as f:
_add_bytes_to_tar(tar, bf.path, f.read()) _add_bytes_to_tar(tar, bf.path, f.read())
@@ -625,57 +603,6 @@ def _add_bytes_to_tar(tar: tarfile.TarFile, name: str, data: bytes) -> None:
tar.addfile(info, io.BytesIO(data)) tar.addfile(info, io.BytesIO(data))
def _force_load_include_files(obj: Any, _seen: set[int] | None = None) -> None:
"""Recursively resolve any ``IncludeFile`` instances in a YAML tree.
Nested ``!include`` returns a deferred ``IncludeFile`` that is only
resolved during the substitution pass. During bundle discovery we need
the referenced files to actually load so the ``track_yaml_loads``
listener fires for them.
``IncludeFile`` instances with unresolved substitution variables in the
filename cannot be loaded — we skip and warn about those.
"""
if _seen is None:
_seen = set()
if isinstance(obj, yaml_util.IncludeFile):
if id(obj) in _seen:
return
_seen.add(id(obj))
if obj.has_unresolved_expressions():
_LOGGER.warning(
"Bundle: cannot resolve !include %s (referenced from %s) "
"with substitutions in path",
obj.file,
obj.parent_file,
)
return
try:
loaded = obj.load()
except EsphomeError as err:
_LOGGER.warning(
"Bundle: failed to load !include %s (referenced from %s): %s",
obj.file,
obj.parent_file,
err,
)
return
_force_load_include_files(loaded, _seen)
elif isinstance(obj, dict):
if id(obj) in _seen:
return
_seen.add(id(obj))
for value in obj.values():
_force_load_include_files(value, _seen)
elif isinstance(obj, (list, tuple)):
if id(obj) in _seen:
return
_seen.add(id(obj))
for item in obj:
_force_load_include_files(item, _seen)
def _resolve_include_path(include_path: Any) -> Path | None: def _resolve_include_path(include_path: Any) -> Path | None:
"""Resolve an include path to absolute, skipping system includes.""" """Resolve an include path to absolute, skipping system includes."""
if isinstance(include_path, str) and include_path.startswith("<"): if isinstance(include_path, str) and include_path.startswith("<"):

View File

@@ -43,7 +43,7 @@ def save_compiled_config(config: ConfigType) -> None:
try: try:
rendered = yaml_util.dump(config, show_secrets=True) rendered = yaml_util.dump(config, show_secrets=True)
write_file(compiled_config_path(CORE.config_filename), rendered, private=True) write_file(compiled_config_path(CORE.config_filename), rendered, private=True)
except Exception as err: # pylint: disable=broad-except except Exception as err: # noqa: BLE001 # pylint: disable=broad-except
_LOGGER.debug("Skipping compiled config cache write: %s", err) _LOGGER.debug("Skipping compiled config cache write: %s", err)
@@ -62,7 +62,7 @@ def load_compiled_config(conf_path: Path) -> ConfigType | None:
try: try:
config = yaml_util.load_yaml(cache_path, clear_secrets=False) config = yaml_util.load_yaml(cache_path, clear_secrets=False)
except Exception: # pylint: disable=broad-except except Exception: # noqa: BLE001 # pylint: disable=broad-except
return None return None
storage = StorageJSON.load(ext_storage_path(conf_path.name)) storage = StorageJSON.load(ext_storage_path(conf_path.name))

View File

@@ -8,7 +8,7 @@
namespace esphome::a01nyub { 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: public:
// Nothing really public. // Nothing really public.

View File

@@ -8,7 +8,7 @@
namespace esphome::a02yyuw { 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: public:
// Nothing really public. // Nothing really public.

View File

@@ -6,7 +6,7 @@
namespace esphome::a4988 { namespace esphome::a4988 {
class A4988 : public stepper::Stepper, public Component { class A4988 final : public stepper::Stepper, public Component {
public: public:
void set_step_pin(GPIOPin *step_pin) { step_pin_ = step_pin; } void set_step_pin(GPIOPin *step_pin) { step_pin_ = step_pin; }
void set_dir_pin(GPIOPin *dir_pin) { dir_pin_ = dir_pin; } void set_dir_pin(GPIOPin *dir_pin) { dir_pin_ = dir_pin; }

View File

@@ -13,7 +13,7 @@ enum SaturationVaporPressureEquation {
}; };
/// This class implements calculation of absolute humidity from temperature and relative humidity. /// 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: public:
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { this->temperature_sensor_ = temperature_sensor; } 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; } void set_humidity_sensor(sensor::Sensor *humidity_sensor) { this->humidity_sensor_ = humidity_sensor; }

View File

@@ -41,7 +41,7 @@ struct AcDimmerDataStore {
#endif #endif
}; };
class AcDimmer : public output::FloatOutput, public Component { class AcDimmer final : public output::FloatOutput, public Component {
public: public:
void setup() override; void setup() override;

View File

@@ -54,7 +54,7 @@ template<typename T> class Aggregator {
SamplingMode mode_{SamplingMode::AVG}; 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: public:
/// Update the sensor's state by reading the current ADC value. /// Update the sensor's state by reading the current ADC value.
/// This method is called periodically based on the update interval. /// This method is called periodically based on the update interval.

View File

@@ -6,9 +6,9 @@
namespace esphome::adc128s102 { namespace esphome::adc128s102 {
class ADC128S102 : public Component, class ADC128S102 final : public Component,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING, public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
spi::DATA_RATE_10MHZ> { spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_10MHZ> {
public: public:
ADC128S102() = default; ADC128S102() = default;

View File

@@ -9,10 +9,10 @@
namespace esphome::adc128s102 { namespace esphome::adc128s102 {
class ADC128S102Sensor : public PollingComponent, class ADC128S102Sensor final : public PollingComponent,
public Parented<ADC128S102>, public Parented<ADC128S102>,
public sensor::Sensor, public sensor::Sensor,
public voltage_sampler::VoltageSampler { public voltage_sampler::VoltageSampler {
public: public:
ADC128S102Sensor(uint8_t channel); ADC128S102Sensor(uint8_t channel);

View File

@@ -9,7 +9,7 @@
namespace esphome::addressable_light { namespace esphome::addressable_light {
class AddressableLightDisplay : public display::DisplayBuffer { class AddressableLightDisplay final : public display::DisplayBuffer {
public: public:
light::AddressableLight *get_light() const { return this->light_; } light::AddressableLight *get_light() const { return this->light_; }

View File

@@ -1 +0,0 @@
CODEOWNERS = ["@kpfleming"]

View File

@@ -87,14 +87,24 @@ void ADE7880::update_sensor_from_s16_register16_(sensor::Sensor *sensor, uint16_
sensor->publish_state(f(val)); sensor->publish_state(f(val));
} }
template<typename F> void ADE7880::update_active_energy_(PowerChannel *channel, uint16_t a_register) {
void ADE7880::update_sensor_from_s32_register16_(sensor::Sensor *sensor, uint16_t a_register, F &&f) { if (channel->forward_active_energy == nullptr && channel->reverse_active_energy == nullptr) {
if (sensor == nullptr) {
return; return;
} }
float val = this->read_s32_register16_(a_register); // The ADE7880 has no separate forward/reverse active energy accumulators. The xWATTHR registers
sensor->publish_state(f(val)); // accumulate signed energy since the last read (positive = imported/forward, negative = exported/
// reverse), so split the value by sign into the forward and reverse running totals.
float val = this->read_s32_register16_(a_register) / 14400.0f;
if (val >= 0.0f) {
if (channel->forward_active_energy != nullptr) {
channel->forward_active_energy->publish_state(channel->forward_active_energy_total += val);
}
} else {
if (channel->reverse_active_energy != nullptr) {
channel->reverse_active_energy->publish_state(channel->reverse_active_energy_total -= val);
}
}
} }
void ADE7880::update() { void ADE7880::update() {
@@ -117,12 +127,7 @@ void ADE7880::update() {
this->update_sensor_from_s24zp_register16_(chan->apparent_power, AVA, [](float val) { return val / 100.0f; }); this->update_sensor_from_s24zp_register16_(chan->apparent_power, AVA, [](float val) { return val / 100.0f; });
this->update_sensor_from_s16_register16_(chan->power_factor, APF, this->update_sensor_from_s16_register16_(chan->power_factor, APF,
[](float val) { return std::abs(val / -327.68f); }); [](float val) { return std::abs(val / -327.68f); });
this->update_sensor_from_s32_register16_(chan->forward_active_energy, AFWATTHR, [&chan](float val) { this->update_active_energy_(chan, AWATTHR);
return chan->forward_active_energy_total += val / 14400.0f;
});
this->update_sensor_from_s32_register16_(chan->reverse_active_energy, ARWATTHR, [&chan](float val) {
return chan->reverse_active_energy_total += val / 14400.0f;
});
} }
if (this->channel_b_ != nullptr) { if (this->channel_b_ != nullptr) {
@@ -133,12 +138,7 @@ void ADE7880::update() {
this->update_sensor_from_s24zp_register16_(chan->apparent_power, BVA, [](float val) { return val / 100.0f; }); this->update_sensor_from_s24zp_register16_(chan->apparent_power, BVA, [](float val) { return val / 100.0f; });
this->update_sensor_from_s16_register16_(chan->power_factor, BPF, this->update_sensor_from_s16_register16_(chan->power_factor, BPF,
[](float val) { return std::abs(val / -327.68f); }); [](float val) { return std::abs(val / -327.68f); });
this->update_sensor_from_s32_register16_(chan->forward_active_energy, BFWATTHR, [&chan](float val) { this->update_active_energy_(chan, BWATTHR);
return chan->forward_active_energy_total += val / 14400.0f;
});
this->update_sensor_from_s32_register16_(chan->reverse_active_energy, BRWATTHR, [&chan](float val) {
return chan->reverse_active_energy_total += val / 14400.0f;
});
} }
if (this->channel_c_ != nullptr) { if (this->channel_c_ != nullptr) {
@@ -149,12 +149,7 @@ void ADE7880::update() {
this->update_sensor_from_s24zp_register16_(chan->apparent_power, CVA, [](float val) { return val / 100.0f; }); this->update_sensor_from_s24zp_register16_(chan->apparent_power, CVA, [](float val) { return val / 100.0f; });
this->update_sensor_from_s16_register16_(chan->power_factor, CPF, this->update_sensor_from_s16_register16_(chan->power_factor, CPF,
[](float val) { return std::abs(val / -327.68f); }); [](float val) { return std::abs(val / -327.68f); });
this->update_sensor_from_s32_register16_(chan->forward_active_energy, CFWATTHR, [&chan](float val) { this->update_active_energy_(chan, CWATTHR);
return chan->forward_active_energy_total += val / 14400.0f;
});
this->update_sensor_from_s32_register16_(chan->reverse_active_energy, CRWATTHR, [&chan](float val) {
return chan->reverse_active_energy_total += val / 14400.0f;
});
} }
ESP_LOGD(TAG, "update took %" PRIu32 " ms", millis() - start); ESP_LOGD(TAG, "update took %" PRIu32 " ms", millis() - start);

View File

@@ -65,7 +65,7 @@ struct ADE7880Store {
static void gpio_intr(ADE7880Store *arg); static void gpio_intr(ADE7880Store *arg);
}; };
class ADE7880 : public i2c::I2CDevice, public PollingComponent { class ADE7880 final : public i2c::I2CDevice, public PollingComponent {
public: public:
void set_irq0_pin(InternalGPIOPin *pin) { this->irq0_pin_ = pin; } void set_irq0_pin(InternalGPIOPin *pin) { this->irq0_pin_ = pin; }
void set_irq1_pin(InternalGPIOPin *pin) { this->irq1_pin_ = pin; } void set_irq1_pin(InternalGPIOPin *pin) { this->irq1_pin_ = pin; }
@@ -105,7 +105,8 @@ class ADE7880 : public i2c::I2CDevice, public PollingComponent {
// the callable will be passed a 'float' value and is expected to return a 'float' // the callable will be passed a 'float' value and is expected to return a 'float'
template<typename F> void update_sensor_from_s24zp_register16_(sensor::Sensor *sensor, uint16_t a_register, F &&f); template<typename F> void update_sensor_from_s24zp_register16_(sensor::Sensor *sensor, uint16_t a_register, F &&f);
template<typename F> void update_sensor_from_s16_register16_(sensor::Sensor *sensor, uint16_t a_register, F &&f); template<typename F> void update_sensor_from_s16_register16_(sensor::Sensor *sensor, uint16_t a_register, F &&f);
template<typename F> void update_sensor_from_s32_register16_(sensor::Sensor *sensor, uint16_t a_register, F &&f);
void update_active_energy_(PowerChannel *channel, uint16_t a_register);
void reset_device_(); void reset_device_();

View File

@@ -84,9 +84,7 @@ constexpr uint16_t CWATTHR = 0xE402;
constexpr uint16_t AFWATTHR = 0xE403; constexpr uint16_t AFWATTHR = 0xE403;
constexpr uint16_t BFWATTHR = 0xE404; constexpr uint16_t BFWATTHR = 0xE404;
constexpr uint16_t CFWATTHR = 0xE405; constexpr uint16_t CFWATTHR = 0xE405;
constexpr uint16_t ARWATTHR = 0xE406; // 0xE406-0xE408 are reserved on the ADE7880 (it does not implement total reactive energy accumulation)
constexpr uint16_t BRWATTHR = 0xE407;
constexpr uint16_t CRWATTHR = 0xE408;
constexpr uint16_t AFVARHR = 0xE409; constexpr uint16_t AFVARHR = 0xE409;
constexpr uint16_t BFVARHR = 0xE40A; constexpr uint16_t BFVARHR = 0xE40A;
constexpr uint16_t CFVARHR = 0xE40B; constexpr uint16_t CFVARHR = 0xE40B;

View File

@@ -10,7 +10,7 @@
namespace esphome::ade7953_i2c { namespace esphome::ade7953_i2c {
class AdE7953I2c : public ade7953_base::ADE7953, public i2c::I2CDevice { class AdE7953I2c final : public ade7953_base::ADE7953, public i2c::I2CDevice {
public: public:
void dump_config() override; void dump_config() override;

View File

@@ -43,7 +43,7 @@ enum ADS1115Samplerate {
ADS1115_860SPS = 0b111 ADS1115_860SPS = 0b111
}; };
class ADS1115Component : public Component, public i2c::I2CDevice { class ADS1115Component final : public Component, public i2c::I2CDevice {
public: public:
void setup() override; void setup() override;
void dump_config() override; void dump_config() override;

View File

@@ -11,10 +11,10 @@
namespace esphome::ads1115 { namespace esphome::ads1115 {
/// Internal holder class that is in instance of Sensor so that the hub can create individual sensors. /// Internal holder class that is in instance of Sensor so that the hub can create individual sensors.
class ADS1115Sensor : public sensor::Sensor, class ADS1115Sensor final : public sensor::Sensor,
public PollingComponent, public PollingComponent,
public voltage_sampler::VoltageSampler, public voltage_sampler::VoltageSampler,
public Parented<ADS1115Component> { public Parented<ADS1115Component> {
public: public:
void update() override; void update() override;
void set_multiplexer(ADS1115Multiplexer multiplexer) { this->multiplexer_ = multiplexer; } void set_multiplexer(ADS1115Multiplexer multiplexer) { this->multiplexer_ = multiplexer; }

View File

@@ -26,9 +26,9 @@ enum ADS1118Gain {
ADS1118_GAIN_0P256 = 0b101, ADS1118_GAIN_0P256 = 0b101,
}; };
class ADS1118 : public Component, class ADS1118 final : public Component,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_TRAILING, public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
spi::DATA_RATE_1MHZ> { spi::CLOCK_PHASE_TRAILING, spi::DATA_RATE_1MHZ> {
public: public:
ADS1118() = default; ADS1118() = default;
void setup() override; void setup() override;

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