Compare commits

...
490 Commits
Author SHA1 Message Date
Jesse HillsandGitHub bbc5669efa Merge pull request #17739 from esphome/bump-2026.7.1
2026.7.1
2026-07-21 10:44:34 +12:00
Guanzhong ChenandJesse Hills 718b04c15a [zephyr] implement ISRInternalGPIOPin::digital_write (#17601) 2026-07-21 09:27:55 +12:00
Jonathan SwobodaandJesse Hills 24a8634a4b [haier] Fix outdoor defrost temperature reporting the coil temperature (#17492) 2026-07-21 09:27:55 +12:00
Jonathan SwobodaandJesse Hills fc6664d737 [emc2101] Fix negative external temperatures reported as large positives (#17494) 2026-07-21 09:27:55 +12:00
dependabot[bot]andJesse Hills 388e411469 Bump aioesphomeapi from 45.6.1 to 45.6.2 (#17654)
Signed-off-by: dependabot[bot] <support@github.com>
2026-07-21 09:17:18 +12:00
Jesse Hills a8ebdcb8f2 Bump version to 2026.7.1 2026-07-21 08:25:51 +12:00
J. Nick KostonandJesse Hills f5f1c48f51 [esp8266] Fail fast when Rosetta 2 is missing on Apple Silicon Macs (#17737) 2026-07-21 08:25:51 +12:00
J. Nick KostonandJesse Hills 7c55de311f [wireguard] Mark private keys sensitive, stop redacting public keys (#17736) 2026-07-21 08:25:46 +12:00
J. Nick KostonandJesse Hills 307faa6c38 [espidf] Resume interrupted toolchain downloads instead of restarting (#17706) 2026-07-21 08:18:16 +12:00
J. Nick KostonandJesse Hills 5f2adcf9b3 [platformio] Include cache path in invalid library error (#17692) 2026-07-21 08:18:16 +12:00
7738464f0b [http_request] Fix usage of http response body (#17713)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-07-21 08:18:16 +12:00
J. Nick KostonandJesse Hills 5a86e26f68 [platformio] Re-download library when cached copy is missing its manifest (#17691)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2026-07-21 08:18:16 +12:00
J. Nick KostonJesse Hillspre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
786b47d8c2 [core] Auto-clean the PlatformIO build environment when the Python version changes (#17671)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-07-21 08:18:15 +12:00
esphome[bot]andJesse Hills 83092ea05c Bump bundled esphome-device-builder to 1.6.8 (#17708) 2026-07-21 08:18:15 +12:00
629afd38f6 [light] Fix pulse and other effects (#17645)
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-07-21 08:18:15 +12:00
J. Nick KostonandJesse Hills 3ffc3a9610 [espidf] Make openocd-esp32 optional so its libusb check cannot break installs (#17686) 2026-07-21 08:18:15 +12:00
J. Nick KostonandJesse Hills 9de7bd7461 [git] Detect interrupted clones and re-clone automatically (#17690) 2026-07-21 08:18:15 +12:00
J. Nick KostonandJesse Hills 5df922e0df [platformio] Accept git URLs passed as the library name (#17697) 2026-07-21 08:18:15 +12:00
J. Nick KostonandJesse Hills b571d2a5ab [micro_wake_word] Download models in parallel (#17701) 2026-07-21 08:18:15 +12:00
Jesse Hills 231a2897c0 [ssd1306] Fix offset_x being ignored on SH1106/SH1107 displays (#17700) 2026-07-21 08:18:15 +12:00
Brandon HarveyandJesse Hills 2223b14794 Split multi-token build.flags entries when generating ESP-IDF component CMakeLists (#17649) 2026-07-21 08:18:15 +12:00
esphome[bot]andJesse Hills 3d340f4d90 Bump bundled esphome-device-builder to 1.6.7 (#17696) 2026-07-21 08:18:15 +12:00
esphome[bot]andJesse Hills 2f99466f3a Bump bundled esphome-device-builder to 1.6.6 (#17681) 2026-07-21 08:18:15 +12:00
esphome[bot]andJesse Hills f05e15522c Bump bundled esphome-device-builder to 1.6.5 (#17675) 2026-07-21 08:18:15 +12:00
Jonathan SwobodaandJesse Hills f37dad683b [esp32] Bump recommended ESP-IDF to 5.5.5 and Arduino to 3.3.10 (#17669) 2026-07-21 08:18:15 +12:00
esphome[bot]andJesse Hills a0fb14bf54 Bump bundled esphome-device-builder to 1.6.4 (#17662) 2026-07-21 08:18:15 +12:00
Jesse Hills d4443be0c1 [nrf52] Install PlatformIO toolchain Python packages into a dedicated venv (#17635) 2026-07-21 08:18:15 +12:00
J. Nick KostonandJesse Hills 0dfb573e18 [logs] Cap the logs reconnect backoff for deep-sleep devices (#17656) 2026-07-21 08:18:15 +12:00
J. Nick KostonandJesse Hills 572fc033cd Ship component requirements.txt files in the sdist and wheel (#17660) 2026-07-21 08:18:15 +12:00
J. Nick KostonandJesse Hills 4062f0a323 Pin cryptography to 48.0.1 on Intel macOS (#17658) 2026-07-21 08:18:15 +12:00
Brandon HarveyandJesse Hills cd40fb1c68 [core] Fix srcFilter exclusions being silently ignored on Windows (#17648) 2026-07-21 08:18:15 +12:00
Jonathan SwobodaandJesse Hills 7afe7750cd [espidf] Suggest installing missing system libraries when the tools install fails (#17619) 2026-07-21 08:18:15 +12:00
esphome[bot]andJesse Hills e08bdf8cca Bump bundled esphome-device-builder to 1.6.3 (#17651) 2026-07-21 08:18:15 +12:00
dependabot[bot]andJesse Hills b3172ecae8 Bump aioesphomeapi from 45.6.0 to 45.6.1 (#17653)
Signed-off-by: dependabot[bot] <support@github.com>
2026-07-21 08:18:14 +12:00
esphome[bot]Jesse Hillsesphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2b4d9c0af9 Bump bundled esphome-device-builder to 1.6.2 (#17640)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-07-21 08:18:14 +12:00
Jesse Hills 3a10d2c187 [nrf52] Set ZEPHYR_SDK_INSTALL_DIR for Zephyr SDK discovery (#17633) 2026-07-21 08:18:14 +12:00
Jonathan SwobodaandJesse Hills 05e2c6b133 [web_server_idf] Use core format_hex_to helper for digest auth (fixes Arduino build) (#17608) 2026-07-21 08:18:14 +12:00
Jonathan SwobodaandJesse Hills 2797349c75 [http_request] Fix use-after-return of header collection state in IDF backend (#17627) 2026-07-21 08:18:14 +12:00
Jonathan SwobodaandJesse Hills 95a01ac2ab [core] Improve framework mirror selection, download errors, and version parsing (#17615) 2026-07-21 08:18:14 +12:00
J. Nick KostonandJesse Hills ea01c909b7 [micro_wake_word] Include the local model file in bundles (#17604) 2026-07-21 08:18:14 +12:00
Jonathan SwobodaandJesse Hills d3655597ea [as3935_i2c] Use repeated start when reading registers (#17584) 2026-07-21 08:18:14 +12:00
Jesse HillsandGitHub 920a8b761b Merge pull request #17583 from esphome/bump-2026.7.0
2026.7.0
2026-07-16 12:59:09 +12:00
Jonathan SwobodaandJesse Hills 5d2372937e [ci] Stop per-PR cache copies from crowding the 10GB Actions cache quota (#17463) 2026-07-16 10:16:18 +12:00
Jesse Hills 6c401d406f Bump version to 2026.7.0 2026-07-16 09:52:34 +12:00
Jesse HillsandGitHub 78180a40c4 Merge pull request #17582 from esphome/bump-2026.7.0b4
2026.7.0b4
2026-07-16 09:44:41 +12:00
Jesse Hills 63fae2c36a Bump version to 2026.7.0b4 2026-07-16 08:23:57 +12:00
Jesse Hills 878d8a2f6a [core] Fix wait_until crash when re-entered from its own continuation (#17571) 2026-07-16 08:23:57 +12:00
esphome[bot]Jesse Hillsesphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
e056e99fd4 Bump bundled esphome-device-builder to 1.6.1 (#17575)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-07-16 08:23:57 +12:00
Jesse Hills 3b915cf16b [api] Warn when Home Assistant actions are sent with no subscribed client (#17560) 2026-07-16 08:23:57 +12:00
esphome[bot]andJesse Hills d1150d148d Bump bundled esphome-device-builder to 1.6.0 (#17573) 2026-07-16 08:23:57 +12:00
luar123andJesse Hills b5b426492a [zigbee] bump esp-zigbee-sdk to 2.0.3 (#17564) 2026-07-16 08:23:57 +12:00
Jonathan SwobodaandJesse Hills 724df7b11e [atc_mithermometer] Make duplicate-packet counter per-instance (#17496) 2026-07-16 08:23:57 +12:00
Daniele PalumboandJesse Hills 26db22546d [mcp23017] reset IPOL registers to 0x00 on setup (#17177)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2026-07-16 08:23:57 +12:00
Jesse HillsandGitHub 00d2d55e09 Merge pull request #17556 from esphome/bump-2026.7.0b3
2026.7.0b3
2026-07-14 16:45:55 +12:00
esphome[bot]andJesse Hills a833685a73 Bump bundled esphome-device-builder to 1.5.0 (#17558) 2026-07-14 14:51:55 +12:00
Jesse Hills 0a1065da75 [script] Recursively expand nested packages when merging component tests (#17557) 2026-07-14 14:11:18 +12:00
Jesse Hills 6fadf353b1 Bump version to 2026.7.0b3 2026-07-14 12:40:45 +12:00
Keith BurzinskiandJesse Hills 8da377ab43 [nextion] Fix unbounded queue growth and OOM crash when display sends no data (#17553) 2026-07-14 12:40:45 +12:00
Jonathan SwobodaandJesse Hills 1da8900ffc [veml7700][as7341][ltr501] Fix device class on raw-count sensors (#17549) 2026-07-14 12:40:45 +12:00
J. Nick KostonandJesse Hills 519ce38b79 [libretiny] Keep renamed board generic-ln882hki validating against generic-ln882h (#17542) 2026-07-14 12:40:45 +12:00
Jesse Hills 989797be53 [web_server] Add HTTP digest authentication with selectable scheme (#17541) 2026-07-14 12:40:45 +12:00
Jesse Hills 583adc9e69 [web_server] Use dict-style packages in tests so they can be batch-grouped (#17544) 2026-07-14 12:40:45 +12:00
Keith BurzinskiandJesse Hills af9a0404d9 [esp32] Do not require verification_key with Secure Boot V2 signing schemes (#17497) 2026-07-14 12:40:45 +12:00
Jesse Hills f38e7f2de2 [web_server] Add CORS origin checking with allowed_origins (#17530) 2026-07-14 12:40:22 +12:00
Jesse Hills 3e75020007 [gsl3670] Fix i2c package variant in esp32-s3-idf test (#17535) 2026-07-14 12:39:14 +12:00
Jesse HillsandGitHub feb53a209b Merge pull request #17532 from esphome/bump-2026.7.0b2
2026.7.0b2
2026-07-13 13:24:45 +12:00
Jonathan SwobodaandJesse Hills c607f64288 [mipi_rgb] Test in isolation to avoid bus/pin merge conflicts (#17534) 2026-07-13 12:01:24 +12:00
Jesse HillsandJonathan Swoboda 7ee7a26cad [mipi_rgb] Use dict-style packages in test so it can be batch-grouped
Convert the i2c include to a named dict-style package key so CI can group
this component's build with others sharing the same bus, instead of flagging
it as needing migration.
2026-07-12 18:47:28 -04:00
Jesse Hills 2a67e5c599 Bump version to 2026.7.0b2 2026-07-13 09:19:24 +12:00
Keith BurzinskiandJesse Hills a9591d7aac [zwave_proxy] Fix parser gaps and harden frame and subscription handling (#17461) 2026-07-13 09:19:24 +12:00
J. Nick KostonandJesse Hills bcac3ebe2b [api] Provision encryption keys over an encrypted zero-PSK noise connection (#17482) 2026-07-13 09:19:23 +12:00
Jesse Hills 27b598c5aa [core] Classify entity metadata visibility for the visual editor (#17503) 2026-07-13 09:19:23 +12:00
luar123andJesse Hills 9ba2cbbfdd [zigbee] prevent task watchdog trigger with large configs. (#17506) 2026-07-13 09:19:23 +12:00
luar123andJesse Hills a4650a2345 [zigbee] Fix merge endpoint (#17511) 2026-07-13 09:19:23 +12:00
J. Nick KostonandJesse Hills 1e5cfe6b0f [web_server] Fix unused function warning for json_state_str (#17524) 2026-07-13 09:19:23 +12:00
J. Nick KostonandJesse Hills 665e788cc9 [mdns] Fix missing device info TXT records when native API is not enabled (#17520) 2026-07-13 09:19:23 +12:00
J. Nick KostonandJesse Hills d89b4c0b59 [core] Make config-hash independent of machine-local paths (#17523) 2026-07-13 09:19:23 +12:00
esphome[bot]andJesse Hills 5afe418a8e Bump bundled esphome-device-builder to 1.4.3 (#17522) 2026-07-13 09:19:23 +12:00
tomaszduda23andJesse Hills 312f6f2049 [deep_sleep] feed watchdog in deep sleep (#17516) 2026-07-13 09:19:23 +12:00
eb0848d538 [mipi_dsi] New model for M5Stack Tab5 (#17500)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 09:19:23 +12:00
Clyde StubbsJesse Hillspre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
1a573919d1 [mipi][mipi_spi] SWRESET handling improved (#17504)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-07-13 09:19:23 +12:00
esphome[bot]andJesse Hills 692cf7abd1 Bump bundled esphome-device-builder to 1.4.2 (#17512) 2026-07-13 09:19:23 +12:00
f0afd9e660 [mipi][mipi_spi][mipi_dsi][mipi_rgb] Transform cleanup (#17405)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 09:19:23 +12:00
esphome[bot]andJesse Hills 262ee421f6 Bump bundled esphome-device-builder to 1.4.1 (#17507) 2026-07-13 09:19:23 +12:00
esphome[bot]andJesse Hills 050a006459 Bump bundled esphome-device-builder to 1.4.0 (#17495) 2026-07-13 09:19:23 +12:00
Jonathan SwobodaandJesse Hills 8518d0633b [web_server] Serialize entity state strings without a copy buffer (#17488) 2026-07-13 09:19:23 +12:00
dependabot[bot]andJesse Hills 284fe85271 Bump aioesphomeapi from 45.5.2 to 45.6.0 (#17490)
Signed-off-by: dependabot[bot] <support@github.com>
2026-07-13 09:19:23 +12:00
Jonathan SwobodaandJesse Hills 020a6a8fd1 [mcp4461] Fix wiper increment/decrement write length (#17487) 2026-07-13 09:19:23 +12:00
Jonathan SwobodaandJesse Hills 196b979df8 [usb_uart] Fix output chunk length truncated to 8 bits (#17480) 2026-07-13 09:19:23 +12:00
Jesse HillsandGitHub 6c4640f4f1 Merge pull request #17460 from esphome/bump-2026.7.0b1
2026.7.0b1
2026-07-09 16:54:10 +12:00
Jonathan SwobodaandJesse Hills 19e89aa7f2 [gsl3670] Reference the test display explicitly so grouped CI builds validate (#17462) 2026-07-09 13:44:40 +12:00
Jesse Hills f1622ac96a Bump version to 2026.7.0b1 2026-07-09 12:30:53 +12:00
Jesse Hills c66f4d667e Merge branch 'release' into dev 2026-07-09 12:21:34 +12:00
Jesse HillsandGitHub 3bfbaaebf3 Merge pull request #17459 from esphome/bump-2026.6.5
2026.6.5
2026-07-09 12:20:50 +12:00
Jesse Hills 98b79b132a Bump version to 2026.6.5 2026-07-09 12:09:05 +12:00
esphome[bot]Jesse Hillsesphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
f6221f0007 Bump bundled esphome-device-builder to 1.1.0 (#17412)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-07-09 12:09:05 +12:00
esphome[bot]andJesse Hills 263b375088 Bump bundled esphome-device-builder to 1.0.29 (#17384) 2026-07-09 12:09:05 +12:00
esphome[bot]andJesse Hills a1f819e9b8 Bump bundled esphome-device-builder to 1.0.28 (#17382) 2026-07-09 12:09:05 +12:00
esphome[bot]Jesse Hillsesphome[bot] <115708604+esphome[bot]@users.noreply.github.com>Jonathan Swoboda
ca7f50f37f Bump bundled esphome-device-builder to 1.0.27 (#17370)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-07-09 12:09:05 +12:00
esphome[bot]andJesse Hills 7b92fe95af Bump bundled esphome-device-builder to 1.0.26 (#17369) 2026-07-09 12:09:05 +12:00
esphome[bot]andJesse Hills 42ddf0870c Bump bundled esphome-device-builder to 1.0.25 (#17333) 2026-07-09 12:09:05 +12:00
esphome[bot]andJesse Hills 0512dd2339 Bump bundled esphome-device-builder to 1.0.24 (#17332) 2026-07-09 12:09:05 +12:00
Clyde StubbsandGitHub 8ccf0dbd37 [gsl3670] Add new touchscreen component (#16285) 2026-07-09 12:04:43 +12:00
Jesse HillsandGitHub 7c130fc970 [core] Hide build & framework internals from the visual editor (#17449) 2026-07-09 11:58:40 +12:00
Clyde StubbsandGitHub 640e0973ac [lvgl] Dynamic rotation features (#16773) 2026-07-08 15:54:28 -04:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
9088875491 Bump github/codeql-action/analyze from 4.36.3 to 4.37.0 (#17456)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-08 15:42:48 -04:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
99ec2cc00a Bump CodSpeedHQ/action from 4.18.2 to 4.18.4 (#17455)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-08 15:42:38 -04:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
99ff7e198a Bump astral-sh/setup-uv from 8.3.1 to 8.3.2 (#17454)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-08 15:42:18 -04:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
d4bb20d34b Bump github/codeql-action/init from 4.36.3 to 4.37.0 (#17453)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-08 15:42:05 -04:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2db001710c Bump astral-sh/setup-uv from 8.3.1 to 8.3.2 in /.github/actions/restore-python (#17452)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-08 15:41:50 -04:00
Jesse HillsandGitHub dd0d0942f5 [image] Restructure into a platform component (#17416) 2026-07-09 07:38:58 +12:00
Jonathan SwobodaandGitHub 26f48ee9ea [lvgl] Fix ImageValidator.process signature to match base (#17451) 2026-07-09 07:21:23 +12:00
luar123andGitHub 84f4fbeaa8 [zigbee] Allow to combine and merge endpoints on esp32 (#17402) 2026-07-08 14:41:36 -04:00
ce46895270 [uart][usb_uart] Implement runtime settings update (#16990)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2026-07-08 13:30:49 -04:00
esphome[bot]GitHubesphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
e7933a5387 Bump bundled esphome-device-builder to 1.3.1 (#17450)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-07-08 13:06:38 -04:00
Clyde StubbsandGitHub b787281388 [lvgl] Add direct use of mapping (#15863) 2026-07-08 13:00:42 -04:00
bba3a9657b [lvgl] Add animations (#16796)
Co-authored-by: clydeps <U5yx99dok9>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-07-08 12:17:57 -04:00
Kevin AhrendtandGitHub 2f5465c0e8 [sendspin] Suppress WiFi roam scanning while playing (#17133) 2026-07-08 12:07:42 -04:00
9c40ed5d71 [provisioning] Add provisioning window (#17152)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 01:01:20 -05:00
esphome[bot]GitHubesphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
93bc02b308 Bump bundled esphome-device-builder to 1.3.0 (#17448)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-07-07 20:39:55 -05:00
Jesse HillsandGitHub b8af90750f [web_server_idf] Map more common HTTP status codes in responses (#17447) 2026-07-08 13:18:46 +12:00
Jesse HillsandGitHub 65ef05dd1f [web_server_idf] Deliver raw POST bodies to custom handlers via handleBody() (#17433) 2026-07-07 20:51:00 -04:00
731486d9b0 [modbus] Finalize unreleased API surface before 2026.7 (#17434)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 00:20:14 +00:00
Jonathan SwobodaandGitHub 731e9fda03 [internal_temperature] Support all ESP32 variants with a temperature sensor (#17438) 2026-07-08 09:12:57 +12:00
dependabot[bot]andGitHub 35c7496cd7 Bump CodSpeedHQ/action from 4.18.1 to 4.18.2 (#17445)
Signed-off-by: dependabot[bot] <support@github.com>
2026-07-07 16:02:38 -05:00
dependabot[bot]andGitHub 71b746b6fa Bump astral-sh/setup-uv from 8.3.0 to 8.3.1 (#17444)
Signed-off-by: dependabot[bot] <support@github.com>
2026-07-07 16:02:26 -05:00
dependabot[bot]andGitHub 1913818b1c Bump astral-sh/setup-uv from 8.3.0 to 8.3.1 in /.github/actions/restore-python (#17442)
Signed-off-by: dependabot[bot] <support@github.com>
2026-07-07 16:02:16 -05:00
J. Nick KostonandGitHub b4ad0eb86b [esp32_ble] Fix boot loop when the hosted co-processor does not answer BT bring-up (#17429) 2026-07-07 14:59:18 +00:00
7f0e826c32 [lvgl] Add paused option to suppress updates on boot (#16973)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-07-07 14:50:36 +00:00
76ee3fe887 [audio_file] Accept mp1/mp2 puremagic detections as MP3 (#17436)
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>
2026-07-07 08:56:48 -04:00
af4a6e7ec3 [usb_uart] Fix FTDI RX data stall / corruption and input restart reliability (#17348)
Co-authored-by: Oliver Kleinecke <kleinecke.oliver@googlemail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-07 21:55:49 +10:00
40c3a4320f [core] add const for litre per hour (#17389)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-07 21:44:44 +10:00
J. Nick KostonandGitHub 3c2dad67f4 [network] Fix logged use_address with MAC suffix and build it at runtime (#17432) 2026-07-07 02:07:34 -05:00
Remco van EssenandGitHub f823a23ea4 [pcm5122] Add analog gain, channel mixing, volume range, standby/powerdown switch, and XSMT enable pin support (#17313) 2026-07-07 00:15:37 -05:00
Keith BurzinskiandGitHub 9aed1d2700 [esp32] Add NVS encryption (HMAC scheme) (#17004) 2026-07-07 16:04:38 +12:00
9857d508d9 [light] Preserve brightness on turn-off. (#17103)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-07 15:57:06 +12:00
Clyde StubbsandGitHub ad7c980c4b [lvgl] Continue activity while display busy (#17374) 2026-07-07 03:16:29 +00:00
J. Nick KostonandGitHub e8d37e5bd3 [libretiny] Use standard logger tag names (#17431) 2026-07-06 20:55:12 -04:00
J. Nick KostonandGitHub a36c3063b2 [web_server] Use known message length in SSE send path (#17400) 2026-07-06 20:55:02 -04:00
J. Nick KostonandGitHub 902cf6a679 [analyze_memory] Report aliased RAM symbols once in the RAM strings report (#17397) 2026-07-06 20:54:53 -04:00
J. Nick KostonandGitHub a10e005bb4 [esp8266] Strip lwIP glue dhcp stub message strings from DRAM (#17395) 2026-07-06 20:54:44 -04:00
d9998eff20 [esp32] Add software OTA downgrade protection (#17315)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 20:39:18 -04:00
Clyde StubbsandGitHub 2dd7ac090f [mipi_spi] Add M5STACK ATOM3SR display (#17344) 2026-07-06 20:37:44 -04:00
Jesse HillsandGitHub c4689989c7 Mark configurable classes as final (20/21: wts01-zephyr_ble_server) (#16971) 2026-07-06 19:42:18 -04:00
ebff49072e [modbus] Store ModbusFrame inline to cut per-frame heap churn (#17282)
Co-authored-by: Claude <noreply@anthropic.com>
2026-07-06 23:12:25 +00:00
esphome[bot]andGitHub 0b311962b5 Bump bundled esphome-device-builder to 1.2.0 (#17430) 2026-07-06 17:09:51 -05:00
Jonathan SwobodaandGitHub b22def399f [espnow] Add max_payload_size option for ESP-NOW v2 frames (#17360) 2026-07-06 17:08:56 -04:00
cdd334284e [rp2] Rename rp2040 platform to rp2 (#17145)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-07-07 08:20:24 +12:00
Jesse HillsandGitHub 9caf431740 [tests] Document dict-style packages requirement for batch grouping (#17420) 2026-07-07 07:55:53 +12:00
tomaszduda23andGitHub 468b32b986 [nrf52] add better error message for OTA error (#17407) 2026-07-06 15:50:57 -04:00
39ad583b39 [nrf52] allow to build for non nrf52840 boards (#17373)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-07-06 15:48:24 -04:00
J. Nick KostonandGitHub 90403576c4 [wifi] Accept boolean-like strings for fast_connect again (#17414) 2026-07-06 14:42:30 -05:00
J. Nick KostonandGitHub 51fa25856d [bluetooth_proxy] Take over stale advertisement subscription instead of rejecting the new subscriber (#17423) 2026-07-06 14:42:14 -05:00
dependabot[bot]andGitHub 27d4b63a8a Bump astral-sh/setup-uv from 8.2.0 to 8.3.0 (#17428)
Signed-off-by: dependabot[bot] <support@github.com>
2026-07-06 14:41:55 -05:00
dependabot[bot]andGitHub 104c2f86f6 Bump astral-sh/setup-uv from 8.2.0 to 8.3.0 in /.github/actions/restore-python (#17427)
Signed-off-by: dependabot[bot] <support@github.com>
2026-07-06 14:41:43 -05:00
dependabot[bot]andGitHub e64a79f431 Bump setuptools from 82.0.1 to 83.0.0 (#17426)
Signed-off-by: dependabot[bot] <support@github.com>
2026-07-06 14:41:29 -05:00
Oliver KleineckeandGitHub b79db76099 [core] helpers.h - Implement pop_back method for vector (#17390) 2026-07-06 13:54:20 -05:00
66ab807596 [modbus] API naming (#17378)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 11:43:31 -05:00
Jesse HillsandGitHub 2067da4ff5 Mark configurable classes as final (11/21: microphone-ms8607) (#16962) 2026-07-06 16:33:07 +12:00
Jesse HillsandGitHub cfdd6d383f Mark configurable classes as final (19/21: uart-wl_134) (#16970) 2026-07-06 16:32:42 +12:00
Jesse HillsandGitHub bdd51bd476 Mark configurable classes as final (16/21: sm10bit_base-ssd1331_spi) (#16967) 2026-07-06 16:32:35 +12:00
Jesse HillsandGitHub af7b6e3589 Mark configurable classes as final (17/21: ssd1351_spi-tem3200) (#16968) 2026-07-06 16:32:26 +12:00
Jesse HillsandGitHub fd86417bf5 [cst328] Update test package (#17415) 2026-07-06 16:29:45 +12:00
J. Nick KostonandGitHub e095c457ff [esp32] Suppress -Wvolatile in the direct ESP-IDF build (#17404) 2026-07-06 03:02:12 +00:00
Jesse HillsandGitHub 39c0f9cc84 [cst328] Use dict-style packages so batch grouping deduplicates the i2c bus (#17413) 2026-07-05 22:57:56 -04:00
tomaszduda23andGitHub 3f94e6dcbb [nrf52] let user select libc version (#17408) 2026-07-05 19:52:39 -04:00
b9588a8984 [mipi_spi] Add Waveshare-ESP32-S3-TOUCH-AMOLED-1.64 (#17386)
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
2026-07-06 09:29:03 +10:00
esphome[bot]GitHubesphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
105d1362a2 Bump bundled esphome-device-builder to 1.1.0 (#17412)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-07-05 15:31:07 -05:00
Clyde StubbsandGitHub f6c260a2c5 [ci] Make import time budget more realistic (#17406) 2026-07-05 08:01:48 -04:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
c720186170 Bump smpclient from 6.0.0 to 7.2.0 (#16928)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-05 06:50:13 +02:00
8ab19c0242 [esp32] Add RTC-backed preferences (honor in_flash flag) (#17073)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 11:48:30 +10:00
esphome[bot]andGitHub 2c24e82ba3 Bump bundled esphome-device-builder to 1.0.29 (#17384) 2026-07-04 15:20:48 -05:00
Jonathan SwobodaandGitHub fcfaa43e1e [ci] Name the sdk-nrf cache steps after the nRF Connect SDK (#17392) 2026-07-04 12:46:16 -04:00
esphome[bot]andGitHub 4c8e45a222 Bump bundled esphome-device-builder to 1.0.28 (#17382) 2026-07-03 23:17:49 -05:00
Citric LiGitHubpre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>Claude Opus 4.8Clyde Stubbs
7878052533 [epaper_spi] Add T133A01 6-color e-paper driver for reTerminal E1004 (#16706)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
2026-07-04 03:50:54 +00:00
e94fcda8b7 [cst328] Touch screen (Waveshare ESP32-S3-Touch-LCD-2.8) (#8011)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
2026-07-04 12:13:22 +10:00
Jonathan SwobodaandGitHub 5738c60206 [nrf52] Run clang-tidy against the native sdk-nrf toolchain (#17364) 2026-07-03 18:09:01 -04:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
a035d84474 Bump docker/login-action from 4.3.0 to 4.4.0 in the docker-actions group (#17380)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-03 16:55:47 -04:00
7ad43358c2 [zigbee] Bump zigbee sdk to 2.0.2 (#16869)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-07-03 14:28:29 -04:00
Jonathan SwobodaandGitHub 187cd51867 [ci] Carry native-toolchain needs on component test batches (#17359) 2026-07-03 13:47:08 -04:00
fd16eec416 [nrf52] switch nrf52 builds to native sdk by default (#17319)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
Co-authored-by: ESPHome Device Builder <device-builder@esphome.io>
2026-07-03 12:16:55 -04:00
tomaszduda23andGitHub ea14a93e67 [nrf52] fix crash report for native build (#17371) 2026-07-03 09:33:42 -04:00
esphome[bot]GitHubesphome[bot] <115708604+esphome[bot]@users.noreply.github.com>Jonathan Swoboda
c456fc98ab Bump bundled esphome-device-builder to 1.0.27 (#17370)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-07-03 09:31:21 -04:00
711d8bb0ad Synchronise Device Classes from Home Assistant (#17372)
Co-authored-by: esphomebot <esphome@openhomefoundation.org>
2026-07-03 08:33:00 -04:00
Anunay KulshresthaGitHubClaude Opus 4.8pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>tomaszduda23
c3233739c5 [zephyr] Implement GPIO interrupts (ISRInternalGPIOPin) (#17077)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.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>
2026-07-03 11:43:21 +02:00
Joseph SpirosandGitHub 5fe36a45ed [core] Skip MAC-suffix mDNS discovery for non-mDNS addresses (#16874) 2026-07-03 11:48:55 +12:00
esphome[bot]andGitHub 0725157bf5 Bump bundled esphome-device-builder to 1.0.26 (#17369) 2026-07-02 18:48:08 -05:00
Jonathan SwobodaandGitHub 9f589ec4fc [api] Register homeassistant.action with synchronous=False to fix stale trigger args in response callbacks (#17367) 2026-07-02 19:47:12 -04:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
5417a16f9d Update argcomplete requirement from >=3.6.3 to >=3.7.0 (#17334)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-02 18:37:22 -04:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
f447c88b4c Bump docker/build-push-action from 7.2.0 to 7.3.0 in /.github/actions/build-image (#17336)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-02 18:37:12 -04:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
0b48ca0027 Bump the docker-actions group with 2 updates (#17361)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-02 18:36:33 -04:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
bef6773281 Bump github/codeql-action/init from 4.36.2 to 4.36.3 (#17362)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-02 18:36:01 -04:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
4f0968f1df Bump github/codeql-action/analyze from 4.36.2 to 4.36.3 (#17363)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-02 18:35:46 -04:00
Jonathan SwobodaandGitHub 648f5e1b06 [nrf52] Install native sdk-nrf into a machine-global cache dir (#17353) 2026-07-02 15:42:59 -04:00
Jonathan SwobodaandGitHub 65fc10d627 [nrf52] Build PlatformIO libraries as Zephyr modules (sdk-nrf) (#17250) 2026-07-02 10:26:34 -04:00
41cf842d5d [zephyr][nrf52] Rebuild native build when config inputs change (#17318)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-07-02 14:13:56 +00:00
792dfbcbbf [st7123] add ST7123 touch controller component (M5Stack Tab5) (#12075)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
2026-07-02 18:40:59 +10:00
TwisterssandGitHub 06c7ac37d1 [epaper_spi] Add Waveshare 7.5" V2 BWR support (#15719) 2026-07-02 17:33:58 +10:00
Oliver KleineckeandGitHub 0666cb8635 [usb_uart] Add per-device-type maximum baud rate cap (#17259) 2026-07-02 13:44:09 +10:00
Clyde StubbsandGitHub 5b8bf51022 [power_supply] Make enable_on_boot high priority (#16914) 2026-07-02 13:56:01 +12:00
Oliver KleineckeandGitHub 4a7c58d5ae [usb_uart] Fix format specifier warnings for uint32_t in ft23xx and pl2303 (#17342) 2026-07-02 11:26:56 +10:00
J. Nick KostonandGitHub 7522780c67 [esp8266] Strip dead libstdc++ throw message strings from DRAM (#17341) 2026-07-01 18:25:34 -05:00
dependabot[bot]andGitHub e4a68c2da3 Bump pillow from 12.2.0 to 12.3.0 (#17335)
Signed-off-by: dependabot[bot] <support@github.com>
2026-07-01 15:25:55 -05:00
esphome[bot]andGitHub 0427d20c5b Bump bundled esphome-device-builder to 1.0.25 (#17333) 2026-07-01 15:02:01 -05:00
Bonne EgglestonGitHubClaude Opus 4.8pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>J. Nick Koston
d25d160686 [modbus_server] Fix register range issues and allow partial reads (#17205)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-07-01 15:01:48 -05:00
esphome[bot]andGitHub 0e260e5cbb Bump bundled esphome-device-builder to 1.0.24 (#17332) 2026-07-01 12:52:09 -05:00
Jesse Hills 58780cd765 Merge branch 'release' into dev 2026-07-01 16:26:24 +12:00
Jesse HillsandGitHub 1fc73731a8 Merge pull request #17317 from esphome/bump-2026.6.4
2026.6.4
2026-07-01 16:25:39 +12:00
Jesse Hills e47feace11 Bump version to 2026.6.4 2026-07-01 13:18:54 +12:00
esphome[bot]andJesse Hills 4472d3b61b Bump bundled esphome-device-builder to 1.0.23 (#17316) 2026-07-01 13:18:54 +12:00
Jonathan SwobodaandJesse Hills 06c5bcbc66 [espnow] Drop oversized received frames to prevent buffer overflow (#17271) 2026-07-01 13:18:54 +12:00
Jonathan SwobodaandJesse Hills 6c44775bf5 [bluetooth_proxy] Fix -Wtype-limits warning with active: false (#17273) 2026-07-01 13:18:54 +12:00
Clyde StubbsandJesse Hills b127363fa0 [mipi_spi] Bug fixes (#17247) 2026-07-01 13:18:54 +12:00
esphome[bot]andJesse Hills 782b58bbeb Bump bundled esphome-device-builder to 1.0.22 2026-07-01 13:18:50 +12:00
5de508ad8c [es8388] Fix DAC unable to unmute once muted (#17221)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-07-01 13:18:40 +12:00
Jesse Hills 054c8ba485 [config_validation] Fix multicast typo in error message (#17206) 2026-07-01 13:18:35 +12:00
Julian LunzandJesse Hills 3b2be021b2 [adc] Only call cyw43_thread_enter/exit for VSYS when WiFi is active on RP2040 (#17203) 2026-07-01 13:11:21 +12:00
esphome[bot]andGitHub 848defedd8 Bump bundled esphome-device-builder to 1.0.23 (#17316) 2026-07-01 13:09:48 +12:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
4c9ed129cf Bump awalsh128/cache-apt-pkgs-action from 1.6.0 to 1.6.3 (#17310)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-30 18:28:04 -04:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
c8b37fb1c8 Bump platformdirs from 4.9.4 to 4.10.0 (#17309)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-30 17:18:33 -04:00
Jonathan SwobodaandGitHub 1b556f5d0c [ethernet] Fix ETH_SPEED_1000M build on IDF 6.0 (enum added in 6.1) (#17311) 2026-06-30 17:17:47 -04:00
Jonathan SwobodaandGitHub 8a3d0aeafb [tests] Add esp32-c61-idf base file for grouped component tests (#17293) 2026-06-30 19:33:17 +00:00
Jonathan SwobodaandGitHub 9468ad628c [espnow] Drop oversized received frames to prevent buffer overflow (#17271) 2026-06-30 14:57:34 -04:00
Jonathan SwobodaandGitHub 990431aa5b [bluetooth_proxy] Fix -Wtype-limits warning with active: false (#17273) 2026-06-30 14:56:30 -04:00
Jonathan SwobodaandGitHub b79cbcbde7 [espidf] Install native ESP-IDF into a machine-global cache dir (#17306) 2026-06-30 14:56:18 -04:00
Bonne EgglestonandGitHub afb5922f37 [modbus] Update client components to use ModbusClientDevice (#11987) 2026-06-30 13:26:51 -05:00
Jonathan SwobodaandGitHub 3035355c0a [ci] Widen import-time margin for CI runner variance (#17287) 2026-06-30 11:38:31 -04:00
Jonathan SwobodaandGitHub 43b3aa0712 [ci] Fix nRF52 zigbee/network test-grouping conflict (#17295) 2026-06-30 11:37:59 -04:00
Jonathan SwobodaandGitHub 12b78e7c47 [qmi8658] Pin i2c_id in test config to fix grouped component test conflict (#17303) 2026-06-30 11:37:17 -04:00
Clyde StubbsandGitHub fb5d8b5d4c [mipi_spi] Bug fixes (#17247) 2026-06-30 08:43:01 -04:00
9e72027b64 [devcontainer] Align base image with production, fix Python venv and build tools (#17296)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 08:33:33 -04:00
faa5f72500 [mqtt] Add LN882X (LN882H) platform support (#17297)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-30 08:16:18 -04:00
Jonathan SwobodaandGitHub 359c6a7265 [libretiny] Update LibreTiny to v1.13.0 (#17288) 2026-06-29 22:34:32 -04:00
J. Nick KostonandGitHub a031da351d Merge pull request #17294 from esphome/bump-device-builder-1.0.22 2026-06-29 19:29:08 -07:00
esphome[bot]andgithub-actions[bot] 091b6a0ba0 Bump bundled esphome-device-builder to 1.0.22 2026-06-30 02:07:06 +00:00
Jesse HillsandGitHub cf9d97d5ae [pixoo] Add Divoom Pixoo display component (#16974) 2026-06-30 11:20:48 +12:00
Clyde StubbsandGitHub 3e1a6b4e11 [cst9220] Add CST9220 and CST9217 touchscreen support (#16888) 2026-06-30 11:18:01 +12:00
Clyde StubbsandGitHub 5c7245dfcd [qmi8658] Motion platform for QMI8658 IMU (#16889) 2026-06-30 11:04:25 +12:00
Clyde StubbsandGitHub 1611345c55 [agents] Add English language AI guidelines for documentation (#17290) 2026-06-29 22:37:17 +00:00
Jonathan SwobodaandGitHub b36e20d60b Revert "Bump awalsh128/cache-apt-pkgs-action from 1.6.0 to 1.6.2 (#17286)" (#17289) 2026-06-29 18:25:16 -04:00
ArdumineandGitHub 93eb6f78e0 [network] Enlarge Zephyr net buffer pool and TCP windows on nRF52/Zephyr plataform (#17278) 2026-06-29 17:32:57 -04:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
e308075e3f Bump puremagic from 1.30 to 2.2.0 (#17285)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-29 16:12:23 -04:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
405607e9d2 Bump esptool from 5.3.0 to 5.3.1 (#17284)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-29 16:06:36 -04:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
797ed23765 Bump tzlocal from 5.4.3 to 5.4.4 (#17283)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-29 16:02:11 -04:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Jonathan Swoboda
8780c7e0ac Bump awalsh128/cache-apt-pkgs-action from 1.6.0 to 1.6.2 (#17286)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-29 16:01:41 -04:00
Jonathan SwobodaandGitHub 136e343988 [ethernet] Generic and YT8531 PHY over RGMII (gigabit) for ESP32-S31 (#17277) 2026-06-29 14:13:11 -04:00
Jonathan SwobodaandGitHub b8690c8e31 [core] Drop Python 3.11 support (#17280) 2026-06-29 12:32:28 -04:00
Kevin AhrendtandGitHub 2778c62d07 [audio] Bump microMP3 to v0.4.0 (#17279) 2026-06-29 11:33:56 -04:00
Jesse Hills 7984349c36 Merge branch 'release' into dev 2026-06-29 22:32:34 +12:00
Jesse HillsandGitHub 0cbbd64577 Merge pull request #17223 from esphome/bump-2026.6.3
2026.6.3
2026-06-29 22:31:44 +12:00
Jesse Hills a618ee11b4 Bump version to 2026.6.3 2026-06-29 20:30:24 +12:00
TomandJesse Hills 6251c26cc6 [espnow] Fix espnow crash when send() is called without a callback (#17266) 2026-06-29 20:30:24 +12:00
Jonathan SwobodaandJesse Hills 4fbe0d87ec [wifi] Fix crash when WiFi is enabled late alongside ESP-NOW (#17239) 2026-06-29 20:30:24 +12:00
esphome[bot]Jesse Hillsesphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
24d8e99c50 Bump bundled esphome-device-builder to 1.0.21 (#17257)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-06-29 20:30:24 +12:00
Jonathan SwobodaandJesse Hills 14b6a0ede1 [espnow] Don't throttle ESP-NOW RX when deep_sleep is present (#17240) 2026-06-29 20:30:24 +12:00
Jonathan SwobodaandJesse Hills 1793ca5eac [core] Suppress unactionable legacy-redaction warning for substitutions (#17242) 2026-06-29 20:30:24 +12:00
esphome[bot]andJesse Hills 62e19bcb27 Bump bundled esphome-device-builder to 1.0.20 (#17244) 2026-06-29 20:30:24 +12:00
84d1c34c28 [core] Fix area saved as null in storage.json (#17219)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-29 20:30:24 +12:00
esphome[bot]Jesse Hillsesphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
f78cbf9200 Bump bundled esphome-device-builder to 1.0.19 (#17217)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-06-29 20:30:23 +12:00
esphome[bot]Jesse Hillsesphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
eb711381d3 Bump bundled esphome-device-builder to 1.0.18 (#17212)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-06-29 20:30:23 +12:00
Jonathan SwobodaandJesse Hills 9a1daa5247 [hbridge] Fix light stuck on one polarity (#17162) 2026-06-29 20:30:18 +12:00
f3d61ca3e1 [mipi][mipi_spi] Swap native dimensions for swap_xy hardware transform (#17201)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 20:29:44 +12:00
Jonathan SwobodaandJesse Hills 29dfd820c6 [wifi] Report STA IP, not SoftAP IP, in wifi_info on ESP8266 (#17185) 2026-06-29 20:29:44 +12:00
Jonathan SwobodaandJesse Hills 8bc5b97298 [network] Set IPv4 type tag on all lwIP platforms, not just esp32 (#17200) 2026-06-29 20:29:44 +12:00
Jonathan SwobodaandJesse Hills 7a64163c4f [esp32] Accept '#' as ESP-IDF source ref separator (#17193) 2026-06-29 20:29:44 +12:00
esphome[bot]andJesse Hills dfe14f9c3a Bump bundled esphome-device-builder to 1.0.17 (#17199) 2026-06-29 20:29:44 +12:00
esphome[bot]andJesse Hills 26cf373ae7 Bump bundled esphome-device-builder to 1.0.16 (#17182) 2026-06-29 20:29:44 +12:00
Geoffrey FrogeyeJesse Hillspre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
94ccddf176 [opentherm] Support power scaling disabled (#17183)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-06-29 20:29:44 +12:00
Clyde StubbsandJesse Hills 2ec24505d0 [mipi_spi] Suppress sequence errors when page selection used (#17176) 2026-06-29 20:29:44 +12:00
esphome[bot]andJesse Hills 4f7faa7712 Bump bundled esphome-device-builder to 1.0.15 (#17170) 2026-06-29 20:29:44 +12:00
Clyde StubbsandJesse Hills b3dcaac262 [mipi_spi] Warn on MODE3 default for display without CS pin (#17153) 2026-06-29 20:29:44 +12:00
ee118d384a [esp32] Use POSIX path for secure-boot signing/verification keys Fixes #17164 (#17166)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-29 20:29:44 +12:00
Jonathan SwobodaandJesse Hills 8d36167e11 [esp32_ble_server] Fix set_value action with by-reference triggers (#17156) 2026-06-29 20:29:44 +12:00
esphome[bot]andJesse Hills 6d559a32df Bump bundled esphome-device-builder to 1.0.14 (#17139) 2026-06-29 20:29:37 +12:00
Jonathan SwobodaandJesse Hills bf0d31b3ab [espidf] Don't fail framework check on broken unrelated PATH tools (#17053) 2026-06-29 20:23:23 +12:00
dependabot[bot]andJesse Hills d8ffb732b7 Bump zeroconf from 0.149.16 to 0.150.0 (#17137)
Signed-off-by: dependabot[bot] <support@github.com>
2026-06-29 20:23:23 +12:00
TomandGitHub 9e8261056c [espnow] Fix espnow crash when send() is called without a callback (#17266) 2026-06-28 23:06:12 -04:00
5f311d281e [esphome] Warn when a YAML merge (<<:) drops a key (#17246)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-29 07:17:01 +10:00
J. Nick KostonandGitHub a336ad6732 [mcp4725] Use constexpr bit shift instead of powf for full-scale value (#17261) 2026-06-28 17:07:51 -04:00
8434d54cc7 [modbus] Reinstate turnaround delay after broadcasts (Revert #17209) (#17263)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-28 17:07:25 -04:00
Jonathan SwobodaandGitHub b62f7a41c9 [multiple] Single-precision float math, avoid double promotion (stragglers) (#17260) 2026-06-28 16:15:38 -04:00
4ebecf514a [modbus_server] Simplify server response handling (#12376)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-06-28 19:41:47 +00:00
Jonathan SwobodaandGitHub 2f32c88ae5 [wifi] Fix crash when WiFi is enabled late alongside ESP-NOW (#17239) 2026-06-28 15:16:42 -04:00
Jonathan SwobodaandGitHub 95449068e7 [multiple] Single-precision float math, avoid double promotion (batch 4/4) (#17256) 2026-06-28 14:18:41 -04:00
Jonathan SwobodaandGitHub 556def78aa [multiple] Single-precision float math, avoid double promotion (batch 3/4) (#17255) 2026-06-28 14:18:30 -04:00
Jonathan SwobodaandGitHub b7803cf9b5 [multiple] Single-precision float math, avoid double promotion (batch 2/4) (#17254) 2026-06-28 14:18:20 -04:00
Jonathan SwobodaandGitHub 40820287f1 [multiple] Single-precision float math, avoid double promotion (batch 1/4) (#17253) 2026-06-28 14:18:13 -04:00
Jonathan SwobodaandGitHub 6210dfb4d0 [core] Use single-precision float math to avoid double promotion (#17252) 2026-06-28 14:18:01 -04:00
esphome[bot]GitHubesphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
45c712b17b Bump bundled esphome-device-builder to 1.0.21 (#17257)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-06-28 09:41:49 -07:00
alorenteGitHubpre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>CopilotCitric LikoosoliCursorClyde StubbsClaude Opus 4.8
8e23065b86 [it8951] Add IT8951 e-paper controller support to epaper_spi (#15346)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Citric Li <37475446+limengdu@users.noreply.github.com>
Co-authored-by: koosoli <koosoli@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 21:14:05 +10:00
Jonathan SwobodaandGitHub d3892b8399 [platformio] Extract toolchain-agnostic PlatformIO library converter (#17243) 2026-06-28 07:03:09 -04:00
Jonathan SwobodaandGitHub bda789052d [espnow] Don't throttle ESP-NOW RX when deep_sleep is present (#17240) 2026-06-27 22:17:09 +00:00
Jonathan SwobodaandGitHub 0fb100f2d1 [core] Suppress unactionable legacy-redaction warning for substitutions (#17242) 2026-06-27 18:05:00 -04:00
esphome[bot]andGitHub fd7fc6b8e8 Bump bundled esphome-device-builder to 1.0.20 (#17244) 2026-06-27 15:00:23 -07:00
690e8c3fb9 [nrf52] add upload for native build (#17100)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-27 15:50:28 -04:00
a0742a9535 [api] Add nRF52 support (#17226)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-27 14:22:34 -04:00
Jonathan SwobodaandGitHub ccc57475b7 [deep_sleep] Add ESP32-C5 support (#17237) 2026-06-27 12:04:45 -04:00
24ec65e68e [esp32] Only warn about S3 PSRAM pins (GPIO33-37) in octal mode (#17222)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-27 11:26:47 -04:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
436938b931 Bump actions/cache/restore from 6.0.0 to 6.1.0 (#17231)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-27 10:33:13 -04:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
063c4371de Bump actions/cache from 6.0.0 to 6.1.0 (#17230)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-27 10:33:01 -04:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
7ad4cbf46f Bump actions/cache/save from 6.0.0 to 6.1.0 (#17229)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-27 10:32:50 -04:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
88875daf52 Bump actions/cache/restore from 6.0.0 to 6.1.0 in /.github/actions/restore-python (#17228)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-27 10:32:39 -04:00
7811781a96 [es8388] Fix DAC unable to unmute once muted (#17221)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-27 10:21:36 -04:00
da5e11d196 [core] Fix area saved as null in storage.json (#17219)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-27 09:49:56 -04:00
ArdumineGitHubtomaszduda23pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>Jonathan Swoboda
75cdabee3d [socket] Add BSD socket support for nRF52 (#16699)
Co-authored-by: tomaszduda23 <tomaszduda23@gmail.com>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-26 09:30:07 -04:00
esphome[bot]GitHubesphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
f9f28a6a00 Bump bundled esphome-device-builder to 1.0.19 (#17217)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-06-26 07:48:52 +02:00
be8523a73c [mdns] Add mDNS to Zephyr and nRF52 (#16924)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-25 16:33:38 -04:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
f49bed47de Bump ruff from 0.15.19 to 0.15.20 (#17216)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-25 16:07:55 -04:00
Franck NijhofandGitHub 239211e521 [time] Defer aioesphomeapi import to speed up config validation (#17214) 2026-06-25 15:34:44 -04:00
Franck NijhofandGitHub cc646b2213 [core] Defer requests import in framework_helpers to speed up config validation (#17215) 2026-06-25 15:34:33 -04:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
ddf075a2dd Bump aioesphomeapi from 45.3.1 to 45.5.2 (#17211)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-25 18:00:52 +00:00
esphome[bot]GitHubesphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
e304c318fb Bump bundled esphome-device-builder to 1.0.18 (#17212)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-06-25 19:47:24 +02:00
Jonathan SwobodaandGitHub e27390bddb [hbridge] Fix light stuck on one polarity (#17162) 2026-06-25 12:36:10 -04:00
rwrozelleGitHubpre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>Jonathan SwobodaClaude Sonnet 4.6
6f36ce6429 [openthread] Provide action to control poll_period when device MTD (#11766)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 12:35:15 -04:00
Jonathan SwobodaandGitHub 1d5490fd91 [modbus] Only apply turnaround delay after broadcasts (#17209) 2026-06-25 11:38:34 -04:00
Jonathan SwobodaandGitHub 18c7f60410 [uart] Validate fixed UART settings at config time for fixed-baud components (#17207) 2026-06-25 10:19:05 -04:00
4f70f6b2a6 [mipi][mipi_spi] Swap native dimensions for swap_xy hardware transform (#17201)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 08:36:36 -04:00
Jesse HillsandGitHub 29a6105730 [ms8607] Mark configurable classes as final (#17147) 2026-06-25 08:31:15 -04:00
Jesse HillsandGitHub 46cf052ec5 [config_validation] Fix multicast typo in error message (#17206) 2026-06-25 08:28:43 -04:00
Jesse HillsandGitHub e5d8c22b47 Mark configurable classes as final (13/21: pmsa003i-rc522) (#16964) 2026-06-25 13:20:11 +02:00
Jesse HillsandGitHub 64acb358a5 Mark configurable classes as final (8/21: hm3301-integration) (#16959) 2026-06-25 13:20:00 +02:00
Jesse HillsandGitHub d511f0614d Mark configurable classes as final (18/21: template-tx20) (#16969) 2026-06-25 13:19:51 +02:00
Jesse HillsandGitHub eb9ca517e3 Mark configurable classes as final (14/21: rc522_i2c-scd4x) (#16965) 2026-06-25 13:19:39 +02:00
Jesse HillsandGitHub f769457bb0 Mark configurable classes as final (15/21: script-slow_pwm) (#16966) 2026-06-25 13:19:30 +02:00
Tomasz WitkeandGitHub 0fcf512148 [image] Use LVGL 9 color formats (#16871) 2026-06-25 21:03:50 +10:00
Jesse HillsandGitHub 8c68e95568 [config_validation] Add tests for 100% validator coverage (#17204) 2026-06-25 22:33:28 +12:00
FaeandGitHub d8eee03556 [host] Fix handling of directory for preferences (#11160) 2026-06-25 10:20:15 +00:00
Julian LunzandGitHub 23933c1b58 [adc] Only call cyw43_thread_enter/exit for VSYS when WiFi is active on RP2040 (#17203) 2026-06-24 23:21:41 -07:00
Jonathan SwobodaandGitHub 8c9f4fba8f [wifi] Report STA IP, not SoftAP IP, in wifi_info on ESP8266 (#17185) 2026-06-24 22:28:15 -04:00
Jonathan SwobodaandGitHub 92554f4e67 [network] Set IPv4 type tag on all lwIP platforms, not just esp32 (#17200) 2026-06-24 22:28:06 -04:00
Jonathan SwobodaandGitHub 1dfafce06a [i2c][spi] Wire ESP32-S31/H4/H21 bus capabilities (#17188) 2026-06-24 21:05:29 -04:00
Jonathan SwobodaandGitHub abbcfd213f [tinyusb][usb_cdc_acm][usb_host][usb_uart] Support ESP32-S31/H4 (#17190) 2026-06-24 20:51:03 -04:00
Jonathan SwobodaandGitHub 23aff5202b [wifi][openthread] Wire ESP32-S31/H4/H21 radio support (#17186) 2026-06-24 20:42:41 -04:00
Jonathan SwobodaandGitHub 91e515ca7c [esp32] Accept '#' as ESP-IDF source ref separator (#17193) 2026-06-24 20:40:28 -04:00
Jonathan SwobodaandGitHub 538f554bdb [psram] Support ESP32-S31/H4 (#17192) 2026-06-24 20:38:55 -04:00
Jonathan SwobodaandGitHub e96717f6cd [waveshare_io_ch32v003] Pin i2c_id in test to avoid grouping conflict (#17191) 2026-06-24 20:16:53 -04:00
dependabot[bot]andGitHub aff5e248ed Bump actions/setup-python from 6.2.0 to 6.3.0 in /.github/actions/restore-python (#17194)
Signed-off-by: dependabot[bot] <support@github.com>
2026-06-25 02:05:30 +02:00
dependabot[bot]andGitHub 155439be74 Bump actions/setup-python from 6.2.0 to 6.3.0 (#17197)
Signed-off-by: dependabot[bot] <support@github.com>
2026-06-25 02:05:13 +02:00
dependabot[bot]andGitHub fa34c67950 Bump CodSpeedHQ/action from 4.17.6 to 4.18.1 (#17198)
Signed-off-by: dependabot[bot] <support@github.com>
2026-06-25 02:04:52 +02:00
dependabot[bot]andGitHub b688474444 Bump ruff from 0.15.18 to 0.15.19 (#17195)
Signed-off-by: dependabot[bot] <support@github.com>
2026-06-25 02:04:37 +02:00
esphome[bot]andGitHub 72b663fc40 Bump bundled esphome-device-builder to 1.0.17 (#17199) 2026-06-25 02:04:22 +02:00
esphome[bot]andGitHub f471329d60 Bump bundled esphome-device-builder to 1.0.16 (#17182) 2026-06-24 17:15:00 +02:00
Geoffrey FrogeyeGitHubpre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
e8acd24fd9 [opentherm] Support power scaling disabled (#17183)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-06-24 09:29:57 -04:00
Clyde StubbsandGitHub 18f29f8d2b [mipi_spi] Suppress sequence errors when page selection used (#17176) 2026-06-24 08:05:23 -04:00
Anton ViktorovandGitHub cbcf23426d [waveshare_io_ch32v003] Waveshare I/O Expander component (#10071) 2026-06-24 20:08:59 +10:00
Jesse HillsandGitHub e6455c5b44 Mark configurable classes as final (12/21: msa3xx-pm2005) (#16963) 2026-06-23 18:27:07 -05:00
Jesse HillsandGitHub 2b8916fc4e [ci] Exclude test changes from small-pr/medium-pr size labels (#17172) 2026-06-24 11:13:34 +12:00
esphome[bot]andGitHub dae078fc56 Bump bundled esphome-device-builder to 1.0.15 (#17170) 2026-06-24 11:06:15 +12:00
Clyde StubbsandGitHub 72686bd4af [mipi_spi] Warn on MODE3 default for display without CS pin (#17153) 2026-06-24 08:43:39 +10:00
344da7c4f4 [docker] Move build deps to base image, drop app apt step (#17167)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2026-06-23 17:35:07 -04:00
Jesse HillsandGitHub 84de814e6f [config_validation] Make bind_key a sensitive dual-mode validator (#17146) 2026-06-24 09:05:39 +12:00
Jonathan SwobodaandGitHub 1d32b6c9e0 [espidf] Enable ccache by default for ESP-IDF builds (#17163) 2026-06-23 16:41:13 -04:00
49536693b7 [esp32] Use POSIX path for secure-boot signing/verification keys Fixes #17164 (#17166)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-23 16:40:49 -04:00
dependabot[bot]andGitHub a24a63e61b Bump actions/cache from 5.0.5 to 6.0.0 (#17168)
Signed-off-by: dependabot[bot] <support@github.com>
2026-06-23 15:03:28 -05:00
dependabot[bot]andGitHub e3b644c2a0 Bump actions/cache from 5.0.5 to 6.0.0 in /.github/actions/restore-python (#17169)
Signed-off-by: dependabot[bot] <support@github.com>
2026-06-23 15:03:16 -05:00
Jonathan SwobodaandGitHub 7763ce958d [tests] Disable Hypothesis deadline on IP validation property tests (#17138) 2026-06-23 15:21:40 -04:00
Jonathan SwobodaandGitHub ff001b9e45 [esp32_ble_server] Fix set_value action with by-reference triggers (#17156) 2026-06-23 15:21:26 -04:00
Jonathan SwobodaandGitHub c2d79c972c [docker] Install ccache in the image (#17157) 2026-06-23 15:04:42 -04:00
Jonathan SwobodaandGitHub e0377bbbd3 [ci] Enable ccache for component batch builds (~7% faster) (#17136) 2026-06-23 15:04:23 -04:00
Berik VisschersandGitHub eae65a6b88 [bme680_bsec][bme68x_bsec2][const] Move BME sensor constants to shared component consts (#17160) 2026-06-23 11:44:48 -04:00
Jesse HillsandGitHub 225d426d95 [core] Use CORE.is_* platform helpers in __main__ (#17144) 2026-06-23 08:40:16 -04:00
Zach IsbachandGitHub 9614bc20a0 [epaper_spi] Add support for Waveshare 2.13" V4 series B (R/B/W) (#16828) 2026-06-23 11:00:19 +00:00
arunderwoodandGitHub 41747c2de7 [epaper_spi] Add support for the Inkplate 2 (#16856) 2026-06-23 16:50:02 +10:00
Jesse HillsandGitHub c70d56807f [motion] Make motion test configs mergeable in CI (#17149) 2026-06-23 16:44:58 +12:00
Jesse HillsandGitHub 69d700727d [docker] Remove dead HA addon env exports (streamer_mode, relative_url) (#17140) 2026-06-23 11:25:24 +12:00
esphome[bot]andGitHub 5fcf656806 Bump bundled esphome-device-builder to 1.0.14 (#17139) 2026-06-22 17:45:22 -05:00
Jonathan SwobodaandGitHub 1ace836744 [espidf] Don't fail framework check on broken unrelated PATH tools (#17053) 2026-06-22 18:41:21 -04:00
3a4831bd7e [ble_nus] Atomic log-line framing (no partial ring-buffer writes) (#17105)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: tomaszduda23 <tomaszduda23@gmail.com>
2026-06-22 21:04:11 +00:00
dependabot[bot]andGitHub 6c1724874b Bump zeroconf from 0.149.16 to 0.150.0 (#17137)
Signed-off-by: dependabot[bot] <support@github.com>
2026-06-22 15:04:22 -05:00
2483576909 [sx126x] Add data whitening options (#17102)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-22 10:51:10 -04:00
esphome[bot]andGitHub 0df1db6205 Bump bundled esphome-device-builder to 1.0.13 (#17132) 2026-06-22 00:01:22 -05:00
J. Nick KostonandGitHub 614eae7a3b [dashboard_import] Store package_import_url in flash on ESP8266 (#17127) 2026-06-21 23:18:06 -05:00
Jesse HillsandGitHub 2fe67a6eda [graphical_display_menu] Mark configurable classes as final (#17129) 2026-06-21 23:17:47 -05:00
Jesse HillsandGitHub e88f69b5f8 Mark configurable classes as final (7/21: gcja5-hlw8032) (#16958) 2026-06-21 21:05:05 -05:00
Jesse HillsandGitHub c69cfd44be Mark configurable classes as final (5/21) (#16956) 2026-06-21 21:04:55 -05:00
Jesse HillsandGitHub 9f5ed6fdfd Mark configurable classes as final (6/21) (#16957) 2026-06-21 21:04:45 -05:00
Jesse HillsandGitHub 0891473280 Mark configurable classes as final (10/21: matrix_keypad-micronova) (#16961) 2026-06-21 21:04:34 -05:00
Jesse HillsandGitHub 2982d7c834 Mark configurable classes as final (9/21) (#16960) 2026-06-21 21:04:16 -05:00
Jesse HillsandGitHub 026bac4cd1 [ld2420] Mark configurable classes as final (#17130) 2026-06-22 13:27:56 +12:00
J. Nick KostonandGitHub 7fcc890e84 [rp2040] Bump arduino-pico framework to 5.6.1 (#17122) 2026-06-22 13:02:46 +12:00
J. Nick KostonandGitHub 44c54b3a75 [json] Bump ArduinoJson to 7.4.3 (#17126) 2026-06-21 23:09:54 +00:00
Clyde StubbsandGitHub faabafad2b [mipi_rgb] Fix offsets for Wave 5 1024x600 (#17057) 2026-06-22 09:54:05 +12:00
Jesse HillsandGitHub cce7cfff29 Mark configurable classes as final (3/21: ble_scanner-ch423) (#16954) 2026-06-22 09:52:32 +12:00
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 HillsandGitHub 73dbc8214b Mark configurable classes as final (4/21: chsc6x-dfplayer) (#16955) 2026-06-22 09:51:31 +12:00
Jesse HillsandGitHub 7d7cdb6c66 Mark configurable classes as final (21/21: zhlt01-zyaura) (#16972) 2026-06-22 09:51:11 +12:00
J. Nick KostonandGitHub 0d7130c499 [docs] Remove leftover dashboard references after dashboard removal (#17125) 2026-06-22 09:48:21 +12:00
J. Nick KostonandGitHub 1d5d581734 [esp8266] Drop stale esphome-docker-base reference (#17123) 2026-06-22 09:34:09 +12:00
Jesse HillsandGitHub d0e3e98d55 [dashboard] Remove legacy web dashboard (#17124) 2026-06-22 09:33:27 +12:00
J. Nick KostonandGitHub c4abc5476e [core] Remove deprecated std::string scheduler/timer overloads (#17111) 2026-06-21 16:22:08 -05:00
J. Nick KostonandGitHub 6c10fc1272 [hub75] Remove deprecated scan_wiring name aliases (#17118) 2026-06-21 16:21:50 -05:00
Kevin AhrendtandGitHub 036768c399 [audio] Fix mono channel MP3 playback (#17106) 2026-06-21 15:19:13 -05:00
J. Nick KostonandGitHub 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 KostonandGitHub c6ead57a9e [packages] Remove deprecated single-package include syntax (#17119) 2026-06-22 08:00:46 +12:00
J. Nick KostonandGitHub 78c6131bbf [web_server] Deprecate version 1 (#17109) 2026-06-21 15:00:36 -05:00
J. Nick KostonandGitHub d8f883bd9d [core] Remove deprecated get_object_id() and get_compilation_time() (#17112) 2026-06-22 07:54:05 +12:00
J. Nick KostonandGitHub d1d77fc217 [remote_base] Remove deprecated MideaData::to_string() (#17117) 2026-06-22 07:51:08 +12:00
J. Nick KostonandGitHub f273221cf4 [core] Remove deprecated value_accuracy_to_string() (#17116) 2026-06-22 07:50:32 +12:00
J. Nick KostonandGitHub 03121d2efe [core] Remove deprecated std::string GPIOPin::dump_summary() (#17115) 2026-06-22 07:49:27 +12:00
J. Nick KostonandGitHub 7c2603d9bc [ethernet] Defer clk_mode removal to 2026.9.0 (#17114) 2026-06-22 07:47:15 +12:00
J. Nick KostonandGitHub 21aee91e67 [web_server] Remove deprecated object ID URL matching (#17113) 2026-06-22 07:45:03 +12:00
J. Nick KostonandGitHub 1bd937d89c [api] Remove pre-1.14 object_id backward-compat code (#17108) 2026-06-22 07:44:00 +12:00
Bonne EgglestonandGitHub 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
esphome[bot]andGitHub 9609d370c0 Bump bundled esphome-device-builder to 1.0.12 (#17091) 2026-06-20 12:22:19 -05:00
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 KostonandGitHub 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]andGitHub 657d9bf4d0 Bump bundled esphome-device-builder to 1.0.11 (#17081) 2026-06-20 00:30:30 -05:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
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]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
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]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
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 SwobodaandGitHub f57d31374e [packet_transport] Mark encryption key as cv.sensitive (#17066) 2026-06-19 23:19:30 -04:00
Jonathan SwobodaandGitHub 50994704a3 [fastled_base] Fix RMT5 intr_priority conflict (#17072) 2026-06-19 23:19:17 -04:00
Jonathan SwobodaandGitHub 350e7bb763 [espidf] Resolve IDF tools path to avoid unnormalized path warning (#17055) 2026-06-19 17:17:06 +12:00
J. Nick KostonandGitHub 4ae6dc355f [select] Remove deprecated state member (#17027) 2026-06-19 17:08:07 +12:00
dependabot[bot]andGitHub 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 MikeandGitHub 1dbd9af617 [sen6x] Remove codeowner (#17056) 2026-06-19 17:04:11 +12:00
Jesse Hills 995eba9178 Merge branch 'release' into dev 2026-06-19 11:35:46 +12:00
esphome[bot]andGitHub a497174da2 Bump bundled esphome-device-builder to 1.0.10 (#17051) 2026-06-19 10:05:20 +12:00
J. Nick KostonandGitHub b97182d302 [logger] Hold recursion guard while draining the task log buffer (#17044) 2026-06-19 09:16:14 +12:00
Jonathan SwobodaandGitHub 8e7518fe9d [esp32] Don't overwrite PlatformIO's factory.bin (#17042) 2026-06-19 09:15:38 +12:00
Jonathan SwobodaandGitHub a0f546e375 [ci] Smoke-test Arduino framework in esp32 PlatformIO job (#17034) 2026-06-18 16:08:22 -04:00
Jonathan SwobodaandGitHub 53e85e07d4 [esp32] Support esphome idedata with the native ESP-IDF toolchain (#17040) 2026-06-18 15:56:21 -04:00
Jonathan SwobodaandGitHub f6c78f7415 [uptime] Revert timestamp sensor device_class to timestamp (#17037) 2026-06-18 15:43:17 -04:00
Jonathan SwobodaandGitHub 19cca9e177 [esp32] Remove framework migration notice (#17023) 2026-06-18 15:41:03 -04:00
Jonathan SwobodaandGitHub 1a553018bf [build] Skip target-platform deps when populating host unit-test config (#17039) 2026-06-18 15:38:57 -04:00
dependabot[bot]andGitHub 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]andGitHub 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
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
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 KostonandGitHub 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 HillsandGitHub 92028e53b5 Mark configurable classes as final (2/21: as3935_i2c-ble_rssi) (#16953) 2026-06-18 15:35:12 +12:00
Jesse HillsandGitHub 11deff2bed Mark configurable classes as final (1/21: a01nyub-aqi) (#16952) 2026-06-18 15:35:03 +12:00
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]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
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]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
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]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
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]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
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
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
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
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
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
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
ArdumineGitHubCopilot Autofix powered by AIpre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>tomaszduda23Jonathan Swoboda
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
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 SwobodaandGitHub ac6a0f34ec [esp32] Make ESP-IDF the default toolchain (#16910) 2026-06-18 13:45:30 +12:00
Jesse HillsandGitHub c214a8ce79 [core] Add generic component alias infrastructure (#16826) 2026-06-18 01:21:00 +00:00
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 LjungqvistGitHubpre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>CursorJonathan Swoboda
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 SwobodaandGitHub 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
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]andGitHub ae7c800de8 Bump bundled esphome-device-builder to 1.0.8 (#17020) 2026-06-17 17:32:37 -05:00
Jesse Hills 084398436d Merge branch 'beta' into dev 2026-06-18 10:08:51 +12:00
esphome[bot]andGitHub 6d9490b5a3 Bump bundled esphome-device-builder to 1.0.7 (#17018) 2026-06-17 15:53:10 -05:00
dependabot[bot]andGitHub 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]andGitHub 900e0b8566 Bump bundled esphome-device-builder to 1.0.6 (#17016) 2026-06-17 14:50:56 -05:00
esphome[bot]andGitHub 40d0cbee3f Bump bundled esphome-device-builder to 1.0.5 (#17014) 2026-06-17 14:07:36 -05:00
esphome[bot]andGitHub 009c6dd995 Bump bundled esphome-device-builder to 1.0.4 (#17013) 2026-06-17 12:38:23 -05:00
esphome[bot]andGitHub e80461eba9 Bump bundled esphome-device-builder to 1.0.3 (#17005) 2026-06-16 22:28:27 -05:00
Jonathan SwobodaandGitHub 29e8949e3e [ota] Scale ESP-IDF OTA erase watchdog to image size (#16998) 2026-06-16 13:23:46 -04:00
Jonathan SwobodaandGitHub 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
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 HillsandGitHub d8fa0e4140 [core] Stop parent git repos from breaking ESP-IDF/PlatformIO builds (#16994) 2026-06-16 20:24:26 +12:00
Jesse HillsandGitHub b09a5f9e43 [ci] Push branch-tagged docker images to ghcr.io for local testing (#16992) 2026-06-16 13:37:31 +12:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Jonathan Swoboda
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 HillsandGitHub 73f839437e [docker] Remove alpine base, build only on debian (#16991) 2026-06-16 13:12:53 +12:00
Jonathan SwobodaandGitHub a7a407c22c [openthread] Fix InstanceLock releasing the lock twice on try_acquire (#16980) 2026-06-16 09:05:50 +12:00
Kevin AhrendtandGitHub 7a2657cea1 [audio] Bump microMP3 to v0.2.3 (#16977) 2026-06-16 08:48:07 +12:00
J. Nick KostonandGitHub 3420cff316 [core] Attribute "took a long time" blocking warning to the owning script (#16768) 2026-06-16 08:46:33 +12:00
Clyde StubbsandGitHub 963465a0a6 [mipi_dsi] Add SWRESET command to M5Stack Tab5-V2 init sequence (#16975) 2026-06-15 08:25:54 -04:00
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 HillsandGitHub c1a7a8ff55 Add PEP 572 walrus operator preference to coding conventions (#16951) 2026-06-15 16:01:44 +12:00
Clyde StubbsandGitHub 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
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 SwobodaandGitHub e191fc5d47 [core] Support platformio_options on the native ESP-IDF toolchain (#16917) 2026-06-15 10:56:03 +12:00
Jonathan SwobodaandGitHub 1e5771a3fa [esp32] Fix idedata generation failing on unset ESPHOME_ARDUINO (#16925) 2026-06-15 09:48:43 +12:00
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
guillempagesandGitHub 35e5c7c7c3 [runtime_image] Improve error logging (#16943) 2026-06-14 07:40:49 +10:00
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]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
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]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
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 JakubowskiGitHubpre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
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
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 KostonandGitHub 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 HillsandGitHub 6a527c7efc [tests] Mock target branch in memory-impact exclusion test (#16913) 2026-06-11 14:04:22 +12:00
Jesse Hills e0b0c1e8d3 Bump version to 2026.7.0-dev 2026-06-11 12:41:19 +12:00
1854 changed files with 42646 additions and 19789 deletions
-1
View File
@@ -1 +0,0 @@
72f02816e288b68ff4ef4b3d6fb66432c893b187a80ad3ebaa29afa443ff9ea6
+1 -1
View File
@@ -1,4 +1,4 @@
ARG BUILD_BASE_VERSION=2025.04.0
ARG BUILD_BASE_VERSION=2026.06.1
FROM ghcr.io/esphome/docker-base:debian-${BUILD_BASE_VERSION} AS base
+5 -2
View File
@@ -15,7 +15,6 @@
// uncomment and edit the path in order to pass through local USB serial to the container
// , "--device=/dev/ttyACM0"
],
"appPort": 6052,
// if you are using avahi in the host device, uncomment these to allow the
// devcontainer to find devices via mdns
//"mounts": [
@@ -41,7 +40,11 @@
],
"settings": {
"python.languageServer": "Pylance",
"python.pythonPath": "/usr/bin/python3",
// Use the container's pre-provisioned venv (built by the Dockerfile, outside the
// bind-mounted workspace) rather than a ./venv that may leak in from the host and
// mismatch the container's Python. See .devcontainer/Dockerfile (esphome-venv).
"python.defaultInterpreterPath": "/home/esphome/.local/esphome-venv/bin/python",
"python.terminal.activateEnvironment": true,
"pylint.args": [
"--rcfile=${workspaceFolder}/pyproject.toml"
],
+2
View File
@@ -1,3 +1,5 @@
# Normalize line endings to LF in the repository
* text eol=lf
*.png binary
*.gif binary
*.apng binary
+2 -2
View File
@@ -42,7 +42,7 @@ runs:
- name: Build and push to ghcr by digest
id: build-ghcr
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
env:
DOCKER_BUILD_SUMMARY: false
DOCKER_BUILD_RECORD_UPLOAD: false
@@ -67,7 +67,7 @@ runs:
- name: Build and push to dockerhub by digest
id: build-dockerhub
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
env:
DOCKER_BUILD_SUMMARY: false
DOCKER_BUILD_RECORD_UPLOAD: false
+10 -4
View File
@@ -2,8 +2,8 @@ name: Cache ESP-IDF
description: >
Resolve the pinned ESP-IDF version and cache the native ESP-IDF install
(toolchains + source) at ~/.esphome-idf. Every job that installs ESP-IDF
natively (clang-tidy for IDF/Arduino and the native-IDF component build)
shares one cache, since the install is identical (ESPHOME_IDF_DEFAULT_TARGETS
natively (clang-tidy for IDF/Arduino and the component test batches) shares
one cache, since the install is identical (ESPHOME_IDF_DEFAULT_TARGETS
defaults to "all", so all toolchains are present regardless of the chip).
Callers must set env ESPHOME_ESP_IDF_PREFIX: ~/.esphome-idf and have the
Python venv already restored.
@@ -11,6 +11,12 @@ inputs:
framework:
description: 'Which pinned IDF version to key on: "espidf" (recommended) or "arduino".'
default: espidf
restore-only:
description: >
When "true", only restore -- never save the cache, even on dev. Use from
jobs that may not produce an ESP-IDF install (e.g. a component batch with
no esp32 target), so a partial/empty install is never written to the key.
default: "false"
runs:
using: composite
steps:
@@ -33,13 +39,13 @@ runs:
# PRs), and PRs are restore-only -- they never push multi-GB artifacts into
# their own scope / the repo quota (e.g. on a version-bump PR).
- name: Cache ESP-IDF install (write on dev)
if: github.ref == 'refs/heads/dev'
if: github.ref == 'refs/heads/dev' && inputs.restore-only != 'true'
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.esphome-idf
key: ${{ runner.os }}-esphome-idf-${{ steps.version.outputs.version }}
- name: Cache ESP-IDF install (restore-only off dev)
if: github.ref != 'refs/heads/dev'
if: github.ref != 'refs/heads/dev' || inputs.restore-only == 'true'
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.esphome-idf
+49
View File
@@ -0,0 +1,49 @@
name: Cache nRF Connect SDK
description: >
Resolve the pinned sdk-nrf version and cache the native sdk-nrf install
(west workspace, Zephyr SDK toolchain, python env) at ~/.esphome-sdk-nrf.
Every job that installs sdk-nrf natively (the nrf52 clang-tidy job and,
once the component tests build natively, their batches) shares one cache.
Callers must set env ESPHOME_SDK_NRF_PREFIX: ~/.esphome-sdk-nrf and have
the Python venv already restored.
inputs:
restore-only:
description: >
When "true", only restore -- never save the cache, even on dev. Use from
jobs that may not produce a complete install (e.g. a component batch
that fails mid-install), so a partial install is never written.
default: "false"
runs:
using: composite
steps:
- name: Resolve sdk-nrf and toolchain versions for cache key
# Both versions are pinned in code, not in any file that feeds the
# other cache keys, so resolve them explicitly. Keying on them means
# the cache invalidates when either is bumped (actions/cache never
# overwrites a key).
id: version
shell: bash
run: |
. venv/bin/activate
version=$(python -c '
from esphome.components.nrf52 import RECOMMENDED_SDK_NRF_VERSION
from esphome.components.nrf52.framework import TOOLCHAIN_VERSION
print(f"{RECOMMENDED_SDK_NRF_VERSION}-{TOOLCHAIN_VERSION}")')
echo "version=$version" >> "$GITHUB_OUTPUT"
# Mirror cache-esp-idf: only dev-branch runs write the shared cache (so it
# lives in the default-branch scope readable by all PRs); PRs are
# restore-only and never push multi-GB artifacts into their own scope.
- name: Cache nRF Connect SDK install (write on dev)
if: github.ref == 'refs/heads/dev' && inputs.restore-only != 'true'
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.esphome-sdk-nrf
# yamllint disable-line rule:line-length
key: ${{ runner.os }}-esphome-sdk-nrf-${{ steps.version.outputs.version }}-${{ hashFiles('esphome/components/nrf52/requirements.txt') }}
- name: Cache nRF Connect SDK install (restore-only off dev)
if: github.ref != 'refs/heads/dev' || inputs.restore-only == 'true'
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.esphome-sdk-nrf
# yamllint disable-line rule:line-length
key: ${{ runner.os }}-esphome-sdk-nrf-${{ steps.version.outputs.version }}-${{ hashFiles('esphome/components/nrf52/requirements.txt') }}
+6 -3
View File
@@ -17,12 +17,12 @@ runs:
steps:
- name: Set up Python ${{ inputs.python-version }}
id: python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: ${{ inputs.python-version }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: venv
# yamllint disable-line rule:line-length
@@ -32,9 +32,12 @@ runs:
# 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
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
with:
enable-cache: true
# Pull request saves land in per-PR scopes nothing else can
# reuse; dev pushes seed the shared copy instead.
save-cache: ${{ github.event_name != 'pull_request' }}
# 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.
+19 -12
View File
@@ -147,19 +147,9 @@ async function detectCoreChanges(changedFiles) {
}
// Strategy: PR size detection
async function detectPRSize(prFiles, totalAdditions, totalDeletions, totalChanges, isMegaPR, SMALL_PR_THRESHOLD, MEDIUM_PR_THRESHOLD, TOO_BIG_THRESHOLD) {
async function detectPRSize(prFiles, totalAdditions, totalDeletions, isMegaPR, SMALL_PR_THRESHOLD, MEDIUM_PR_THRESHOLD, TOO_BIG_THRESHOLD) {
const labels = new Set();
if (totalChanges <= SMALL_PR_THRESHOLD) {
labels.add('small-pr');
return labels;
}
if (totalChanges <= MEDIUM_PR_THRESHOLD) {
labels.add('medium-pr');
return labels;
}
const testAdditions = prFiles
.filter(file => file.filename.startsWith('tests/'))
.reduce((sum, file) => sum + (file.additions || 0), 0);
@@ -167,7 +157,24 @@ async function detectPRSize(prFiles, totalAdditions, totalDeletions, totalChange
.filter(file => file.filename.startsWith('tests/'))
.reduce((sum, file) => sum + (file.deletions || 0), 0);
const nonTestChanges = (totalAdditions - testAdditions) - (totalDeletions - testDeletions);
const nonTestAdditions = totalAdditions - testAdditions;
const nonTestDeletions = totalDeletions - testDeletions;
// small/medium count churn (additions + deletions) so a balanced refactor isn't undersized.
const nonTestChurn = nonTestAdditions + nonTestDeletions;
if (nonTestChurn <= SMALL_PR_THRESHOLD) {
labels.add('small-pr');
return labels;
}
if (nonTestChurn <= MEDIUM_PR_THRESHOLD) {
labels.add('medium-pr');
return labels;
}
// too-big uses net line delta (additions - deletions), matching the review message in reviews.js.
const nonTestChanges = nonTestAdditions - nonTestDeletions;
// Don't add too-big if mega-pr label is already present
if (nonTestChanges > TOO_BIG_THRESHOLD && !isMegaPR) {
+1 -1
View File
@@ -123,7 +123,7 @@ module.exports = async ({ github, context }) => {
detectNewComponents(github, context, prFiles),
detectNewPlatforms(github, context, prFiles, apiData),
detectCoreChanges(changedFiles),
detectPRSize(prFiles, totalAdditions, totalDeletions, totalChanges, isMegaPR, SMALL_PR_THRESHOLD, MEDIUM_PR_THRESHOLD, TOO_BIG_THRESHOLD),
detectPRSize(prFiles, totalAdditions, totalDeletions, isMegaPR, SMALL_PR_THRESHOLD, MEDIUM_PR_THRESHOLD, TOO_BIG_THRESHOLD),
detectDashboardChanges(changedFiles),
detectGitHubActionsChanges(changedFiles),
detectCodeOwner(github, context, changedFiles),
@@ -1,6 +1,6 @@
const { describe, it } = require('node:test');
const assert = require('node:assert/strict');
const { detectNewPlatforms, detectNewComponents } = require('../detectors');
const { detectNewPlatforms, detectNewComponents, detectPRSize } = require('../detectors');
// Minimal GitHub API mock — only repos.getContent is called by detectNewPlatforms/detectNewComponents
// to check for CONFIG_SCHEMA in newly added files.
@@ -145,3 +145,79 @@ describe('detectNewComponents', () => {
assert.equal(result.labels.size, 0);
});
});
// ---------------------------------------------------------------------------
// detectPRSize
// ---------------------------------------------------------------------------
describe('detectPRSize', () => {
const SMALL = 30;
const MEDIUM = 100;
const TOO_BIG = 1000;
function size(prFiles, isMegaPR = false) {
const totalAdditions = prFiles.reduce((sum, file) => sum + (file.additions || 0), 0);
const totalDeletions = prFiles.reduce((sum, file) => sum + (file.deletions || 0), 0);
return detectPRSize(prFiles, totalAdditions, totalDeletions, isMegaPR, SMALL, MEDIUM, TOO_BIG);
}
it('counts only non-test changes toward small-pr', async () => {
// 10 source + 5000 test lines -> non-test churn of 10 is still small.
const labels = await size([
{ filename: 'esphome/components/foo/foo.cpp', additions: 10, deletions: 0 },
{ filename: 'tests/components/foo/test.esp32-idf.yaml', additions: 5000, deletions: 0 },
]);
assert.ok(labels.has('small-pr'));
assert.equal(labels.size, 1);
});
it('counts additions and deletions as churn (not net delta)', async () => {
// A balanced refactor (40 added, 40 removed) is 80 lines of churn -> medium, not small.
const labels = await size([
{ filename: 'esphome/components/foo/foo.cpp', additions: 40, deletions: 40 },
]);
assert.ok(labels.has('medium-pr'));
assert.equal(labels.size, 1);
});
it('labels medium-pr when non-test changes exceed small threshold', async () => {
const labels = await size([
{ filename: 'esphome/components/foo/foo.cpp', additions: 60, deletions: 0 },
{ filename: 'tests/components/foo/test.esp32-idf.yaml', additions: 5000, deletions: 0 },
]);
assert.ok(labels.has('medium-pr'));
assert.equal(labels.size, 1);
});
it('uses net delta (not churn) for too-big', async () => {
// 600 added + 600 removed: 1200 churn (above too-big) but 0 net delta -> not too-big.
const labels = await size([
{ filename: 'esphome/components/foo/foo.cpp', additions: 600, deletions: 600 },
]);
assert.equal(labels.size, 0);
});
it('labels too-big when non-test changes exceed the big threshold', async () => {
const labels = await size([
{ filename: 'esphome/components/foo/foo.cpp', additions: 2000, deletions: 0 },
{ filename: 'tests/components/foo/test.esp32-idf.yaml', additions: 5000, deletions: 0 },
]);
assert.ok(labels.has('too-big'));
assert.equal(labels.size, 1);
});
it('does not label too-big when mega-pr is set', async () => {
const labels = await size([
{ filename: 'esphome/components/foo/foo.cpp', additions: 2000, deletions: 0 },
], true);
assert.equal(labels.size, 0);
});
it('produces no size label for a large mega-pr in the gap above medium', async () => {
// Non-test changes land between MEDIUM and TOO_BIG: not small/medium, and mega-pr suppresses too-big.
const labels = await size([
{ filename: 'esphome/components/foo/foo.cpp', additions: 500, deletions: 0 },
], true);
assert.equal(labels.size, 0);
});
});
-1
View File
@@ -41,7 +41,6 @@ function hasCoreChanges(changedFiles) {
*/
function hasDashboardChanges(changedFiles) {
return changedFiles.some(file =>
file.startsWith('esphome/dashboard/') ||
file.startsWith('esphome/components/dashboard_import/')
);
}
+1 -1
View File
@@ -24,7 +24,7 @@ jobs:
if: github.event.pull_request.state == 'open' && (github.event.action != 'labeled' || github.event.sender.type != 'Bot')
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Generate a token
id: generate-token
+7 -4
View File
@@ -21,17 +21,20 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.11"
python-version: "3.12"
- name: Set up uv
# ``--system`` (below) installs into the setup-python interpreter;
# no venv is created or restored by this workflow.
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
with:
enable-cache: true
# Pull-request-only workflow: a save could never be shared and
# would only consume quota.
save-cache: "false"
# 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.
-76
View File
@@ -1,76 +0,0 @@
name: Clang-tidy Hash CI
on:
pull_request:
paths:
- ".clang-tidy"
- "platformio.ini"
- "requirements_dev.txt"
- "sdkconfig.defaults"
- ".clang-tidy.hash"
- "script/clang_tidy_hash.py"
- ".github/workflows/ci-clang-tidy-hash.yml"
permissions:
contents: read # actions/checkout for the PR head
pull-requests: write # pulls.createReview / listReviews / dismissReview when the clang-tidy hash is out of date
jobs:
verify-hash:
name: Verify clang-tidy hash
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.11"
- name: Verify hash
run: |
python script/clang_tidy_hash.py --verify
- if: failure()
name: Show hash details
run: |
python script/clang_tidy_hash.py
echo "## Job Failed" | tee -a $GITHUB_STEP_SUMMARY
echo "You have modified clang-tidy configuration but have not updated the hash." | tee -a $GITHUB_STEP_SUMMARY
echo "Please run 'script/clang_tidy_hash.py --update' and commit the changes." | tee -a $GITHUB_STEP_SUMMARY
- if: failure() && github.event.pull_request.head.repo.full_name == github.repository
name: Request changes
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
await github.rest.pulls.createReview({
pull_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
event: 'REQUEST_CHANGES',
body: 'You have modified clang-tidy configuration but have not updated the hash.\nPlease run `script/clang_tidy_hash.py --update` and commit the changes.'
})
- if: success() && github.event.pull_request.head.repo.full_name == github.repository
name: Dismiss review
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
let reviews = await github.rest.pulls.listReviews({
pull_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo
});
for (let review of reviews.data) {
if (review.user.login === 'github-actions[bot]' && review.state === 'CHANGES_REQUESTED') {
await github.rest.pulls.dismissReview({
pull_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
review_id: review.id,
message: 'Clang-tidy hash now matches configuration.'
});
}
}
+101 -21
View File
@@ -1,25 +1,38 @@
---
name: CI for docker images
# Only run when docker paths change
# Only run on PRs that touch the docker image, its build inputs, or any code
# whose toolchain the compile smoke test exercises (core + target platforms).
on:
push:
branches: [dev, beta, release]
paths:
- "docker/**"
- ".github/workflows/ci-docker.yml"
- "requirements*.txt"
- "platformio.ini"
- "script/platformio_install_deps.py"
pull_request:
paths:
# Docker image and its build inputs.
- "docker/**"
- ".github/workflows/ci-docker.yml"
- "requirements*.txt"
- "pyproject.toml"
- "platformio.ini"
- "esphome/idf_component.yml"
- "script/platformio_install_deps.py"
# Core, build pipeline, toolchain, and target-platform changes can change
# how a toolchain is set up or built, so re-run the per-toolchain compile
# smoke test when they change.
- "esphome/core/**"
- "esphome/writer.py"
- "esphome/build_gen/**"
- "esphome/espidf/**"
- "esphome/platformio/**"
- "esphome/components/bk72xx/**"
- "esphome/components/esp32/**"
- "esphome/components/esp8266/**"
- "esphome/components/host/**"
- "esphome/components/libretiny/**"
- "esphome/components/ln882x/**"
- "esphome/components/nrf52/**"
- "esphome/components/rp2040/**"
- "esphome/components/rtl87xx/**"
- "esphome/components/zephyr/**"
permissions:
contents: read # actions/checkout only
@@ -48,13 +61,13 @@ jobs:
tag: ${{ steps.tag.outputs.tag }}
push: ${{ steps.tag.outputs.push }}
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.11"
python-version: "3.12"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
- name: Determine tag and whether to push
id: tag
@@ -83,7 +96,7 @@ jobs:
- name: Log in to the GitHub container registry
if: steps.tag.outputs.push == 'true'
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0
with:
registry: ghcr.io
username: ${{ github.actor }}
@@ -96,7 +109,26 @@ jobs:
--arch "${{ matrix.os == 'ubuntu-24.04-arm' && 'aarch64' || 'amd64' }}" \
--build-type "${{ matrix.build_type }}" \
--registry ghcr \
build ${{ steps.tag.outputs.push == 'true' && '--push --no-cache-to' || '' }}
build ${{ steps.tag.outputs.push == 'true' && '--push --no-cache-to' || '' }} ${{ (matrix.os == 'ubuntu-24.04' && matrix.build_type == 'docker') && '--load' || '' }}
# The amd64 "docker" image is also loaded locally (above) and handed to
# compile-test as an artifact, so the smoke test reuses this build instead
# of building the image a second time. Using an artifact (rather than the
# pushed image) keeps it working for fork PRs, which never push to ghcr.io.
- name: Export image for compile-test
if: matrix.os == 'ubuntu-24.04' && matrix.build_type == 'docker'
run: docker save "ghcr.io/esphome/esphome-amd64:${{ steps.tag.outputs.tag }}" | gzip > compile-test-image.tar.gz
- name: Upload compile-test image artifact
if: matrix.os == 'ubuntu-24.04' && matrix.build_type == 'docker'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
# The tar is already gzipped, so upload it as-is. archive: false skips
# the redundant zip and makes the file name the artifact name (the
# `name` input is ignored in that mode).
path: compile-test-image.tar.gz
retention-days: 1
archive: false
manifest:
name: Push ${{ matrix.build_type }} manifest to ghcr.io
@@ -113,16 +145,16 @@ jobs:
- "ha-addon"
- "docker"
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.11"
python-version: "3.12"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
- name: Log in to the GitHub container registry
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0
with:
registry: ghcr.io
username: ${{ github.actor }}
@@ -135,3 +167,51 @@ jobs:
--build-type "${{ matrix.build_type }}" \
--registry ghcr \
manifest
# Smoke-test the built image by compiling one minimal config per target
# platform / toolchain. This catches missing system dependencies in the image
# that only surface when a given toolchain is downloaded and run. The image is
# the amd64 "docker" build produced by check-docker (shared as an artifact).
compile-test:
name: Compile ${{ matrix.id }}
needs: check-docker
runs-on: ubuntu-24.04
permissions:
contents: read # actions/checkout to load the test configs
strategy:
fail-fast: false
# Cap concurrency so this smoke test doesn't hog all the shared runners.
max-parallel: 2
matrix:
# One entry per distinct toolchain. ESP32 variants (c3/c6/s2/s3/p4)
# share a toolchain bundle, so esp32 is exercised on the base variant
# across the full framework x toolchain cross-product (arduino/esp-idf
# framework, each built with the platformio and native esp-idf
# toolchains) so both toolchains stay covered regardless of which one is
# the default.
id:
- esp8266-arduino
- esp32-arduino-platformio
- esp32-arduino-esp-idf
- esp32-idf-platformio
- esp32-idf-esp-idf
- rp2040-arduino
- bk72xx-arduino
- rtl87xx-arduino
- ln882x-arduino
- nrf52
- host
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Download image artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: compile-test-image.tar.gz
- name: Load image
run: docker load --input compile-test-image.tar.gz
- name: Compile ${{ matrix.id }}
run: |
docker run --rm \
-v "${{ github.workspace }}/docker/test_configs:/config" \
"ghcr.io/esphome/esphome-amd64:${{ needs.check-docker.outputs.tag }}" \
compile "${{ matrix.id }}.yaml"
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Run tests
working-directory: .github/scripts/auto-label-pr
@@ -49,7 +49,7 @@ jobs:
- name: Check out code from base repository
if: steps.pr.outputs.skip != 'true'
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
# Always check out from the base repository (esphome/esphome), never from forks
# Use the PR's target branch to ensure we run trusted code from the main repo
@@ -60,7 +60,7 @@ jobs:
if: steps.pr.outputs.skip != 'true'
uses: ./.github/actions/restore-python
with:
python-version: "3.11"
python-version: "3.12"
cache-key: ${{ hashFiles('.cache-key') }}
- name: Download memory analysis artifacts
+175 -145
View File
@@ -12,8 +12,8 @@ permissions:
contents: read # actions/checkout for all jobs; individual jobs add their own scopes when they need to write
env:
DEFAULT_PYTHON: "3.11"
PYUPGRADE_TARGET: "--py311-plus"
DEFAULT_PYTHON: "3.12"
PYUPGRADE_TARGET: "--py312-plus"
concurrency:
# yamllint disable-line rule:line-length
@@ -28,18 +28,18 @@ jobs:
cache-key: ${{ steps.cache-key.outputs.key }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Generate cache-key
id: cache-key
run: echo key="${{ hashFiles('requirements.txt', 'requirements_dev.txt', 'requirements_test.txt', '.pre-commit-config.yaml') }}" >> $GITHUB_OUTPUT
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: venv
# yamllint disable-line rule:line-length
@@ -49,9 +49,12 @@ jobs:
# 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
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
with:
enable-cache: true
# Pull request saves land in per-PR scopes nothing else can
# reuse; dev pushes seed the shared copy instead.
save-cache: ${{ github.event_name != 'pull_request' }}
# 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.
@@ -74,7 +77,7 @@ jobs:
if: needs.determine-jobs.outputs.python-linters == 'true'
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python
uses: ./.github/actions/restore-python
with:
@@ -97,7 +100,7 @@ jobs:
if: needs.determine-jobs.outputs.core-ci == 'true'
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python
uses: ./.github/actions/restore-python
with:
@@ -112,7 +115,7 @@ jobs:
script/build_codeowners.py --check
script/build_language_schema.py --check
script/generate-esp32-boards.py --check
script/generate-rp2040-boards.py --check
script/generate-rp2-boards.py --check
script/ci_check_duplicate_test_ids.py
import-time:
@@ -124,7 +127,7 @@ jobs:
if: needs.determine-jobs.outputs.import-time == 'true'
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python
uses: ./.github/actions/restore-python
with:
@@ -152,17 +155,17 @@ jobs:
if: needs.determine-jobs.outputs.device-builder == 'true'
steps:
- name: Check out esphome (this PR)
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
path: esphome
- name: Check out esphome/device-builder
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: esphome/device-builder
ref: main
path: device-builder
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.13"
- name: Set up uv
@@ -171,9 +174,12 @@ jobs:
# install step (order-of-magnitude faster on cold boots,
# with its own wheel cache). actions/setup-python still
# provides the interpreter.
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
with:
enable-cache: true
# Pull request saves land in per-PR scopes nothing else can
# reuse; dev pushes seed the shared copy instead.
save-cache: ${{ github.event_name != 'pull_request' }}
# 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.
@@ -203,7 +209,7 @@ jobs:
fail-fast: false
matrix:
python-version:
- "3.11"
- "3.12"
- "3.13"
- "3.14"
os:
@@ -225,7 +231,7 @@ jobs:
if: needs.determine-jobs.outputs.core-ci == 'true'
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python
id: restore-python
uses: ./.github/actions/restore-python
@@ -250,7 +256,7 @@ jobs:
token: ${{ secrets.CODECOV_TOKEN }}
- name: Save Python virtual environment cache
if: github.ref == 'refs/heads/dev'
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: venv
key: ${{ runner.os }}-${{ steps.restore-python.outputs.python-version }}-venv-${{ needs.common.outputs.cache-key }}
@@ -270,8 +276,8 @@ jobs:
python-linters: ${{ steps.determine.outputs.python-linters }}
import-time: ${{ steps.determine.outputs.import-time }}
device-builder: ${{ steps.determine.outputs.device-builder }}
native-idf: ${{ steps.determine.outputs.native-idf }}
native-idf-components: ${{ steps.determine.outputs.native-idf-components }}
esp32-platformio: ${{ steps.determine.outputs.esp32-platformio }}
esp32-platformio-components: ${{ steps.determine.outputs.esp32-platformio-components }}
changed-components: ${{ steps.determine.outputs.changed-components }}
changed-components-with-tests: ${{ steps.determine.outputs.changed-components-with-tests }}
directly-changed-components-with-tests: ${{ steps.determine.outputs.directly-changed-components-with-tests }}
@@ -285,7 +291,7 @@ jobs:
benchmarks: ${{ steps.determine.outputs.benchmarks }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
# Fetch enough history to find the merge base
fetch-depth: 2
@@ -295,7 +301,7 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Restore components graph cache
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: .temp/components_graph.json
key: components-graph-${{ hashFiles('esphome/components/**/*.py') }}
@@ -324,8 +330,8 @@ jobs:
echo "python-linters=$(echo "$output" | jq -r '.python_linters')" >> $GITHUB_OUTPUT
echo "import-time=$(echo "$output" | jq -r '.import_time')" >> $GITHUB_OUTPUT
echo "device-builder=$(echo "$output" | jq -r '.device_builder')" >> $GITHUB_OUTPUT
echo "native-idf=$(echo "$output" | jq -r '.native_idf')" >> $GITHUB_OUTPUT
echo "native-idf-components=$(echo "$output" | jq -r '.native_idf_components')" >> $GITHUB_OUTPUT
echo "esp32-platformio=$(echo "$output" | jq -r '.esp32_platformio')" >> $GITHUB_OUTPUT
echo "esp32-platformio-components=$(echo "$output" | jq -r '.esp32_platformio_components')" >> $GITHUB_OUTPUT
echo "changed-components=$(echo "$output" | jq -c '.changed_components')" >> $GITHUB_OUTPUT
echo "changed-components-with-tests=$(echo "$output" | jq -c '.changed_components_with_tests')" >> $GITHUB_OUTPUT
echo "directly-changed-components-with-tests=$(echo "$output" | jq -c '.directly_changed_components_with_tests')" >> $GITHUB_OUTPUT
@@ -339,7 +345,7 @@ jobs:
echo "benchmarks=$(echo "$output" | jq -r '.benchmarks')" >> $GITHUB_OUTPUT
- name: Save components graph cache
if: github.ref == 'refs/heads/dev'
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: .temp/components_graph.json
key: components-graph-${{ hashFiles('esphome/components/**/*.py') }}
@@ -357,24 +363,27 @@ jobs:
bucket: ${{ fromJson(needs.determine-jobs.outputs.integration-test-buckets) }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Python 3.13
id: python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.13"
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: venv
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{ needs.common.outputs.cache-key }}
- 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
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
with:
enable-cache: true
# Pull request saves land in per-PR scopes nothing else can
# reuse; dev pushes seed the shared copy instead.
save-cache: ${{ github.event_name != 'pull_request' }}
# 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.
@@ -409,7 +418,7 @@ jobs:
if: github.event_name == 'pull_request' && (needs.determine-jobs.outputs.cpp-unit-tests-run-all == 'true' || needs.determine-jobs.outputs.cpp-unit-tests-components != '[]')
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python
uses: ./.github/actions/restore-python
@@ -438,7 +447,7 @@ jobs:
(github.event_name == 'pull_request' && needs.determine-jobs.outputs.benchmarks == 'true')
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python
uses: ./.github/actions/restore-python
@@ -456,7 +465,7 @@ jobs:
echo "binary=$BINARY" >> $GITHUB_OUTPUT
- name: Run CodSpeed benchmarks
uses: CodSpeedHQ/action@9d332c4d90b43981c3e55ae8e38e68709996240f # v4.17.0
uses: CodSpeedHQ/action@9f3a37ece7abc84992501a7fcd54d1704f3458fa # v4.18.4
with:
run: |
. venv/bin/activate
@@ -475,6 +484,8 @@ jobs:
GH_TOKEN: ${{ github.token }}
# esp32-arduino-tidy installs ESP-IDF natively; share the native IDF cache.
ESPHOME_ESP_IDF_PREFIX: ~/.esphome-idf
# nrf52-tidy installs sdk-nrf natively; pin it to a cacheable path.
ESPHOME_SDK_NRF_PREFIX: ~/.esphome-sdk-nrf
strategy:
fail-fast: false
max-parallel: 2
@@ -491,12 +502,12 @@ jobs:
- id: clang-tidy
name: Run script/clang-tidy for ZEPHYR
options: --environment nrf52-tidy --grep USE_ZEPHYR --grep USE_NRF52
pio_cache_key: tidy-zephyr
cache_sdk_nrf: true
ignore_errors: false
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
# Need history for HEAD~1 to work for checking changed files
fetch-depth: 2
@@ -509,25 +520,28 @@ jobs:
- name: Cache platformio
if: github.ref == 'refs/heads/dev' && matrix.pio_cache_key
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.platformio
key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
- name: Cache platformio
if: github.ref != 'refs/heads/dev' && matrix.pio_cache_key
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.platformio
key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
- name: Cache ESP-IDF install
# Shared with the IDF tidy + native-IDF build jobs (same install).
if: matrix.cache_idf
uses: ./.github/actions/cache-esp-idf
with:
framework: arduino
- name: Cache nRF Connect SDK install
if: matrix.cache_sdk_nrf
uses: ./.github/actions/cache-sdk-nrf
- name: Register problem matchers
run: |
echo "::add-matcher::.github/workflows/matchers/gcc.json"
@@ -537,15 +551,12 @@ jobs:
id: check_full_scan
run: |
. venv/bin/activate
# determine-jobs.clang-tidy-full-scan is true when core C++ changed
# OR the ci-run-all label forced --force-all. Independent of the
# hash check, both must produce a full scan in the job itself.
# determine-jobs.clang-tidy-full-scan is true when core C++ or a
# clang-tidy-relevant config file changed, or the ci-run-all label
# forced --force-all.
if [ "${{ needs.determine-jobs.outputs.clang-tidy-full-scan }}" = "true" ]; then
echo "full_scan=true" >> $GITHUB_OUTPUT
echo "reason=determine_jobs" >> $GITHUB_OUTPUT
elif python script/clang_tidy_hash.py --check; then
echo "full_scan=true" >> $GITHUB_OUTPUT
echo "reason=hash_changed" >> $GITHUB_OUTPUT
else
echo "full_scan=false" >> $GITHUB_OUTPUT
echo "reason=normal" >> $GITHUB_OUTPUT
@@ -583,7 +594,7 @@ jobs:
ESPHOME_ESP_IDF_PREFIX: ~/.esphome-idf
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
# Need history for HEAD~1 to work for checking changed files
fetch-depth: 2
@@ -595,7 +606,6 @@ jobs:
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Cache ESP-IDF install
# Shared with the Arduino tidy + native-IDF build jobs (same install).
uses: ./.github/actions/cache-esp-idf
- name: Register problem matchers
@@ -607,15 +617,12 @@ jobs:
id: check_full_scan
run: |
. venv/bin/activate
# determine-jobs.clang-tidy-full-scan is true when core C++ changed
# OR the ci-run-all label forced --force-all. Independent of the
# hash check, both must produce a full scan in the job itself.
# determine-jobs.clang-tidy-full-scan is true when core C++ or a
# clang-tidy-relevant config file changed, or the ci-run-all label
# forced --force-all.
if [ "${{ needs.determine-jobs.outputs.clang-tidy-full-scan }}" = "true" ]; then
echo "full_scan=true" >> $GITHUB_OUTPUT
echo "reason=determine_jobs" >> $GITHUB_OUTPUT
elif python script/clang_tidy_hash.py --check; then
echo "full_scan=true" >> $GITHUB_OUTPUT
echo "reason=hash_changed" >> $GITHUB_OUTPUT
else
echo "full_scan=false" >> $GITHUB_OUTPUT
echo "reason=normal" >> $GITHUB_OUTPUT
@@ -667,7 +674,7 @@ jobs:
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
# Need history for HEAD~1 to work for checking changed files
fetch-depth: 2
@@ -679,7 +686,6 @@ jobs:
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Cache ESP-IDF install
# Shared with the Arduino tidy + native-IDF build jobs (same install).
uses: ./.github/actions/cache-esp-idf
- name: Register problem matchers
@@ -691,15 +697,12 @@ jobs:
id: check_full_scan
run: |
. venv/bin/activate
# determine-jobs.clang-tidy-full-scan is true when core C++ changed
# OR the ci-run-all label forced --force-all. Independent of the
# hash check, both must produce a full scan in the job itself.
# determine-jobs.clang-tidy-full-scan is true when core C++ or a
# clang-tidy-relevant config file changed, or the ci-run-all label
# forced --force-all.
if [ "${{ needs.determine-jobs.outputs.clang-tidy-full-scan }}" = "true" ]; then
echo "full_scan=true" >> $GITHUB_OUTPUT
echo "reason=determine_jobs" >> $GITHUB_OUTPUT
elif python script/clang_tidy_hash.py --check; then
echo "full_scan=true" >> $GITHUB_OUTPUT
echo "reason=hash_changed" >> $GITHUB_OUTPUT
else
echo "full_scan=false" >> $GITHUB_OUTPUT
echo "reason=normal" >> $GITHUB_OUTPUT
@@ -755,7 +758,7 @@ jobs:
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
# Need history for HEAD~1 to work for checking changed files
fetch-depth: 2
@@ -767,7 +770,6 @@ jobs:
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Cache ESP-IDF install
# Shared with the IDF/Arduino clang-tidy jobs + native-IDF build (same install).
uses: ./.github/actions/cache-esp-idf
- name: Register problem matchers
@@ -779,15 +781,12 @@ jobs:
id: check_full_scan
run: |
. venv/bin/activate
# determine-jobs.clang-tidy-full-scan is true when core C++ changed
# OR the ci-run-all label forced --force-all. Independent of the
# hash check, both must produce a full scan in the job itself.
# determine-jobs.clang-tidy-full-scan is true when core C++ or a
# clang-tidy-relevant config file changed, or the ci-run-all label
# forced --force-all.
if [ "${{ needs.determine-jobs.outputs.clang-tidy-full-scan }}" = "true" ]; then
echo "full_scan=true" >> $GITHUB_OUTPUT
echo "reason=determine_jobs" >> $GITHUB_OUTPUT
elif python script/clang_tidy_hash.py --check; then
echo "full_scan=true" >> $GITHUB_OUTPUT
echo "reason=hash_changed" >> $GITHUB_OUTPUT
else
echo "full_scan=false" >> $GITHUB_OUTPUT
echo "reason=normal" >> $GITHUB_OUTPUT
@@ -811,17 +810,24 @@ jobs:
if: always()
test-build-components-split:
name: Test components batch (${{ matrix.components }})
name: Test components batch (${{ matrix.batch.components }})
runs-on: ubuntu-24.04
needs:
- common
- determine-jobs
if: github.event_name == 'pull_request' && fromJSON(needs.determine-jobs.outputs.component-test-count) > 0
env:
# esp32 component builds use the native ESP-IDF toolchain (default), so
# share the tidy jobs' install location -- the restore below lands here.
ESPHOME_ESP_IDF_PREFIX: ~/.esphome-idf
# nrf52 component builds install sdk-nrf natively; pin it to the shared
# cacheable path so the restore below lands where the build looks.
ESPHOME_SDK_NRF_PREFIX: ~/.esphome-sdk-nrf
strategy:
fail-fast: false
max-parallel: ${{ (startsWith(github.base_ref, 'beta') || startsWith(github.base_ref, 'release')) && 8 || 4 }}
matrix:
components: ${{ fromJson(needs.determine-jobs.outputs.component-test-batches) }}
batch: ${{ fromJson(needs.determine-jobs.outputs.component-test-batches) }}
steps:
- name: Show disk space
run: |
@@ -829,21 +835,38 @@ jobs:
df -h
- name: List components
run: echo ${{ matrix.components }}
run: echo ${{ matrix.batch.components }}
- name: Cache apt packages
uses: awalsh128/cache-apt-pkgs-action@acb598e5ddbc6f68a970c5da0688d2f3a9f04d05 # v1.5.3
with:
packages: libsdl2-dev
version: 1.0
- name: Install apt packages
# Not cached: this job is pull-request-only, so a cache save could
# never be shared and would only consume quota.
run: |
sudo apt-get update -qq
sudo apt-get install -y --no-install-recommends libsdl2-dev ccache
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python
uses: ./.github/actions/restore-python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Cache ESP-IDF install (restore-only)
# Only batches whose test platforms include esp32 need the native
# ESP-IDF install; never save -- just reuse the shared install the
# dev tidy jobs already cached when present.
if: matrix.batch.needs_idf
uses: ./.github/actions/cache-esp-idf
with:
restore-only: true
- name: Cache nRF Connect SDK install (restore-only)
# Only batches whose test platforms include nrf52 need the native
# sdk-nrf install; never save -- just reuse the shared install the
# dev nrf52 tidy job cached when present.
if: matrix.batch.needs_nrf
uses: ./.github/actions/cache-sdk-nrf
with:
restore-only: true
- name: Validate and compile components with intelligent grouping
run: |
. venv/bin/activate
@@ -874,7 +897,7 @@ jobs:
fi
# Convert space-separated components to comma-separated for Python script
components_csv=$(echo "${{ matrix.components }}" | tr ' ' ',')
components_csv=$(echo "${{ matrix.batch.components }}" | tr ' ' ',')
# Only isolate directly changed components when targeting dev branch
# For beta/release branches, group everything for faster CI
@@ -947,23 +970,27 @@ jobs:
echo "All components in this batch are validate-only -- skipping compile stage."
fi
test-native-idf:
name: Test components with native ESP-IDF
- name: Print ccache statistics
# esphome stores the cache under the IDF tools path; expand the leading
# ~ in ESPHOME_ESP_IDF_PREFIX so ccache reads the dir the build used.
run: CCACHE_DIR="${ESPHOME_ESP_IDF_PREFIX/#\~/$HOME}/ccache" ccache -s
test-esp32-platformio:
name: Test esp32 components with PlatformIO
runs-on: ubuntu-24.04
needs:
- common
- determine-jobs
if: github.event_name == 'pull_request' && needs.determine-jobs.outputs.native-idf == 'true'
if: github.event_name == 'pull_request' && needs.determine-jobs.outputs.esp32-platformio == 'true'
env:
ESPHOME_ESP_IDF_PREFIX: ~/.esphome-idf
# Comma-joined subset of the native-IDF representative component list,
# computed by script/determine-jobs.py (native_idf_components_to_test).
# Comma-joined subset of the esp32 PlatformIO representative component list,
# computed by script/determine-jobs.py (esp32_platformio_components_to_test).
# Single source of truth -- the full list lives in
# script/determine-jobs.py::NATIVE_IDF_TEST_COMPONENTS.
TEST_COMPONENTS: ${{ needs.determine-jobs.outputs.native-idf-components }}
# script/determine-jobs.py::ESP32_PLATFORMIO_TEST_COMPONENTS.
TEST_COMPONENTS: ${{ needs.determine-jobs.outputs.esp32-platformio-components }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python
uses: ./.github/actions/restore-python
@@ -971,66 +998,53 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Prepare build storage on /mnt
# Bind-mount the larger /mnt disk over the IDF install + build dirs BEFORE
# restoring the cache, so the ~4.5GB restore lands on the roomier volume
# instead of being shadowed by a mount set up later in the run step.
run: |
root_avail=$(df -k / | awk 'NR==2 {print $4}')
mnt_avail=$(df -k /mnt 2>/dev/null | awk 'NR==2 {print $4}')
echo "Available space: / has ${root_avail}KB, /mnt has ${mnt_avail}KB"
if [ -n "$mnt_avail" ] && [ "$mnt_avail" -gt "$root_avail" ]; then
echo "Using /mnt for build files (more space available)"
sudo mkdir -p /mnt/esphome-idf
sudo chown $USER:$USER /mnt/esphome-idf
mkdir -p ~/.esphome-idf
sudo mount --bind /mnt/esphome-idf ~/.esphome-idf
sudo mkdir -p /mnt/test_build_components_build
sudo chown $USER:$USER /mnt/test_build_components_build
mkdir -p tests/test_build_components/build
sudo mount --bind /mnt/test_build_components_build tests/test_build_components/build
else
echo "Using / for build files (more space available than /mnt or /mnt unavailable)"
fi
- name: Cache ESP-IDF install
# Shared with the IDF/Arduino clang-tidy jobs (same install); restores
# into the /mnt bind-mount prepared above when present.
uses: ./.github/actions/cache-esp-idf
- name: Run native ESP-IDF compile test
- name: Run PlatformIO compile test
run: |
. venv/bin/activate
echo "Testing components: $TEST_COMPONENTS"
echo ""
# Show disk space before validation
echo "Disk space before config validation:"
df -h
echo ""
# Run config validation (auto-grouped by test_build_components.py)
python3 script/test_build_components.py -e config -t esp32-idf -c "$TEST_COMPONENTS" -f --toolchain esp-idf
# compile validates config first, so a separate config pass is
# redundant for this smoke test. ESP-IDF framework via PlatformIO:
python3 script/test_build_components.py -e compile -t esp32-idf -c "$TEST_COMPONENTS" -f --toolchain platformio
echo ""
echo "Config validation passed! Starting compilation..."
echo "ESP-IDF-via-PlatformIO build passed! Starting Arduino smoke test..."
echo ""
# Show disk space before compilation
echo "Disk space before compilation:"
df -h
echo ""
# Arduino framework via PlatformIO (only components with an esp32-ard test are built):
python3 script/test_build_components.py -e compile -t esp32-ard -c "$TEST_COMPONENTS" -f --toolchain platformio
# 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
pre-commit-seed-cache:
name: Seed pre-commit cache
runs-on: ubuntu-latest
needs:
- common
# Saves a dev-scoped pre-commit cache that pull request runs can
# restore, since pre-commit.ci lite itself never runs on dev pushes.
if: github.event_name == 'push' && github.ref == 'refs/heads/dev'
steps:
- name: Check out code from GitHub
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python
uses: ./.github/actions/restore-python
with:
path: ~/.esphome-idf
key: ${{ runner.os }}-esphome-${{ needs.common.outputs.cache-key }}
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Cache pre-commit environments
id: cache-pre-commit
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cache/pre-commit
# Must match the restore key in pre-commit-ci-lite
# yamllint disable-line rule:line-length
key: pre-commit-3|${{ env.pythonLocation }}|${{ hashFiles('.pre-commit-config.yaml') }}
- name: Install pre-commit hook environments
if: steps.cache-pre-commit.outputs.cache-hit != 'true'
run: |
python -m pip install pre-commit
pre-commit install-hooks
pre-commit-ci-lite:
name: pre-commit.ci lite
@@ -1041,15 +1055,28 @@ jobs:
if: github.event_name == 'pull_request' && !startsWith(github.base_ref, 'beta') && !startsWith(github.base_ref, 'release') && needs.determine-jobs.outputs.core-ci == 'true'
steps:
- name: Check out code from GitHub
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python
uses: ./.github/actions/restore-python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- uses: esphome/pre-commit-action@43cd1109c09c544d97196f7730ee5b2e0cc6d81e # v3.0.1 fork with pinned actions/cache
# Inlined from esphome/pre-commit-action with a restore-only cache
# step: the pre-commit-seed-cache job owns saving this cache, so
# pull request runs never write per-PR copies.
- name: Restore pre-commit cache
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cache/pre-commit
# Must match the key pre-commit-seed-cache saves
# yamllint disable-line rule:line-length
key: pre-commit-3|${{ env.pythonLocation }}|${{ hashFiles('.pre-commit-config.yaml') }}
- name: Run pre-commit
env:
SKIP: pylint,clang-tidy-hash,ci-custom
SKIP: pylint,ci-custom
run: |
python -m pip install pre-commit
pre-commit run --show-diff-on-failure --color=always --all-files
- uses: pre-commit-ci/lite-action@5d6cc0eb514c891a40562a58a8e71576c5c7fb43 # v1.1.0
if: always()
@@ -1067,7 +1094,7 @@ jobs:
skip: ${{ steps.check-script.outputs.skip || steps.check-tests.outputs.skip }}
steps:
- name: Check out target branch
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.base_ref }}
@@ -1143,7 +1170,7 @@ jobs:
- name: Restore cached memory analysis
id: cache-memory-analysis
if: steps.check-script.outputs.skip != 'true' && steps.check-tests.outputs.skip != 'true'
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: memory-analysis-target.json
key: ${{ steps.cache-key.outputs.cache-key }}
@@ -1167,7 +1194,7 @@ jobs:
- name: Cache platformio
if: steps.check-script.outputs.skip != 'true' && steps.check-tests.outputs.skip != 'true' && steps.cache-memory-analysis.outputs.cache-hit != 'true'
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.platformio
key: platformio-memory-${{ fromJSON(needs.determine-jobs.outputs.memory_impact).platform }}-${{ hashFiles('platformio.ini') }}
@@ -1209,7 +1236,7 @@ jobs:
- name: Save memory analysis to cache
if: steps.check-script.outputs.skip != 'true' && steps.check-tests.outputs.skip != 'true' && steps.cache-memory-analysis.outputs.cache-hit != 'true' && steps.build.outcome == 'success'
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: memory-analysis-target.json
key: ${{ steps.cache-key.outputs.cache-key }}
@@ -1249,14 +1276,14 @@ jobs:
flash_usage: ${{ steps.extract.outputs.flash_usage }}
steps:
- name: Check out PR branch
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python
uses: ./.github/actions/restore-python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Cache platformio
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.platformio
key: platformio-memory-${{ fromJSON(needs.determine-jobs.outputs.memory_impact).platform }}-${{ hashFiles('platformio.ini') }}
@@ -1318,7 +1345,7 @@ jobs:
GH_TOKEN: ${{ github.token }}
steps:
- name: Check out code
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Restore Python
uses: ./.github/actions/restore-python
with:
@@ -1365,7 +1392,7 @@ jobs:
- determine-jobs
- device-builder
- test-build-components-split
- test-native-idf
- test-esp32-platformio
- pre-commit-ci-lite
- memory-impact-target-branch
- memory-impact-pr-branch
@@ -1381,4 +1408,7 @@ jobs:
# 1. The target branch has a build issue independent of this PR
# 2. This PR fixes a build issue on the target branch
# In either case, we only care that the PR branch builds successfully.
echo "$NEEDS_JSON" | jq -e 'del(.["memory-impact-target-branch"]) | all(.result != "failure")'
# Every other job must have succeeded or been skipped; a "cancelled" or
# "failure" result fails this check so CI is not reported green when the
# workflow was cancelled.
echo "$NEEDS_JSON" | jq -e 'del(.["memory-impact-target-branch"]) | all(.result == "success" or .result == "skipped")'
@@ -26,7 +26,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout base branch
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.event.pull_request.base.sha }}
sparse-checkout: |
@@ -29,7 +29,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout base branch
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
ref: ${{ github.event.pull_request.base.sha }}
+3 -3
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
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
uses: github/codeql-action/init@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
@@ -84,6 +84,6 @@ jobs:
exit 1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
uses: github/codeql-action/analyze@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
with:
category: "/language:${{matrix.language}}"
@@ -1,119 +0,0 @@
name: Add Dashboard Deprecation Comment
on:
pull_request_target:
types: [opened, synchronize]
# All API calls (pulls.listFiles + issues.{list,create,update}Comment) are performed with
# the App token minted below, so the workflow's GITHUB_TOKEN does not need any scopes.
permissions: {}
jobs:
dashboard-deprecation-comment:
name: Dashboard deprecation comment
runs-on: ubuntu-latest
# Release-bump PRs (bump-X.Y.Z -> beta, beta -> release) inevitably
# roll up everything merged into dev since the last cut, which can
# include dashboard changes that have already been reviewed once.
# The bot's purpose is to warn new contributors before they invest
# time -- that only applies to PRs entering dev.
if: github.event.pull_request.base.ref == 'dev'
steps:
- name: Generate a token
id: generate-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
client-id: ${{ vars.ESPHOME_GITHUB_APP_CLIENT_ID }}
private-key: ${{ secrets.ESPHOME_GITHUB_APP_PRIVATE_KEY }}
# pulls.listFiles + issues.{list,create,update}Comment on PRs. For PR resources
# the issues.*Comment APIs require the pull-requests scope, not issues.
permission-pull-requests: write
- name: Add dashboard deprecation comment
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
github-token: ${{ steps.generate-token.outputs.token }}
script: |
const commentMarker = "<!-- This comment was generated automatically by the dashboard-deprecation-comment workflow. -->";
const commentBody = `Thanks for opening this PR!
Heads up: the legacy ESPHome dashboard (\`esphome/dashboard/\` and \`tests/dashboard/\`) is **deprecated** and is being replaced by [ESPHome Device Builder](https://github.com/esphome/device-builder). We are not adding new features to the legacy dashboard and it will eventually be removed from this repository.
What this means for your PR:
- **New features / enhancements**: please port the change to [esphome/device-builder](https://github.com/esphome/device-builder) instead. We are unlikely to review or merge new dashboard features here.
- **Bug fixes**: small fixes may still be considered, but please check first whether the same issue exists in Device Builder, where the fix will have a longer life.
- **Security issues**: please do not file a public PR. Report privately via [GitHub security advisories](https://github.com/esphome/esphome/security/advisories/new) so we can coordinate a fix.
We appreciate the contribution and apologize for the friction; flagging this early so your time isn't spent on a change that may not land.
---
(Added by the PR bot)
${commentMarker}`;
async function getDashboardChanges(github, owner, repo, prNumber) {
const changedFiles = await github.paginate(
github.rest.pulls.listFiles,
{
owner: owner,
repo: repo,
pull_number: prNumber,
per_page: 100,
}
);
return changedFiles.filter(file =>
file.filename.startsWith('esphome/dashboard/') ||
file.filename.startsWith('tests/dashboard/')
);
}
async function findBotComment(github, owner, repo, prNumber) {
const comments = await github.paginate(
github.rest.issues.listComments,
{
owner: owner,
repo: repo,
issue_number: prNumber,
per_page: 100,
}
);
return comments.find(comment =>
comment.body.includes(commentMarker) && comment.user.type === "Bot"
);
}
const prNumber = context.payload.pull_request.number;
const { owner, repo } = context.repo;
const dashboardChanges = await getDashboardChanges(github, owner, repo, prNumber);
const existingComment = await findBotComment(github, owner, repo, prNumber);
if (dashboardChanges.length === 0) {
// PR doesn't (or no longer) touches the legacy dashboard. If we previously
// commented (e.g. files were removed in a later push), leave the comment in
// place for history rather than thrash on edit/delete.
return;
}
if (existingComment) {
if (existingComment.body === commentBody) {
return;
}
await github.rest.issues.updateComment({
owner: owner,
repo: repo,
comment_id: existingComment.id,
body: commentBody,
});
} else {
await github.rest.issues.createComment({
owner: owner,
repo: repo,
issue_number: prNumber,
body: commentBody,
});
}
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
name: Validate PR title
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
+13 -13
View File
@@ -20,7 +20,7 @@ jobs:
branch_build: ${{ steps.tag.outputs.branch_build }}
deploy_env: ${{ steps.tag.outputs.deploy_env }}
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Get tag
id: tag
# yamllint disable rule:line-length
@@ -60,9 +60,9 @@ jobs:
contents: read # actions/checkout to build the sdist/wheel
id-token: write # OIDC token for PyPI Trusted Publishing (pypa/gh-action-pypi-publish)
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.x"
- name: Build
@@ -92,22 +92,22 @@ jobs:
os: "ubuntu-24.04-arm"
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Set up Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.11"
python-version: "3.12"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
- name: Log in to docker hub
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to the GitHub container registry
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0
with:
registry: ghcr.io
username: ${{ github.actor }}
@@ -168,7 +168,7 @@ jobs:
- ghcr
- dockerhub
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Download digests
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
@@ -178,17 +178,17 @@ jobs:
merge-multiple: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
- name: Log in to docker hub
if: matrix.registry == 'dockerhub'
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to the GitHub container registry
if: matrix.registry == 'ghcr'
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0
with:
registry: ghcr.io
username: ${{ github.actor }}
+4 -4
View File
@@ -28,16 +28,16 @@ jobs:
permission-pull-requests: write # pulls.create / pulls.update to open or refresh the sync PR
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Checkout Home Assistant
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: home-assistant/core
path: lib/home-assistant
- name: Setup Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.14"
@@ -47,7 +47,7 @@ jobs:
# setup-python interpreter so subsequent ``pre-commit`` /
# ``script/run-in-env.py`` steps find the deps without a
# ``uv run`` prefix.
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
with:
enable-cache: true
# Pin uv version so the action does not have to fetch the
+2 -9
View File
@@ -6,7 +6,7 @@ ci:
autoupdate_commit_msg: 'pre-commit: autoupdate'
autoupdate_schedule: off # Disabled until ruff versions are synced between deps and pre-commit
# Skip hooks that have issues in pre-commit CI environment
skip: [pylint, clang-tidy-hash]
skip: [pylint]
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
@@ -40,7 +40,7 @@ repos:
rev: v3.21.2
hooks:
- id: pyupgrade
args: [--py311-plus]
args: [--py312-plus]
- repo: https://github.com/adrienverge/yamllint.git
rev: v1.37.1
hooks:
@@ -59,13 +59,6 @@ repos:
language: system
types: [python]
files: ^esphome/.+\.py$
- id: clang-tidy-hash
name: Update clang-tidy hash
entry: python script/clang_tidy_hash.py --update-if-changed
language: python
files: ^(\.clang-tidy|platformio\.ini|requirements_dev\.txt|sdkconfig\.defaults|esphome/idf_component\.yml)$
pass_filenames: false
additional_dependencies: []
- id: ci-custom
name: ci-custom
entry: python script/run-in-env.py script/ci-custom.py
+27 -9
View File
@@ -9,12 +9,12 @@ This document provides essential context for AI models interacting with this pro
## 2. Core Technologies & Stack
* **Languages:** Python (>=3.11), C++ (gnu++20)
* **Languages:** Python (>=3.12), C++ (gnu++20)
* **Frameworks & Runtimes:** PlatformIO, Arduino, ESP-IDF.
* **Build Systems:** PlatformIO is the primary build system. CMake is used as an alternative.
* **Configuration:** YAML.
* **Key Libraries/Dependencies:**
* **Python:** `voluptuous` (for configuration validation), `PyYAML` (for parsing configuration files), `paho-mqtt` (for MQTT communication), `tornado` (for the web server), `aioesphomeapi` (for the native API).
* **Python:** `voluptuous` (for configuration validation), `PyYAML` (for parsing configuration files), `paho-mqtt` (for MQTT communication), `aioesphomeapi` (for the native API).
* **C++:** `ArduinoJson` (for JSON serialization/deserialization), `AsyncMqttClient-esphome` (for MQTT), `ESPAsyncWebServer` (for the web server).
* **Package Manager(s):** `pip` (for Python dependencies), `platformio` (for C++/PlatformIO dependencies).
* **Communication Protocols:** Protobuf (for native API), MQTT, HTTP.
@@ -35,7 +35,6 @@ This document provides essential context for AI models interacting with this pro
2. **Code Generation** (`esphome/codegen.py`, `esphome/cpp_generator.py`): Manages Python to C++ code generation, template processing, and build flag management.
3. **Component System** (`esphome/components/`): Contains modular hardware and software components with platform-specific implementations and dependency management.
4. **Core Framework** (`esphome/core/`): Manages the application lifecycle, hardware abstraction, and component registration.
5. **Dashboard** (`esphome/dashboard/`): A web-based interface for device configuration, management, and OTA updates.
* **Platform Support:**
1. **ESP32** (`components/esp32/`): Espressif ESP32 family. Supports multiple variants (Original, C2, C3, C5, C6, H2, P4, S2, S3) with ESP-IDF framework. Arduino framework supports only a subset of the variants (Original, C3, S2, S3).
@@ -59,6 +58,19 @@ This document provides essential context for AI models interacting with this pro
- Protected/private fields: `lower_snake_case_with_trailing_underscore_`
- Favor descriptive names over abbreviations
* **Python Idioms:**
* **Assignment expressions (PEP 572):** Prefer the walrus operator (`:=`) wherever it removes a redundant lookup or a throwaway temporary. The most common case in component code is presence-checking a config key and then indexing it separately — fetch once with `.get()` and bind in the condition instead:
```python
# Bad - looks up CONF_BLAH twice
if CONF_BLAH in config:
cg.add(var.set_blah(config[CONF_BLAH]))
# Good - single lookup, value bound inline
if (blah := config.get(CONF_BLAH)) is not None:
cg.add(var.set_blah(blah))
```
The same applies to `while` loops and comprehensions where it avoids recomputing a value. Don't contort code to use it — reach for `:=` only when it genuinely cuts repetition or an extra assignment line.
* **C++ Field Visibility:**
* **Prefer `protected`:** Use `protected` for most class fields to enable extensibility and testing. Fields should be `lower_snake_case_with_trailing_underscore_`.
* **Use `private` for safety-critical cases:** Use `private` visibility when direct field access could introduce bugs or violate invariants:
@@ -415,13 +427,14 @@ This document provides essential context for AI models interacting with this pro
When a PR's only edits to a component are `validate.*.yaml` files (no source changes, no `test.*.yaml` changes, and the component isn't pulled in as a dependency of another changed component), CI skips the compile stage for that component entirely and only runs config validation. This is decided in `script/determine-jobs.py` via `_component_change_is_validate_only` and surfaced as the `validate_only_components` output that the `test-build-components-split` job consumes.
* **Test Grouping with Packages:** Components that use shared bus packages can be grouped together in CI to reduce build count. **Never define buses (uart, i2c, spi, modbus) directly in test YAML files** — always use packages from `test_build_components/common/`:
* **Test Grouping with Packages:** Components that use shared bus packages can be grouped together in CI to reduce build count. **Never define buses (uart, i2c, spi, modbus) directly in test YAML files** — always use packages from `test_build_components/common/`.
All includes in test files must go through dict-style `packages:` so that batch grouping works correctly — the grouping scripts only understand dict-style packages. Never use list-style packages (`packages: [- !include ...]`) or top-level merge keys (`<<: !include common.yaml`). Bus packages are keyed by the bus name; the component's `common.yaml` is keyed by the component name (e.g. `cst328: !include common.yaml`):
```yaml
# test.esp32-idf.yaml — use packages for buses
# test.esp32-idf.yaml — everything included via named packages
packages:
uart: !include ../../test_build_components/common/uart_115200/esp32-idf.yaml
<<: !include common.yaml
my_component: !include common.yaml
```
```yaml
# common.yaml — component config only, NO bus definitions
@@ -443,7 +456,6 @@ This document provides essential context for AI models interacting with this pro
* **Debug Tools:**
- `esphome config <file>.yaml` to validate configuration.
- `esphome compile <file>.yaml` to compile without uploading.
- Check the Dashboard for real-time logs.
- Use component-specific debug logging.
* **Common Issues:**
- **Import Errors**: Check component dependencies and `PYTHONPATH`.
@@ -645,7 +657,7 @@ This document provides essential context for AI models interacting with this pro
If you need a real-world example, search for components that use `@dataclass` with `CORE.data` in the codebase. Note: Some components may use `TypedDict` for dictionary-based storage; both patterns are acceptable depending on your needs.
**Why this matters:**
- Module-level globals persist between compilation runs if the dashboard doesn't fork/exec
- Module-level globals persist between compilation runs if the host process (e.g. device-builder) doesn't fork/exec
- `CORE.data` automatically clears between runs
- Namespacing under `DOMAIN` prevents key collisions between components
- `@dataclass` provides type safety and cleaner attribute access
@@ -698,3 +710,9 @@ This document provides essential context for AI models interacting with this pro
_LOGGER.warning(f"'{CONF_OLD_KEY}' deprecated, use '{CONF_NEW_KEY}'. Removed in 2026.6.0")
config[CONF_NEW_KEY] = config.pop(CONF_OLD_KEY) # Auto-migrate
```
## 9. English Language
The project uses English for non-code content. When drafting documentation, code comments, commit messages,
PR descriptions, and similar text, avoid technical jargon. Instead, express concepts in plain English,
using standard technical terms only when required. Ensure the text is readily comprehensible to a wide
audience, including non-native English speakers.
+13 -2
View File
@@ -122,7 +122,9 @@ esphome/components/cover/* @esphome/core
esphome/components/cs5460a/* @balrog-kun
esphome/components/cse7761/* @berfenger
esphome/components/cst226/* @clydebarrow
esphome/components/cst328/* @latonita
esphome/components/cst816/* @clydebarrow
esphome/components/cst9220/* @clydebarrow
esphome/components/ct_clamp/* @jesserockz
esphome/components/current_based/* @djwmarcx
esphome/components/dac7678/* @NickB1
@@ -185,6 +187,7 @@ esphome/components/ezo_pmp/* @carlos-sarmiento
esphome/components/factory_reset/* @anatoly-savchenkov
esphome/components/fastled_base/* @OttoWinter
esphome/components/feedback/* @ianchi
esphome/components/file/* @esphome/core
esphome/components/fingerprint_grow/* @alexborro @loongyh @OnFreund
esphome/components/font/* @clydebarrow @esphome/core
esphome/components/fs3000/* @kahrendt
@@ -206,6 +209,7 @@ esphome/components/gree/switch/* @nagyrobi
esphome/components/grove_gas_mc_v2/* @YorkshireIoT
esphome/components/grove_tb6612fng/* @max246
esphome/components/growatt_solar/* @leeuwte
esphome/components/gsl3670/* @clydebarrow
esphome/components/gt911/* @clydebarrow @jesserockz
esphome/components/haier/* @paveldn
esphome/components/haier/binary_sensor/* @paveldn
@@ -266,6 +270,7 @@ esphome/components/integration/* @OttoWinter
esphome/components/internal_temperature/* @Mat931
esphome/components/interval/* @esphome/core
esphome/components/ir_rf_proxy/* @kbx81
esphome/components/it8951/* @koosoli @limengdu @Passific
esphome/components/jsn_sr04t/* @Mafus1
esphome/components/json/* @esphome/core
esphome/components/kamstrup_kmp/* @cfeenstra1024
@@ -385,6 +390,7 @@ esphome/components/pcm5122/* @remcom
esphome/components/pi4ioe5v6408/* @jesserockz
esphome/components/pid/* @OttoWinter
esphome/components/pipsolar/* @andreashergert1984
esphome/components/pixoo/* @jesserockz
esphome/components/pm1006/* @habbie
esphome/components/pm2005/* @andrewjswan
esphome/components/pmsa003i/* @sjtrny
@@ -400,10 +406,12 @@ esphome/components/pn7160_i2c/* @jesserockz @kbx81
esphome/components/pn7160_spi/* @jesserockz @kbx81
esphome/components/power_supply/* @esphome/core
esphome/components/preferences/* @esphome/core
esphome/components/provisioning/* @esphome/core
esphome/components/psram/* @esphome/core
esphome/components/pulse_meter/* @cstaahl @stevebaxter @TrentHouliston
esphome/components/pvvx_mithermometer/* @pasiz
esphome/components/pylontech/* @functionpointer
esphome/components/qmi8658/* @clydebarrow
esphome/components/qmp6988/* @andrewpc
esphome/components/qr_code/* @wjtje
esphome/components/qspi_dbi/* @clydebarrow
@@ -421,7 +429,7 @@ esphome/components/rf_bridge/* @jesserockz
esphome/components/rgbct/* @jesserockz
esphome/components/ring_buffer/* @kahrendt
esphome/components/router/speaker/* @kahrendt
esphome/components/rp2040/* @jesserockz
esphome/components/rp2/* @jesserockz
esphome/components/rp2040_ble/* @bdraco
esphome/components/rp2040_pio_led_strip/* @Papa-DMan
esphome/components/rp2040_pwm/* @jesserockz
@@ -445,7 +453,7 @@ esphome/components/select/* @esphome/core
esphome/components/sen0321/* @notjj
esphome/components/sen21231/* @shreyaskarnik
esphome/components/sen5x/* @martgras
esphome/components/sen6x/* @martgras @mebner86 @mikelawrence @tuct
esphome/components/sen6x/* @martgras @mebner86 @tuct
esphome/components/sendspin/* @kahrendt
esphome/components/sendspin/media_player/* @kahrendt
esphome/components/sendspin/media_source/* @kahrendt
@@ -497,6 +505,7 @@ esphome/components/ssd1331_base/* @kbx81
esphome/components/ssd1331_spi/* @kbx81
esphome/components/ssd1351_base/* @kbx81
esphome/components/ssd1351_spi/* @kbx81
esphome/components/st7123/* @miniskipper
esphome/components/st7567_base/* @latonita
esphome/components/st7567_i2c/* @latonita
esphome/components/st7567_spi/* @latonita
@@ -561,6 +570,7 @@ esphome/components/uart/packet_transport/* @clydebarrow
esphome/components/udp/* @clydebarrow
esphome/components/ufire_ec/* @pvizeli
esphome/components/ufire_ise/* @pvizeli
esphome/components/ufm01/* @ljungqvist
esphome/components/ultrasonic/* @ssieb @swoboda1337
esphome/components/update/* @jesserockz
esphome/components/uponor_smatrix/* @kroimon
@@ -577,6 +587,7 @@ esphome/components/wake_on_lan/* @clydebarrow @willwill2will54
esphome/components/watchdog/* @oarcher
esphome/components/water_heater/* @dhoeben
esphome/components/waveshare_epaper/* @clydebarrow
esphome/components/waveshare_io_ch32v003/* @latonita
esphome/components/web_server/ota/* @esphome/core
esphome/components/web_server_base/* @esphome/core
esphome/components/web_server_idf/* @dentra
+1 -1
View File
@@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 2026.6.2
PROJECT_NUMBER = 2026.7.1
# 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
+1
View File
@@ -6,3 +6,4 @@ recursive-include esphome *.cpp *.h *.tcc *.c
recursive-include esphome *.py.script
recursive-include esphome *.jinja
recursive-include esphome LICENSE.txt
recursive-include esphome requirements.txt
+148
View File
@@ -0,0 +1,148 @@
# 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.
## The web server is an open HTTP API by design
The `web_server` component exposes a plain HTTP interface for viewing and
controlling entities, and, when the `web_server` OTA platform is enabled, for
uploading firmware at `/update`. Its only access controls are the optional
`web_server` `auth:` credentials and the network the device sits on.
When `auth:` is not configured, every endpoint is reachable by any client that
can reach the device. This is intentional; enabling `web_server` without `auth:`
is choosing an open control surface, in the same way that running native OTA
without a password leaves OTA open. The API is documented and is meant to be
called by other devices, scripts, and pages.
As defense-in-depth, the web server checks the `Origin` header on browser requests
to its entity control and state endpoints: a request whose `Origin` does not match
the address the device is served on is rejected, and the `allowed_origins` option
widens that list. This blocks the common "confused deputy" (CSRF) case where a page
the operator visits drives the device through their browser. It is **not** an
authentication boundary: it only constrains browsers. Any client that omits the
`Origin` header — `curl`, scripts, or other non-browser callers on the same
network — reaches every endpoint exactly as before. The check also does not cover
the web OTA `/update` endpoint. The device performs no CSRF-token or `Referer`
validation. The following are therefore **not** vulnerabilities in this repository:
- Requests without an `Origin` header (for example `curl`) reaching the control
endpoints, whether or not `web_server` `auth:` is set.
- Requests from an origin the operator added to `allowed_origins`.
- Cross-origin or CSRF firmware upload through the web OTA endpoint (`/update`) when
web OTA is enabled without `web_server` `auth:`. The `/update` endpoint is not
covered by the `Origin` check; this is the same exposure as running OTA without a
password.
The supported defenses are `web_server` `auth:`, protecting OTA (a web password or
a native OTA password), and keeping devices on a trusted, segmented network. See
the security best practices guide linked above.
What remains in scope is bypassing `web_server` `auth:` when it *is* configured,
and any memory-safety or protocol bug in the server reachable without credentials.
This section documents the current design and scope; it is not a judgment that the
design is optimal or that it will not change.
## 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).
- Access to the device web server or its web OTA endpoint by non-browser clients
(those that send no `Origin` header). The web server is an open HTTP API by
design (see above); browser cross-origin requests are blocked by default, but the
real controls are `web_server` `auth:` and network isolation.
- Anything in the dashboard / device-builder — report that in its own repository
(linked at the top).
- Deployments where the operator removed protections or exposed credentials. See
the security best practices guide:
https://esphome.io/guides/security_best_practices/
## Reporting a vulnerability
If you believe you've found an issue that crosses the unauthenticated boundary
above, please report it privately via GitHub Security Advisories rather than a
public issue. For issues that require config-write access, please review this
document first — they are very likely out of scope by design. For dashboard /
device-builder issues, report against that repository and consult its threat
model (linked at the top).
+2 -12
View File
@@ -1,5 +1,5 @@
ARG BUILD_VERSION=dev
ARG BUILD_BASE_VERSION=2026.06.0
ARG BUILD_BASE_VERSION=2026.06.1
ARG BUILD_TYPE=docker
FROM ghcr.io/esphome/docker-base:debian-${BUILD_BASE_VERSION} AS base-source-docker
@@ -11,16 +11,6 @@ FROM base-source-${BUILD_TYPE} AS base
RUN git config --system --add safe.directory "*" \
&& git config --system advice.detachedHead false
# Install build tools for Python packages that require compilation
# (e.g., ruamel.yaml.clib used by ESP-IDF's idf-component-manager).
# Also install libusb-1.0 at runtime so the ESP-IDF tools installer can
# validate openocd-esp32 (it dynamically links libusb-1.0.so.0); without
# it idf_tools.py rejects the openocd install with exit 127 and aborts
# the whole framework setup.
RUN apt-get update \
&& apt-get install -y --no-install-recommends build-essential libusb-1.0-0 \
&& rm -rf /var/lib/apt/lists/*
ENV PIP_DISABLE_PIP_VERSION_CHECK=1
RUN pip install --no-cache-dir -U pip uv==0.10.1
@@ -32,7 +22,7 @@ RUN \
-r /requirements.txt
# Install the ESPHome Device Builder dashboard.
RUN uv pip install --no-cache-dir esphome-device-builder==1.0.12
RUN uv pip install --no-cache-dir esphome-device-builder==1.6.8
RUN \
platformio settings set enable_telemetry No \
+5
View File
@@ -21,6 +21,11 @@ export PLATFORMIO_PLATFORMS_DIR="${pio_cache_base}/platforms"
export PLATFORMIO_PACKAGES_DIR="${pio_cache_base}/packages"
export PLATFORMIO_CACHE_DIR="${pio_cache_base}/cache"
# Keep the native toolchain installs on the persistent cache root, not the
# container's ephemeral user cache dir (re-downloaded on every restart).
export ESPHOME_ESP_IDF_PREFIX="$(dirname "${pio_cache_base}")/idf"
export ESPHOME_SDK_NRF_PREFIX="$(dirname "${pio_cache_base}")/sdk-nrf"
# If /build is mounted, use that as the build path
# otherwise use path in /config (so that builds aren't lost on container restart)
if [[ -d /build ]]; then
@@ -15,18 +15,15 @@ export PLATFORMIO_PLATFORMS_DIR="${pio_cache_base}/platforms"
export PLATFORMIO_PACKAGES_DIR="${pio_cache_base}/packages"
export PLATFORMIO_CACHE_DIR="${pio_cache_base}/cache"
# Keep the native toolchain installs on the persistent /data volume, not the
# container's ephemeral user cache dir (wiped on every add-on update/restart).
export ESPHOME_ESP_IDF_PREFIX=/data/cache/idf
export ESPHOME_SDK_NRF_PREFIX=/data/cache/sdk-nrf
if bashio::config.true 'leave_front_door_open'; then
export DISABLE_HA_AUTHENTICATION=true
fi
if bashio::config.true 'streamer_mode'; then
export ESPHOME_STREAMER_MODE=true
fi
if bashio::config.has_value 'relative_url'; then
export ESPHOME_DASHBOARD_RELATIVE_URL=$(bashio::config 'relative_url')
fi
if bashio::config.has_value 'default_compile_process_limit'; then
export ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT=$(bashio::config 'default_compile_process_limit')
else
+7
View File
@@ -0,0 +1,7 @@
esphome:
name: docker-test-bk72xx-arduino
bk72xx:
board: generic-bk7231n-qfn32-tuya
logger:
@@ -0,0 +1,10 @@
esphome:
name: docker-test-esp32-ard-idf
esp32:
variant: esp32
framework:
type: arduino
toolchain: esp-idf
logger:
@@ -0,0 +1,10 @@
esphome:
name: docker-test-esp32-ard-pio
esp32:
variant: esp32
framework:
type: arduino
toolchain: platformio
logger:
@@ -0,0 +1,10 @@
esphome:
name: docker-test-esp32-idf-idf
esp32:
variant: esp32
framework:
type: esp-idf
toolchain: esp-idf
logger:
@@ -0,0 +1,10 @@
esphome:
name: docker-test-esp32-idf-pio
esp32:
variant: esp32
framework:
type: esp-idf
toolchain: platformio
logger:
+7
View File
@@ -0,0 +1,7 @@
esphome:
name: docker-test-esp8266-arduino
esp8266:
board: d1_mini
logger:
+6
View File
@@ -0,0 +1,6 @@
esphome:
name: docker-test-host
host:
logger:
+7
View File
@@ -0,0 +1,7 @@
esphome:
name: docker-test-ln882x-arduino
ln882x:
board: generic-ln882h
logger:
+8
View File
@@ -0,0 +1,8 @@
esphome:
name: docker-test-nrf52
nrf52:
board: adafruit_itsybitsy_nrf52840
bootloader: adafruit_nrf52_sd140_v6
logger:
+7
View File
@@ -0,0 +1,7 @@
esphome:
name: docker-test-rp2040-arduino
rp2040:
variant: rp2040
logger:
+7
View File
@@ -0,0 +1,7 @@
esphome:
name: docker-test-rtl87xx-arduino
rtl87xx:
board: generic-rtl8710bn-2mb-788k
logger:
+114 -80
View File
@@ -52,11 +52,6 @@ from esphome.const import (
CONF_WEB_SERVER,
CONF_WIFI,
ENV_NOGITIGNORE,
KEY_CORE,
KEY_TARGET_PLATFORM,
PLATFORM_ESP32,
PLATFORM_ESP8266,
PLATFORM_RP2040,
SECRETS_FILES,
Toolchain,
)
@@ -230,8 +225,9 @@ def _discover_mac_suffix_devices() -> list[str] | None:
Returns:
- ``None`` when discovery isn't applicable (``name_add_mac_suffix`` off,
mDNS disabled, or ``CORE.address`` is already an IP). Callers should
then fall back to whatever default OTA address they normally use.
mDNS disabled, or ``CORE.address`` isn't a ``.local`` mDNS address).
Callers should then fall back to whatever default OTA address they
normally use.
- ``[]`` when discovery ran but found nothing. Callers should NOT fall
back to the base name: with ``name_add_mac_suffix`` enabled, the base
name by definition doesn't exist on the network.
@@ -241,7 +237,7 @@ def _discover_mac_suffix_devices() -> list[str] | None:
``aioesphomeapi`` via :func:`_resolve_network_devices`) reuses the IPs we
already have without opening a second Zeroconf client.
"""
if not (has_name_add_mac_suffix() and has_mdns() and has_non_ip_address()):
if not (has_name_add_mac_suffix() and has_mdns() and has_mdns_address()):
return None
from esphome.zeroconf import discover_mdns_devices
@@ -268,6 +264,36 @@ def _ota_hostnames_for_default(purpose: Purpose) -> list[str]:
return _resolve_with_cache(CORE.address, purpose)
def _unresolved_default_error(purpose: Purpose, defaults: list[str]) -> str:
"""Build the error when a default device target produced no usable host.
When the OTA default was requested and the address resolves but the config
lacks the transport the purpose needs (``api:`` for logs, an ``ota:``
platform for uploads), name that gap instead of the misleading
"could not be resolved" / set-use_address hint.
"""
if "OTA" in defaults and has_resolvable_address():
if purpose == Purpose.LOGGING and not has_api():
return (
"Cannot view logs over the network: no 'api:' component is "
"configured. 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(
default: list[str] | str | None,
check_default: str | None,
@@ -317,14 +343,7 @@ def choose_upload_log_host(
else:
resolved.append(device)
if not resolved:
if CORE.dashboard:
hint = "If you know the IP, set 'use_address' in your network config."
else:
hint = "If you know the IP, try --device <IP>"
raise EsphomeError(
f"All specified devices {defaults} could not be resolved. "
f"Is the device connected to the network? {hint}"
)
raise EsphomeError(_unresolved_default_error(purpose, defaults))
return resolved
# No devices specified, show interactive chooser
@@ -336,7 +355,7 @@ def choose_upload_log_host(
bootsel_permission_error = False
if (
purpose == Purpose.UPLOADING
and CORE.data.get(KEY_CORE, {}).get(KEY_TARGET_PLATFORM) == PLATFORM_RP2040
and CORE.is_rp2
and (picotool := _find_picotool()) is not None
):
bootsel = detect_rp2040_bootsel(picotool)
@@ -383,7 +402,7 @@ def choose_upload_log_host(
# Show helpful BOOTSEL instructions for RP2040 when no BOOTSEL device is found
if (
purpose == Purpose.UPLOADING
and CORE.data.get(KEY_CORE, {}).get(KEY_TARGET_PLATFORM) == PLATFORM_RP2040
and CORE.is_rp2
and not any(get_port_type(opt[1]) == PortType.BOOTSEL for opt in options)
):
if bootsel_permission_error:
@@ -485,17 +504,22 @@ def has_mdns() -> bool:
def has_non_ip_address() -> bool:
"""Check if CORE.address is set and is not an IP address."""
"""Check if ``CORE.address`` is set and is not an IP address."""
return CORE.address is not None and not is_ip_address(CORE.address)
def has_mdns_address() -> bool:
"""Check if ``CORE.address`` is a ``.local`` mDNS hostname."""
return CORE.address is not None and CORE.address.endswith(".local")
def has_ip_address() -> bool:
"""Check if CORE.address is a valid IP address."""
"""Check if ``CORE.address`` is a valid IP address."""
return CORE.address is not None and is_ip_address(CORE.address)
def has_resolvable_address() -> bool:
"""Check if CORE.address is resolvable (via mDNS, DNS, or is an IP address)."""
"""Check if ``CORE.address`` is resolvable (via mDNS, DNS, or is an IP address)."""
# Any address (IP, mDNS hostname, or regular DNS hostname) is resolvable
# The resolve_ip_address() function in helpers.py handles all types via AsyncResolver
if CORE.address is None:
@@ -504,7 +528,7 @@ def has_resolvable_address() -> bool:
if has_ip_address():
return True
# The dashboard pre-resolves the device and passes the IPs via
# device-builder pre-resolves the device and passes the IPs via
# --mdns-address-cache/--dns-address-cache; honor a cached address even when the
# device has mDNS disabled (e.g. a .local host found via ping).
if CORE.address_cache and CORE.address_cache.get_addresses(CORE.address):
@@ -514,7 +538,7 @@ def has_resolvable_address() -> bool:
return True
# .local mDNS hostnames are only resolvable if mDNS is enabled
return not CORE.address.endswith(".local")
return not has_mdns_address()
def has_name_add_mac_suffix() -> bool:
@@ -752,6 +776,13 @@ def compile_program(args: ArgsProtocol, config: ConfigType) -> int:
check_placeholder_credentials(config)
# Keep this here, NOT in codegen: config-hash and --only-generate must keep
# working on machines that cannot run the toolchain.
if CORE.is_esp8266:
from esphome.components.esp8266 import check_rosetta
check_rosetta()
# 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
_LOGGER.info("Compiling app... Build path: %s", CORE.build_path)
@@ -961,7 +992,7 @@ def upload_using_platformio(config: ConfigType, port: str) -> int:
# RP2040 platform-raspberrypi build recipe expects firmware.bin.signed for
# the upload target, but 'nobuild' skips the build phase that creates it.
# Create it here so the upload doesn't fail.
if CORE.data.get(KEY_CORE, {}).get(KEY_TARGET_PLATFORM) == PLATFORM_RP2040:
if CORE.is_rp2:
idedata = toolchain.get_idedata(config)
build_dir = Path(idedata.firmware_elf_path).parent
firmware_bin = build_dir / "firmware.bin"
@@ -1146,10 +1177,10 @@ def upload_program(
check_permissions(host)
exit_code = 1
if CORE.target_platform in (PLATFORM_ESP32, PLATFORM_ESP8266):
if CORE.is_esp32 or CORE.is_esp8266:
file = getattr(args, "file", None)
exit_code = upload_using_esptool(config, host, file, args.upload_speed)
elif CORE.target_platform == PLATFORM_RP2040 or CORE.is_libretiny:
elif CORE.is_rp2 or CORE.is_libretiny:
exit_code = upload_using_platformio(config, host)
# else: Unknown target platform, exit_code remains 1
@@ -1470,12 +1501,37 @@ _LEGACY_REDACTION_REMOVAL = "2026.12.0"
def _redact_with_legacy_fallback(output: str) -> str:
unmarked: set[str] = set()
# Track the top-level ``substitutions:`` block. Its keys are arbitrary
# user-chosen names with no schema validator, so the ``cv.sensitive(...)``
# migration named in the warning can't be applied to them. Their values are
# still redacted, but emitting the (unactionable) deprecation warning would
# only confuse users.
in_substitutions = False
def _replace(m: re.Match[str]) -> str:
unmarked.add(m.group("key"))
return f"{m.group('key')}: \\033[8m{m.group('val')}\\033[28m"
output = _LEGACY_REDACTION_RE.sub(_replace, output)
lines = output.split("\n")
for i, line in enumerate(lines):
# A non-indented, non-blank line is a top-level key that opens or
# closes the substitutions block.
if line and not line[0].isspace():
in_substitutions = line.startswith(f"{CONF_SUBSTITUTIONS}:")
m = _LEGACY_REDACTION_RE.search(line)
if m is None:
continue
key = m.group("key")
if not in_substitutions:
# Public keys (e.g. wireguard's peer_public_key) are not secret;
# redacting them and telling maintainers to mark them cv.sensitive
# would be wrong on both counts. Substitution keys are user-named
# with no schema behind them, so anything secret-shaped there
# (public or not) stays conservatively redacted.
if "public" in key.split("_"):
continue
unmarked.add(key)
lines[i] = (
f"{line[: m.start()]}{key}: "
f"\\033[8m{m.group('val')}\\033[28m{line[m.end() :]}"
)
output = "\n".join(lines)
for key in sorted(unmarked):
_LOGGER.warning(
"Field '%s' is being redacted by a legacy substring heuristic. "
@@ -1606,10 +1662,7 @@ def command_run(args: ArgsProtocol, config: ConfigType) -> int | None:
# After BOOTSEL upload, wait for a new serial port to appear
# so it shows up in the log chooser
if (
successful_device is None
and CORE.data.get(KEY_CORE, {}).get(KEY_TARGET_PLATFORM) == PLATFORM_RP2040
):
if successful_device is None and CORE.is_rp2:
_wait_for_serial_port(known_ports=pre_upload_ports)
# If exactly one new serial port appeared, use it directly
serial_ports = get_serial_ports()
@@ -1692,9 +1745,13 @@ def command_bundle(args: ArgsProtocol, config: ConfigType) -> int | None:
def command_dashboard(args: ArgsProtocol) -> int | None:
from esphome.dashboard import dashboard
return dashboard.start_dashboard(args)
raise EsphomeError(
"The built-in dashboard has been removed from ESPHome. "
"Install and run ESPHome Device Builder instead:\n"
" pip install esphome-device-builder\n"
" esphome-device-builder\n"
"See https://github.com/esphome/device-builder for more information."
)
def run_multiple_configs(
@@ -2350,50 +2407,31 @@ def parse_args(argv):
)
parser_clean_all = subparsers.add_parser(
"clean-all", help="Clean all build and platform files."
"clean-all",
help="Clean all build and platform files, including machine-global "
"toolchain caches shared by all configurations, so other projects will "
"re-download them on next build.",
)
parser_clean_all.add_argument(
"configuration", help="Your YAML file or configuration directory.", nargs="*"
)
parser_dashboard = subparsers.add_parser(
"dashboard", help="Create a simple web server for a dashboard."
# The dashboard moved to ESPHome Device Builder; the command is kept only to
# print a redirect (see command_dashboard). Accept and ignore the old flags
# so legacy invocations reach that message instead of failing on argparse
# "unrecognized arguments".
parser_dashboard = subparsers.add_parser("dashboard")
parser_dashboard.add_argument("configuration", nargs="?", help=argparse.SUPPRESS)
parser_dashboard.add_argument("--port", help=argparse.SUPPRESS)
parser_dashboard.add_argument("--address", help=argparse.SUPPRESS)
parser_dashboard.add_argument("--username", help=argparse.SUPPRESS)
parser_dashboard.add_argument("--password", help=argparse.SUPPRESS)
parser_dashboard.add_argument("--socket", help=argparse.SUPPRESS)
parser_dashboard.add_argument(
"--open-ui", action="store_true", help=argparse.SUPPRESS
)
parser_dashboard.add_argument(
"configuration", help="Your YAML configuration file directory."
)
parser_dashboard.add_argument(
"--port",
help="The HTTP port to open connections on. Defaults to 6052.",
type=int,
default=6052,
)
parser_dashboard.add_argument(
"--address",
help="The address to bind to.",
type=str,
default="0.0.0.0",
)
parser_dashboard.add_argument(
"--username",
help="The optional username to require for authentication.",
type=str,
default="",
)
parser_dashboard.add_argument(
"--password",
help="The optional password to require for authentication.",
type=str,
default="",
)
parser_dashboard.add_argument(
"--open-ui", help="Open the dashboard UI in a browser.", action="store_true"
)
parser_dashboard.add_argument(
"--ha-addon", help=argparse.SUPPRESS, action="store_true"
)
parser_dashboard.add_argument(
"--socket", help="Make the dashboard serve under a unix socket", type=str
"--ha-addon", action="store_true", help=argparse.SUPPRESS
)
parser_vscode = subparsers.add_parser("vscode")
@@ -2488,11 +2526,7 @@ def run_esphome(argv):
elif args.quiet:
args.log_level = "CRITICAL"
setup_log(
log_level=args.log_level,
# Show timestamp for dashboard access logs
include_timestamp=args.command == "dashboard",
)
setup_log(log_level=args.log_level)
if args.command in PRE_CONFIG_ACTIONS:
try:
+34 -10
View File
@@ -8,7 +8,7 @@ memory-constrained platforms like ESP8266.
from __future__ import annotations
from collections import defaultdict
from dataclasses import dataclass
from dataclasses import dataclass, field
import logging
from pathlib import Path
import re
@@ -65,6 +65,7 @@ class RamSymbol:
size: int
section: str
demangled: str = "" # Demangled name, set after batch demangling
aliases: list[str] = field(default_factory=list) # Other names at same address
class RamStringsAnalyzer:
@@ -235,6 +236,11 @@ class RamStringsAnalyzer:
except (subprocess.CalledProcessError, FileNotFoundError):
return
# Track symbols by address so aliases (multiple names for the same
# object, e.g. the newlib __lock___* mutexes that all alias one
# StaticSemaphore_t) are reported once instead of once per name.
symbols_by_addr: dict[int, RamSymbol] = {}
for line in output.split("\n"):
parts = line.split()
if len(parts) < 4:
@@ -253,6 +259,18 @@ class RamStringsAnalyzer:
if sym_type not in DATA_SYMBOL_TYPES:
continue
if (existing := symbols_by_addr.get(addr)) is not None:
# Prefer a global (uppercase type) name as the primary so
# nm output order can't hide it behind a local alias.
if sym_type.isupper() and existing.sym_type.islower():
existing.aliases.append(existing.name)
existing.name = name
existing.sym_type = sym_type
else:
existing.aliases.append(name)
existing.size = max(existing.size, size)
continue
# Check if symbol is in a RAM section
for section_name in self.ram_sections:
if section_name not in self.sections:
@@ -260,15 +278,15 @@ class RamStringsAnalyzer:
section = self.sections[section_name]
if section.address <= addr < section.address + section.size:
self.ram_symbols.append(
RamSymbol(
name=name,
sym_type=sym_type,
address=addr,
size=size,
section=section_name,
)
symbol = RamSymbol(
name=name,
sym_type=sym_type,
address=addr,
size=size,
section=section_name,
)
symbols_by_addr[addr] = symbol
self.ram_symbols.append(symbol)
break
def _demangle_symbols(self) -> None:
@@ -436,7 +454,13 @@ class RamStringsAnalyzer:
for symbol in largest_symbols:
# Use demangled name if available, otherwise raw name
display_name = symbol.demangled or symbol.name
name_display = display_name[:49] if len(display_name) > 49 else display_name
# Truncate the name, not the alias note, so merged aliases stay
# visible even for long demangled C++ names.
alias_note = f" (+{len(symbol.aliases)} aliases)" if symbol.aliases else ""
max_name_len = 49 - len(alias_note)
if len(display_name) > max_name_len:
display_name = display_name[:max_name_len]
name_display = display_name + alias_note
lines.append(
f"{name_display:<50} {symbol.sym_type:<6} {symbol.size:>8} B {symbol.section}"
)
+3 -6
View File
@@ -12,12 +12,9 @@ from __future__ import annotations
import asyncio
from collections.abc import Awaitable, Callable
import threading
from typing import Generic, TypeVar
_T = TypeVar("_T")
class AsyncThreadRunner(threading.Thread, Generic[_T]):
class AsyncThreadRunner[T](threading.Thread):
"""Run an async coroutine in a daemon thread and expose its result.
The runner catches all exceptions from the coroutine and stores them in
@@ -35,10 +32,10 @@ class AsyncThreadRunner(threading.Thread, Generic[_T]):
result = runner.result
"""
def __init__(self, coro_factory: Callable[[], Awaitable[_T]]) -> None:
def __init__(self, coro_factory: Callable[[], Awaitable[T]]) -> None:
super().__init__(daemon=True)
self._coro_factory = coro_factory
self.result: _T | None = None
self.result: T | None = None
self.exception: BaseException | None = None
self.event = threading.Event()
+18 -8
View File
@@ -6,6 +6,11 @@ from pathlib import Path
from esphome.components.esp32 import get_esp32_variant, idf_version
import esphome.config_validation as cv
from esphome.core import CORE
from esphome.framework_helpers import (
get_project_compile_flags,
get_project_cxx_compile_flags,
get_project_link_flags,
)
from esphome.helpers import mkdir_p, write_file_if_changed
# Replaces the IDF default C++ standard (-std=gnu++2b appended to
@@ -84,17 +89,20 @@ def get_project_cmakelists(minimal: bool = False) -> str:
# esphome__micro-mp3) rather than just src/. Required so suppressions
# like ``-Wno-error=maybe-uninitialized`` actually silence warnings in
# third-party components we don't author.
project_compile_opts = [
flag
for flag in sorted(CORE.build_flags)
if flag.startswith("-D")
or (flag.startswith("-W") and not flag.startswith("-Wl,"))
]
project_compile_opts = get_project_compile_flags()
extra_compile_options = "\n".join(
f'idf_build_set_property(COMPILE_OPTIONS "{flag}" APPEND)'
for flag in project_compile_opts
)
# Flags registered via cg.add_cxx_build_flag() go on CXX_COMPILE_OPTIONS
# (not COMPILE_OPTIONS) because GCC warns when a C++-only flag such as
# -Wno-volatile is passed on a C compile.
cxx_compile_options = "\n".join(
f'idf_build_set_property(CXX_COMPILE_OPTIONS "{flag}" APPEND)'
for flag in get_project_cxx_compile_flags()
)
cpp_standard_options = (
CPP_STANDARD_TEMPLATE.format(standard=CORE.cpp_standard)
if CORE.cpp_standard
@@ -159,6 +167,8 @@ include($ENV{{IDF_PATH}}/tools/cmake/project.cmake)
{cpp_standard_options}
{cxx_compile_options}
{extra_compile_options}
{managed_components_property}
@@ -188,8 +198,8 @@ def get_component_cmakelists() -> str:
# Extract linker options (-Wl, flags). Compile flags (-D, -W) are
# emitted project-wide via idf_build_set_property in
# get_project_cmakelists so they reach every component, not just src/.
link_opts = [flag for flag in CORE.build_flags if flag.startswith("-Wl,")]
link_opts_str = "\n ".join(sorted(link_opts)) if link_opts else ""
link_opts = get_project_link_flags()
link_opts_str = "\n ".join(link_opts) if link_opts else ""
return f"""\
# Auto-generated by ESPHome
+2 -3
View File
@@ -108,7 +108,6 @@ Import("env")
def write_cxx_flags_script() -> None:
path = CORE.relative_build_path(CXX_FLAGS_FILE_NAME)
contents = CXX_FLAGS_FILE_CONTENTS
if not CORE.is_host:
contents += 'env.Append(CXXFLAGS=["-Wno-volatile"])'
contents += "\n"
for flag in sorted(CORE.cxx_build_flags):
contents += f'env.Append(CXXFLAGS=["{flag}"])\n'
write_file_if_changed(path, contents)
+35 -2
View File
@@ -7,7 +7,7 @@ and compiled directly: ``esphome compile my_device.esphomebundle.tar.gz``
from __future__ import annotations
from dataclasses import dataclass
from dataclasses import dataclass, field
from enum import StrEnum
import io
import json
@@ -32,6 +32,8 @@ from esphome.core import CORE, EsphomeError
_LOGGER = logging.getLogger(__name__)
DOMAIN = "bundle"
BUNDLE_EXTENSION = ".esphomebundle.tar.gz"
MANIFEST_FILENAME = "manifest.json"
CURRENT_MANIFEST_VERSION = 1
@@ -120,6 +122,32 @@ def _find_used_secret_keys(yaml_files: list[Path]) -> set[str]:
return keys
@dataclass
class BundleData:
"""Files components asked to include, keyed under DOMAIN in CORE.data."""
extra_files: list[Path] = field(default_factory=list)
def _get_data() -> BundleData:
if DOMAIN not in CORE.data:
CORE.data[DOMAIN] = BundleData()
return CORE.data[DOMAIN]
def add_bundle_file(path: Path) -> None:
"""Register a file that a bundle must include.
Bundle discovery walks the validated config, so it only finds files the config
names. Components call this during validation for files it cannot see, such as a
file that is referenced from inside another file.
A relative path is taken as relative to the config directory. Files outside the
config directory are skipped when the bundle is built.
"""
_get_data().extra_files.append(CORE.relative_config_path(path))
@dataclass
class BundleFile:
"""A file to include in the bundle."""
@@ -286,13 +314,18 @@ class ConfigBundleCreator:
with known file extensions are also resolved and checked.
Core ESPHome concepts that use relative paths or directories
are handled explicitly.
are handled explicitly. Files the config does not name at all are
registered by their component with add_bundle_file().
"""
config = self._config
# Generic walk: find all file paths in the validated config
self._walk_config_for_files(config)
# Files registered by components during validation
for extra_file in _get_data().extra_files:
self._add_file(extra_file)
# --- Core ESPHome concepts needing explicit handling ---
# esphome.includes / includes_c - can be relative paths and directories
+1
View File
@@ -25,6 +25,7 @@ from esphome.cpp_generator import ( # noqa: F401
add,
add_build_flag,
add_build_unflag,
add_cxx_build_flag,
add_define,
add_global,
add_library,
+6
View File
@@ -0,0 +1,6 @@
# Importing `esphome.loader` here installs the component-alias
# ``sys.meta_path`` finder before any submodule lookup runs. Without this,
# `from esphome.components import <legacy_alias>` from a fresh interpreter
# can race the finder install and raise ImportError, since the legacy
# alias dir no longer exists on disk.
from esphome import loader as _loader # noqa: F401
+1 -1
View File
@@ -25,7 +25,7 @@ void A01nyubComponent::check_buffer_() {
if (this->buffer_[3] == checksum) {
float distance = (this->buffer_[1] << 8) + this->buffer_[2];
if (distance > 280) {
float meters = distance / 1000.0;
float meters = distance / 1000.0f;
ESP_LOGV(TAG, "Distance from sensor: %f mm, %f m", distance, meters);
this->publish_state(meters);
} else {
+1 -1
View File
@@ -8,7 +8,7 @@
namespace esphome::a01nyub {
class A01nyubComponent : public sensor::Sensor, public Component, public uart::UARTDevice {
class A01nyubComponent final : public sensor::Sensor, public Component, public uart::UARTDevice {
public:
// Nothing really public.
+1 -1
View File
@@ -8,7 +8,7 @@
namespace esphome::a02yyuw {
class A02yyuwComponent : public sensor::Sensor, public Component, public uart::UARTDevice {
class A02yyuwComponent final : public sensor::Sensor, public Component, public uart::UARTDevice {
public:
// Nothing really public.
+1 -1
View File
@@ -6,7 +6,7 @@
namespace esphome::a4988 {
class A4988 : public stepper::Stepper, public Component {
class A4988 final : public stepper::Stepper, public Component {
public:
void set_step_pin(GPIOPin *step_pin) { step_pin_ = step_pin; }
void set_dir_pin(GPIOPin *dir_pin) { dir_pin_ = dir_pin; }
@@ -13,7 +13,7 @@ enum SaturationVaporPressureEquation {
};
/// This class implements calculation of absolute humidity from temperature and relative humidity.
class AbsoluteHumidityComponent : public sensor::Sensor, public Component {
class AbsoluteHumidityComponent final : public sensor::Sensor, public Component {
public:
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { this->temperature_sensor_ = temperature_sensor; }
void set_humidity_sensor(sensor::Sensor *humidity_sensor) { this->humidity_sensor_ = humidity_sensor; }
+1 -1
View File
@@ -216,7 +216,7 @@ void AcDimmer::setup() {
}
void AcDimmer::write_state(float state) {
state = std::acos(1 - (2 * state)) / std::numbers::pi; // RMS power compensation
state = std::acos(1 - (2 * state)) / std::numbers::pi_v<float>; // RMS power compensation
auto new_value = static_cast<uint16_t>(roundf(state * 65535));
if (new_value != 0 && this->store_.value == 0)
this->store_.init_cycle = this->init_with_half_cycle_;
+1 -1
View File
@@ -41,7 +41,7 @@ struct AcDimmerDataStore {
#endif
};
class AcDimmer : public output::FloatOutput, public Component {
class AcDimmer final : public output::FloatOutput, public Component {
public:
void setup() override;
+4 -4
View File
@@ -227,12 +227,12 @@ ESP32_VARIANT_ADC2_PIN_TO_CHANNEL = {
def validate_adc_pin(value):
if str(value).upper() == "VCC":
if CORE.is_rp2040:
if CORE.is_rp2:
return pins.internal_gpio_input_pin_schema(29)
return cv.only_on([PLATFORM_ESP8266])("VCC")
if str(value).upper() == "TEMPERATURE":
return cv.only_on_rp2040("TEMPERATURE")
return cv.only_on_rp2("TEMPERATURE")
if CORE.is_esp32:
conf = pins.internal_gpio_input_pin_schema(value)
@@ -261,11 +261,11 @@ def validate_adc_pin(value):
raise cv.Invalid("ESP8266: Only pin A0 (GPIO17) supports ADC")
return conf
if CORE.is_rp2040:
if CORE.is_rp2:
conf = pins.internal_gpio_input_pin_schema(value)
number = conf[CONF_NUMBER]
if number not in (26, 27, 28, 29):
raise cv.Invalid("RP2040: Only pins 26, 27, 28 and 29 support ADC")
raise cv.Invalid("RP2: Only pins 26, 27, 28 and 29 support ADC")
return conf
if CORE.is_libretiny:
+5 -5
View File
@@ -54,7 +54,7 @@ template<typename T> class Aggregator {
SamplingMode mode_{SamplingMode::AVG};
};
class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler {
class ADCSensor final : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler {
public:
/// Update the sensor's state by reading the current ADC value.
/// This method is called periodically based on the update interval.
@@ -123,9 +123,9 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
void set_autorange(bool autorange) { this->autorange_ = autorange; }
#endif // USE_ESP32
#ifdef USE_RP2040
#ifdef USE_RP2
void set_is_temperature() { this->is_temperature_ = true; }
#endif // USE_RP2040
#endif // USE_RP2
protected:
uint8_t sample_count_{1};
@@ -152,9 +152,9 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
static adc_oneshot_unit_handle_t shared_adc_handles[2];
#endif // USE_ESP32
#ifdef USE_RP2040
#ifdef USE_RP2
bool is_temperature_{false};
#endif // USE_RP2040
#endif // USE_RP2
#ifdef USE_ZEPHYR
const struct adc_dt_spec *channel_ = nullptr;
@@ -1,4 +1,4 @@
#ifdef USE_RP2040
#ifdef USE_RP2
#include "adc_sensor.h"
#include "esphome/core/log.h"
@@ -17,7 +17,7 @@
namespace esphome::adc {
static const char *const TAG = "adc.rp2040";
static const char *const TAG = "adc.rp2";
void ADCSensor::setup() {
static bool initialized = false;
@@ -66,15 +66,18 @@ float ADCSensor::sample() {
}
uint8_t pin = this->pin_->get_pin();
#ifdef CYW43_USES_VSYS_PIN
#if defined(CYW43_USES_VSYS_PIN) && defined(USE_WIFI)
if (pin == PICO_VSYS_PIN) {
// Measuring VSYS on Raspberry Pico W needs to be wrapped with
// `cyw43_thread_enter()`/`cyw43_thread_exit()` as discussed in
// https://github.com/raspberrypi/pico-sdk/issues/1222, since Wifi chip and
// VSYS ADC both share GPIO29
// VSYS ADC both share GPIO29.
// The USE_WIFI guard is required because CYW43_USES_VSYS_PIN can be defined
// transitively (e.g. via lwip_wrap.h) even on non-WiFi boards where the CYW43
// driver is never initialized; calling cyw43_thread_enter() there hard-faults.
cyw43_thread_enter();
}
#endif // CYW43_USES_VSYS_PIN
#endif // defined(CYW43_USES_VSYS_PIN) && defined(USE_WIFI)
adc_gpio_init(pin);
adc_select_input(pin - 26);
@@ -84,11 +87,11 @@ float ADCSensor::sample() {
aggr.add_sample(raw);
}
#ifdef CYW43_USES_VSYS_PIN
#if defined(CYW43_USES_VSYS_PIN) && defined(USE_WIFI)
if (pin == PICO_VSYS_PIN) {
cyw43_thread_exit();
}
#endif // CYW43_USES_VSYS_PIN
#endif // defined(CYW43_USES_VSYS_PIN) && defined(USE_WIFI)
if (this->output_raw_) {
return aggr.aggregate();
@@ -99,4 +102,4 @@ float ADCSensor::sample() {
} // namespace esphome::adc
#endif // USE_RP2040
#endif // USE_RP2
+1 -1
View File
@@ -201,7 +201,7 @@ FILTER_SOURCE_FILES = filter_source_files_from_platform(
PlatformFramework.ESP32_IDF,
},
"adc_sensor_esp8266.cpp": {PlatformFramework.ESP8266_ARDUINO},
"adc_sensor_rp2040.cpp": {PlatformFramework.RP2040_ARDUINO},
"adc_sensor_rp2.cpp": {PlatformFramework.RP2_ARDUINO},
"adc_sensor_libretiny.cpp": {
PlatformFramework.BK72XX_ARDUINO,
PlatformFramework.RTL87XX_ARDUINO,
+3 -3
View File
@@ -6,9 +6,9 @@
namespace esphome::adc128s102 {
class ADC128S102 : public Component,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING,
spi::DATA_RATE_10MHZ> {
class ADC128S102 final : public Component,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_10MHZ> {
public:
ADC128S102() = default;
@@ -9,10 +9,10 @@
namespace esphome::adc128s102 {
class ADC128S102Sensor : public PollingComponent,
public Parented<ADC128S102>,
public sensor::Sensor,
public voltage_sampler::VoltageSampler {
class ADC128S102Sensor final : public PollingComponent,
public Parented<ADC128S102>,
public sensor::Sensor,
public voltage_sampler::VoltageSampler {
public:
ADC128S102Sensor(uint8_t channel);
@@ -9,7 +9,7 @@
namespace esphome::addressable_light {
class AddressableLightDisplay : public display::DisplayBuffer {
class AddressableLightDisplay final : public display::DisplayBuffer {
public:
light::AddressableLight *get_light() const { return this->light_; }
+1 -1
View File
@@ -65,7 +65,7 @@ struct ADE7880Store {
static void gpio_intr(ADE7880Store *arg);
};
class ADE7880 : public i2c::I2CDevice, public PollingComponent {
class ADE7880 final : public i2c::I2CDevice, public PollingComponent {
public:
void set_irq0_pin(InternalGPIOPin *pin) { this->irq0_pin_ = pin; }
void set_irq1_pin(InternalGPIOPin *pin) { this->irq1_pin_ = pin; }
+1 -1
View File
@@ -10,7 +10,7 @@
namespace esphome::ade7953_i2c {
class AdE7953I2c : public ade7953_base::ADE7953, public i2c::I2CDevice {
class AdE7953I2c final : public ade7953_base::ADE7953, public i2c::I2CDevice {
public:
void dump_config() override;
+1 -1
View File
@@ -43,7 +43,7 @@ enum ADS1115Samplerate {
ADS1115_860SPS = 0b111
};
class ADS1115Component : public Component, public i2c::I2CDevice {
class ADS1115Component final : public Component, public i2c::I2CDevice {
public:
void setup() override;
void dump_config() override;
@@ -11,10 +11,10 @@
namespace esphome::ads1115 {
/// Internal holder class that is in instance of Sensor so that the hub can create individual sensors.
class ADS1115Sensor : public sensor::Sensor,
public PollingComponent,
public voltage_sampler::VoltageSampler,
public Parented<ADS1115Component> {
class ADS1115Sensor final : public sensor::Sensor,
public PollingComponent,
public voltage_sampler::VoltageSampler,
public Parented<ADS1115Component> {
public:
void update() override;
void set_multiplexer(ADS1115Multiplexer multiplexer) { this->multiplexer_ = multiplexer; }
+3 -3
View File
@@ -26,9 +26,9 @@ enum ADS1118Gain {
ADS1118_GAIN_0P256 = 0b101,
};
class ADS1118 : public Component,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_TRAILING,
spi::DATA_RATE_1MHZ> {
class ADS1118 final : public Component,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
spi::CLOCK_PHASE_TRAILING, spi::DATA_RATE_1MHZ> {
public:
ADS1118() = default;
void setup() override;
@@ -10,10 +10,10 @@
namespace esphome::ads1118 {
class ADS1118Sensor : public PollingComponent,
public sensor::Sensor,
public voltage_sampler::VoltageSampler,
public Parented<ADS1118> {
class ADS1118Sensor final : public PollingComponent,
public sensor::Sensor,
public voltage_sampler::VoltageSampler,
public Parented<ADS1118> {
public:
void update() override;
+3 -3
View File
@@ -7,7 +7,7 @@
namespace esphome::ags10 {
class AGS10Component : public PollingComponent, public i2c::I2CDevice {
class AGS10Component final : public PollingComponent, public i2c::I2CDevice {
public:
/**
* Sets TVOC sensor.
@@ -100,7 +100,7 @@ class AGS10Component : public PollingComponent, public i2c::I2CDevice {
template<size_t N> optional<std::array<uint8_t, N>> read_and_check_(uint8_t a_register);
};
template<typename... Ts> class AGS10NewI2cAddressAction : public Action<Ts...>, public Parented<AGS10Component> {
template<typename... Ts> class AGS10NewI2cAddressAction final : public Action<Ts...>, public Parented<AGS10Component> {
public:
TEMPLATABLE_VALUE(uint8_t, new_address)
@@ -116,7 +116,7 @@ enum AGS10SetZeroPointActionMode {
CUSTOM_VALUE,
};
template<typename... Ts> class AGS10SetZeroPointAction : public Action<Ts...>, public Parented<AGS10Component> {
template<typename... Ts> class AGS10SetZeroPointAction final : public Action<Ts...>, public Parented<AGS10Component> {
public:
TEMPLATABLE_VALUE(uint16_t, value)
TEMPLATABLE_VALUE(AGS10SetZeroPointActionMode, mode)
+1 -1
View File
@@ -10,7 +10,7 @@ namespace esphome::aht10 {
enum AHT10Variant { AHT10, AHT20 };
class AHT10Component : public PollingComponent, public i2c::I2CDevice {
class AHT10Component final : public PollingComponent, public i2c::I2CDevice {
public:
void setup() override;
void update() override;
+1 -1
View File
@@ -61,7 +61,7 @@ static const uint8_t AIC3204_ADC_PTM = 0x3D; // Register 61 - ADC Power Tu
static const uint8_t AIC3204_AN_IN_CHRG = 0x47; // Register 71 - Analog Input Quick Charging Config
static const uint8_t AIC3204_REF_STARTUP = 0x7B; // Register 123 - Reference Power Up Config
class AIC3204 : public audio_dac::AudioDac, public Component, public i2c::I2CDevice {
class AIC3204 final : public audio_dac::AudioDac, public Component, public i2c::I2CDevice {
public:
void setup() override;
void dump_config() override;
+1 -1
View File
@@ -6,7 +6,7 @@
namespace esphome::aic3204 {
template<typename... Ts> class SetAutoMuteAction : public Action<Ts...> {
template<typename... Ts> class SetAutoMuteAction final : public Action<Ts...> {
public:
explicit SetAutoMuteAction(AIC3204 *aic3204) : aic3204_(aic3204) {}
@@ -7,7 +7,7 @@
namespace esphome::airthings_ble {
class AirthingsListener : public esp32_ble_tracker::ESPBTDeviceListener {
class AirthingsListener final : public esp32_ble_tracker::ESPBTDeviceListener {
public:
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
};
@@ -12,7 +12,7 @@ static const char *const SERVICE_UUID = "b42e3882-ade7-11e4-89d3-123b93f75cba";
static const char *const CHARACTERISTIC_UUID = "b42e3b98-ade7-11e4-89d3-123b93f75cba";
static const char *const ACCESS_CONTROL_POINT_CHARACTERISTIC_UUID = "b42e3ef4-ade7-11e4-89d3-123b93f75cba";
class AirthingsWaveMini : public airthings_wave_base::AirthingsWaveBase {
class AirthingsWaveMini final : public airthings_wave_base::AirthingsWaveBase {
public:
AirthingsWaveMini();
@@ -19,7 +19,7 @@ static const char *const CHARACTERISTIC_UUID_WAVE_RADON_GEN2 = "b42e4dcc-ade7-11
static const char *const ACCESS_CONTROL_POINT_CHARACTERISTIC_UUID_WAVE_RADON_GEN2 =
"b42e50d8-ade7-11e4-89d3-123b93f75cba";
class AirthingsWavePlus : public airthings_wave_base::AirthingsWaveBase {
class AirthingsWavePlus final : public airthings_wave_base::AirthingsWaveBase {
public:
void setup() override;
@@ -27,7 +27,7 @@ static_assert(std::is_trivially_copyable_v<StateAnyForwarder>);
static_assert(sizeof(StateEnterForwarder<ACP_STATE_TRIGGERED>) <= sizeof(void *));
static_assert(std::is_trivially_copyable_v<StateEnterForwarder<ACP_STATE_TRIGGERED>>);
template<typename... Ts> class ArmAwayAction : public Action<Ts...> {
template<typename... Ts> class ArmAwayAction final : public Action<Ts...> {
public:
explicit ArmAwayAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
@@ -39,7 +39,7 @@ template<typename... Ts> class ArmAwayAction : public Action<Ts...> {
AlarmControlPanel *alarm_control_panel_;
};
template<typename... Ts> class ArmHomeAction : public Action<Ts...> {
template<typename... Ts> class ArmHomeAction final : public Action<Ts...> {
public:
explicit ArmHomeAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
@@ -51,7 +51,7 @@ template<typename... Ts> class ArmHomeAction : public Action<Ts...> {
AlarmControlPanel *alarm_control_panel_;
};
template<typename... Ts> class ArmNightAction : public Action<Ts...> {
template<typename... Ts> class ArmNightAction final : public Action<Ts...> {
public:
explicit ArmNightAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
@@ -63,7 +63,7 @@ template<typename... Ts> class ArmNightAction : public Action<Ts...> {
AlarmControlPanel *alarm_control_panel_;
};
template<typename... Ts> class DisarmAction : public Action<Ts...> {
template<typename... Ts> class DisarmAction final : public Action<Ts...> {
public:
explicit DisarmAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
@@ -75,7 +75,7 @@ template<typename... Ts> class DisarmAction : public Action<Ts...> {
AlarmControlPanel *alarm_control_panel_;
};
template<typename... Ts> class PendingAction : public Action<Ts...> {
template<typename... Ts> class PendingAction final : public Action<Ts...> {
public:
explicit PendingAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
@@ -85,7 +85,7 @@ template<typename... Ts> class PendingAction : public Action<Ts...> {
AlarmControlPanel *alarm_control_panel_;
};
template<typename... Ts> class TriggeredAction : public Action<Ts...> {
template<typename... Ts> class TriggeredAction final : public Action<Ts...> {
public:
explicit TriggeredAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
@@ -95,7 +95,7 @@ template<typename... Ts> class TriggeredAction : public Action<Ts...> {
AlarmControlPanel *alarm_control_panel_;
};
template<typename... Ts> class AlarmControlPanelCondition : public Condition<Ts...> {
template<typename... Ts> class AlarmControlPanelCondition final : public Condition<Ts...> {
public:
AlarmControlPanelCondition(AlarmControlPanel *parent) : parent_(parent) {}
bool check(const Ts &...x) override {
+1 -1
View File
@@ -31,7 +31,7 @@ static const int16_t GENI_RESPONSE_POWER_OFFSET = 12;
static const int16_t GENI_RESPONSE_MOTOR_POWER_OFFSET = 16; // not sure
static const int16_t GENI_RESPONSE_MOTOR_SPEED_OFFSET = 20;
class Alpha3 : public esphome::ble_client::BLEClientNode, public PollingComponent {
class Alpha3 final : public esphome::ble_client::BLEClientNode, public PollingComponent {
public:
void setup() override;
void update() override;
+1 -1
View File
@@ -27,7 +27,7 @@
namespace esphome::am2315c {
class AM2315C : public PollingComponent, public i2c::I2CDevice {
class AM2315C final : public PollingComponent, public i2c::I2CDevice {
public:
void dump_config() override;
void update() override;
+1 -1
View File
@@ -6,7 +6,7 @@
namespace esphome::am2320 {
class AM2320Component : public PollingComponent, public i2c::I2CDevice {
class AM2320Component final : public PollingComponent, public i2c::I2CDevice {
public:
void setup() override;
void dump_config() override;
+5 -5
View File
@@ -114,13 +114,13 @@ void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
this->decoder_->decode(param->notify.value, param->notify.value_len);
if (this->decoder_->has_position()) {
this->position = ((float) this->decoder_->position_ / 100.0);
this->position = ((float) this->decoder_->position_ / 100.0f);
if (!this->invert_position_)
this->position = 1 - this->position;
if (this->position > 0.97)
this->position = 1.0;
if (this->position < 0.02)
this->position = 0.0;
if (this->position > 0.97f)
this->position = 1.0f;
if (this->position < 0.02f)
this->position = 0.0f;
this->publish_state();
}
+1 -1
View File
@@ -14,7 +14,7 @@ namespace esphome::am43 {
namespace espbt = esphome::esp32_ble_tracker;
class Am43Component : public cover::Cover, public esphome::ble_client::BLEClientNode, public Component {
class Am43Component final : public cover::Cover, public esphome::ble_client::BLEClientNode, public Component {
public:
void setup() override;
void loop() override;
+1 -1
View File
@@ -14,7 +14,7 @@ namespace esphome::am43 {
namespace espbt = esphome::esp32_ble_tracker;
class Am43 : public esphome::ble_client::BLEClientNode, public PollingComponent {
class Am43 final : public esphome::ble_client::BLEClientNode, public PollingComponent {
public:
void setup() override;
void update() override;
@@ -7,7 +7,7 @@
namespace esphome::analog_threshold {
class AnalogThresholdBinarySensor : public Component, public binary_sensor::BinarySensor {
class AnalogThresholdBinarySensor final : public Component, public binary_sensor::BinarySensor {
public:
void dump_config() override;
void setup() override;
+20 -98
View File
@@ -1,114 +1,36 @@
import logging
# ---------------------------------------------------------------------------
# Legacy top-level `animation:` deprecation shim -- REMOVE this whole file after
# 2027.1.0.
#
# Animations are now a platform of the `image:` component (`platform:
# animation`); the real schema, actions and codegen live in `image.py`. This
# module only keeps the deprecated top-level `animation:` key working during the
# deprecation window: it reuses that schema/codegen and adds a one-shot
# deprecation warning (with a pasteable migrated `image:` block) at validation
# time. Deleting this file drops the top-level form entirely.
# ---------------------------------------------------------------------------
from esphome import automation
import esphome.codegen as cg
from esphome.components.const import CONF_LOOP
import esphome.components.image as espImage
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_REPEAT
_LOGGER = logging.getLogger(__name__)
from .image import ANIMATION_CONFIG_SCHEMA, setup_animation
AUTO_LOAD = ["image"]
AUTO_LOAD = ["image", "file"]
CODEOWNERS = ["@syndlex"]
DEPENDENCIES = ["display"]
MULTI_CONF = True
MULTI_CONF_NO_DEFAULT = True
CONF_START_FRAME = "start_frame"
CONF_END_FRAME = "end_frame"
CONF_FRAME = "frame"
DOMAIN = "animation"
animation_ns = cg.esphome_ns.namespace("animation")
LEGACY_REMOVAL_VERSION = "2027.1.0"
Animation_ = animation_ns.class_("Animation", espImage.Image_)
# Actions
NextFrameAction = animation_ns.class_(
"AnimationNextFrameAction", automation.Action, cg.Parented.template(Animation_)
)
PrevFrameAction = animation_ns.class_(
"AnimationPrevFrameAction", automation.Action, cg.Parented.template(Animation_)
)
SetFrameAction = animation_ns.class_(
"AnimationSetFrameAction", automation.Action, cg.Parented.template(Animation_)
_capture_legacy_entry, _warn_legacy_animation = (
espImage.legacy_platform_migration_warning(DOMAIN, DOMAIN, LEGACY_REMOVAL_VERSION)
)
CONFIG_SCHEMA = cv.All(
espImage.IMAGE_SCHEMA.extend(
{
cv.Required(CONF_ID): cv.declare_id(Animation_),
cv.Optional(CONF_LOOP): cv.All(
{
cv.Optional(CONF_START_FRAME, default=0): cv.positive_int,
cv.Optional(CONF_END_FRAME): cv.positive_int,
cv.Optional(CONF_REPEAT): cv.positive_int,
}
),
},
),
espImage.validate_settings,
)
CONFIG_SCHEMA = cv.All(_capture_legacy_entry, ANIMATION_CONFIG_SCHEMA)
FINAL_VALIDATE_SCHEMA = _warn_legacy_animation
NEXT_FRAME_SCHEMA = automation.maybe_simple_id(
{
cv.GenerateID(): cv.use_id(Animation_),
}
)
PREV_FRAME_SCHEMA = automation.maybe_simple_id(
{
cv.GenerateID(): cv.use_id(Animation_),
}
)
SET_FRAME_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.use_id(Animation_),
cv.Required(CONF_FRAME): cv.uint16_t,
}
)
@automation.register_action(
"animation.next_frame", NextFrameAction, NEXT_FRAME_SCHEMA, synchronous=True
)
@automation.register_action(
"animation.prev_frame", PrevFrameAction, PREV_FRAME_SCHEMA, synchronous=True
)
@automation.register_action(
"animation.set_frame", SetFrameAction, SET_FRAME_SCHEMA, synchronous=True
)
async def animation_action_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
if (frame := config.get(CONF_FRAME)) is not None:
template_ = await cg.templatable(frame, args, cg.uint16)
cg.add(var.set_frame(template_))
return var
async def to_code(config):
(
prog_arr,
width,
height,
image_type,
trans_value,
frame_count,
) = await espImage.write_image(config, all_frames=True)
var = cg.new_Pvariable(
config[CONF_ID],
prog_arr,
width,
height,
frame_count,
image_type,
trans_value,
)
if loop_config := config.get(CONF_LOOP):
start = loop_config[CONF_START_FRAME]
end = loop_config.get(CONF_END_FRAME, frame_count)
count = loop_config.get(CONF_REPEAT, -1)
cg.add(var.set_loop(start, end, count))
to_code = setup_animation
+4 -4
View File
@@ -5,7 +5,7 @@
namespace esphome::animation {
class Animation : public image::Image {
class Animation final : public image::Image {
public:
Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, image::ImageType type,
image::Transparency transparent);
@@ -35,7 +35,7 @@ class Animation : public image::Image {
int loop_current_iteration_;
};
template<typename... Ts> class AnimationNextFrameAction : public Action<Ts...> {
template<typename... Ts> class AnimationNextFrameAction final : public Action<Ts...> {
public:
AnimationNextFrameAction(Animation *parent) : parent_(parent) {}
void play(const Ts &...x) override { this->parent_->next_frame(); }
@@ -44,7 +44,7 @@ template<typename... Ts> class AnimationNextFrameAction : public Action<Ts...> {
Animation *parent_;
};
template<typename... Ts> class AnimationPrevFrameAction : public Action<Ts...> {
template<typename... Ts> class AnimationPrevFrameAction final : public Action<Ts...> {
public:
AnimationPrevFrameAction(Animation *parent) : parent_(parent) {}
void play(const Ts &...x) override { this->parent_->prev_frame(); }
@@ -53,7 +53,7 @@ template<typename... Ts> class AnimationPrevFrameAction : public Action<Ts...> {
Animation *parent_;
};
template<typename... Ts> class AnimationSetFrameAction : public Action<Ts...> {
template<typename... Ts> class AnimationSetFrameAction final : public Action<Ts...> {
public:
AnimationSetFrameAction(Animation *parent) : parent_(parent) {}
TEMPLATABLE_VALUE(uint16_t, frame)
+115
View File
@@ -0,0 +1,115 @@
from esphome import automation
import esphome.codegen as cg
from esphome.components.const import CONF_LOOP
from esphome.components.file.image import image_schema, write_image
from esphome.components.image import Image_, validate_settings
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_REPEAT
from esphome.types import ConfigType
CODEOWNERS = ["@syndlex"]
AUTO_LOAD = ["file"]
DEPENDENCIES = ["display"]
CONF_START_FRAME = "start_frame"
CONF_END_FRAME = "end_frame"
CONF_FRAME = "frame"
animation_ns = cg.esphome_ns.namespace("animation")
Animation_ = animation_ns.class_("Animation", Image_)
# Actions
NextFrameAction = animation_ns.class_(
"AnimationNextFrameAction", automation.Action, cg.Parented.template(Animation_)
)
PrevFrameAction = animation_ns.class_(
"AnimationPrevFrameAction", automation.Action, cg.Parented.template(Animation_)
)
SetFrameAction = animation_ns.class_(
"AnimationSetFrameAction", automation.Action, cg.Parented.template(Animation_)
)
ANIMATION_SCHEMA = image_schema(Animation_).extend(
{
cv.Optional(CONF_LOOP): cv.All(
{
cv.Optional(CONF_START_FRAME, default=0): cv.positive_int,
cv.Optional(CONF_END_FRAME): cv.positive_int,
cv.Optional(CONF_REPEAT): cv.positive_int,
}
),
},
)
# Shared schema used by both the (deprecated) top-level `animation:` key and the
# `image:` `platform: animation` entry.
ANIMATION_CONFIG_SCHEMA = cv.All(ANIMATION_SCHEMA, validate_settings)
NEXT_FRAME_SCHEMA = automation.maybe_simple_id(
{
cv.GenerateID(): cv.use_id(Animation_),
}
)
PREV_FRAME_SCHEMA = automation.maybe_simple_id(
{
cv.GenerateID(): cv.use_id(Animation_),
}
)
SET_FRAME_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.use_id(Animation_),
cv.Required(CONF_FRAME): cv.uint16_t,
}
)
@automation.register_action(
"animation.next_frame", NextFrameAction, NEXT_FRAME_SCHEMA, synchronous=True
)
@automation.register_action(
"animation.prev_frame", PrevFrameAction, PREV_FRAME_SCHEMA, synchronous=True
)
@automation.register_action(
"animation.set_frame", SetFrameAction, SET_FRAME_SCHEMA, synchronous=True
)
async def animation_action_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
if (frame := config.get(CONF_FRAME)) is not None:
template_ = await cg.templatable(frame, args, cg.uint16)
cg.add(var.set_frame(template_))
return var
async def setup_animation(config: ConfigType) -> None:
(
prog_arr,
width,
height,
image_type,
trans_value,
frame_count,
) = await write_image(config, all_frames=True)
var = cg.new_Pvariable(
config[CONF_ID],
prog_arr,
width,
height,
frame_count,
image_type,
trans_value,
)
if loop_config := config.get(CONF_LOOP):
start = loop_config[CONF_START_FRAME]
end = loop_config.get(CONF_END_FRAME, frame_count)
count = loop_config.get(CONF_REPEAT, -1)
cg.add(var.set_loop(start, end, count))
CONFIG_SCHEMA = ANIMATION_CONFIG_SCHEMA
to_code = setup_animation
+1 -1
View File
@@ -17,7 +17,7 @@ namespace espbt = esphome::esp32_ble_tracker;
static const uint16_t ANOVA_SERVICE_UUID = 0xFFE0;
static const uint16_t ANOVA_CHARACTERISTIC_UUID = 0xFFE1;
class Anova : public climate::Climate, public esphome::ble_client::BLEClientNode, public PollingComponent {
class Anova final : public climate::Climate, public esphome::ble_client::BLEClientNode, public PollingComponent {
public:
void setup() override;
void loop() override;
+2 -2
View File
@@ -6,9 +6,9 @@
namespace esphome::anova {
float ftoc(float f) { return (f - 32.0) * (5.0f / 9.0f); }
float ftoc(float f) { return (f - 32.0f) * (5.0f / 9.0f); }
float ctof(float c) { return (c * 9.0f / 5.0f) + 32.0; }
float ctof(float c) { return (c * 9.0f / 5.0f) + 32.0f; }
AnovaPacket *AnovaCodec::clean_packet_() {
this->packet_.length = strlen((char *) this->packet_.data);
+1 -1
View File
@@ -39,7 +39,7 @@ enum AmbientLightGain : uint8_t {
};
static const uint8_t AMBIENT_LIGHT_GAIN_VALUES[] = {1, 3, 6, 9, 18};
class APDS9306 : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice {
class APDS9306 final : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice {
public:
void setup() override;
float get_setup_priority() const override { return setup_priority::BUS; }
+1 -1
View File
@@ -12,7 +12,7 @@
namespace esphome::apds9960 {
class APDS9960 : public PollingComponent, public i2c::I2CDevice {
class APDS9960 final : public PollingComponent, public i2c::I2CDevice {
#ifdef USE_SENSOR
SUB_SENSOR(red)
SUB_SENSOR(green)
+35 -7
View File
@@ -112,6 +112,23 @@ CONF_MAX_SEND_QUEUE = "max_send_queue"
CONF_STATE_SUBSCRIPTION_ONLY = "state_subscription_only"
def _register_provisioning_source(config: ConfigType) -> ConfigType:
"""Register the API as a provisioning source when encryption is enabled.
With no ``key`` the device boots unprovisioned and is set up on first
connection; a YAML ``key`` means it is born provisioned. Either way the API
drives the provisioning manager, so it counts as a source for `provisioning:`.
A hardcoded ``key`` is reported so `provisioning:` can warn about it.
"""
if (encryption := config.get(CONF_ENCRYPTION)) is not None:
from esphome.components import provisioning
provisioning.register_source("api")
if CONF_KEY in encryption:
provisioning.report_hardcoded_credentials("api")
return config
def validate_encryption_key(value):
value = cv.string_strict(value)
try:
@@ -300,21 +317,23 @@ CONFIG_SCHEMA = cv.All(
CONF_LISTEN_BACKLOG,
esp8266=1, # Limited RAM (~40KB free), LWIP raw sockets
esp32=4, # More RAM (520KB), BSD sockets
rp2040=1, # Limited RAM (264KB), LWIP raw sockets like ESP8266
rp2=1, # Limited RAM (264KB), LWIP raw sockets like ESP8266
bk72xx=4, # Moderate RAM, BSD-style sockets
rtl87xx=4, # Moderate RAM, BSD-style sockets
host=4, # Abundant resources
ln882x=4, # Moderate RAM
nrf52=4, # ~256KB RAM, BSD sockets
): cv.int_range(min=1, max=10),
cv.SplitDefault(
CONF_MAX_CONNECTIONS,
esp8266=4, # ~40KB free RAM, each connection uses ~500-1000 bytes
esp32=5, # 520KB RAM available
rp2040=4, # 264KB RAM but LWIP constraints
rp2=4, # 264KB RAM but LWIP constraints
bk72xx=5, # Moderate RAM
rtl87xx=5, # Moderate RAM
host=8, # Abundant resources
ln882x=5, # Moderate RAM
nrf52=4, # ~256KB RAM, BSD sockets, Thread (single HA controller)
): cv.int_range(min=1, max=20),
# Maximum queued send buffers per connection before dropping connection
# Each buffer uses ~8-12 bytes overhead plus actual message size
@@ -324,7 +343,7 @@ CONFIG_SCHEMA = cv.All(
CONF_MAX_SEND_QUEUE,
esp8266=4, # Limited RAM, need to fail fast
esp32=8, # More RAM, can buffer more
rp2040=8, # Moderate RAM
rp2=8, # Moderate RAM
bk72xx=8, # Moderate RAM
nrf52=8, # Moderate RAM
rtl87xx=8, # Moderate RAM
@@ -335,6 +354,7 @@ CONFIG_SCHEMA = cv.All(
).extend(cv.COMPONENT_SCHEMA),
cv.rename_key(CONF_SERVICES, CONF_ACTIONS),
_consume_api_sockets,
_register_provisioning_source,
)
@@ -468,8 +488,11 @@ async def to_code(config: ConfigType) -> None:
cg.add_define("USE_API_NOISE_PSK_FROM_YAML")
else:
# No key provided, but encryption desired
# This will allow a plaintext client to provide a noise key,
# send it to the device, and then switch to noise.
# Until a key is set, the device accepts both Noise connections
# using the well-known all-zeros PSK (preferred: the key travels
# encrypted, protecting against passive sniffing) and plaintext
# connections (deprecated, remove after 2027.2.0) so a client can
# provide a noise key and the device then switches to noise only.
# The key will be saved in flash and used for future connections
# and plaintext disabled. Only a factory reset can remove it.
cg.add_define("USE_API_PLAINTEXT")
@@ -538,17 +561,20 @@ HOMEASSISTANT_ACTION_ACTION_SCHEMA = cv.All(
)
# synchronous=False: when on_success/on_error is configured, play() stores the
# trigger args until the HomeassistantActionResponse arrives, so non-owning args
# (StringRef into the API receive buffer) must not be used.
@automation.register_action(
"homeassistant.action",
HomeAssistantServiceCallAction,
HOMEASSISTANT_ACTION_ACTION_SCHEMA,
synchronous=True,
synchronous=False,
)
@automation.register_action(
"homeassistant.service",
HomeAssistantServiceCallAction,
HOMEASSISTANT_ACTION_ACTION_SCHEMA,
synchronous=True,
synchronous=False,
)
async def homeassistant_service_to_code(
config: ConfigType,
@@ -642,6 +668,8 @@ HOMEASSISTANT_EVENT_ACTION_SCHEMA = cv.Schema(
)
# synchronous=True is safe here: the event schema has no on_success/on_error,
# so play() never stores the trigger args.
@automation.register_action(
"homeassistant.event",
HomeAssistantServiceCallAction,
+19
View File
@@ -158,6 +158,16 @@ message AuthenticationResponse {
bool invalid_password = 1;
}
// Reason a party is requesting the connection be closed.
enum DisconnectReason {
// No specific reason / not provided (default for older peers).
DISCONNECT_REASON_UNSPECIFIED = 0;
// The device's provisioning window has expired. The device must be reset
// (power-cycled) to reopen the provisioning window before it will accept a
// connection again.
DISCONNECT_REASON_PROVISIONING_CLOSED = 1;
}
// Request to close the connection.
// Can be sent by both the client and server
message DisconnectRequest {
@@ -166,6 +176,10 @@ message DisconnectRequest {
option (no_delay) = true;
// Do not close the connection before the acknowledgement arrives
// Optional reason the connection is being closed. Older peers that do not
// send this field will report DISCONNECT_REASON_UNSPECIFIED (0).
DisconnectReason reason = 1;
}
message DisconnectResponse {
@@ -296,6 +310,11 @@ message DeviceInfoResponse {
// Serial proxy instance metadata
repeated SerialProxyInfo serial_proxies = 25 [(field_ifdef) = "USE_SERIAL_PROXY", (fixed_array_size_define) = "SERIAL_PROXY_COUNT"];
// Device is unprovisioned and accepts Noise handshakes with the well-known
// all-zeros PSK, so the api encryption key can be provisioned without being
// sent in plaintext (protects against passive sniffing, not active MITM)
bool api_encryption_provisionable = 26 [(field_ifdef) = "USE_API_NOISE"];
}
message ListEntitiesRequest {
+79 -14
View File
@@ -25,6 +25,9 @@
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
#include "esphome/core/version.h"
#ifdef USE_PROVISIONING
#include "esphome/components/provisioning/provisioning.h"
#endif
#ifdef USE_DEEP_SLEEP
#include "esphome/components/deep_sleep/deep_sleep_component.h"
@@ -195,6 +198,29 @@ APIConnection::~APIConnection() {
#endif
}
#if defined(USE_API_NOISE) && defined(USE_API_PLAINTEXT)
void APIConnection::upgrade_helper_to_noise_() {
// The client opened with a Noise hello while this device has no encryption
// key set. Replace the plaintext helper with a Noise helper so the key can
// be provisioned over an encrypted channel: the noise context PSK is all
// zeros when unprovisioned, and NNpsk0 still runs a fresh ephemeral X25519
// exchange, so a passive listener cannot read the session. A publicly known
// PSK authenticates nobody; this protects against sniffing only.
auto *plaintext = static_cast<APIPlaintextFrameHelper *>(this->helper_.get());
uint8_t header[3];
uint8_t header_len = plaintext->get_consumed_header(header);
auto *noise = new APINoiseFrameHelper(plaintext->release_socket_for_switch(), this->parent_->get_noise_ctx());
// Carry over the peername-based client name (Hello has not arrived yet)
const char *name = plaintext->get_client_name();
noise->set_client_name(name, strlen(name));
this->helper_.reset(noise); // destroys the plaintext helper
APIError err = noise->init_from_handoff(header, header_len);
if (err != APIError::OK) {
this->fatal_error_with_log_(LOG_STR("Noise handoff failed"), err);
}
}
#endif // USE_API_NOISE && USE_API_PLAINTEXT
void APIConnection::destroy_active_iterator_() {
switch (this->active_iterator_) {
case ActiveIterator::LIST_ENTITIES:
@@ -253,6 +279,15 @@ void APIConnection::loop() {
// No more data available
break;
} else if (err != APIError::OK) {
#if defined(USE_API_NOISE) && defined(USE_API_PLAINTEXT)
// Checked inside the error branch to keep the hot err == OK path
// free of it; this can only fire on the first bytes of a plaintext
// helper on an unprovisioned device
if (err == APIError::PROTOCOL_SWITCH_TO_NOISE) {
this->upgrade_helper_to_noise_();
return;
}
#endif
this->fatal_error_with_log_(LOG_STR("Reading failed"), err);
return;
} else {
@@ -375,7 +410,7 @@ void APIConnection::finalize_iterator_sync_() {
void APIConnection::process_iterator_batch_(ComponentIterator &iterator) {
size_t initial_size = this->deferred_batch_.size();
size_t max_batch = this->get_max_batch_size_();
size_t max_batch = MAX_INITIAL_PER_BATCH;
while (!iterator.completed() && (this->deferred_batch_.size() - initial_size) < max_batch) {
iterator.advance();
}
@@ -418,16 +453,6 @@ uint16_t APIConnection::fill_and_encode_entity_info(EntityBase *entity, InfoResp
// Set common fields that are shared by all entity types
msg.key = entity->get_object_id_hash();
// API 1.14+ clients compute object_id client-side from the entity name
// For older clients, we must send object_id for backward compatibility
// See: https://github.com/esphome/backlog/issues/76
// TODO: Remove this backward compat code before 2026.7.0 - all clients should support API 1.14 by then
// Buffer must remain in scope until encode_to_buffer is called
char object_id_buf[OBJECT_ID_MAX_LEN];
if (!conn->client_supports_api_version(1, 14)) {
msg.object_id = entity->get_object_id_to(object_id_buf);
}
if (entity->has_own_name()) {
msg.name = entity->get_name();
}
@@ -1358,7 +1383,7 @@ void APIConnection::on_voice_assistant_set_configuration(const VoiceAssistantSet
#ifdef USE_ZWAVE_PROXY
void APIConnection::on_z_wave_proxy_frame(const ZWaveProxyFrame &msg) {
zwave_proxy::global_zwave_proxy->send_frame(msg.data, msg.data_len);
zwave_proxy::global_zwave_proxy->send_frame(this, msg.data, msg.data_len);
}
void APIConnection::on_z_wave_proxy_request(const ZWaveProxyRequest &msg) {
@@ -1734,6 +1759,19 @@ bool APIConnection::send_hello_response_(const HelloRequest &msg) {
resp.server_info = ESPHOME_VERSION_REF;
resp.name = StringRef(App.get_name());
#ifdef USE_PROVISIONING
if (provisioning::global_provisioning_manager != nullptr && provisioning::global_provisioning_manager->closed()) {
// The provisioning window has closed without the device being provisioned.
// Acknowledge the hello so the client can read the server name, then request
// disconnect with the reason. Authentication is intentionally not completed.
this->log_client_(ESPHOME_LOG_LEVEL_WARN, LOG_STR("Provisioning closed; rejecting connection"));
this->send_message(resp);
DisconnectRequest req;
req.reason = enums::DISCONNECT_REASON_PROVISIONING_CLOSED;
return this->send_message(req);
}
#endif
// Auto-authenticate - password auth was removed in ESPHome 2026.1.0
this->complete_authentication_();
@@ -1769,7 +1807,7 @@ bool APIConnection::send_device_info_response_() {
// Manufacturer string - define once, handle ESP8266 PROGMEM separately
#if defined(USE_ESP8266) || defined(USE_ESP32)
#define ESPHOME_MANUFACTURER "Espressif"
#elif defined(USE_RP2040)
#elif defined(USE_RP2)
#define ESPHOME_MANUFACTURER "Raspberry Pi"
#elif defined(USE_BK72XX)
#define ESPHOME_MANUFACTURER "Beken"
@@ -1854,6 +1892,12 @@ bool APIConnection::send_device_info_response_() {
#endif
#ifdef USE_API_NOISE
resp.api_encryption_supported = true;
#ifndef USE_API_NOISE_PSK_FROM_YAML
// No key from YAML: while no key is set, the key can be provisioned over a
// zero-PSK Noise connection. Gated on the YAML define (not the plaintext
// one) so this advertisement survives the plaintext removal in 2027.2.0.
resp.api_encryption_provisionable = !this->parent_->get_noise_ctx().has_psk();
#endif
#endif
#ifdef USE_DEVICES
size_t device_index = 0;
@@ -1884,7 +1928,8 @@ void APIConnection::on_hello_request(const HelloRequest &msg) {
this->on_fatal_error();
}
}
void APIConnection::on_disconnect_request() {
void APIConnection::on_disconnect_request(const DisconnectRequest & /*msg*/) {
// The reason is informational when a client disconnects us; we always ack and close.
if (!this->send_disconnect_response_()) {
this->on_fatal_error();
}
@@ -2012,6 +2057,15 @@ bool APIConnection::send_noise_encryption_set_key_response_(const NoiseEncryptio
NoiseEncryptionSetKeyResponse resp;
resp.success = false;
#ifdef USE_PROVISIONING
// Refuse to set a key once the provisioning window has closed (defense in depth;
// such connections are already rejected at hello).
if (provisioning::global_provisioning_manager != nullptr && provisioning::global_provisioning_manager->closed()) {
ESP_LOGW(TAG, "Provisioning closed; rejecting key set");
return this->send_message(resp);
}
#endif
psk_t psk{};
if (msg.key_len == 0) {
if (this->parent_->clear_noise_psk(true)) {
@@ -2021,10 +2075,21 @@ bool APIConnection::send_noise_encryption_set_key_response_(const NoiseEncryptio
}
} else if (base64_decode(msg.key, msg.key_len, psk.data(), psk.size()) != psk.size()) {
ESP_LOGW(TAG, "Invalid encryption key length");
} else if (APINoiseContext::is_all_zeros(psk)) {
// Accepting the reserved provisioning PSK would report success without
// enabling encryption (or silently clear an existing key)
ESP_LOGW(TAG, "Rejecting all-zero encryption key");
} else if (!this->parent_->save_noise_psk(psk, true)) {
ESP_LOGW(TAG, "Failed to save encryption key");
} else {
resp.success = true;
#ifdef USE_API_PLAINTEXT
if (this->helper_->frame_footer_size() == 0) {
// Plaintext transport has no frame footer; Noise always has the MAC footer.
// Remove after 2027.2.0 together with plaintext support on keyless devices.
ESP_LOGW(TAG, "Key received over plaintext; deprecated, will be removed in 2027.2.0");
}
#endif
}
return this->send_message(resp);
+17 -18
View File
@@ -18,8 +18,8 @@
#ifdef USE_ESP32_CRASH_HANDLER
#include "esphome/components/esp32/crash_handler.h"
#endif
#ifdef USE_RP2040_CRASH_HANDLER
#include "esphome/components/rp2040/crash_handler.h"
#ifdef USE_RP2_CRASH_HANDLER
#include "esphome/components/rp2/crash_handler.h"
#endif
#ifdef USE_ESP8266_CRASH_HANDLER
#include "esphome/components/esp8266/crash_handler.h"
@@ -43,10 +43,7 @@ class APIServer;
// Keepalive timeout in milliseconds
static constexpr uint32_t KEEPALIVE_TIMEOUT_MS = 60000;
// Maximum number of entities to process in a single batch during initial state/info sending
// API 1.14+ clients compute object_id client-side, so messages are smaller and we can fit more per batch
// TODO: Remove MAX_INITIAL_PER_BATCH_LEGACY before 2026.7.0 - all clients should support API 1.14 by then
static constexpr size_t MAX_INITIAL_PER_BATCH_LEGACY = 24; // For clients < API 1.14 (includes object_id)
static constexpr size_t MAX_INITIAL_PER_BATCH = 34; // For clients >= API 1.14 (no object_id)
static constexpr size_t MAX_INITIAL_PER_BATCH = 34;
// Verify MAX_MESSAGES_PER_BATCH (defined in api_frame_helper.h) can hold the initial batch
static_assert(MAX_MESSAGES_PER_BATCH >= MAX_INITIAL_PER_BATCH,
"MAX_MESSAGES_PER_BATCH must be >= MAX_INITIAL_PER_BATCH");
@@ -169,10 +166,14 @@ class APIConnection final : public APIServerConnectionBase {
#endif
bool try_send_log_message(int level, const char *tag, const char *line, size_t message_len);
#ifdef USE_API_HOMEASSISTANT_SERVICES
void send_homeassistant_action(const HomeassistantActionRequest &call) {
// Returns whether this client has subscribed to Home Assistant actions; the message
// is only handed to the send path when subscribed. A true return does not guarantee
// delivery - it lets the caller warn when no connected client has the subscription.
bool send_homeassistant_action(const HomeassistantActionRequest &call) {
if (!this->flags_.service_call_subscription)
return;
return false;
this->send_message(call);
return true;
}
#ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES
void on_homeassistant_action_response(const HomeassistantActionResponse &msg);
@@ -262,7 +263,7 @@ class APIConnection final : public APIServerConnectionBase {
void on_get_time_response(const GetTimeResponse &value);
#endif
void on_hello_request(const HelloRequest &msg);
void on_disconnect_request();
void on_disconnect_request(const DisconnectRequest &msg);
void on_ping_request();
void on_device_info_request();
void on_list_entities_request() { this->begin_iterator_(ActiveIterator::LIST_ENTITIES); }
@@ -282,8 +283,8 @@ class APIConnection final : public APIServerConnectionBase {
esp32::crash_handler_log();
esp32::crash_handler_clear();
#endif
#ifdef USE_RP2040_CRASH_HANDLER
rp2040::crash_handler_log();
#ifdef USE_RP2_CRASH_HANDLER
rp2::crash_handler_log();
#endif
#ifdef USE_ESP8266_CRASH_HANDLER
esp8266::crash_handler_log();
@@ -481,13 +482,6 @@ class APIConnection final : public APIServerConnectionBase {
inline bool check_voice_assistant_api_connection_() const;
#endif
// Get the max batch size based on client API version
// API 1.14+ clients don't receive object_id, so messages are smaller and more fit per batch
// TODO: Remove this method before 2026.7.0 and use MAX_INITIAL_PER_BATCH directly
size_t get_max_batch_size_() const {
return this->client_supports_api_version(1, 14) ? MAX_INITIAL_PER_BATCH : MAX_INITIAL_PER_BATCH_LEGACY;
}
// Send keepalive ping or disconnect unresponsive client.
// Cold path — extracted from loop() to reduce instruction cache pressure.
void __attribute__((noinline)) check_keepalive_(uint32_t now);
@@ -636,6 +630,11 @@ class APIConnection final : public APIServerConnectionBase {
void destroy_active_iterator_();
void begin_iterator_(ActiveIterator type);
void finalize_iterator_sync_();
#if defined(USE_API_NOISE) && defined(USE_API_PLAINTEXT)
// Swap the plaintext helper for a Noise helper after the client opened
// with a Noise hello on an unprovisioned device (zero-PSK provisioning).
void upgrade_helper_to_noise_();
#endif
#ifdef USE_CAMERA
std::unique_ptr<camera::CameraImageReader> image_reader_;
#endif

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