Compare commits

...
Author SHA1 Message Date
J. Nick Koston a50888d261 [dlms_meter] Key the slot requests by hub 2026-10-08 11:26:34 -10:00
J. Nick Koston 15d230debd [dlms_meter] Size entity storage with slot_counter 2026-10-08 11:23:26 -10:00
J. Nick Koston 3f21d68ae3 [dlms_meter] Spell out each removed key's settings and test the errors 2026-10-08 11:23:21 -10:00
J. Nick Koston 134ded3331 [dlms_meter] Remove deprecated predefined sensor keys 2026-10-08 10:56:59 -10:00
dependabot[bot] 542de8f03a Bump j178/prek-action from 3.0.0 to 3.0.1 (#20372)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-08 09:36:01 -10:00
dependabot[bot] 06864917a5 Bump filelock from 4.0.10 to 4.0.12 (#20371)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-08 19:31:14 +00:00
dependabot[bot] bcfe6fd1d2 Bump prek from 0.5.4 to 0.5.5 (#20370)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-08 19:30:04 +00:00
Kevin AhrendtandJ. Nick Koston e395e626ec [api][sendspin] Add a request for the Sendspin pairing token (#20355)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-08 19:12:20 +00:00
esphome[bot] f9b5eca2b9 Bump aioesphomeapi from 46.8.0 to 46.9.0 (#20358) 2026-10-08 18:48:19 +00:00
Kevin Ahrendt 920295527f [sendspin] Add an unpaired access switch (#20350) 2026-10-08 08:13:31 -10:00
Kevin Ahrendt 9c79fa7430 [sendspin] Add the dynamic pairing code (#20349) 2026-10-08 13:36:12 -04:00
J. Nick Koston 4b2f8122e3 [udp] Keep constant write payloads in shared flash tables (#20268) 2026-10-08 07:31:09 -10:00
J. Nick Koston 052f5a4cc6 [core] Declare component log tags with ESPHOME_LOG_TAG (#20291) 2026-10-08 07:24:17 -10:00
Kevin Ahrendt 78bf74eafb [sendspin] Add the static pairing code, pairing window and unpaired access option (#20348) 2026-10-08 07:19:14 -10:00
Kevin Ahrendt b1c145dd5f [sendspin] Update to sendspin-cpp v0.9.3 (#20347) 2026-10-08 06:33:46 -10:00
J. Nick Koston 3055343b6d [esp8266] Use toolchain 10.3.0-esphome.3, which trims newlib RAM (#20335) 2026-10-08 06:15:11 -10:00
J. Nick Koston 70f64374d4 [esp8266] Use toolchain 10.3.0-esphome.3, which trims newlib RAM (#20335) 2026-10-08 06:11:51 -10:00
J. Nick Koston 1362e52aa7 [esp8266] Fix Arduino core regressions that keep data in RAM and link the unused unwinder (#20337) 2026-10-08 06:09:39 -10:00
J. Nick Koston e51e0e683f [esp8266] Force-include the throw stubs into the core and libraries on the native toolchain (#20336) 2026-10-08 06:06:45 -10:00
J. Nick Koston 7910d02b71 [web_server] Emit the resource tables with cg.extern_progmem_array (#20300) 2026-10-08 11:34:34 -04:00
J. Nick Koston f224cb37d6 [esp32_ble_server] Cover string, typed number, repeated and trigger argument set_value payloads in the component test (#20308) 2026-10-08 11:32:50 -04:00
J. Nick Koston 79813a1eac [canbus] Keep constant send payloads in shared flash tables (#20309) 2026-10-08 11:14:02 -04:00
J. Nick Koston 9066ad6cd0 [modbus_client] Cover shared constant and trigger argument write values in the component test (#20327) 2026-10-08 11:13:03 -04:00
J. Nick Koston f5e4c0356f [core] Move zstd_module from the api wizard to esphome.helpers (#20342) 2026-10-08 11:08:38 -04:00
J. Nick Koston 3c634ecfd8 [noise] Bump noise-c to 0.1.31 and libsodium to 1.10021.12 (#20345) 2026-10-08 06:48:08 -04:00
Jesse HillsandJ. Nick Koston 57eeca6341 [combination] Use cv.rename_key for the deprecated coeffecient key (#20159)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-07 20:15:43 -10:00
esphome[bot] 23ba80c9c7 Bump bundled esphome-device-builder to 1.22.0 (#20334) 2026-10-07 20:03:30 -10:00
Jesse Hills 73bbd75d3f [core] Let cv.rename_key pass non-dict input through (#20331) 2026-10-08 17:29:56 +13:00
J. Nick Koston 0a7763d902 [esp8266] Normalize misspelled boards and fall back to PlatformIO for unknown ones (#20322) 2026-10-08 17:05:18 +13:00
J. Nick Koston 7aeed8455e [api] Add a progmem option for bytes fields kept in flash (#20318) 2026-10-08 16:24:27 +13:00
J. Nick Koston 8eba5a1c28 [core] Warn on every run that Intel Mac support ends by 2027.6.0 (#20303) 2026-10-08 16:23:01 +13:00
J. Nick Koston 0f552a8eef Pin smp to 4.1.0 on Intel macOS so cbor2 installs from a wheel (#20302) 2026-10-08 16:14:40 +13:00
David Buezas 31c488e5b0 [cc1101] Keep the remote_receiver interrupt on a shared GDO0 pin in begin_tx (#20296) 2026-10-07 22:53:12 -04:00
J. Nick Koston 1ee4940d4d [remote_base] Cover shared, empty and trigger argument abbwelcome payloads in the component test (#20307) 2026-10-07 16:43:44 -10:00
J. Nick Koston 640ef1bb17 [canbus] Cover shared, string and trigger argument send payloads in the component tests (#20306) 2026-10-07 16:43:34 -10:00
luar123 42edfe0681 [CI] Remove obsolete platform labels (#20301) 2026-10-07 22:18:47 -04:00
Keith BurzinskiandClaude Opus 5.5 6a6d263c0d [core] Wake the main loop on its own task notification index on ESP32 (#20326)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 21:17:16 -05:00
esphome[bot] 68a5e33182 Bump aioesphomeapi from 46.7.0 to 46.8.0 (#20321) 2026-10-08 00:17:23 +00:00
mrtoy-me 883af036a9 [tas58xx] Improve TAS5805M audio performance and use GPIO1 for TAS5825M faults (#20319) 2026-10-07 13:57:46 -10:00
esphome[bot] 8f003dfa3b Bump aioesphomeapi from 46.6.0 to 46.7.0 (#20317) 2026-10-07 13:31:24 -10:00
Jesse Hills 6d3792e5c5 [ci] Only run sync dependency versions workflow on PRs targeting dev (#20316) 2026-10-08 12:22:07 +13:00
Jesse Hills c73ec262e9 Merge branch 'beta' into dev 2026-10-08 12:15:20 +13:00
Jesse Hills 33cf262616 Merge pull request #20305 from esphome/bump-2026.10.0b1
2026.10.0b1
2026-10-08 12:14:49 +13:00
Jesse Hills 7a1bfed81f Bump version to 2026.11.0-dev 2026-10-08 12:01:52 +13:00
J. Nick Koston 12c03d539a [cc1101] Keep constant send_packet payloads in shared flash tables (#20264) 2026-10-07 10:54:56 -10:00
J. Nick Koston 24fc86c2c4 [core] Deprecate TemplatableValue::is_static_string and get_static_string (#20280) 2026-10-07 20:48:46 +00:00
Jesse Hills 5e4990badc Bump version to 2026.10.0b1 2026-10-08 09:27:43 +13:00
J. Nick Koston 16568c0453 [sx126x] Keep constant send_packet payloads in shared flash tables (#20273) 2026-10-07 10:24:14 -10:00
J. Nick Koston 677c089614 [sx127x] Keep constant send_packet payloads in shared flash tables (#20275) 2026-10-07 10:23:59 -10:00
J. Nick Koston 6db2e781ce [api] Keep Home Assistant action fields in a flash table (#20237) 2026-10-07 10:09:49 -10:00
J. Nick Koston 3296bccd3c [api] Keep the protobuf write cursor in a register and emit sub-message tags inline (#20124) 2026-10-07 10:08:25 -10:00
J. Nick Koston 0323fd36d4 [core] Clone git PlatformIO packages in parallel in the prefetch (#18837) 2026-10-08 09:00:07 +13:00
J. Nick Koston 1ba323e9a3 [core] Keep log tags in flash on ESP8266 with ESPHOME_LOG_TAG (#20290) 2026-10-07 09:51:17 -10:00
Jesse HillsandJ. Nick Koston 6f940a486b [api] Add a device wizard for Home Assistant onboarding (#19966)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-07 09:49:31 -10:00
dependabot[bot] 82cffe81f3 Bump platformdirs from 4.12.2 to 4.12.3 (#20297)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-07 19:30:59 +00:00
dependabot[bot] 887e3c302f Bump filelock from 4.0.9 to 4.0.10 (#20298)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-07 19:30:26 +00:00
TheSwert 2560a4436e [audio_http][core] Add ca_certificate_path to audio_http media source (#19392) 2026-10-07 09:28:10 -10:00
Clyde StubbsandClaude Sonnet 5 6dec759261 [core] Select I/O expander pin providers by I2C address (#18939)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-07 09:18:40 -10:00
1c0d406cbb [serial_proxy] Add USB_SERIAL port type and device identity (#19308)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2026-10-07 19:05:07 +00:00
Jesse Hills ae6ebd2d9d [epaper_spi] Add is_updating condition (#20261) 2026-10-07 14:52:25 -04:00
J. Nick Koston 13a8efd694 [api] Reply when an infrared/RF raw timings transmit has completed (#19086) 2026-10-07 08:25:44 -10:00
678975b6b2 [usb_host] iManufacturer and iProduct descriptor filters (#19095)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
2026-10-07 13:25:28 -05:00
006f5f0fc9 [xiaomi_mccgq02hl] Add Xiaomi Mijia Door/Window Sensor 2 (MCCGQ02HL) (#20104)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-07 07:09:35 -10:00
J. Nick Koston cf96c53279 [espnow] Keep constant send payloads in shared flash tables (#20271) 2026-10-07 05:40:12 -10:00
J. Nick Kostonandpre-commit-ci-lite[bot] 72413494b0 [esp32] Disable mbedTLS TLS and TLS-only crypto when no component needs them (#18877)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-10-07 05:29:17 -10:00
J. Nick Koston 51dbd3a63f [core] Test that if and while branches resume after delays, stops, nesting and script modes (#20282) 2026-10-07 08:33:20 -04:00
J. Nick Koston 19561d44a2 [cc1101] Cover shared, string and trigger argument send_packet payloads in the component test (#20263) 2026-10-07 08:32:18 -04:00
J. Nick Koston 1661f84deb [udp] Cover identical constant and trigger argument write payloads in the component test (#20266) 2026-10-07 08:32:04 -04:00
J. Nick Koston f0035c1db6 [sx127x] Cover shared constant, string and trigger argument send_packet payloads in the component test (#20274) 2026-10-07 08:31:24 -04:00
J. Nick Koston b78f00a037 [espnow] Cover a send payload lambda that reads trigger arguments in the component test (#20270) 2026-10-07 08:31:04 -04:00
J. Nick Koston 40809ffa10 [sx126x] Cover shared constant and trigger argument send_packet payloads in the component test (#20272) 2026-10-07 08:30:48 -04:00
luar123 e4d8913680 [zigbee] Add zigbee support for number on esp32 (#20243) 2026-10-07 08:21:17 -04:00
Bascht74 d234a4d51e [tcp_uart][uart_tcp] Add a disconnects sensor (#20096) 2026-10-06 20:45:29 -10:00
Raphael HehlandJ. Nick Koston 3cc89a35a2 [hoermann_hcp] Send each command once, and the lamp as a state (#19765)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-06 20:23:08 -10:00
Jonathan Swoboda 15aac378df [ci] Run label checks in parallel instead of cancelling them (#20259) 2026-10-06 20:19:41 -10:00
J. Nick Kostonandpre-commit-ci-lite[bot] 0e04b230fe [api] Add outgoing connections so the device can dial Home Assistant (or anything that implements native API) (#18919)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-10-07 16:22:14 +13:00
Guillaume RischardandClyde Stubbs f3f79c301b [mipi_spi] Add Waveshare ESP32-S3-Touch-LCD-1.46 SPD2010 model (#19056)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
2026-10-07 13:24:08 +11:00
Francesco Pace 923700dcc9 [espectre] Add Wi-Fi CSI motion detection component (#19801) 2026-10-06 19:46:06 -05:00
Clyde Stubbs 19dce8840d [ci] Ignore esphome/idf_component.yml in external component bot (#20258) 2026-10-07 11:19:44 +11:00
Jesse Hills e040647530 [homeassistant] Add text, select and button platforms (#19964) 2026-10-07 11:46:36 +13:00
Bascht74andJ. Nick Koston ab5a4b4f04 [uart_tcp] Claim the UART, reject duplicate server ports, pace on loop_interval (#20180)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-06 15:50:28 -06:00
dependabot[bot] cb1b4b0bd7 Update tzdata requirement from >=2026.4 to >=2026.5 (#20250)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-06 17:07:13 -04:00
dependabot[bot] 597a4e46b1 Bump pylint from 4.1.1 to 4.1.2 (#20249)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-06 17:06:37 -04:00
Clyde Stubbs e68a499429 [motion] Add gesture detection (#16787) 2026-10-06 15:57:20 -05:00
Jonathan Swoboda a56c85949a [wireguard] Bump the library to esphome/wireguard 0.4.8 (#20246) 2026-10-06 16:28:03 -04:00
J. Nick Koston 4eeb110247 [esp32] Extract prefetched ESP-IDF tool archives in parallel (#18841) 2026-10-07 09:26:46 +13:00
mrtoy-me 194273955c [tas58xx] Add support for tas5825 dac (#19971) 2026-10-06 15:22:00 -05:00
Clyde StubbsandClaude Opus 5.5 490d478e54 [mipi_spi] Light platform for AMOLED display brightness (#20212)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 07:02:34 +11:00
Keith BurzinskiandClaude Fable 5.1 1520d228bc [i2s_audio] Park the speaker data-out pin low at setup (#20226)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-06 11:43:49 -06:00
Bascht74 61ac0cb753 [tcp_uart] Keep received bytes readable after the connection drops (#20164) 2026-10-06 11:39:02 -06:00
dependabot[bot] 2b02602a49 Bump smpclient from 7.2.0 to 7.3.0 (#17443)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-06 11:37:14 -06:00
Bascht74andJ. Nick Koston cad670785b [socket] Add host tests for TcpClientLink and TcpListener (#20167)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-06 11:28:06 -06:00
Kevin Ahrendt 980fed9e96 [sendspin] Require a type on the switch platform (#20189) 2026-10-06 11:23:57 -06:00
Jonathan Swoboda f670993fe9 [ethernet] Pin the IDF version in the KSZ8851SNL test fixtures (#20245) 2026-10-06 11:40:53 -04:00
Jonathan Swoboda fb20966353 [wireguard] Restart the connection while the peer is offline so the endpoint is re-resolved (#20231) 2026-10-06 11:16:57 -04:00
Clyde Stubbs 74d0a90308 [esphome] Revise warning text for password use (#20230) 2026-10-06 11:11:25 -04:00
J. Nick Koston fef3804aa5 [espidf] Fix V1 ECDSA signed builds when skipping the bootloader (#20244) 2026-10-06 09:09:47 -06:00
luar123 e66ef45df7 [zigbee] add missing tests (#20234) 2026-10-06 11:08:01 -04:00
J. Nick Koston 1f53ccb572 [api] Cover Home Assistant action fields in the api test config (#20236) 2026-10-06 11:04:45 -04:00
b63794b53c [web_server_idf] Respect explicit HTTP header limit (#18657)
Co-authored-by: jeroen85 <26403565+jeroen85@users.noreply.github.com>
Co-authored-by: Jeroen Jansen <jeroen@Jeroens-MacBook-Air.local>
2026-10-06 11:00:50 -04:00
J. Nick Koston a69c648c6f [neopixelbus] Cover addressable_color_wipe with a random color on ESP8266 (#20221) 2026-10-06 09:04:52 -04:00
J. Nick Koston 29090d50a7 [event] Cover how configs and components read and set event types in the component test (#20216) 2026-10-06 09:04:37 -04:00
J. Nick Koston 9b1a75b0ed [fan] Cover preset mode reads and runtime sets in the component test (#20209) 2026-10-06 09:03:58 -04:00
J. Nick Koston b7ddd1daa2 [climate] Cover custom mode reads and runtime sets in the component test (#20208) 2026-10-06 09:03:20 -04:00
Bascht74 6002c28a8d [host] Ignore SIGPIPE so a write to a closed peer fails with EPIPE (#20228) 2026-10-06 22:38:16 +11:00
Bascht74 718b6651fe [ci] Run pylint when its version, dependencies or configuration change (#20202) 2026-10-05 21:20:28 -05:00
J. Nick Koston 6191b7553e [light] Cover get_effects() as published configs use it in the component test (#20203) 2026-10-05 21:07:37 -05:00
Bascht74 082a0b746a [lvgl] Remove redundant __aexit__ override (#20201) 2026-10-05 19:45:15 -05:00
J. Nick Koston 8d8bd73112 [ble_device_base] Keep MAC filter lists in shared flash tables (#20088) 2026-10-05 19:28:36 -05:00
J. Nick Koston 3a3707926d [wifi] Run the LN882x fallback AP on its own instead of retrying AP+STA every loop (#20166) 2026-10-05 19:25:14 -05:00
J. Nick Koston 80dc93d6ce [core] [select] Keep select options in flash with a shared ConstVector (#20117) 2026-10-05 19:24:31 -05:00
J. Nick Koston 40af90ad25 [logger] Use the static ring buffer structure as its handle on ESP32 (#20129) 2026-10-05 19:24:13 -05:00
J. Nick Koston 4097f45677 [sx1509] Keep the keypad keys in flash (#20111) 2026-10-05 19:23:39 -05:00
esphome[bot] 039f07d332 Bump bundled esphome-device-builder to 1.21.0 (#20199) 2026-10-05 22:10:00 +00:00
dependabot[bot] cdcc7bbf74 Bump ruff from 0.16.9 to 0.16.10 (#20196)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-05 22:07:49 +00:00
dependabot[bot]andJ. Nick Koston 9af9414dcc Bump cryptography from 48.0.1 to 50.0.2 (#20193)
Co-authored-by: J. Nick Koston <nick@koston.org>
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-05 22:07:43 +00:00
dependabot[bot] cf334e8758 Bump pylint from 4.0.10 to 4.1.1 (#20194)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-05 22:07:37 +00:00
dependabot[bot]andJ. Nick Koston 888043762e Bump filelock from 4.0.6 to 4.0.9 (#20195)
Co-authored-by: J. Nick Koston <nick@koston.org>
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-05 22:07:33 +00:00
dependabot[bot] 7af96c8d36 Bump CodSpeedHQ/action from 5.2.1 to 5.4.0 (#20197)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-05 22:07:10 +00:00
J. Nick Koston a778baddca [usb_uart] Only compile the vendor drivers that are configured (#20190) 2026-10-05 17:56:10 -04:00
Samuel Sieb fc441646f4 [unicode][font] move extract_unicode_codepoint to new component (#19969) 2026-10-05 17:40:06 +00:00
J. Nick Koston 4a47a984a0 [audio_file] Keep audio files in flash instead of building them in setup() (#20072) 2026-10-05 12:41:33 -04:00
J. Nick Koston 8735aee847 [wifi] Leave the AP before an LN882x reboots (#20176) 2026-10-05 10:56:24 -05:00
J. Nick Koston c8eaf518ba [libretiny] Flag the LN882H SDK default MAC (#20175) 2026-10-05 10:56:02 -05:00
J. Nick Koston bb8c23805a [wifi] Connect LN882x to the BSSID asked for (#20174) 2026-10-05 10:55:11 -05:00
J. Nick Koston f334b9ea11 [core] Decode base64 in a single pass (#20123) 2026-10-05 10:54:17 -05:00
J. Nick Koston 666cf169b9 [remote_base] Keep RC switch protocols in flash (#20081) 2026-10-05 10:54:03 -05:00
J. Nick Koston a058937fe0 [api] Count packed varints a machine word at a time (#20122) 2026-10-05 10:53:47 -05:00
J. Nick Koston 417fdd9dc7 [nrf52] Share the ccache between devices (#20028) 2026-10-05 10:53:35 -05:00
Sebastian Noack bc388b1de5 [epaper_spi] Add SSD1681 and the Waveshare 1.54in V2 panel (#18073) 2026-10-05 11:51:49 -04:00
Jonathan Swoboda 9820896430 [wireguard] Bump the library to esphome/wireguard 0.4.7 (#20188) 2026-10-05 11:34:07 -04:00
J. Nick Koston 4258b226a4 [wifi] Default LN882x power save to none, as on BK72xx and RTL87xx (#20170) 2026-10-05 10:26:22 -05:00
J. Nick Koston 5be0aca6d2 [libretiny] Build LN882x with 1-byte enums to match the SDK's WiFi library (#20173) 2026-10-05 10:24:19 -05:00
J. Nick Koston 1110857ffa [wifi] Keep ESP8266's report of a replaced attempt from failing the next one (#20183) 2026-10-05 10:21:52 -05:00
J. Nick Koston 9a97419cfc [ethernet] Store PHY registers in a StaticVector sized from the config (#20131) 2026-10-05 10:21:23 -05:00
ca74e71348 [emontx] Use total state class for energy sensors (#20128)
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-10-05 09:16:21 -05:00
Jonathan Swoboda 00303fb72b [esp32][api][noise] Resolve arduino's libsodium requirement to the converted esphome/libsodium (#20185) 2026-10-05 09:55:18 -04:00
Bascht74 aa02a0996a [socket][tcp_uart][uart_tcp] Reject an IPv6 host at config time (#20165) 2026-10-05 08:48:33 -05:00
Bascht74 59b49d34cc [tcp_uart][uart_tcp] Expose the no-arg load_settings() and mark UartTcp final (#20163) 2026-10-05 08:47:18 -05:00
Bascht74 862047386c [modbus] Reject send_wait_time and turnaround_time above 65535 ms (#20160) 2026-10-05 08:46:36 -05:00
Clyde Stubbs bbd05e4811 [esp32] Work around ESP-IDF stack bug on P4 (#20181) 2026-10-05 07:50:18 -05:00
J. Nick Koston a02fa71dc8 Merge pull request #20179 from esphome/sync/integration-durations 2026-10-05 07:48:03 -05:00
esphome[bot] 99fd1bca3e [ci] Refresh integration test durations 2026-10-05 05:58:43 +00:00
J. Nick Koston 2d73ea24ca [libretiny] Keep a scan that finds no networks from resetting the LN882x (#20169) 2026-10-04 21:23:09 -05:00
Max Slotov 645144ad4d [uart] Validate parity and stop bits correctly (#20161) 2026-10-05 13:21:48 +11:00
J. Nick Koston 88378ab354 [mipi_spi] Keep the init sequence in flash (#20089) 2026-10-04 21:21:28 -05:00
J. Nick Koston f91ac5ed1d [mipi_dsi] Keep the init sequence in flash (#20091) 2026-10-04 21:20:56 -05:00
J. Nick Koston 3b319240c6 [mipi_rgb] Keep the init sequence in flash (#20090) 2026-10-04 21:20:32 -05:00
Clyde StubbsandJ. Nick Koston d9b3f299e0 [exponential_moving_average] Add EMA preserved across reboots (#19882)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-05 13:19:12 +11:00
J. Nick Koston 6f2d2a2001 [libretiny] Fix LN882x boot crash with the logger or a uart on UART0 (#20162) 2026-10-05 14:53:47 +13:00
esphome[bot] 8b9d51cf93 Bump bundled esphome-device-builder to 1.20.0 (#20172)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-10-04 20:21:46 -05:00
J. Nick Koston 66eec7af15 [wifi] Keep a second fallback AP start from resetting the LN882x (#20168) 2026-10-05 13:58:35 +13:00
J. Nick Koston 60e511282a [udp] Keep addresses in a shared flash table (#20085) 2026-10-05 13:45:18 +13:00
J. Nick Koston e4be4c7fcb [select] Keep select.is option lists in flash (#20108) 2026-10-04 18:30:45 -05:00
J. Nick Koston 99bf080092 [select] Cover select.is in the component test (#20112) 2026-10-04 18:30:44 -05:00
J. Nick Koston 0017b9c439 [matrix_keypad] Keep the keys in flash (#20110) 2026-10-05 12:26:42 +13:00
J. Nick Koston ad1b6ab37e [web_server] Keep allowed origins in a shared flash table (#20086) 2026-10-05 12:23:36 +13:00
J. Nick Koston acf7257293 [core] Use the static queue structure as the FreeRTOSQueue handle (#20133) 2026-10-05 12:17:22 +13:00
J. Nick Koston b36c5441cb [esp32] Remove the unused loop_task_handle global (#20132) 2026-10-05 12:16:16 +13:00
J. Nick Koston feedea4954 [web_server] Remove dead ESP-IDF event source connect callback and pack expose_log_ (#20130) 2026-10-05 12:15:58 +13:00
J. Nick Koston 4f9cd7821a [core] Log ignored --skip-bootloader at debug level (#20059) 2026-10-05 12:14:42 +13:00
J. Nick Koston 31e0a31c65 [api] Skip the listen backlog setter when it matches the default (#20066) 2026-10-05 12:13:41 +13:00
J. Nick Koston e09cab94d4 [version] Skip the hide hash and hide timestamp setters when they match the defaults (#20067) 2026-10-05 12:13:01 +13:00
J. Nick Koston e708c85100 [uart] Skip the parity setter when it matches the default (#20069) 2026-10-05 12:12:34 +13:00
J. Nick Koston cfbbd13b86 [remote_receiver] Pack carrier_duty_percent_ next to with_dma_ (#20135) 2026-10-05 12:05:29 +13:00
J. Nick Koston 8f0e032b5e [esp32_ble] Group the one byte io_cap_ with the other byte fields (#20136) 2026-10-05 12:05:15 +13:00
J. Nick Koston 0dc10d9f30 [improv_ble] Pack the byte fields into the padding after connecting_sta_ (#20137) 2026-10-05 12:04:44 +13:00
J. Nick Koston c4aa8fa9c5 [wifi] Place ap_ before the byte fields so they pack after it (#20138) 2026-10-05 12:02:40 +13:00
J. Nick Koston 9b83f37b78 [core] Let the empty Scheduler mutex take no space on single-threaded platforms (#20139) 2026-10-05 12:02:27 +13:00
J. Nick Koston a1a4474a01 [script] Let the empty argument tuple take no space for scripts without parameters (#20140) 2026-10-05 12:02:24 +13:00
J. Nick Koston 3a06318b65 [template] Use distinct empty types so both unused select members take no space (#20141) 2026-10-05 12:02:18 +13:00
J. Nick Koston 2f9e37d6a9 [wifi] Let the empty pending_ flags struct take no space (#20142) 2026-10-05 12:00:23 +13:00
J. Nick Koston fe2e541dc8 [template] Pack restore_value_ next to optimistic_ in TemplateNumber (#20149) 2026-10-05 09:26:21 +13:00
J. Nick Koston 474c2791fe [ledc] Pack initialized_ next to bit_depth_ (#20150) 2026-10-05 09:25:35 +13:00
J. Nick Koston 7421b165e8 [rtttl] Pack state_ after wholenote_duration_ (#20151) 2026-10-05 09:25:28 +13:00
J. Nick Koston 52fc5f2c75 [ld2450] Pack direction_dedup_ after zone_config_ (#20152) 2026-10-05 09:25:25 +13:00
Jesse Hills 4f539d3391 [homeassistant] Use input_number.set_value for input_number entities (#19965) 2026-10-05 08:56:34 +13:00
guillempages aad7907c37 [http_request] Reduce log level for missing header (#18932) 2026-10-05 06:55:55 +11:00
J. Nick Koston fb8f679be9 [ring_buffer] Use the static ring buffer structure as its handle (#20134) 2026-10-04 15:40:57 -04:00
J. Nick Koston 84435e7d81 [sendspin] Pack mdns_advertised_ next to task_stack_in_psram_ (#20148) 2026-10-04 15:39:20 -04:00
J. Nick Koston d0473438d9 [i2s_audio] Pack pdm_ with the other microphone bools (#20145) 2026-10-04 15:38:54 -04:00
J. Nick Koston 2ee13d59ac [speaker] Pack mute_state_ next to state_ (#20143) 2026-10-04 15:38:32 -04:00
J. Nick Koston bccdbc23c4 [mixer] Pack has_contributed_ next to pause_state_ (#20144) 2026-10-04 15:38:12 -04:00
J. Nick Koston 4b5375145a [microphone] Move bits_per_sample_ to the end of MicrophoneSource (#20146) 2026-10-04 15:38:05 -04:00
J. Nick Koston c6e3a5a165 [voice_assistant] Group the one byte members at the end (#20147) 2026-10-04 15:37:12 -04:00
J. Nick Koston 63cb75726b [noise] Use the shared PROGMEM table helper for the PSK (#20101) 2026-10-04 13:06:05 -05:00
abderrahmen bejaoui b070c828b3 [es8311] Encode the REG02 MCLK pre-multiplier instead of writing the factor (#20121) 2026-10-04 11:37:03 +00:00
J. Nick Koston 2d56559f04 [lvgl] Reject ESP8266 at validation (#20099) 2026-10-03 06:36:33 -05:00
J. Nick Koston 8691a43879 [font] Keep the glyph table in flash (#20079) 2026-10-03 06:29:06 -05:00
J. Nick Koston f5c79eeec0 [qmi8658] Pin motion_id in test config to fix grouped component test conflict (#20075) 2026-10-02 19:29:34 -05:00
dependabot[bot] fcf8fe2c11 Bump pre-commit-ci/lite-action from 1.1.0 to 1.2.0 (#20057)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-02 14:38:02 -05:00
dependabot[bot] 4447f8a065 Bump platformdirs from 4.12.1 to 4.12.2 (#20054)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-02 14:37:45 -05:00
dependabot[bot] 911bd3dadb Bump pylint from 4.0.9 to 4.0.10 (#20056)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-02 14:37:34 -05:00
dependabot[bot] 03f264775f Bump filelock from 4.0.5 to 4.0.6 (#20055)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-02 14:37:16 -05:00
Bascht74andJ. Nick Koston fe5a155c13 [const] Hoist the allowed ips key to the shared constants (#20053)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-02 19:14:51 +00:00
25eaf1d025 [wifi] Start IPv6 link-local on connect, not on DHCPv4 lease (#19749)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-02 19:07:58 +00:00
Bascht74 5328e5f5e0 [tcp_uart] Add a server role with an IPv4 allow list (#20026) 2026-10-02 13:47:10 -05:00
de0a0b2af4 [ethernet] Fix IPv6 SLAAC: link-local on link-up, pass all-nodes multicast (#19752)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-02 13:46:16 -05:00
J. Nick Koston a5178a0f3d [bmp280_base][bmp3xx_base][bmp581_base][spa06_base][mlx90393] Use the sub_* helpers for optional child entities (#20051) 2026-10-02 14:33:03 -04:00
J. Nick Koston 483b9ebe2c [combination][libretiny][sound_level] Use the sub_* helpers for optional child entities (#20050) 2026-10-02 14:32:45 -04:00
J. Nick Koston 3cc4df4b3a [rd03d][sim800l][tas58xx][bedjet][hoermann_hcp] Use the sub_* helpers for optional child entities (#20049) 2026-10-02 14:32:26 -04:00
J. Nick Koston 8a49ba7e98 [iaqcore][t6615][hc8][as3935][tx20] Use the sub_* helpers for optional child entities (#20048) 2026-10-02 14:32:09 -04:00
J. Nick Koston b130c79267 [xiaomi_hhccpot002][xiaomi_miscale][xiaomi_wx08zm][radon_eye_rd200][am43] Use the sub_* helpers for optional child entities (#20047) 2026-10-02 14:32:00 -04:00
J. Nick Koston b0d5681022 [lc709203f][max17043][pulse_counter][pulse_meter][duty_time] Use the sub_* helpers for optional child entities (#20046) 2026-10-02 14:30:17 -04:00
J. Nick Koston b15bc083f6 [mcp9600][tem3200][kmeteriso][ufire_ec][ufire_ise] Use the sub_* helpers for optional child entities (#20045) 2026-10-02 14:30:01 -04:00
J. Nick Koston 690227f6d8 [honeywellabp][honeywellabp2_i2c][npi19][xgzp68xx][xdb401] Use the sub_* helpers for optional child entities (#20044) 2026-10-02 14:29:49 -04:00
J. Nick Koston 46b12b43b2 [ens210][htu31d][hyt271][honeywell_hih_i2c][ms8607] Use the sub_* helpers for optional child entities (#20043) 2026-10-02 14:29:27 -04:00
J. Nick Koston ba9d54b8ad [bmi160][mpu6050][mpu6886][mlx90614][max31855] Use the sub_* helpers for optional child entities (#20042) 2026-10-02 14:29:14 -04:00
J. Nick Koston 7169c0c930 [ags10][cm1106][sds011][pm1006][senseair] Use the sub_* helpers for optional child entities (#20041) 2026-10-02 14:28:59 -04:00
J. Nick Koston e3fcf173c2 [bmp085][dps310][lps22][mpl3115a2][ms5611] Use the sub_* helpers for optional child entities (#20040) 2026-10-02 14:28:44 -04:00
J. Nick Koston 6d33b0401d [nrf52] Use the shared managed ccache settings for the native build (#20027) 2026-10-02 13:28:40 -05:00
J. Nick Koston 78f39c7fee [hdc1080][hdc2010][hdc2080][hdc302x][hte501] Use the sub_* helpers for optional child entities (#20039) 2026-10-02 14:28:31 -04:00
J. Nick Koston 79ad67b56f [aht10][am2315c][am2320][dht][dht12] Use the sub_* helpers for optional child entities (#20038) 2026-10-02 14:28:18 -04:00
Bascht74andJ. Nick Koston b6c79fdc9a [socket] Share the server side as TcpListener (#20037)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-02 13:07:00 -05:00
J. Nick Koston cbe89c940d [atm90e32] Use the sub_* helpers for optional child entities (#20034) 2026-10-02 12:30:24 -04:00
J. Nick Koston 4f1be0fcb2 [shtcx] Use the sub_* helpers for optional child entities (#20033) 2026-10-02 12:29:58 -04:00
J. Nick Koston 25a30fc472 [mhz19] Use the sub_* helpers for optional child entities (#20032) 2026-10-02 12:29:37 -04:00
J. Nick Koston 2d286031a4 [sht3xd] Use the sub_* helpers for optional child entities (#20031) 2026-10-02 12:29:09 -04:00
J. Nick Koston 995109aa4f [es8388] Use the sub_* helpers for optional child entities (#20030) 2026-10-02 12:28:41 -04:00
Bascht74andJ. Nick Koston 5def4d46ca [socket] Add an IPv4 allow list (#20025)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-02 11:22:21 -05:00
J. Nick Koston a531ea07a2 [xiaomi_cgdk2] Use the sub_* helpers for optional child entities (#20020) 2026-10-02 12:11:02 -04:00
J. Nick Koston 984de58853 [xiaomi_mhoc401] Use the sub_* helpers for optional child entities (#20021) 2026-10-02 12:10:41 -04:00
J. Nick Koston 09ba060fd5 [xiaomi_xmwsdj04mmc] Use the sub_* helpers for optional child entities (#20022) 2026-10-02 12:10:15 -04:00
J. Nick Koston d0eddc8fc8 [xiaomi_cgpr1] Use the sub_* helpers for optional child entities (#20023) 2026-10-02 12:09:50 -04:00
J. Nick Koston bf176633b2 [xiaomi_mhoc303] Use the sub_* helpers for optional child entities (#20024) 2026-10-02 12:09:28 -04:00
26398747a1 [uart_tcp] Add a hardware UART copied to one TCP socket (#19887)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-02 10:23:29 -05:00
J. Nick Koston 6491eaeaae [tcp_uart] Report a failed flush when the flush drops the link (#20013) 2026-10-02 10:10:28 -05:00
J. Nick Koston 7096546d83 [nrf52] Create the precompiled header in ccache depend mode (#20019) 2026-10-02 15:07:13 +00:00
J. Nick Koston 1c31e9d28d [xiaomi_cgd1] Use the sub_* helpers for optional child entities (#20018) 2026-10-02 10:50:58 -04:00
J. Nick Koston f595d8677c [xiaomi_cgg1] Use the sub_* helpers for optional child entities (#20017) 2026-10-02 10:50:48 -04:00
J. Nick Koston 416d0c46ac [xiaomi_lywsdcgq] Use the sub_* helpers for optional child entities (#20016) 2026-10-02 10:50:37 -04:00
J. Nick Koston 97235a20b8 [xiaomi_lywsd02] Use the sub_* helpers for optional child entities (#20015) 2026-10-02 10:49:07 -04:00
J. Nick Koston 61446e3e4f [xiaomi_lywsd02mmc] Use the sub_* helpers for optional child entities (#20014) 2026-10-02 10:49:00 -04:00
1440998f3c [core] Add new options suspend_loop and loop_interval (#18136)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@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: J. Nick Koston <nick@home-assistant.io>
2026-10-02 09:09:14 -05:00
J. Nick Koston 4c9a44301d [zyaura] Use the sub_* helpers for optional child entities (#20008) 2026-10-02 09:07:52 -05:00
J. Nick Koston 54cb4c711a [mmc5983] Use the sub_* helpers for optional child entities (#20007) 2026-10-02 09:07:39 -05:00
J. Nick Koston 7b018b9dca [socket] Move the outgoing buffer into the TCP client link (#20010) 2026-10-02 09:07:35 -05:00
J. Nick Koston 65fef7d5d8 [cs5460a] Use the sub_* helpers for optional child entities (#20006) 2026-10-02 09:07:26 -05:00
J. Nick Koston 87d4bea24b [pm2005] Use the sub_* helpers for optional child entities (#20004) 2026-10-02 09:07:14 -05:00
J. Nick Koston d938bc9913 [ee895] Use the sub_* helpers for optional child entities (#20005) 2026-10-02 09:06:56 -05:00
Jonathan Swoboda a27c73b44e [wifi] Warn when eap is configured with esp32_hosted (#20011) 2026-10-02 09:38:20 -04:00
J. Nick Koston 0c0015ef88 [const] Move the role and reconnect interval keys to shared constants (#20009) 2026-10-02 08:35:07 -05:00
4724aea42e [tcp_uart] Add a TCP client that implements a UART (#19884)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-02 08:00:40 -05:00
J. Nick Koston d970e9292e [esphome][ota][core] Compress OTA uploads on ESP32, RP2040, LibreTiny and host (#19037) 2026-10-02 08:00:10 -05:00
J. Nick Koston 797682cccc [espidf] Skip the lwip sources that compile to empty objects (#20000) 2026-10-02 07:59:52 -05:00
J. Nick Koston a58f3d34aa [core] Compile util.cpp only on MQTT builds (#20003) 2026-10-02 07:59:36 -05:00
J. Nick Koston 3fd8f00e97 [socket] Compile the lookup and client link helpers only when required (#20002) 2026-10-02 07:31:41 -05:00
Jeff BrownandJ. Nick Koston 12b016e3d2 [substitutions] Support !include with a condition (#19455)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-02 07:27:29 -05:00
J. Nick Koston 8617368294 [espidf] Read the ESP-IDF version in process instead of starting idf_tools (#19998) 2026-10-01 19:48:00 -05:00
J. Nick Koston 6479e079d2 [espidf] Build all and size in one ninja run (#19997) 2026-10-01 19:39:18 -05:00
J. Nick Koston ff97353767 [espidf] Resolve the ESP-IDF tool paths once per build (#19996) 2026-10-01 19:39:06 -05:00
Bascht74andJ. Nick Koston b324a7c6e0 [socket] Add a reconnecting TCP client link (#19999)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-01 23:40:09 +00:00
99070187b8 [bluetooth_connection] Cancel pending Bluedroid opens on disconnect (#19859)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-01 21:15:23 +00:00
J. Nick Koston dff793a949 [espidf] Mirror registry components locally so configures skip the registry (#19949) 2026-10-01 15:20:27 -05:00
4075c15bea [stcc4] Add component for the Sensirion STCC4 CO2 sensor (#19434)
Co-authored-by: will <will-tm@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-01 15:17:48 -05:00
esphome[bot] 2d37c6f58b Bump bundled esphome-device-builder to 1.19.0 (#19995) 2026-10-01 14:51:43 -05:00
dependabot[bot] d49741dc10 Bump prek from 0.5.3 to 0.5.4 (#19993)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-01 14:48:16 -05:00
dependabot[bot] 8ac184228d Bump platformdirs from 4.12.0 to 4.12.1 (#19994)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-01 14:32:17 -05:00
dependabot[bot] 96c6120849 Bump zeroconf from 0.151.3 to 0.151.5 (#19992)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-01 14:32:05 -05:00
dependabot[bot] f7b4ea9f0e Bump filelock from 4.0.4 to 4.0.5 (#19991)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-01 14:31:37 -05:00
J. Nick Koston 7962b52d2a [ci] Skip the bootloader in the component batch build jobs (#19931) 2026-10-01 14:05:15 -05:00
bfb7e76b2e [light] Add color option to strobe and other color state (#19874)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-01 19:02:58 +00:00
J. Nick Koston 0ed997da92 [espidf] Add --skip-bootloader for OTA only builds (#19925) 2026-10-01 13:55:30 -05:00
J. Nick Koston 3ed1bc7e20 [esp32_ble_tracker] Guard Bluedroid against stale queued connection requests (#19375) 2026-10-01 13:50:46 -05:00
J. Nick Koston 5d4b436fc0 [mk2pvrouter] Fix an ignored qualifiers warning in the group length check (#19986) 2026-10-01 13:35:56 -05:00
J. Nick Koston 1346342250 [light] Fix a double promotion warning in the light test config (#19989) 2026-10-01 13:35:41 -05:00
J. Nick Koston 36bcce563a [sensor] Fix a double promotion warning in the NTC resistance filter (#19985) 2026-10-01 13:35:15 -05:00
J. Nick Koston e500ece4d5 [uart] Fix a maybe uninitialized warning in the fixed size read (#19988) 2026-10-01 13:35:01 -05:00
J. Nick Koston 2de4f4cf81 [speaker] Fix the fall through warning in the media player turn off command (#19987) 2026-10-01 13:34:43 -05:00
J. Nick Koston e9f0a53ec9 [display] Fix the null pointer comparison warning in the display log macro (#19984) 2026-10-01 13:34:17 -05:00
J. Nick Koston 3bae9e4ee2 [climate] Fix the restore state memset warning on every platform (#19983) 2026-10-01 13:33:50 -05:00
J. Nick KostonandJonathan Swoboda 788d2bc631 [ci] Balance component test batches by estimated build time (#19980)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-10-01 18:19:30 +00:00
Remco van Essen afba431600 [resampler] Add resampler microphone platform (#19953) 2026-10-01 13:15:13 -05:00
5e67258818 [noise] Import noise-c and libsodium as ESP-IDF managed components (#18351)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-01 12:59:00 -05:00
J. Nick Koston 879c5cfc5b [xiaomi_gcls002] Use the sub_* helpers for optional child entities (#19973) 2026-10-01 12:37:55 -04:00
J. Nick Koston b4cb164e5d [xiaomi_jqjcy01ym] Use the sub_* helpers for optional child entities (#19974) 2026-10-01 11:37:39 -05:00
J. Nick Koston af9c4cb778 [xiaomi_mjyd02yla] Use the sub_* helpers for optional child entities (#19975) 2026-10-01 12:37:12 -04:00
J. Nick Koston 852fb0c5a5 [midea] Use the sub_* helpers for optional child entities (#19976) 2026-10-01 12:37:09 -04:00
J. Nick Koston 9a855b4377 [ina260] Use the sub_* helpers for optional child entities (#19977) 2026-10-01 12:36:37 -04:00
eb423be9d2 [socket] Add an IPv4 lookup next to set_sockaddr (#19909)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-01 11:36:09 -05:00
J. Nick Koston 54f6fb0eb2 [espidf] Skip ldgen when only app code changes (#18857) 2026-10-01 11:25:48 -05:00
J. Nick Koston 826571518a [noise] Generate the responder ephemeral key ahead of the handshake (#19000) 2026-10-01 16:15:31 +00:00
56a7a11162 [openthread] rewrite openthread esp.cpp to use new esp functions (#16334)
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>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-01 12:48:31 +00:00
Jeff BrownandJ. Nick Koston 2accef437f [pmsa003i] Fix spurious component failures and warnings on power up (#19933)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-01 12:46:49 +00:00
tomaszduda23andClaude Sonnet 4.6 5a3e95b161 [nrf52] Add nRF Connect SDK 3.4.0 / Zephyr 4.x support (#19863)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-10-01 07:41:18 -05:00
J. Nick Koston 7e32770c5a [ci] Pin SOURCE_DATE_EPOCH in the idf.py equivalence check (#19970) 2026-10-01 12:37:42 +00:00
Panda 3dc608c772 [core] Parse ESPHOME_SERIAL_LOGGING_RESET as a boolean (#19963) 2026-10-01 06:56:32 -05:00
esphome[bot] 5a11f8d7c3 Bump bundled esphome-device-builder to 1.18.0 (#19968) 2026-10-01 04:58:06 +00:00
mtmatteo79 94ed17815c [bthome_mithermometer] Decode 0.1C temperature and 1% humidity objects (#19956) 2026-09-30 19:55:39 -04:00
J. Nick Koston 6f5d7475f0 [esp8266] Derive the native image header flash size from the linker script (#19961) 2026-10-01 00:50:40 +02:00
568c1ae494 [zigbee] Implement switch support for esp32 (#17579)
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>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-09-30 22:45:50 +00:00
Clyde StubbsandClaude Opus 5.5 cb47dfcf0e [light] Fix white flash on turn-on during an effect, start strobe on its first color (#19916)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 07:22:41 +10:00
Nate ClarkandClaude Opus 5.5 35474aae3d [mqtt] Stop disabling the mbedTLS hardware MPI accelerator for TLS (#19958)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 22:24:22 +02:00
dependabot[bot] ea5832a410 Bump pytest-mock from 3.15.1 to 3.16.0 (#19952)
Signed-off-by: dependabot[bot] <support@github.com>
2026-09-30 22:21:01 +02:00
Remco van Essen 1d69ddc09e [tas58xx] Add TAS58xx audio amplifier (TAS5805M) (#19595) 2026-09-30 20:19:23 +02:00
Keith Burzinski e742e64fb0 [serial_proxy] Never let a client write stall the main loop past a fixed budget (#19923) 2026-09-30 12:33:17 -05:00
Remco van Essen 52079dd3f3 [audio_dac] Add a hook that runs once the I2S clocks are running (#19900) 2026-09-30 09:45:15 -04:00
J. Nick Koston 3fac97867e [espidf] Resolve cmake through the IDF environment when reading the CMake cache (#19947) 2026-09-30 13:17:07 +02:00
Panda 6c43247395 [core] Ignore empty CLI address cache entries (#19924) 2026-09-30 12:41:52 +02:00
J. Nick Koston e8c5cd56a8 [xdb401] Fix format warnings in the timing and raw pressure logs (#19945) 2026-09-30 11:28:46 +02:00
J. Nick Koston 9208804707 [json] Fix format truncation warnings in the arena tests (#19938) 2026-09-30 09:10:20 +00:00
J. Nick Koston 199cc08270 [midea] Fix a format warning in the esp32 test config (#19946) 2026-09-30 05:00:04 -04:00
J. Nick Koston 6121bb3bcf [ufm01] Fix a format warning when logging the frame sync position (#19944) 2026-09-30 04:59:23 -04:00
J. Nick Koston c2d830ebd3 [pmwcs3] Use the sub_* helpers for optional child entities (#19943) 2026-09-30 04:58:45 -04:00
J. Nick Koston a706fa9e6b [gdk101] Use the sub_* helpers for optional child entities (#19942) 2026-09-30 04:58:30 -04:00
J. Nick Koston 08ed25833d [mmc5603] Use the sub_* helpers for optional child entities (#19941) 2026-09-30 04:58:14 -04:00
J. Nick Koston 3062e1f844 [smt100] Use the sub_* helpers for optional child entities (#19940) 2026-09-30 04:58:02 -04:00
J. Nick Koston 39f41656b9 [seeed_mr60bha2] Use the sub_* helpers for optional child entities (#19939) 2026-09-30 04:57:46 -04:00
J. Nick Koston 987618b49e [micro_wake_word] Fix a format warning when logging a model version mismatch (#19937) 2026-09-30 04:57:01 -04:00
J. Nick Koston e7f2159f0e [api] Fix format warnings in the api test config (#19936) 2026-09-30 04:56:37 -04:00
J. Nick Koston f327cbbcfe [dlms_meter] Fix a format warning when logging the receive timeout (#19935) 2026-09-30 04:56:15 -04:00
J. Nick Koston 3cccdca771 [modbus] Fix a format warning when logging the frame buffer size (#19934) 2026-09-30 04:55:50 -04:00
Clyde StubbsandClaude Opus 5.5 5baad05926 [lvgl] Use contextvars to improve lvgl scheduling (#19242)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 04:54:38 -04:00
J. Nick Koston e111f1130c [max9611] Use the sub_* helpers for optional child entities (#19930) 2026-09-30 04:14:51 -04:00
J. Nick Koston 4d534dc420 [uponor_smatrix] Use the sub_* helpers for optional child entities (#19929) 2026-09-30 04:14:40 -04:00
J. Nick Koston 0fd0f72550 [alpha3] Use the sub_* helpers for optional child entities (#19928) 2026-09-30 04:14:27 -04:00
J. Nick Koston 4a34315fc7 [ufm01] Use the sub_* helpers for optional child entities (#19927) 2026-09-30 04:14:11 -04:00
J. Nick Koston 4a00cebd61 [kuntze] Use the sub_* helpers for optional child entities (#19926) 2026-09-30 04:13:53 -04:00
cd0a0f3fca [zigbee] Add cluster: device_class to expose ESP32 sensors via measurement clusters instead of Analog/Binary Input (#17388)
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-09-29 21:48:08 -04:00
J. Nick Koston 23afdc1d9b [bme680] Use the sub_* helpers for optional child entities (#19917) 2026-09-29 21:32:45 -04:00
J. Nick Koston ef4d06e0ea [ld6002b] Use the sub_* helpers for optional child entities (#19918) 2026-09-29 21:32:13 -04:00
J. Nick Koston 83d689d944 [xiaomi_hhccjcy10] Use the sub_* helpers for optional child entities (#19919) 2026-09-29 21:32:02 -04:00
J. Nick Koston 203a7f70a3 [emc2101] Use the sub_* helpers for optional child entities (#19920) 2026-09-29 21:31:50 -04:00
J. Nick Koston 7d904b6330 [ens160_base] Use the sub_* helpers for optional child entities (#19921) 2026-09-29 21:31:39 -04:00
Jesse Hills ac1190094a [ethernet] Add KSZ8851SNL SPI ethernet support (#19883) 2026-09-30 13:48:10 +13:00
J. Nick Koston ed88002c37 [core] Extract native toolchain package archives in parallel (#18840) 2026-09-30 02:23:05 +02:00
Keith BurzinskiandClaude Fable 5.1 ef0819f983 [nfc] Hold NDEF records in a fixed-size list (#19912)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-30 01:12:55 +02:00
J. Nick Koston 7187acec04 [core] Add a line based yaml editor and use it for rename (#19410) 2026-09-30 11:15:14 +13:00
J. Nick Koston 25ddc94d5d [web_server_idf] Build state documents in a stack arena so a state event does no heap allocation (#19383) 2026-09-30 11:10:40 +13:00
J. Nick Koston 5502f620d4 [web_server_idf] Send events straight from the caller's buffers instead of a std::string (#19376) 2026-09-30 11:10:40 +13:00
guillempagesandJ. Nick Koston 99c7f9e625 [runtime_image] Improve format logs (#19903)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-09-29 23:28:28 +02:00
Clyde Stubbs 35d41adf86 [lvgl] Animation updates and debug borders option (#19420) 2026-09-30 07:20:13 +10:00
Keith BurzinskiandClaude Fable 5.1 662c7db222 [nfc] Pass tags to triggers by reference and keep the listener list in static storage (#19902)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-29 15:00:23 -05:00
dependabot[bot] 6b465443bc Bump filelock from 4.0.3 to 4.0.4 (#19905) 2026-09-29 21:48:38 +02:00
dependabot[bot] fc270cb90f Bump platformdirs from 4.11.14 to 4.12.0 (#19904) 2026-09-29 21:48:15 +02:00
J. Nick Koston 46be513a39 [core] Create the PlatformIO HTTP cache dir before the first registry lookup (#19906) 2026-09-29 19:34:38 +00:00
Jesse Hills cc6828f34f Merge branch 'release' into dev 2026-09-30 08:13:38 +13:00
Jesse Hills 0b2b16e664 Merge pull request #19872 from esphome/bump-2026.9.1
2026.9.1
2026-09-29 20:12:00 +01:00
Bascht74andJonathan Swoboda a43d3382b5 [socket] Let Zephyr clang-tidy see the BSD socket implementation (#19901)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-09-29 13:58:12 -04:00
J. Nick Koston b8ba8aa1a8 [espidf] Run cmake and ninja directly instead of through idf.py (#19871) 2026-09-29 19:55:22 +02:00
J. Nick Koston a0f933ea60 [htu21d] Use the sub_* helpers for optional child entities (#19894) 2026-09-29 13:54:55 -04:00
J. Nick Koston b6cd92e915 [hmc5883l] Use the sub_* helpers for optional child entities (#19893) 2026-09-29 13:54:52 -04:00
J. Nick Koston 4618c73b05 [ccs811] Use the sub_* helpers for optional child entities (#19895) 2026-09-29 13:54:33 -04:00
J. Nick Koston 1b86959ee0 [inkbird_ibsth1_mini] Use the sub_* helpers for optional child entities (#19896) 2026-09-29 13:54:11 -04:00
J. Nick Koston 1a9f43909a [mopeka_std_check] Use the sub_* helpers for optional child entities (#19897) 2026-09-29 13:53:59 -04:00
J. Nick Koston 55a7340d06 [esp8266] Make the native arduino toolchain the default (#19886) 2026-09-29 18:52:28 +02:00
J. Nick Koston b22f2e6b4c [core] Leave the precompiled header off on Windows for a GCC with bug 14940 (#19875) 2026-09-29 18:51:12 +02:00
J. Nick Koston a7e0afeb46 [esp8266] Use our own Arduino core builds for the native toolchain (#19876) 2026-09-29 18:38:51 +02:00
J. Nick Koston 5be41dec15 [esp8266] Use our own compiler builds for the native toolchain (#19849) 2026-09-29 18:31:24 +02:00
J. Nick Koston 5fdb73aceb [libretiny] Load the precompiled header on Apple Silicon Macs (#19857) 2026-09-29 18:26:40 +02:00
luar123 c1303c048b [esp32][rp2][esp8266][libretiny]mask gpio flags (1/3) (#19393) 2026-09-29 15:50:07 +00:00
n-IA-haneandn-IA-hane 627832f7ba [speaker_source] Account for playback callbacks during speaker writes (#19499)
Co-authored-by: n-IA-hane <n-IA-hane@users.noreply.github.com>
2026-09-29 10:36:37 -04:00
Christopher PruijsenandJonathan Swoboda 757a1c42f9 [dfrobot_sen0395] Fix output_latency treating milliseconds as seconds (#19873)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-09-29 09:03:19 -04:00
Roman Priesol 9b9d548ad5 [speaker] Skip a playlist item that fails instead of retrying it forever (#19862) 2026-09-29 07:56:34 -04:00
J. Nick Koston eb9aae04d0 [rp2] Bump arduino-pico framework to 6.1.1 (#19881) 2026-09-29 12:51:03 +02:00
54a9238425 [epaper_spi] reTerminal Sticky: Four-level grayscale for SSD1677 panels, and a full-refresh action (#19213)
Co-authored-by: Pierre <pierre@mysweethome.ch>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-09-29 19:38:01 +10:00
J. Nick Koston 21c9604918 [network] Keep lwip off the PSRAM tier when Ethernet is configured (#19367) 2026-09-29 01:56:17 -05:00
J. Nick Koston bf7aa1dc60 [sm300d2] Use the sub_* helpers for optional child entities (#19866) 2026-09-28 19:58:53 -04:00
J. Nick Koston 571cbb72eb [xiaomi_lywsd03mmc] Use the sub_* helpers for optional child entities (#19870) 2026-09-28 19:58:05 -04:00
J. Nick Koston 8032542076 [as5600] Use the sub_* helpers for optional child entities (#19869) 2026-09-28 19:57:51 -04:00
J. Nick Koston aba15f8ff6 [thermopro_ble] Use the sub_* helpers for optional child entities (#19868) 2026-09-28 19:57:37 -04:00
Jesse Hills 7a4ffa98f6 Bump version to 2026.9.1 2026-09-29 12:57:36 +13:00
J. Nick Koston eca4c2d0a5 [ethernet] Move received frames to PSRAM on SPI ethernet chips (#19377) 2026-09-29 12:57:35 +13:00
Suliman AbdulrazzaqandClyde Stubbs 43b6b41d16 [lvgl] Honor esphome build_flags when generating lv_conf.h (#19743)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
2026-09-29 12:57:35 +13:00
Clyde Stubbs b6d70d4e34 [mipi_spi] Fix reversion in dc/cs sequence for spi_16 (#19745) 2026-09-29 12:57:35 +13:00
tomaszduda23andJ. Nick Koston 4af2db7e8a [nrf52] set WDT timeout to 30sec for Adafruit bootloader (#19596)
Co-authored-by: J. Nick Koston <nick+github@koston.org>
2026-09-29 12:57:35 +13:00
J. Nick Koston 92059abf6c [web_server] Mask the value of a password text entity, not only its state (#19385) 2026-09-29 12:57:35 +13:00
Jonathan SwobodaandClaude Fable 5.1 e6d124d229 [esp32] Disable newlib nano printf formatting on ESP32-C2 (#19418)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-29 12:57:35 +13:00
J. Nick Koston 0575eb14f2 [esphome] Keep the OTA encryption key without the api server so safe mode uploads work (#19349) 2026-09-29 12:57:35 +13:00
J. Nick Koston c732903390 [esp32_hosted] Fix ESP32-P4 build without wifi, espnow or BLE (#19374) 2026-09-29 12:57:35 +13:00
J. Nick Koston eaee1cdd86 [espidf] Switch the Windows console to UTF-8 while idf.py runs (#19352) 2026-09-29 12:57:35 +13:00
J. Nick Koston 964a54f7a7 [esp32][rp2] Print the previous boot crash report before the logger reads it (#19351) 2026-09-29 12:57:34 +13:00
rexmoriarty 3e5797d153 [mixer] Don't discard a start request while reaping a stopped task (#19368) 2026-09-29 12:57:34 +13:00
J. Nick Koston 7e99301644 [ota] Skip the web_server plaintext warning when web_server ota is disabled (#19348) 2026-09-29 12:57:34 +13:00
08387ad289 [mipi_dsi] Let IDF pick the DPHY PLL reference clock (#18984)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
2026-09-29 12:57:34 +13:00
J. Nick Koston 3f06553023 [ltr501] Use the sub_* helpers for optional child entities (#19867) 2026-09-28 19:57:25 -04:00
Clyde Stubbs 2d6a7dae54 [mipi_rgb] Fix SWRESET delay for st7701s (#17646) 2026-09-29 09:40:39 +10:00
J. Nick Koston 30dc453dc1 [ethernet] Move received frames to PSRAM on SPI ethernet chips (#19377) 2026-09-29 10:30:51 +13:00
Jonathan Swoboda 89cc689864 [esp32_hosted][esp32_ble] Support esp_hosted 3.x alongside 2.x, keep builds on 2.12.13 for now (#19621) 2026-09-28 17:08:06 -04:00
Clyde Stubbs 13c27429a4 [split_buffer] Allow direct access to buffer chunks (#19819) 2026-09-29 06:30:29 +10:00
dependabot[bot]andesphome[bot] 8cbc18d040 Bump flake8 from 7.3.0 to 7.4.1 (#19850)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-09-28 15:43:38 -04:00
dependabot[bot]andesphome[bot] 728449f69d Bump ruff from 0.16.8 to 0.16.9 (#19851)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-09-28 15:32:15 -04:00
dependabot[bot] 7e7035db72 Bump github/codeql-action/analyze from 4.38.1 to 4.38.2 (#19856)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-28 15:31:53 -04:00
dependabot[bot] 45f8dbb4da Bump platformdirs from 4.11.12 to 4.11.14 (#19854)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-28 15:31:37 -04:00
dependabot[bot] eb97d15f2e Bump github/codeql-action/init from 4.38.1 to 4.38.2 (#19855)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-28 15:31:05 -04:00
dependabot[bot] 98754bedcd Bump pylint from 4.0.8 to 4.0.9 (#19853)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-28 15:30:51 -04:00
dependabot[bot] 9e8044868c Bump filelock from 4.0.1 to 4.0.3 (#19852)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-28 15:30:32 -04:00
J. Nick Koston fc803fcc6d [libretiny] Enable the PlatformIO precompiled header on Linux hosts (#19827) 2026-09-28 18:06:53 +00:00
esphome[bot] 025cf1f638 Bump bundled esphome-device-builder to 1.17.2 (#19848) 2026-09-28 19:50:10 +02:00
J. Nick Koston 45189ef445 [nrf52] Precompile the core headers on the sdk-nrf build (#19826) 2026-09-28 19:49:54 +02:00
J. Nick Koston d31ffd4ca3 [esp32] Enable the precompiled header on the PlatformIO toolchain (#19825) 2026-09-28 19:41:59 +02:00
J. Nick Koston 4ec8fae5df [rp2] Enable the PlatformIO precompiled header (#19824) 2026-09-28 19:26:51 +02:00
J. Nick Koston c52ce65d1c [esp8266] Precompile the prefix header on PlatformIO builds (#19823) 2026-09-28 19:19:46 +02:00
esphome[bot] 1f08ea68e6 [ci] Refresh integration test durations (#19847) 2026-09-28 17:12:41 +00:00
J. Nick Koston 6c2a4d3999 [host] Precompile the core headers on the native build (#19822) 2026-09-28 18:58:27 +02:00
J. Nick Koston 446c47f7f8 [esp8266] Precompile the prefix header on the native toolchain (#19821) 2026-09-28 18:51:44 +02:00
J. Nick Koston 787ee418b0 [esp32] Precompile the core headers for ESP-IDF source compiles (#19820) 2026-09-28 18:28:03 +02:00
tomaszduda23andJonathan Swoboda 45b9c8b94c [zephyr_mcumgr] add LED which is controled by bootloader (#16322)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-09-28 17:37:44 +02:00
tomaszduda23andJ. Nick Koston b4f2334b99 [nrf52] Use full newlib when the logger is enabled, disable unused serial and printk (#19766)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-09-28 16:57:00 +02:00
esphome[bot] 3fb4a9cb3a Bump bundled esphome-device-builder to 1.17.1 (#19838) 2026-09-28 14:34:33 +00:00
J. Nick Koston b56198f242 [tsl2591] Use the sub_* helpers for optional child entities (#19831) 2026-09-28 08:44:32 -04:00
J. Nick Koston 21d9aff769 [qmc5883l] Use the sub_* helpers for optional child entities (#19832) 2026-09-28 08:44:20 -04:00
J. Nick Koston b4b4000633 [wireguard] Use the sub_* helpers for optional child entities (#19833) 2026-09-28 08:44:07 -04:00
J. Nick Koston 9f7d7284a2 [pzemdc] Use the sub_* helpers for optional child entities (#19834) 2026-09-28 08:42:26 -04:00
J. Nick Koston 4c23564851 [hlw8032] Use the sub_* helpers for optional child entities (#19835) 2026-09-28 08:41:34 -04:00
PandaandJ. Nick Koston b8252f56ea [platformio] Fix ccache path for non-root containers (#19807)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-09-28 14:27:01 +02:00
Jesse HillsandJ. Nick Koston 32e11c4f4a [host] Build natively with ninja instead of PlatformIO (#19214)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-09-28 11:29:59 +02:00
Keith BurzinskiandClaude Fable 5.1 bb7af14012 [pn71xx] Remove heap allocation from tag handling and NCI transport (#19738)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-28 10:58:57 +02:00
Patrick bd69b17ef6 [mqtt] fix compiler warnings when idf_send_async is used (#19815) 2026-09-28 10:54:20 +02:00
a000ce90bc [ble_nus] Increase BLE MTU for NUS (#19629)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-09-28 10:53:45 +02:00
esphome[bot] 1183beb1fb [ci] Refresh integration test durations (#19817) 2026-09-28 09:18:19 +02:00
J. Nick Koston 8be570f369 [pzemac] Use the sub_* helpers for optional child entities (#19808) 2026-09-27 20:52:00 -04:00
J. Nick Koston b8fc2f2d43 [pzem004t] Use the sub_* helpers for optional child entities (#19809) 2026-09-27 20:51:49 -04:00
J. Nick Koston 561158bb51 [mopeka_pro_check] Use the sub_* helpers for optional child entities (#19810) 2026-09-27 20:51:37 -04:00
J. Nick Koston 084c6db721 [airthings_wave_base] Use the sub_* helpers for optional child entities (#19811) 2026-09-27 20:51:23 -04:00
J. Nick Koston 378edf5f44 [bme280_base] Use the sub_* helpers for optional child entities (#19812) 2026-09-27 20:51:11 -04:00
esphome[bot] 04b4e05e85 Bump bundled esphome-device-builder to 1.17.0 (#19814) 2026-09-28 00:28:04 +00:00
techdregs dd777bcecf [core] Share conversion rate configuration constant (#19800) 2026-09-28 02:24:42 +02:00
J. Nick Koston 2e927c98ca [airthings_wave_plus] Use the sub_* helpers for optional child entities (#19793) 2026-09-28 01:13:25 +02:00
J. Nick Koston 9b7f9b694e [hlw8012] Use the sub_* helpers for optional child entities (#19792) 2026-09-28 01:13:13 +02:00
J. Nick Koston f3e7603a2f [bl0942] Use the sub_* helpers for optional child entities (#19791) 2026-09-28 01:13:01 +02:00
J. Nick Koston a991fe6759 [xiaomi_hhccjcy01] Use the sub_* helpers for optional child entities (#19790) 2026-09-28 01:12:48 +02:00
Suliman AbdulrazzaqandClyde Stubbs 1303b29a6e [lvgl] Honor esphome build_flags when generating lv_conf.h (#19743)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
2026-09-27 23:12:34 +00:00
J. Nick Koston c6c6176e7e [bthome_mithermometer] Use the sub_* helpers for optional child entities (#19789) 2026-09-28 01:12:28 +02:00
J. Nick Koston ee458dc0d5 [sgp30] Use the sub_* helpers for optional child entities (#19786) 2026-09-28 01:12:15 +02:00
J. Nick Koston 6a217aeb6e [sdm_meter] Use the sub_* helpers for optional child entities (#19781) 2026-09-28 01:12:01 +02:00
J. Nick Koston a73ed08829 [havells_solar] Use the sub_* helpers for optional child entities (#19777) 2026-09-28 01:11:47 +02:00
J. Nick Koston 9bce95b69f [atm90e26] Use the sub_* helpers for optional child entities (#19764) 2026-09-28 01:11:35 +02:00
J. Nick Koston c7ab338364 [bl0939] Use the sub_* helpers for optional child entities (#19763) 2026-09-28 01:11:22 +02:00
J. Nick Koston a3065edb29 [sun_gtil2] Use the sub_* helpers for optional child entities (#19762) 2026-09-28 01:11:10 +02:00
J. Nick Koston f5204883c7 [ina2xx_base] Use the sub_* helpers for optional child entities (#19761) 2026-09-28 01:10:58 +02:00
J. Nick Koston 8e9ceb32b9 [ezo_pmp] Use the sub_* helpers for optional child entities (#19760) 2026-09-28 01:10:45 +02:00
J. Nick Koston 58437a539a [sps30] Use the sub_* helpers for optional child entities (#19759) 2026-09-28 01:10:32 +02:00
J. Nick Koston 6d9614f4ac [ruuvitag] Use the sub_* helpers for optional child entities (#19758) 2026-09-28 01:10:19 +02:00
J. Nick Koston fa8f8593af [sy6970] Use the sub_* helpers for optional child entities (#19757) 2026-09-28 01:10:07 +02:00
J. Nick Koston 1fc9a3aa7d [pmsx003] Use the sub_* helpers for optional child entities (#19756) 2026-09-28 01:10:01 +02:00
J. Nick Koston ded631e2b3 [cse7766] Use the sub_* helpers for optional child entities (#19782) 2026-09-28 01:09:33 +02:00
J. Nick Koston 023c81c5b4 [vbus] Use the sub_* helpers for optional child entities (#19755) 2026-09-28 01:09:17 +02:00
J. Nick Koston f728b8221c [ina226] Use the sub_* helpers for optional child entities (#19784) 2026-09-28 00:58:06 +02:00
J. Nick Koston 667290988a [ld2450] Use the sub_* helpers for optional child entities (#19775) 2026-09-28 00:57:50 +02:00
J. Nick Koston d0e1e026d8 [seeed_mr24hpc1] Use the sub_* helpers for optional child entities (#19798) 2026-09-28 00:57:43 +02:00
J. Nick Koston 5f518e70f4 [ld2410] Use the sub_* helpers for optional child entities (#19774) 2026-09-28 00:57:40 +02:00
J. Nick Koston 80cc64a5f2 [ina219] Use the sub_* helpers for optional child entities (#19785) 2026-09-28 00:57:25 +02:00
J. Nick Koston f8d2d1263d [bl0940] Use the sub_* helpers for optional child entities (#19783) 2026-09-28 00:57:18 +02:00
J. Nick Koston 934591058f [nrf52] Test that the mcumgr image is offered beside a UF2 (#19804) 2026-09-28 00:57:12 +02:00
J. Nick Koston 2bfd2bd4d7 [pvvx_mithermometer] Use the sub_* helpers for optional child entities (#19788) 2026-09-28 00:57:06 +02:00
J. Nick Koston 03da54c2f5 [bl0906] Use the sub_* helpers for optional child entities (#19796) 2026-09-28 00:57:01 +02:00
J. Nick Koston 87c8dc703a [seeed_mr60fda2] Use the sub_* helpers for optional child entities (#19799) 2026-09-28 00:56:47 +02:00
J. Nick Koston 5adae2c978 [veml7700] Use the sub_* helpers for optional child entities (#19778) 2026-09-28 00:56:40 +02:00
J. Nick Koston 82c58da1f2 [atc_mithermometer] Use the sub_* helpers for optional child entities (#19787) 2026-09-28 00:56:26 +02:00
J. Nick Koston 7d7c6177c0 [hm3301] Use the sub_* helpers for optional child entities (#19795) 2026-09-28 00:56:16 +02:00
J. Nick Koston 9fa14132f4 [ltr_als_ps] Use the sub_* helpers for optional child entities (#19780) 2026-09-28 00:56:07 +02:00
J. Nick Koston f2cd2ba449 [tcs34725] Use the sub_* helpers for optional child entities (#19797) 2026-09-28 00:55:58 +02:00
J. Nick Koston af13aa5555 [growatt_solar] Use the sub_* helpers for optional child entities (#19779) 2026-09-28 00:43:15 +02:00
J. Nick Koston 32c03f2079 [xiaomi_body_scale] Use the sub_* helpers for optional child entities (#19772) 2026-09-28 00:42:58 +02:00
J. Nick Koston 6072805950 [scd30] Use the sub_* helpers for optional child entities (#19794) 2026-09-28 00:42:44 +02:00
J. Nick Koston d34b09dd7d [ld2420] Use the sub_* helpers for optional child entities (#19776) 2026-09-28 00:09:15 +02:00
J. Nick Koston 7cda9a2666 [core][sensor][binary_sensor][text_sensor][button][switch][number][select][debug][xiaomi_rtcgq02lm][tfluna][ld2412] Bind sub entity config and parent once with sub_* helpers (#19754) 2026-09-27 19:19:10 +02:00
J. Nick Koston 1f175c1942 [nrf52] Fetch only the nRF Connect SDK projects a build needs (#19735) 2026-09-27 19:18:08 +02:00
tomaszduda23 a91023e87c [nrf52] allow to download mcumgr image if exists (#19769)
thanks
2026-09-27 16:14:37 +02:00
Clyde Stubbs 97f362a718 [mipi_spi] Fix reversion in dc/cs sequence for spi_16 (#19745) 2026-09-27 10:04:17 -04:00
Christoffer AnderssonandJ. Nick Koston b1d07759e2 [light] Resume the previous effect on off/on (#17791)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-09-27 12:42:36 +00:00
J. Nick Koston b9dfae50c1 [ci] Compile esp8266 component tests with the native toolchain (#19740) 2026-09-27 12:09:06 +02:00
J. Nick Koston 06d88e0381 [core][sensor][binary_sensor][text_sensor][debug][xiaomi_rtcgq02lm] Add new_sub_sensor, new_sub_binary_sensor and new_sub_text_sensor helpers (#19725) 2026-09-27 12:08:36 +02:00
J. Nick Koston a6d525a44e [core] Make the generated apply function a template argument of ApplyAction and ApplyCondition (#19655) 2026-09-27 12:07:55 +02:00
J. Nick Koston 2844236e77 [light] Shrink LightState by 12 bytes per light (#19711) 2026-09-27 11:53:03 +02:00
Pat Satyshurandclydebarrow ea4754e798 [template][time] Add template time platform (#14838)
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
2026-09-27 09:48:08 +00:00
Clyde Stubbs b8af6cb005 [counter] Add persistent counter sensor (#19746) 2026-09-27 18:25:54 +10:00
Raphael Hehl c26f2d28e6 [hoermann_hcp] Add a door state text sensor (#19747) 2026-09-27 07:48:36 +01:00
Keith BurzinskiandClaude Fable 5.1 514ee178f9 [pn532] Remove runtime heap allocation from the frame and tag paths (#19744)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-27 05:55:38 +01:00
Raphael HehlandJ. Nick Koston b619df886a [hoermann_hcp] Add the motor's serial number and firmware version as text sensors (#19543)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-09-27 05:30:30 +01:00
J. Nick Koston 89ce3ab98c [nrf52] Run analyze-memory on the sdk-nrf toolchain (#19730) 2026-09-27 05:05:26 +01:00
J. Nick Koston 5d415ad505 [nrf52] Stream the SDK clone and resume it after an interruption (#19731) 2026-09-27 05:04:41 +01:00
J. Nick Koston 2d17ad2c91 [pn532] Use register_apply_condition for pn532.is_writing (#19737) 2026-09-26 16:45:04 -05:00
CAT the Techandclydebarrow 7dd26ba9cc [lvgl] Ensure final animation frame completes before restart (#19642)
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
2026-09-27 07:41:34 +10:00
J. Nick Koston b1594348f1 [tfluna] Name the shared bus in the test fixture so grouping works (#19742) 2026-09-26 22:34:47 +01:00
J. Nick Koston e7574c1d9c [media_player] Name the player in the test automations so grouping works (#19741) 2026-09-26 22:17:14 +01:00
J. Nick Koston b5b6d0bacd [ci] Add the native ESP8266 compile smoke test (#19714) 2026-09-26 21:00:48 +01:00
Keith BurzinskiandClaude Opus 5.5 6c5010032b [pn532] Check InDataExchange status and fix tag handling (#19591)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 14:44:48 -05:00
J. Nick Koston 746691d5c2 [esp8266] Add native toolchain that builds the Arduino core without PlatformIO (#18539) 2026-09-26 20:44:09 +01:00
Keith BurzinskiandClaude Opus 5.5 93ec8d9d95 [pn71xx] Fix NCI transport and card emulation bugs; share code between PN7150 and PN7160 (#19589)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 14:24:45 -05:00
J. Nick Koston dea1200c35 [nrf52] Reboot straight into the app on a 2001 baud touch (#19727) 2026-09-26 20:08:45 +01:00
Paul Traina abd95c5154 [ld2410][mqtt][packages][security] Mark string-valued secrets cv.sensitive (#19387) 2026-09-26 18:43:06 +01:00
cdc77e2126 [xiaomi_body_scale] Add Xiaomi body scale S200 and S400 (#16476)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-09-26 16:11:50 +01:00
Frédéric MetrichandClaude Opus 5.5 04a53264b4 [mk2pvrouter] Add text_sensor platform (#19720)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 15:37:31 +01:00
Frédéric MetrichandClaude Opus 5.5 499ce56c2b [mk2pvrouter] Add binary_sensor platform (#19719)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 14:49:47 +01:00
Jonathan Swoboda 72ac32a2c4 [gree] Encode temperature as offset from 16°C (#19718) 2026-09-26 08:41:52 -04:00
Clyde Stubbs a7e0c0f8a2 [light][lvgl] Add color names for light states (#19712) 2026-09-26 21:19:38 +10:00
J. Nick Koston 1ea45be19e [const] Share CONF_HOST (#19716) 2026-09-26 07:12:05 -04:00
Clyde StubbsandClaude Sonnet 5 7d5117e570 [snapshot] Add feature to record animated gifs from screen (#19421)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-26 20:18:15 +10:00
J. Nick Koston 8132fbef52 [socket] Add outgoing connections to the raw lwIP TCP implementation (#18982) 2026-09-26 11:01:53 +01:00
esphome[bot] 9744058201 Bump bundled esphome-device-builder to 1.16.0 (#19710) 2026-09-26 02:14:12 +00:00
J. Nick Koston 1de5a58dbe [display] Use register_apply_condition for display.is_displaying_page (#19705) 2026-09-26 01:36:31 +00:00
d2c33b0f6f [systa_bus] Add systa_bus component (#15473)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-09-26 02:24:51 +01:00
J. Nick Koston 0d445fb3bc [router] Use register_apply_action for router.speaker.switch_output (#19695) 2026-09-26 02:16:32 +01:00
J. Nick Koston 76de6a672c [core] Render an id constant in an apply field as the object it names (#19694) 2026-09-26 02:16:01 +01:00
J. Nick Koston 2dc7a64e52 [it8951] Use register_apply_action for it8951.update (#19691) 2026-09-25 21:10:10 -04:00
J. Nick Koston a61746045b [core] Pass the copy flag to JsonString explicitly in the StringRef conversion (#19709) 2026-09-26 00:51:49 +00:00
J. Nick Koston a35299af0b [emontx] Use register_apply_action for emontx.send_command (#19687) 2026-09-26 01:34:52 +01:00
J. Nick Koston 3f2de92e5d [openthread] Use register_apply_action for openthread.set_poll_period (#19697) 2026-09-26 01:34:38 +01:00
J. Nick Koston b8c32854ed [bl0906] Use register_apply_action for bl0906.reset_energy (#19688) 2026-09-26 01:34:22 +01:00
J. Nick Koston 0b2e5bfbbe [pid] Use register_apply_action for climate.pid.autotune (#19708) 2026-09-25 20:34:16 -04:00
J. Nick Koston a1d8ea6072 [aic3204] Use register_apply_action for aic3204.set_auto_mute_mode (#19684) 2026-09-26 01:34:06 +01:00
J. Nick Koston 93fabe8b84 [alarm_control_panel] Use register_apply_condition for is_armed and ready (#19707) 2026-09-25 20:33:57 -04:00
J. Nick Koston 2fc48a3d9e [sun] Use register_apply_condition for is_above_horizon and is_below_horizon (#19704) 2026-09-25 20:33:50 -04:00
J. Nick Koston 1a158e5ba0 [mitsubishi_cn105] Use register_apply_action for set_remote_temperature and clear_remote_temperature (#19706) 2026-09-25 20:33:47 -04:00
J. Nick Koston 0569a1262f [voice_assistant] Use register_apply_condition for is_running and connected (#19703) 2026-09-25 20:33:36 -04:00
J. Nick Koston 0b5a1748f7 [tinyusb] Use register_apply_condition for tinyusb.is_mounted (#19702) 2026-09-25 20:33:25 -04:00
J. Nick Koston d71a6e662b [snapshot] Use register_apply_action for snapshot.take (#19696) 2026-09-25 20:33:21 -04:00
J. Nick Koston c8d3f43712 [dfplayer] Use register_apply_condition for dfplayer.is_playing (#19701) 2026-09-25 20:33:17 -04:00
J. Nick Koston 01a2aaef08 [motion] Use register_apply_action for motion.clear_calibration (#19700) 2026-09-25 20:32:57 -04:00
J. Nick Koston 447348d617 [lightwaverf] Use register_apply_action for lightwaverf.send_raw (#19698) 2026-09-25 20:32:53 -04:00
J. Nick Koston c902083ef3 [dfrobot_sen0395] Use register_apply_action for dfrobot_sen0395.reset (#19693) 2026-09-25 20:31:49 -04:00
J. Nick Koston d1dd4e53ce [zephyr_ble_server] Use register_apply_action for ble_server.numeric_comparison_reply (#19692) 2026-09-25 20:31:41 -04:00
J. Nick Koston c0c1643efe [zigbee] Use register_apply_action for zigbee.factory_reset (#19690) 2026-09-25 20:31:05 -04:00
J. Nick Koston 532a6ed239 [mixer] Use register_apply_action for mixer_speaker.apply_ducking (#19689) 2026-09-25 20:30:56 -04:00
J. Nick Koston 3a9048cc62 [speaker_source] Use register_apply_action for speaker_source.set_playlist_delay (#19686) 2026-09-25 20:30:16 -04:00
J. Nick Koston d34b21cb7e [pipsolar] Use register_apply_action for output.pipsolar.set_level (#19685) 2026-09-25 20:30:12 -04:00
J. Nick Koston 5043e12690 [cs5460a] Use register_apply_action for cs5460a.restart (#19683) 2026-09-25 20:29:14 -04:00
J. Nick Koston 3e85af29c6 [hc8] Use register_apply_action for hc8.calibrate (#19681) 2026-09-25 20:29:04 -04:00
J. Nick Koston 1889f6c9b5 [max17043] Use register_apply_action for max17043.sleep_mode (#19682) 2026-09-25 20:28:58 -04:00
J. Nick Koston 2f4eb3e1ee [audio_adc] Use register_apply_action for audio_adc.set_mic_gain (#19679) 2026-09-25 20:28:40 -04:00
Boris Krivonog bcae1f28ef [web_server] Expose temperature units for climate and water heater entities (#19572) 2026-09-26 01:11:25 +01:00
87b10d70a5 [mk2pvrouter] Add tag-based sensor defaults (#18870)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-09-26 01:05:06 +01:00
cfb7d8089e [remote_base] Add AEG Hob2Hood Infrared Protocol (#4687)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-09-26 00:42:21 +01:00
bfe1f7bd17 [pzem6l24] Add PZEM-6L24 three-phase energy monitor sensor (#17849)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-09-25 23:03:14 +00:00
d5d0901e2c [tfluna] New component for the TF-Luna Low Cost LiDAR Distance Sensor (#9199)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick+github@koston.org>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-09-25 23:55:14 +01:00
64388318e8 [light] Replace restore_mode with restore_state (#18997)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-09-26 08:47:25 +10:00
J. Nick Koston e83dc25301 [esp_ldo] Use register_apply_action for esp_ldo.voltage.adjust (#19680) 2026-09-26 06:28:18 +10:00
J. Nick Koston a81cf8f5cc [ufire_ec] Use register_apply_action for calibrate_probe and reset (#19675) 2026-09-25 09:58:52 -10:00
J. Nick Koston 6925c5127e [ags10] Use register_apply_action for new_i2c_address and set_zero_point (#19676) 2026-09-25 19:58:34 +00:00
J. Nick Koston d96d0b80f7 [cm1106] Use register_apply_action for cm1106.calibrate_zero (#19678) 2026-09-25 19:56:49 +00:00
J. Nick Koston 27ea1a90e3 [hub75] Use register_apply_action for hub75.set_brightness (#19677) 2026-09-25 20:56:31 +01:00
J. Nick Koston 4f47913745 [rx8130] Use register_apply_action for read_time and write_time (#19674) 2026-09-25 20:55:21 +01:00
J. Nick Koston 447a30da0f [mcp4461] Use register_apply_action for the four wiper actions (#19669) 2026-09-25 15:41:54 -04:00
J. Nick Koston d8696c4f28 [ufire_ise] Use register_apply_action for calibrate_probe_low, calibrate_probe_high and reset (#19670) 2026-09-25 15:41:23 -04:00
J. Nick Koston 0ef68c17fd [uart_mux] Use the apply helpers for select_local, select_bridge and is_local (#19671) 2026-09-25 15:41:18 -04:00
J. Nick Koston aef3a586de [max6956] Use register_apply_action for set_brightness_global and set_brightness_mode (#19672) 2026-09-25 19:40:48 +00:00
J. Nick Koston 5aeab5f361 [hdc302x] Use register_apply_action for heater_on and heater_off (#19673) 2026-09-25 15:40:42 -04:00
dependabot[bot] 9654dd3912 Bump platformdirs from 4.11.11 to 4.11.12 (#19668)
Signed-off-by: dependabot[bot] <support@github.com>
2026-09-25 19:34:38 +00:00
J. Nick Koston 6622e1fe64 [grove_tb6612fng] Use register_apply_action for the six motor actions (#19664) 2026-09-25 19:28:12 +00:00
J. Nick Koston 0c548f77fa [bm8563] Use register_apply_action for read_time, write_time and start_timer (#19665) 2026-09-25 20:24:57 +01:00
J. Nick Koston dfd9d2639f [ezo_pmp] Use register_apply_action for the twelve ezo_pmp actions (#19663) 2026-09-25 15:21:23 -04:00
J. Nick Koston cf9d807bf0 [core] Make Action::stop_complex and Action::is_running non-virtual (#19662) 2026-09-25 19:20:57 +00:00
J. Nick Koston 29512cf323 [pmwcs3] Use register_apply_action for the calibration and address actions (#19666) 2026-09-25 15:20:46 -04:00
J. Nick Koston 456e13adee [ld2410] Use register_apply_action for bluetooth_password.set (#19667) 2026-09-25 19:20:44 +00:00
tronikosandJ. Nick Koston a1b0e85a9e [core] Report unknown for switch, climate and water heater with no state (#19458)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-09-25 19:18:32 +00:00
J. Nick Koston 14bd50d469 [sound_level] Use register_apply_action for sound_level.start and sound_level.stop (#19656) 2026-09-25 18:56:32 +00:00
J. Nick Koston 1edc327835 [cc1101] Use register_apply_action for begin_tx, begin_rx, reset and set_idle (#19661) 2026-09-25 14:56:06 -04:00
J. Nick Koston 288a7b376a [htu21d] Use register_apply_action for htu21d.set_heater and htu21d.set_heater_level (#19658) 2026-09-25 14:52:16 -04:00
J. Nick Koston c16f75f0e2 [pcf85063] Use register_apply_action for pcf85063.write_time and pcf85063.read_time (#19659) 2026-09-25 18:51:41 +00:00
J. Nick Koston 432cae58ae [nau7802] Use register_apply_action for the three calibration actions (#19657) 2026-09-25 18:51:14 +00:00
J. Nick Koston 11c8e8c0c0 [scd30] Use register_apply_action for scd30.force_recalibration_with_reference (#19652) 2026-09-25 14:32:05 -04:00
J. Nick Koston 6a115e6b25 [sen5x] Use register_apply_action for sen5x.start_fan_autoclean (#19653) 2026-09-25 14:31:39 -04:00
J. Nick Koston 9c2616ef7e [pzemdc] Use register_apply_action for pzemdc.reset_energy (#19651) 2026-09-25 14:31:11 -04:00
J. Nick Koston 9ae83f2830 [hbridge] Use register_apply_action for fan.hbridge.brake (#19649) 2026-09-25 14:30:30 -04:00
J. Nick Koston 340e105a76 [pzemac] Use register_apply_action for pzemac.reset_energy (#19650) 2026-09-25 14:28:01 -04:00
J. Nick Koston 857a519946 [sim800l] Use register_apply_action for the five sim800l actions (#19645) 2026-09-25 19:12:51 +01:00
J. Nick Koston 15d756d559 [sps30] Use register_apply_action for the three sps30 actions (#19644) 2026-09-25 18:12:35 +00:00
J. Nick Koston 22035cb9f5 [tm1651] Use register_apply_action for the five tm1651 actions (#19647) 2026-09-25 19:12:27 +01:00
J. Nick Koston 32624e248c [fingerprint_grow] Use register_apply_action for the six fingerprint_grow actions (#19640) 2026-09-25 19:11:51 +01:00
J. Nick Koston ad6996a63c [key_collector] Use register_apply_action for key_collector.enable and key_collector.disable (#19646) 2026-09-25 19:11:05 +01:00
J. Nick Koston 262ade0598 [senseair] Use register_apply_action for the five senseair actions (#19643) 2026-09-25 14:09:37 -04:00
J. Nick Koston 2157ff5b4b [ds1307] Use register_apply_action for ds1307.write_time and ds1307.read_time (#19638) 2026-09-25 14:09:26 -04:00
esphome[bot] 63796e74bb Bump aioesphomeapi from 46.5.0 to 46.6.0 (#19648) 2026-09-25 19:05:26 +01:00
J. Nick Koston a088eff428 [binary_sensor] Use register_apply_action for binary_sensor.invalidate_state (#19637) 2026-09-25 17:53:02 +00:00
J. Nick Koston 0cb421f793 [max7219digit] Use register_apply_action for the seven max7219digit actions (#19641) 2026-09-25 13:45:38 -04:00
J. Nick Koston 23c317f163 [pcf8563] Use register_apply_action for pcf8563.write_time and pcf8563.read_time (#19639) 2026-09-25 18:43:10 +01:00
J. Nick Koston 11e016be59 [sx126x] Use register_apply_action for the five mode and calibration actions (#19635) 2026-09-25 13:16:06 -04:00
J. Nick Koston bdb492c196 [sx127x] Use register_apply_action for the five mode and calibration actions (#19636) 2026-09-25 13:15:13 -04:00
J. Nick Koston d6831c8c75 [duty_time] Use the apply helpers for the start, stop, reset and is_running automations (#19634) 2026-09-25 18:13:46 +01:00
J. Nick Koston c73a71e037 [animation] Use register_apply_action for next_frame, prev_frame and set_frame (#19633) 2026-09-25 13:08:18 -04:00
J. Nick Koston 4bb6c0f877 [update] Use the apply helpers for update.check and update.is_available (#19632) 2026-09-25 13:07:48 -04:00
J. Nick Koston eb85778a60 [mhz19] Use register_apply_action for the four mhz19 actions (#19631) 2026-09-25 13:07:15 -04:00
J. Nick Koston c561c6bbe8 [scd4x] Use register_apply_action for perform_forced_calibration and factory_reset (#19630) 2026-09-25 13:02:58 -04:00
J. Nick Koston 7294da9f49 [integration] Use register_apply_action for sensor.integration.reset and set_value (#19628) 2026-09-25 12:34:39 -04:00
J. Nick Koston 96d0d677b6 [alarm_control_panel] Use register_apply_action for pending and triggered, drop the chime and ready actions that had no class (#19627) 2026-09-25 12:33:35 -04:00
J. Nick Koston c598d2dee5 [display_menu_base] Use the apply helpers for the eight display_menu actions and is_active (#19626) 2026-09-25 12:32:46 -04:00
J. Nick Koston 73548b0f15 [midea] Use register_apply_action for the eight midea_ac actions (#19625) 2026-09-25 12:32:16 -04:00
J. Nick Koston 9a9169305e [haier] Use register_apply_action for the thirteen climate.haier actions (#19624) 2026-09-25 12:31:34 -04:00
J. Nick Koston 54d05180b7 [duty_time] Use one import style for the automation module (#19622) 2026-09-25 12:30:10 -04:00
J. Nick Koston 1dcf0a0f9d [valve] Use register_apply_action for valve.open, valve.close, valve.stop and valve.toggle (#19618) 2026-09-25 17:00:33 +01:00
J. Nick Koston bb84409261 [sprinkler] Use register_apply_action for the seven keyless controller actions (#19617) 2026-09-25 11:58:56 -04:00
J. Nick Koston 044c4cb9bb [core] Migrate trivial action and condition builders to the shared helpers (#19322) 2026-09-25 16:46:12 +01:00
J. Nick Koston d346835e79 [wireguard] Use the apply helpers for the enable, disable, enabled and peer_online automations (#19616) 2026-09-25 11:04:51 -04:00
J. Nick Koston db566b25d0 [microphone] Use the apply helpers for the capture, mute and condition automations (#19619) 2026-09-25 11:04:14 -04:00
J. Nick Koston 45bcff2c2c [micro_wake_word] Use the apply helpers for the start, stop and model automations (#19620) 2026-09-25 11:03:32 -04:00
J. Nick Koston af4a8dac8e [esp32] Keep the NVS cache in PSRAM when PSRAM is guaranteed (#19378) 2026-09-25 15:56:01 +01:00
J. Nick Koston d3fd124c59 [core] Lint against std::nothrow, use RAMAllocator instead (#19244) 2026-09-25 15:50:32 +01:00
J. Nick Koston 2d8e43ef78 [ota] Add allow_plaintext_upload for devices that never migrated to encryption (#19396) 2026-09-25 15:42:52 +01:00
J. Nick Koston 370d76d013 [deep_sleep] Use register_apply_action for deep_sleep.prevent and deep_sleep.allow (#19607) 2026-09-25 10:26:57 -04:00
J. Nick Koston 3fb48e0bfa [script] Use register_apply_action for script.stop (#19599) 2026-09-25 10:23:33 -04:00
J. Nick Koston a17285afc7 [fan] Use register_apply_action for fan.toggle and fan.turn_off (#19600) 2026-09-25 10:23:03 -04:00
J. Nick Koston f7dd9c21c5 [lock] Use register_apply_action for lock.lock, lock.unlock and lock.open (#19601) 2026-09-25 10:21:47 -04:00
J. Nick Koston 898ab7c701 [safe_mode] Use register_apply_action for safe_mode.mark_successful (#19605) 2026-09-25 10:21:18 -04:00
J. Nick Koston 28343e1bc9 [mqtt] Use register_apply_action for mqtt.publish, mqtt.enable and mqtt.disable (#19602) 2026-09-25 10:20:13 -04:00
J. Nick Koston 6fa56e18bd [sendspin] Use register_apply_action for the switch, static delay and transition_finished actions (#19608) 2026-09-25 10:18:52 -04:00
luar123 cc5ed93247 [zigbee] move init back into setup (#19427) 2026-09-25 09:29:16 +01:00
J. Nick Koston 91210c5ef5 [button] Use register_apply_action for button.press (#19598) 2026-09-25 03:37:03 -04:00
tomaszduda23andJ. Nick Koston 8db901cbfc [nrf52] set WDT timeout to 30sec for Adafruit bootloader (#19596)
Co-authored-by: J. Nick Koston <nick+github@koston.org>
2026-09-25 06:00:33 +02:00
J. Nick Koston 4a2f17bc8b [noise] Run the ESP8266 at 160 MHz during the handshake (#19233) 2026-09-25 00:24:34 +01:00
J. Nick Koston 253793c4df [web_server] Mask the value of a password text entity, not only its state (#19385) 2026-09-24 18:56:28 -04:00
Jeff Brown 3ef0acd4b2 [sgp30][sgp4x] Migrate CONF_HUMIDITY_SOURCE to const.py (#19436) 2026-09-24 17:54:58 -04:00
dependabot[bot] 73ca04579c Update pyparsing requirement from >=3.3.2 to >=3.3.3 (#19592)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-24 17:53:46 -04:00
dependabot[bot] 4ccc5fa37e Bump astral-sh/setup-uv from 10.1.0 to 10.2.0 in /.github/actions/restore-python (#19593)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-24 17:53:25 -04:00
dependabot[bot] f70880ce96 Bump astral-sh/setup-uv from 10.1.0 to 10.2.0 (#19594)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-24 17:53:09 -04:00
057bf88e4a [mcp3204] Read a conversion in one SPI transaction (#19423)
Co-authored-by: Claude Opus 5 <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>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-09-24 14:06:07 -04:00
tomaszduda23 16757a84e6 [nrf52] add device power manager support to deep sleep component (#19494) 2026-09-24 13:57:16 -04:00
J. Nick Koston a9111a8993 [ota] Treat a client that leaves after the handshake as a clean close (#19401) 2026-09-24 18:36:45 +01:00
J. Nick Koston 211fd3ce54 [core] Dispatch controller updates through generated direct calls (#19588) 2026-09-24 17:24:10 +00:00
Janne Snabb 9df7e825d3 [core] Keep static task TCB in internal RAM (#19426) 2026-09-24 17:59:03 +01:00
Jonathan Swoboda e63428ddce [nextion] Keep add_waveform_data() available without a waveform sensor (#19590) 2026-09-24 12:31:16 -04:00
J. Nick Koston f8e8295088 [api] Decode without a vtable (#19029) 2026-09-24 16:14:25 +01:00
J. Nick Koston 9f5ac44db1 [api] Default response only string fields to a null StringRef (#19025) 2026-09-24 15:52:37 +01:00
dependabot[bot] 6f71b9102b Bump click from 8.3.3 to 8.5.0 (#18925)
Signed-off-by: dependabot[bot] <support@github.com>
2026-09-24 15:49:07 +01:00
04da1827e3 [light] Add transition_state_publish_interval option to publish intermediate states during transitions (#11968)
Co-authored-by: Cursor <cursoragent@cursor.com>
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 (1M context) <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-09-24 14:48:46 +00:00
d2fefe2299 [TAS2780] Add TAS2780 audio amplifier (#15708)
Co-authored-by: Claude Sonnet 4.6 <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@home-assistant.io>
2026-09-24 15:45:51 +01:00
J. Nick Koston f1f83cda07 [api] Collapse the three protobuf decode virtuals into one (#19016) 2026-09-24 15:30:11 +01:00
Ivan KruglovandJ. Nick Koston 7dc98c683b [uart] clock_source option (#16486)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-09-24 15:16:40 +01:00
Bonne EgglestonandClaude 071fc08e32 [core] Silence -Wunused-function for user builds (#19571)
Co-authored-by: Claude <noreply@anthropic.com>
2026-09-24 14:15:52 +00:00
J. Nick Koston c27cbb132d [api] Make generated encode and size entry points type erased (#19015) 2026-09-24 15:13:52 +01:00
J. Nick Koston 7e308bb83d [tests] Define USE_WEBSERVER_PORT in the light unit test override (#19584) 2026-09-24 14:58:54 +01:00
J. Nick Koston 68235f34c5 [automation] Remove dead trigger classes from switch, button, binary_sensor and event (#19563) 2026-09-24 13:49:38 +00:00
J. Nick Koston b59518f6ab [remote_transmitter] Use register_apply_action for the digital_write action (#19550) 2026-09-24 13:28:29 +00:00
J. Nick Koston da148bedfe [api] Pass the encode cursor by value through the protobuf helpers (#19014) 2026-09-24 14:25:50 +01:00
Jesse Hills b0c9a7d5bd [core] Lint duplicate UNIT_/CONF_ constants against both const homes (#19422) 2026-09-24 14:21:32 +01:00
J. Nick Koston 22177680c4 [tests] Point the binary_sensor conditions at the renamed publish target (#19583) 2026-09-24 14:15:18 +01:00
J. Nick Koston e92b1e27ca [core] Let register_apply_action read the parent id from another schema key (#19548) 2026-09-24 09:12:04 -04:00
Ryan Ronnander fed5dd6a9d [light] Accept the top-level white key from Home Assistant's MQTT JSO… (#19570) 2026-09-24 13:11:40 +00:00
J. Nick Koston cbd985826c [speaker] Use register_apply_condition for speaker.is_playing and speaker.is_stopped (#19562) 2026-09-24 09:10:09 -04:00
J. Nick Koston 2ce2b7b25c [event] Remove the unused std::vector set_event_types overload (#19566) 2026-09-24 09:09:35 -04:00
J. Nick Koston acc9324fa8 [tests] Give the binary_sensor and sensor publish targets distinct ids (#19564) 2026-09-24 09:07:41 -04:00
J. Nick Koston cbb4c49011 [automation] Remove dead trigger classes from sensor, number and text_sensor (#19565) 2026-09-24 09:07:07 -04:00
J. Nick Koston afc5c29569 [event] Trigger events without building a temporary std::string (#19567) 2026-09-24 09:06:29 -04:00
J. Nick Koston 993c96875c [core][select] Pass the select.set option as a plain literal (#19568) 2026-09-24 09:05:24 -04:00
J. Nick Koston 75360a5d52 [core] Collapse ESP8266 flash string ifdefs onto the progmem helpers (#19578) 2026-09-24 13:05:21 +00:00
J. Nick Koston 8b11d3da39 [switch] Use register_apply_condition for switch.is_on and switch.is_off (#19553) 2026-09-24 08:57:58 -04:00
J. Nick Koston 5790181f44 [binary_sensor] Use register_apply_condition for binary_sensor.is_on and binary_sensor.is_off (#19554) 2026-09-24 08:57:34 -04:00
J. Nick Koston 4780936867 [fan] Use register_apply_condition for fan.is_on and fan.is_off (#19555) 2026-09-24 08:57:07 -04:00
J. Nick Koston 22959ea1cf [light] Use register_apply_condition for light.is_on and light.is_off (#19556) 2026-09-24 08:56:40 -04:00
Leonardo Rivera e9aecb5fe8 [climate_ir] Store the supported modes in one mask (#19580) 2026-09-24 12:56:11 +00:00
J. Nick Koston 6768cb818b [media_player] Use register_apply_condition for the state and muted conditions (#19557) 2026-09-24 08:55:39 -04:00
J. Nick Koston 1861d8282f [script] Use register_apply_condition for script.is_running (#19558) 2026-09-24 08:55:12 -04:00
J. Nick Koston c39ffd7099 [lock] Use register_apply_condition for lock.is_locked and lock.is_unlocked (#19559) 2026-09-24 08:54:42 -04:00
J. Nick Koston 1671bb6dd3 [time] Use register_apply_condition for time.has_time (#19560) 2026-09-24 08:54:03 -04:00
J. Nick Koston 0ceefd5f91 [mqtt] Use register_apply_condition for mqtt.connected (#19561) 2026-09-24 08:53:29 -04:00
J. Nick Koston 4477e35d66 [dfplayer] Use register_apply_action for the play, volume, eq, device and transport actions (#19549) 2026-09-24 08:52:34 -04:00
J. Nick Koston e98c4bd375 [online_image] Use register_apply_action for the set_url and release actions (#19547) 2026-09-24 08:51:30 -04:00
J. Nick Koston eae8ff7f91 [audio_dac] Use register_apply_action for the mute_off, mute_on and set_volume actions (#19546) 2026-09-24 08:51:04 -04:00
J. Nick Koston f24e1f31f2 [rotary_encoder] Use register_apply_action for the set_value action (#19545) 2026-09-24 08:50:33 -04:00
J. Nick Koston 4cbced407d [ha-addon] Announce the MCP server to Home Assistant (#19413) 2026-09-24 08:49:40 -04:00
J. Nick Koston 64127632f5 [core][logger] Keep ESP8266 log strings in flash and deprecate the RAM option (#19577) 2026-09-24 13:11:50 +01:00
Philip Poten cd5ff787f4 [lvgl] Allow negative text_letter_space, text_line_space (#19576) 2026-09-24 19:18:32 +10:00
J. Nick Koston 698eb71862 [core] Add ESPHOME_strlen_P to the progmem helpers (#19574) 2026-09-24 10:03:33 +01:00
J. Nick Koston e1126a152e [text_sensor] Use register_apply_condition for text_sensor.state (#19541) 2026-09-23 23:30:54 +00:00
J. Nick Koston 807556171b [core][cover] Add register_apply_condition for conditions that only test the parent (#19540) 2026-09-24 11:00:59 +12:00
J. Nick Koston f4300d9cee [esp8266] Add the native ninja emission (#18560) 2026-09-24 10:45:54 +12:00
J. Nick Koston e3951f2ab4 [esp8266_pwm] Use register_apply_action for the set_frequency action (#19516) 2026-09-24 10:30:19 +12:00
J. Nick Koston 438307f548 [light] Use register_apply_action for the light control actions (#19476) 2026-09-24 10:28:36 +12:00
J. Nick Koston b7b1507daf [update] Use register_apply_action for update.perform (#19505) 2026-09-23 18:28:03 -04:00
J. Nick Koston 1be1f67516 [climate] Use register_apply_action for climate.control (#19475) 2026-09-24 10:27:34 +12:00
J. Nick Koston 21dc6af22e [binary_sensor] Use register_apply_action for binary_sensor.template.publish (#19506) 2026-09-23 18:27:29 -04:00
J. Nick Koston 20703f32bb [mitsubishi_cn105] Use register_apply_action for vane.control (#19477) 2026-09-23 17:27:01 -05:00
J. Nick Koston 2ddc6a0741 [sensor] Use register_apply_action for sensor.template.publish (#19507) 2026-09-23 18:26:41 -04:00
J. Nick KostonandJonathan Swoboda c1d82168d8 [event] Use register_apply_action for event.trigger (#19508)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-09-23 23:26:20 +01:00
J. Nick Koston 281c54bcb8 [globals] Use register_apply_action for globals.set (#19481) 2026-09-24 10:25:42 +12:00
J. Nick Koston a47271b1f1 [fan] Use register_apply_action for fan.turn_on (#19473) 2026-09-23 17:25:33 -05:00
J. Nick Koston 22efbd9778 [esp32_ble_tracker] Use register_apply_action for start_scan and stop_scan (#19520) 2026-09-23 23:25:04 +01:00
J. Nick Koston 6a97172867 [stepper] Use register_apply_action for the setter actions (#19470) 2026-09-23 17:24:56 -05:00
J. Nick Koston c275365258 [rp2040_pwm] Use register_apply_action for the set_frequency action (#19517) 2026-09-23 23:24:44 +01:00
J. Nick Koston 4efeee245c [text] Use register_apply_action for text.set (#19509) 2026-09-23 18:24:25 -04:00
J. Nick Koston bd91d3ff23 [display] Use register_apply_action for display.page.show_next and show_previous (#19519) 2026-09-23 23:24:12 +01:00
J. Nick Koston 2a26d5ab62 [number] Use register_apply_action for the set and operation actions (#19510) 2026-09-23 18:23:55 -04:00
J. Nick Koston f58fe2e3ea [select] Use register_apply_action for the set, set_index and operation actions (#19511) 2026-09-23 18:23:30 -04:00
J. Nick Koston 07c8cfdde3 [cc1101] Use register_apply_action for the setter actions (#19472) 2026-09-23 17:23:22 -05:00
J. Nick Koston 802f2b9f24 [libretiny_pwm] Use register_apply_action for the set_frequency action (#19518) 2026-09-23 23:23:20 +01:00
J. Nick Koston 7df946b665 [ledc] Use register_apply_action for the set_frequency action (#19515) 2026-09-23 23:22:44 +01:00
J. Nick Koston aaed14d018 [media_player] Use register_apply_action for the media player actions (#19514) 2026-09-23 23:22:20 +01:00
J. Nick Koston 62b501049f [datetime] Use register_apply_action for date.set, time.set and datetime.set (#19513) 2026-09-23 23:21:39 +01:00
J. Nick Koston 606525ed18 [output] Use register_apply_action for the output actions (#19512) 2026-09-23 17:21:21 -05:00
J. Nick Koston bd1efeae94 [servo] Use register_apply_action for servo.write and servo.detach (#19531) 2026-09-23 17:20:29 -05:00
J. Nick Koston 0110215e31 [template] Use register_apply_action for lock.template.publish and valve.template.publish (#19525) 2026-09-23 17:20:05 -05:00
J. Nick Koston 170bb02fa3 [ln882h_ble_tracker] Use register_apply_action for stop_scan (#19523) 2026-09-23 23:16:08 +01:00
J. Nick Koston 4f6af3cfab [alarm_control_panel] Use register_apply_action for arm_away, arm_home, arm_night and disarm (#19521) 2026-09-23 23:15:51 +01:00
J. Nick Koston 8f14adaa0c [bk72xx_ble_tracker] Use register_apply_action for stop_scan (#19522) 2026-09-23 23:15:25 +01:00
J. Nick Koston 761dd9dd9c [rp2_ble_tracker] Use register_apply_action for stop_scan (#19524) 2026-09-23 23:15:10 +01:00
J. Nick Koston 2e809c3626 [pulse_counter] Use register_apply_action for pulse_counter.set_total_pulses (#19532) 2026-09-23 23:13:44 +01:00
J. Nick Koston bbd8472b66 [espnow] Use register_apply_action for peer.add, peer.delete and set_channel (#19538) 2026-09-23 16:57:32 -05:00
J. Nick Koston 01f9416a79 [text_sensor] Use register_apply_action for text_sensor.template.publish (#19539) 2026-09-23 16:55:58 -05:00
J. Nick Koston 4e2d1cd7fc [hlk_fm22x] Use register_apply_action for scan, enroll, delete, delete_all and reset (#19537) 2026-09-23 16:55:16 -05:00
J. Nick Koston 5e8f620362 [voice_assistant] Use register_apply_action for start, start_continuous and stop (#19536) 2026-09-23 16:53:34 -05:00
J. Nick Koston 9d87fcd3b7 [esp8266] Add the native build spec (#18559) 2026-09-23 22:52:05 +01:00
J. Nick Koston fa7ccc934e [pulse_meter] Use register_apply_action for pulse_meter.set_total_pulses (#19533) 2026-09-23 16:52:00 -05:00
J. Nick Koston 4a7fe83a17 [speaker] Use register_apply_action for the speaker actions (#19535) 2026-09-23 16:51:20 -05:00
Jonathan Swoboda c33ccc7043 [esp32][esp32_hosted] Bump recommended Arduino to 3.3.12 and platform to 55.03.312 and PlatformIO to 6.2.0 (#19417) 2026-09-23 17:50:54 -04:00
J. Nick Koston 4f95ffaa1f [network] Remove deprecated IPAddress::str() from the host build (#19528) 2026-09-23 16:49:59 -05:00
J. Nick Koston 487ef61d0a [nextion] Use register_apply_action for set_brightness and the publish actions (#19529) 2026-09-23 16:47:31 -05:00
J. Nick Koston ce6a66959a [sprinkler] Use register_apply_action for the setter actions (#19471) 2026-09-23 22:41:47 +01:00
dalkleinandClaude Opus 5 70f8ec73c9 [modbus] Log the discarded bytes, not just how many (#19428)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 17:40:01 +01:00
Bonne Eggleston d78d640abd [modbus] Drop a deferred server reply when another frame arrives first (#19534) 2026-09-23 17:33:00 +01:00
cf3552b46e [pid] Pid climate add set deadband parameters action (#14730)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-09-23 17:21:25 +01:00
f6e97b7ada [RX8025T] add RX8025T realtime clock (#13274)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick+github@koston.org>
2026-09-23 17:19:09 +01:00
Leonardo Rivera 35607e99a7 [mitsubishi] Use the climate_ir traits() for supported modes (#19485) 2026-09-23 17:18:41 +01:00
myztillxandJ. Nick Koston b36c6bd7ca [dfplayer] Add set_current_track_repeat action (#9186)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-09-23 16:58:06 +01:00
esphome[bot] 9c5479287e Bump bundled esphome-device-builder to 1.15.0 (#19526) 2026-09-23 15:29:47 +00:00
Leonardo Rivera 93df1e1259 [gree] Remove the traits() override now handled by climate_ir (#19484) 2026-09-23 16:29:23 +01:00
tomaszduda23andpre-commit-ci-lite[bot] 26abc1325e [nrf52] disable USB if not configured by esphome to save power (#19493)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-09-23 14:48:39 +02:00
J. Nick Koston 3da7533159 [remote_base] Generate the gobox code as uint64_t (#19478) 2026-09-23 12:24:30 +01:00
J. Nick Koston 1a8a661b56 [rtttl] Use register_apply_action for rtttl.play and rtttl.stop (#19482) 2026-09-23 12:22:05 +01:00
J. Nick Koston 3b570d84ae [rf_bridge] Use register_apply_action for the actions (#19497) 2026-09-23 12:19:46 +01:00
J. Nick Koston 547624edf9 [at581x] Use register_apply_action for the settings and reset actions (#19496) 2026-09-23 12:19:24 +01:00
J. Nick Koston 14baf03aad [cover] Use register_apply_action for the open, close, stop and toggle actions (#19500) 2026-09-23 12:19:07 +01:00
J. Nick Koston 418ae73870 [template] Use register_apply_action for climate.template.publish (#19495) 2026-09-23 12:18:55 +01:00
J. Nick Koston e7ff903284 [valve] Use register_apply_action for valve.control (#19474) 2026-09-23 12:17:06 +01:00
J. Nick Koston 56f7280d55 [switch] Use register_apply_action for the switch actions (#19498) 2026-09-23 12:16:47 +01:00
J. Nick Koston 01fc7511ae [core] Call a lambda whose only statement returns a braced value (#19503) 2026-09-23 12:15:21 +01:00
J. Nick Kostonandesphbot 8c3ebd3414 [docker] Drop the unused PlatformIO library preinstall (#19504)
Co-authored-by: esphbot <287758279+esphbot@users.noreply.github.com>
2026-09-23 12:14:04 +01:00
ca46954335 [fujitsu_general] Read the mode, fan speed and swing fields with their real width (#18297)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-09-23 12:03:59 +01:00
Leonardo RiveraandJ. Nick Koston a4bebc3083 [mitsubishi] Store the fan mode and default directions in one byte each (#19489)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-09-23 10:29:11 +00:00
Leonardo RiveraandJ. Nick Koston c609db16b3 [gree] Store the model in one byte (#19488)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-09-23 11:28:32 +01:00
Leonardo Rivera 00fa63ea19 [climate_ir] Only advertise HEAT_COOL when heat and cool are both supported (#19483) 2026-09-23 11:14:57 +01:00
J. Nick Koston e8d3c06b60 [core][cover] Add register_apply_action for actions that only forward values (#19469) 2026-09-23 09:36:42 +00:00
Leonardo Rivera 359eba7145 [fujitsu_general] Use constexpr for the protocol constants (#19486) 2026-09-23 01:33:30 +01:00
Jeff Brown e76edeb19e [yaml_util][substitutions] Repair bad tests (#19468) 2026-09-23 01:15:38 +01:00
luar123 79532f2a85 [zigbee] Require mbedTLS extras for Zigbee crypto (#19457) 2026-09-22 22:48:50 +01:00
dependabot[bot] 2b805b897b Bump platformdirs from 4.11.10 to 4.11.11 (#19465) 2026-09-22 22:08:24 +01:00
dependabot[bot] 44cf2b594a Bump filelock from 4.0.0 to 4.0.1 (#19464) 2026-09-22 22:08:00 +01:00
esphome[bot] f4aaa595f8 Bump aioesphomeapi from 46.4.1 to 46.5.0 (#19463) 2026-09-22 17:59:19 +00:00
dependabot[bot] a78e955691 Bump esphome/workflows/.github/workflows/stale.yml from a1c1485ab46ef41a84a6a9d8abd7fa4b7628fd70 to cc3e76de337dc59bc1cba8da58d963cd23b873f1 (#19444)
Signed-off-by: dependabot[bot] <support@github.com>
2026-09-21 22:42:45 +01:00
dependabot[bot] 632f63b34b Bump github/codeql-action/init from 4.38.0 to 4.38.1 (#19445)
Signed-off-by: dependabot[bot] <support@github.com>
2026-09-21 22:42:33 +01:00
dependabot[bot] cd50f949ae Bump github/codeql-action/analyze from 4.38.0 to 4.38.1 (#19447)
Signed-off-by: dependabot[bot] <support@github.com>
2026-09-21 22:42:25 +01:00
dependabot[bot] 352121d60f Bump esphome/workflows/.github/workflows/lock.yml from 2026.8.1 to 2026.9.0 (#19448)
Signed-off-by: dependabot[bot] <support@github.com>
2026-09-21 22:42:02 +01:00
dependabot[bot] 3b4a6f21f9 Bump filelock from 3.32.6 to 4.0.0 (#19442)
Signed-off-by: dependabot[bot] <support@github.com>
2026-09-21 22:41:52 +01:00
dependabot[bot]andesphome[bot] cd012b57ed Bump ruff from 0.16.7 to 0.16.8 (#19441)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
Signed-off-by: dependabot[bot] <support@github.com>
2026-09-21 22:41:36 +01:00
dependabot[bot] 8f3f25b832 Bump codecov/codecov-action from 7.1.0 to 7.1.1 (#19446)
Signed-off-by: dependabot[bot] <support@github.com>
2026-09-21 22:41:22 +01:00
dependabot[bot] 81dd57b402 Bump platformdirs from 4.11.8 to 4.11.10 (#19443)
Signed-off-by: dependabot[bot] <support@github.com>
2026-09-21 22:41:00 +01:00
Iago VeigaandClyde Stubbs 5764a72f1b [syslog] Add configurable RFC 3164 and RFC 5424 formats (#18993)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
2026-09-22 07:26:06 +10:00
esphome[bot] 7a27d5dde1 [ci] Refresh integration test durations (#19435) 2026-09-21 11:45:23 +01:00
Jonathan SwobodaandClaude Fable 5.1 a00d241cad [esp32] Disable newlib nano printf formatting on ESP32-C2 (#19418)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 18:08:56 -04:00
Jesse Hills c10c06eeb5 [ci] Add code-review agent skill for GitHub Copilot (#19370) 2026-09-20 00:44:33 +12:00
rwrozelleandClaude Sonnet 5 f94661561d [socket] Fix set_sockaddr() accepting malformed IPv4 addresses (#19394)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-18 18:10:11 -05:00
Xiretza a53efcf646 [core] fix capitalisation of "pin" (#19405) 2026-09-18 18:30:35 -04:00
Keith BurzinskiandClaude Opus 5 623aec2984 [mdns] Advertise ota_signed when signed OTA verification is compiled in (#19407)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-18 17:18:44 -05:00
dependabot[bot] aba49c3d4f Bump docker/build-push-action from 7.3.0 to 7.4.0 in /.github/actions/build-image (#19403)
Signed-off-by: dependabot[bot] <support@github.com>
2026-09-18 14:32:42 -05:00
dependabot[bot] 28189ea026 Bump docker/setup-buildx-action from 4.3.0 to 4.4.0 in the docker-actions group (#19404)
Signed-off-by: dependabot[bot] <support@github.com>
2026-09-18 14:32:33 -05:00
dependabot[bot] f6897f2257 Bump codecov/codecov-action from 7.0.0 to 7.1.0 (#19382)
Signed-off-by: dependabot[bot] <support@github.com>
2026-09-17 14:57:00 -05:00
9309cf96b9 [web_server_idf] Close stalled EventSource sessions via HTTPD (#17800)
Co-authored-by: Jeroen Jansen <jeroen@Jeroens-MacBook-Air.local>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-09-17 10:38:56 -05:00
J. Nick Koston a63dc0d9c9 [esphome] Keep the OTA encryption key without the api server so safe mode uploads work (#19349) 2026-09-17 10:20:41 -05:00
J. Nick Koston f433f99e90 [esp32_hosted] Fix ESP32-P4 build without wifi, espnow or BLE (#19374) 2026-09-17 14:57:59 +00:00
J. Nick Koston 1dbbf3f45a [espidf] Switch the Windows console to UTF-8 while idf.py runs (#19352) 2026-09-17 09:40:22 -05:00
J. Nick Koston 91ab229086 [core] Lint bare string literal ternaries in ESP_LOG arguments (#18908) 2026-09-17 09:17:08 -05:00
J. Nick Koston 8ac3dba11a [esp32] Add a flash chip option that drops the unused flash vendor drivers (#19217) 2026-09-17 08:40:30 -05:00
J. Nick Koston 7d17efc15f [espidf] Emit json2 size data so the link edge is not blocked (#18848) 2026-09-17 08:35:46 -05:00
J. Nick Koston 271f85185d [sensor] Remove deprecated raw_state member (#19077) 2026-09-17 08:35:15 -05:00
J. Nick Koston 28b2a689a7 [web_server] Reduce flash used by the JSON and request helpers (#19303) 2026-09-17 08:35:02 -05:00
J. Nick Koston 40934484c6 [output] Use BinaryOutput::set_state() instead of hand written turn_on/turn_off branches (#19366) 2026-09-17 08:34:36 -05:00
J. Nick Koston 05bba3fdc2 [image] Blend grayscale alpha with Color::gradient (#19356) 2026-09-17 08:17:28 -05:00
J. Nick Koston 508c24c5e3 [esp32][rp2] Print the previous boot crash report before the logger reads it (#19351) 2026-09-17 08:17:11 -05:00
J. Nick Koston 2a8d6b69cd [rp2] Bump arduino-pico framework to 6.1.0 (#19261) 2026-09-17 08:16:41 -05:00
J. Nick Koston a7559547b2 [remote_receiver] Treat buffer size as bytes on the pulse ring targets (#19101) 2026-09-17 08:15:13 -05:00
J. Nick Koston 942322738a [remote_receiver] [remote_transmitter] Keep the RMT setup error message as a pointer to the literal (#19210) 2026-09-17 08:14:57 -05:00
J. Nick Koston 7e3d4a48d1 [modbus_controller] Remove deprecated helper shims (#19076) 2026-09-17 08:56:29 -04:00
Kevin Ahrendt 8cd22eacea [audio] Bump esp-audio-libs to v4.0.1 (#19372) 2026-09-17 08:23:55 -04:00
rexmoriarty cf0a87de28 [mixer] Don't discard a start request while reaping a stopped task (#19368) 2026-09-17 05:47:40 -04:00
Jesse Hills 4be8302190 [ci] Move skills to .agents/skills with .claude and .github symlinks (#19369) 2026-09-16 23:36:08 -05:00
Kevin Ahrendt 34158e17b8 [mixer] Raise ducking decibel_reduction maximum to 255 (#19347) 2026-09-16 20:42:29 -05:00
J. Nick Koston 1a04359454 [switch] Use Switch::control() instead of hand written turn_on/turn_off branches (#19364) 2026-09-16 19:04:14 -05:00
Kevin Ahrendt 8bcb5004da [sendspin] Add a switch platform to enable and disable the client (#19361) 2026-09-17 11:35:07 +12:00
J. Nick Koston b001514cf3 [remote_base] Accept protocols registered by external components (#19332) 2026-09-17 11:33:52 +12:00
J. Nick Koston ffb1ad288f [remote_receiver] Wake the main loop when the RMT callback stores a frame (#19102) 2026-09-17 11:32:13 +12:00
J. Nick Koston 3fe87688d9 [esp32] Enable octal flash when the flash mode is opi (#19218) 2026-09-17 11:31:27 +12:00
J. Nick Koston abfe350119 [core] Fold the modbus write and bits tests into the shared mesh fixture (#18946) 2026-09-17 11:23:29 +12:00
J. Nick Koston f8012bc467 [esp32] Redefine __FILE__ to the basename so assert paths stay out of RAM (#19106) 2026-09-17 11:22:32 +12:00
J. Nick Koston bb5c06c58a [image] Drop the unused stride_ member (#19353) 2026-09-17 11:17:56 +12:00
J. Nick Koston 9288311978 [ota] Skip the web_server plaintext warning when web_server ota is disabled (#19348) 2026-09-17 10:19:04 +12:00
J. Nick Koston 7386daed5f [ld2450] Use a user provided default constructor for MultiTargetSwitch (#19192) 2026-09-17 09:58:45 +12:00
J. Nick Koston dc300ae5d4 [esp8266] Drop the dead NEW_OOM_ABORT flag and its nothrow advice (#19247) 2026-09-17 09:58:14 +12:00
J. Nick Koston cfcaeff27b [esp32_hosted] Keep the update manifest URL as a pointer to the literal (#19212) 2026-09-17 09:57:47 +12:00
417b841db7 [icnt86] Support ICNT86 touch driver (#18331)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-09-17 07:53:53 +10:00
J. Nick Koston 711733f9af [modbus] Remove deprecated send() (#19079) 2026-09-17 09:53:43 +12:00
J. Nick Koston 2f248390b2 [modbus] Remove disable_crc validation stub (#19078) 2026-09-17 09:53:35 +12:00
J. Nick Koston 3d4765d05a [template] Remove deprecated bypass_before_arming() (#19075) 2026-09-17 09:53:28 +12:00
J. Nick Koston 59a6760f5e [nextion] Remove deprecated get_wave_chan_id() (#19080) 2026-09-17 09:53:22 +12:00
J. Nick Koston bafa096a1d [display] Trim the per pixel cost of draw_pixel_at (#19360) 2026-09-16 16:19:47 -05:00
Kevin Ahrendt 845de7b7dd [sendspin] Bump sendspin-cpp to v0.8.0 (#19357) 2026-09-16 19:38:18 +00:00
dependabot[bot] 6f15dc331f Bump prek from 0.5.2 to 0.5.3 (#19358)
Signed-off-by: dependabot[bot] <support@github.com>
2026-09-16 19:33:41 +00:00
Kevin Ahrendt fdf6998a86 [sendspin] Start mDNS service disabled, enable once server is running (#19326) 2026-09-16 15:08:40 -04:00
Kevin Ahrendt 3804ec423f [i2s_audio] Resync DMA lockstep in place instead of restarting the speaker task (#19319) 2026-09-16 13:52:56 -04:00
J. Nick Koston e13d424768 [speaker] Remove deprecated codec_support_enabled option (#19074) 2026-09-16 09:41:14 -05:00
J. Nick Koston 544f8ae771 [http_request] Take the URL and method as C strings (#19215) 2026-09-16 09:14:52 -05:00
J. Nick Koston 3a9bb7e5bc [http_request] Keep the update manifest URL as a pointer to the literal (#19211) 2026-09-16 08:41:48 -05:00
Jesse Hills 4c7aee1a77 [improv_ble] Rename from esp32_improv and decouple from ESP32 (#19264) 2026-09-16 20:49:56 +12:00
Jesse Hills 9b21b64836 Merge branch 'release' into dev 2026-09-16 20:47:35 +12:00
Jesse Hills c6e4c87e52 Merge pull request #19338 from esphome/bump-2026.9.0
2026.9.0
2026-09-16 20:47:05 +12:00
J. Nick Koston 237d87880f [pcf8574] Use a user provided default constructor for PCF8574GPIOPin (#19207) 2026-09-16 20:42:42 +12:00
J. Nick Koston dd40678ad7 [pca9554] Use a user provided default constructor for PCA9554GPIOPin (#19208) 2026-09-16 20:42:37 +12:00
J. Nick Koston 0d4c9f3243 [core] Use a user provided default constructor for Automation (#19197) 2026-09-16 20:42:32 +12:00
J. Nick Koston 0d5683525e [core] Use a user provided default constructor for DelayAction (#19137) 2026-09-16 20:42:29 +12:00
J. Nick Koston 0f01fa89b8 [api] Skip setters that pass the default port, reboot timeout and batch delay (#19227) 2026-09-16 20:42:25 +12:00
J. Nick Koston fd011ec337 [esp8266_pwm] Use a user provided default constructor for ESP8266PWM (#19198) 2026-09-16 20:42:19 +12:00
J. Nick Koston 17c5daa245 [ld2412] Drop the unused gate index from GateThresholdNumber (#19107) 2026-09-16 20:38:33 +12:00
J. Nick Koston a5931ea20e [fan] Initialize Fan::restore_mode_ (#19118) 2026-09-16 20:38:21 +12:00
J. Nick Koston 7e750ec611 [text_sensor] Use a user provided default constructor (#19112) 2026-09-16 20:38:10 +12:00
J. Nick Koston 3180381a26 [alarm_control_panel] Initialize the state members (#19120) 2026-09-16 20:38:07 +12:00
J. Nick Koston 925ddd6c09 [template] Use a user provided default constructor for TemplateEvent (#19127) 2026-09-16 20:38:00 +12:00
J. Nick Koston a495fe56f5 [template] Use a user provided default constructor for TemplateSelect (#19130) 2026-09-16 20:37:48 +12:00
J. Nick Koston cbb6600263 [template] Use a user provided default constructor for TemplateButton (#19124) 2026-09-16 20:37:41 +12:00
J. Nick Koston da73140c9c [template] Use a user provided default constructor for TemplateTextSensor (#19126) 2026-09-16 20:37:36 +12:00
J. Nick Koston b7064f2bed [update] Initialize UpdateInfo::progress (#19121) 2026-09-16 20:37:31 +12:00
J. Nick Koston 4714b77f45 [core] Construct App without value initialization (#19109) 2026-09-16 20:37:28 +12:00
J. Nick Koston 569001d554 [template] Use a user provided default constructor for TemplateSensor (#19125) 2026-09-16 20:34:07 +12:00
J. Nick Koston ac3ef0a02d [preferences] Use a user provided default constructor for IntervalSyncer (#19164) 2026-09-16 20:34:00 +12:00
J. Nick Koston 7eebe18e1a [ethernet_info] Use a user provided default constructor for IPAddressEthernetInfo (#19166) 2026-09-16 20:33:48 +12:00
J. Nick Koston ba679d91e4 [restart] Use a user provided default constructor for RestartButton (#19168) 2026-09-16 20:33:42 +12:00
J. Nick Koston 5b53c63eab [http_request] Use a user provided default constructor for HttpRequestIDF (#19175) 2026-09-16 20:33:18 +12:00
J. Nick Koston 4e0245da0d [gpio] Use a user provided default constructor for GPIOBinarySensor (#19143) 2026-09-16 20:33:13 +12:00
J. Nick Koston df4176f9e1 [uart] Use a user provided default constructor for IDFUARTComponent (#19142) 2026-09-16 20:33:08 +12:00
J. Nick Koston 25a3f7068e [status] Use a user provided default constructor for StatusBinarySensor (#19138) 2026-09-16 20:33:01 +12:00
J. Nick Koston ba50e23b81 [uptime] Use a user provided default constructor for UptimeSecondsSensor (#19140) 2026-09-16 20:32:58 +12:00
J. Nick Koston 14e640c903 [internal_temperature] Use a user provided default constructor for InternalTemperatureSensor (#19141) 2026-09-16 20:32:48 +12:00
J. Nick Koston 7f61909415 [binary_sensor] Use a user provided default constructor for SettleFilter (#19135) 2026-09-16 20:32:42 +12:00
J. Nick Koston 8ffa10f86c [version] Use a user provided default constructor for VersionTextSensor (#19139) 2026-09-16 20:32:33 +12:00
J. Nick Koston 0c1cf6d0a9 [template] Use a user provided default constructor for TemplateBinarySensor (#19128) 2026-09-16 20:32:27 +12:00
J. Nick Koston 9a76f1be4f [core] Give StaticVector a user provided default constructor (#19110) 2026-09-16 20:32:21 +12:00
J. Nick Koston 9162dc38ce [bluetooth_connection] Use a user provided default constructor for BluedroidGattClient (#19201) 2026-09-16 18:23:50 +12:00
J. Nick Koston e50428fce5 [safe_mode] Use a user provided default constructor for SafeModeComponent (#19163) 2026-09-16 18:22:34 +12:00
J. Nick Koston 511095d3da [scd4x] Use a user provided default constructor for PerformForcedCalibrationAction (#19176) 2026-09-16 18:21:58 +12:00
J. Nick Koston 2fde80454f [ld2412] Use a user provided default constructor for LightThresholdNumber (#19193) 2026-09-16 18:21:30 +12:00
J. Nick Koston 43e35c4fc2 [ld2412] Use a user provided default constructor for EngineeringModeSwitch (#19190) 2026-09-16 18:20:10 +12:00
J. Nick Koston b0f87615a2 [ld2450] Use a user provided default constructor for ZoneTypeSelect (#19188) 2026-09-16 18:19:54 +12:00
J. Nick Koston bd47f04479 [ld2450] Use a user provided default constructor for PresenceTimeoutNumber (#19195) 2026-09-16 06:19:51 +00:00
J. Nick Koston 313d0c1eb3 [number] Initialize Number::state (#19114) 2026-09-16 18:19:22 +12:00
J. Nick Koston 794aefb276 [ld2412] Use a user provided default constructor for BluetoothSwitch (#19189) 2026-09-16 18:17:17 +12:00
J. Nick Koston 4d648bf872 [ld2450] Use a user provided default constructor for BluetoothSwitch (#19191) 2026-09-16 18:17:13 +12:00
J. Nick Koston 7d1c9d6488 [ld2450] Use a user provided default constructor for BaudRateSelect (#19187) 2026-09-16 18:17:08 +12:00
J. Nick Koston e77a852599 [ld2412] Use a user provided default constructor for LightOutControlSelect (#19186) 2026-09-16 18:17:03 +12:00
J. Nick Koston 711d016a50 [ld2412] Use a user provided default constructor for BaudRateSelect (#19184) 2026-09-16 18:16:58 +12:00
J. Nick Koston d16a1ec288 [ld2412] Use a user provided default constructor for DistanceResolutionSelect (#19185) 2026-09-16 18:16:53 +12:00
J. Nick Koston 644f0ededb [ld2450] Use a user provided default constructor for FactoryResetButton (#19182) 2026-09-16 18:16:47 +12:00
J. Nick Koston 42aea9efbc [ld2450] Use a user provided default constructor for RestartButton (#19183) 2026-09-16 18:16:44 +12:00
J. Nick Koston 274bfe7aee [binary_sensor] Use a user provided default constructor (#19111) 2026-09-16 18:15:51 +12:00
J. Nick Koston 11ce3594c0 [binary_sensor] Use a user provided default constructor for DelayedOffFilter (#19206) 2026-09-16 18:10:49 +12:00
J. Nick Koston 5198ef8dd8 [binary_sensor] Use a user provided default constructor for DelayedOnFilter (#19205) 2026-09-16 18:10:43 +12:00
J. Nick Koston f686fb606b [gpio] Use a user provided default constructor for GPIOSwitch (#19203) 2026-09-16 18:10:30 +12:00
J. Nick Koston ad20711a22 [restart] Use a user provided default constructor for RestartSwitch (#19204) 2026-09-16 18:10:24 +12:00
J. Nick Koston f30a3bc3e1 [bluetooth_connection] Use a user provided default constructor for BluetoothConnection (#19200) 2026-09-16 18:10:21 +12:00
J. Nick Koston 2ffa57c907 [esp32_ble_tracker] Initialize ESPBTClient::app_id (#19199) 2026-09-16 18:07:27 +12:00
J. Nick KostonandJesse Hills f0315ea1ec [template][modbus_controller] Move set_assumed_state into the headers (#19279)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2026-09-16 05:38:36 +00:00
J. Nick KostonandJesse Hills d61e0e4695 [tsl2561][tsl2591] Move set_gain into the headers (#19284)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2026-09-16 05:26:51 +00:00
J. Nick Koston 4fba837d40 [adc] Move set_sampling_mode into the header (#19293) 2026-09-16 17:18:32 +12:00
J. Nick Koston 7ed4950c69 [bme280_base][bme680][bmp280_base] Move set_iir_filter into the headers (#19281) 2026-09-16 17:18:22 +12:00
J. Nick Koston 02435e7455 [waveshare_epaper] Move set_full_update_every into the header (#19280) 2026-09-16 17:17:12 +12:00
J. Nick Koston fdb86cbd2e [tsl2591] Move the single store setters into the header (#19288) 2026-09-16 17:16:53 +12:00
J. Nick Koston 37a08e6cb6 [lc709203f] Move the single store setters into the header (#19290) 2026-09-16 17:16:47 +12:00
J. Nick Koston d186b4ba9f [openthread] Move set_mdns into the header (#19295) 2026-09-16 17:16:42 +12:00
J. Nick Koston 1e8f5fa481 [haier] Move set_send_wifi into the header (#19299) 2026-09-16 17:16:24 +12:00
J. Nick Koston 534b4a0f44 [template] Move the cover and valve set_has_* setters into the headers (#19283) 2026-09-16 17:16:18 +12:00
J. Nick Koston 7e3a1cf272 [tsl2561] Move set_is_cs_package into the header (#19289) 2026-09-16 17:16:10 +12:00
J. Nick Koston 456119ec38 [graphical_display_menu] Move set_display and set_font into the header (#19291) 2026-09-16 17:16:05 +12:00
J. Nick Koston 031b980421 [web_server] Move the CSS and JS setters into the header (#19286) 2026-09-16 17:15:07 +12:00
J. Nick Koston cc9f4730b4 [mqtt_subscribe] Move set_qos into the headers (#19282) 2026-09-16 17:14:39 +12:00
J. Nick Koston ee6e675d58 [esp32_camera] Move the single store setters into the header (#19285) 2026-09-16 17:14:22 +12:00
J. Nick Koston 2f76ac6362 [thermostat] Move set_default_preset into the header (#19294) 2026-09-16 17:14:05 +12:00
J. Nick Koston a6fa67b7ca [bang_bang] Move the single store setters into the header (#19287) 2026-09-16 17:13:49 +12:00
J. Nick Koston da3ddee767 [st7789v] Move set_model_str into the header (#19296) 2026-09-16 17:13:34 +12:00
J. Nick Koston f95a876ef2 [max44009] Move set_mode into the header (#19297) 2026-09-16 17:13:20 +12:00
J. Nick Koston 70097ae02f [sds011] Move set_rx_mode_only into the header (#19298) 2026-09-16 17:13:06 +12:00
J. Nick Koston 1eb445bacb [template] Move set_optimistic into the headers (#19278) 2026-09-16 17:12:48 +12:00
J. Nick Koston b0996ac404 [template] Skip the switch optimistic and assumed state setters when they match the default (#19232) 2026-09-16 17:11:57 +12:00
Jesse Hills 160bc29c7c Bump version to 2026.9.0 2026-09-16 17:01:34 +12:00
Keith BurzinskiandClaude Fable 5.1 a264093d86 [uart] Add IDFUARTComponent::flush_input() and use it in uart_mux (#19336)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 21:57:06 -05:00
Jesse Hills 47be8c811f Merge branch 'beta' into dev 2026-09-16 13:38:02 +12:00
Jesse Hills b0cc913884 Merge pull request #19334 from esphome/bump-2026.9.0b5
2026.9.0b5
2026-09-16 13:37:44 +12:00
d824a32ef1 [uart_mux] New component to share a UART between a CDC-ACM bridge and local consumers (#19066)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-09-15 20:32:15 -05:00
Kevin Ahrendt ec1d8fa953 [mdns] Add runtime service enable/disable API (ESP32 only) (#19325) 2026-09-15 20:06:40 -05:00
Jesse Hills 3f7725a684 Bump version to 2026.9.0b5 2026-09-16 12:00:05 +12:00
Christopher PruijsenandJ. Nick Koston 0f500628dd [file] Keep resolved image paths as Path so config-hash normalizes them (#19267)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-09-16 12:00:03 +12:00
Bonne Eggleston fe0f04b2e4 [modbus] Add allow_broadcast_read and expect_broadcast_write_response options (#19304) 2026-09-16 12:00:03 +12:00
Edvard Filistovič 45362dbc5b [bk72xx_ble] Keep wifi power save off while BLE is compiled in (#19317) 2026-09-16 12:00:03 +12:00
J. Nick Koston a1ad794d03 [esp8266_pwm] Skip the frequency setter when it matches the default (#19224) 2026-09-16 12:00:03 +12:00
J. Nick Koston f8bda9fbad [logger] Skip the hardware UART setter when it matches the default (#19230) 2026-09-16 12:00:03 +12:00
J. Nick Koston 808b7210db [wifi] Skip setters that pass the default priority, timeouts, power save and auth mode (#19229) 2026-09-16 12:00:03 +12:00
J. Nick Koston 81be397056 [light] Skip the flash transition setter and the empty effect list (#19228) 2026-09-16 12:00:03 +12:00
J. Nick Koston eae6af437b [output] Skip the power limit setters when they match the defaults (#19225) 2026-09-16 12:00:03 +12:00
J. Nick Koston 6582c618f1 [web_server] Skip setters that pass the default port, log and include internal values (#19226) 2026-09-16 12:00:03 +12:00
J. Nick Koston 8c999d3152 [number] Fix the default mode check so mode auto is no longer emitted (#19231) 2026-09-16 12:00:03 +12:00
Jesse Hills 076dc017ab [core] Mark filters, manual_ip and interlock as advanced (#19272) 2026-09-16 12:00:03 +12:00
tronikos b7acd9c0dc [template] Stop water heater republishing when a temperature is unknown (#19013) 2026-09-16 12:00:03 +12:00
Jeff Brown 20d199ae20 [pmsa003i] Fix read from uninitialized stack memory (#19053) 2026-09-16 12:00:03 +12:00
J. Nick Koston 57860dc06c [core] Add register_simple_action and register_parented_action helpers (#19321) 2026-09-15 16:10:46 -05:00
dependabot[bot] 565dc8092c Update tzdata requirement from >=2026.3 to >=2026.4 (#19328)
Signed-off-by: dependabot[bot] <support@github.com>
2026-09-15 14:42:52 -05:00
Keith Burzinski f19403192d [usb_uart] Add claim_comm_interface option (#18969) 2026-09-15 11:47:06 -05:00
Christopher PruijsenandJ. Nick Koston 457bb3ecc9 [file] Keep resolved image paths as Path so config-hash normalizes them (#19267)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-09-15 11:33:33 -05:00
Bonne Eggleston 6362ae71c0 [modbus] Add allow_broadcast_read and expect_broadcast_write_response options (#19304) 2026-09-15 11:29:03 -05:00
Edvard Filistovič e163ae5299 [bk72xx_ble] Keep wifi power save off while BLE is compiled in (#19317) 2026-09-15 08:15:06 -05:00
esphome[bot] e737dc8dac Synchronise Device Classes from Home Assistant (#19318) 2026-09-15 07:33:24 +00:00
Kevin Ahrendt 8d61b35cdc [speaker] Make media player volume and mute independent (#19307) 2026-09-14 18:59:05 -04:00
0af1f22e77 [speaker_source] keep mute when volume changes (#18412)
Signed-off-by: carriewattsmake <carriewattsmake@users.noreply.github.com>
Co-authored-by: carriewattsmake <carriewattsmake@users.noreply.github.com>
Co-authored-by: Kevin Ahrendt <kevin.ahrendt@openhomefoundation.org>
2026-09-14 18:57:57 -04:00
7a25ca0741 [speaker_source] Don't remap a requested volume of zero (#19063)
Co-authored-by: rexmoriarty <181678468+rexmoriarty@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Kevin Ahrendt <kevin.ahrendt@openhomefoundation.org>
2026-09-14 18:11:25 -04:00
Kevin Ahrendt 2472838e13 [speaker][speaker_source] Make a volume of zero silent with an audio DAC (#19305) 2026-09-14 16:46:30 -05:00
Keith BurzinskiandClaude Fable 5.1 328077c4f8 [tinyusb] Add on_mount/on_unmount triggers and is_mounted condition (#19067)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-14 21:16:14 +00:00
Kevin Ahrendt 588ad529e0 [i2s_audio] Ramp software volume changes (#19302) 2026-09-14 15:55:08 -05:00
dependabot[bot] 5d713ad9ad Bump filelock from 3.32.5 to 3.32.6 (#19311)
Signed-off-by: dependabot[bot] <support@github.com>
2026-09-14 19:34:19 +00:00
dependabot[bot] e66b590842 Bump platformdirs from 4.11.7 to 4.11.8 (#19309)
Signed-off-by: dependabot[bot] <support@github.com>
2026-09-14 19:34:01 +00:00
dependabot[bot]andesphome[bot] 00dfe0f712 Bump ruff from 0.16.6 to 0.16.7 (#19310)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
Signed-off-by: dependabot[bot] <support@github.com>
2026-09-14 19:33:20 +00:00
dependabot[bot] 2c0e97421d Bump astral-sh/setup-uv from 10.0.1 to 10.1.0 (#19312)
Signed-off-by: dependabot[bot] <support@github.com>
2026-09-14 19:31:22 +00:00
dependabot[bot] 5d89480dba Bump github/codeql-action/analyze from 4.37.9 to 4.38.0 (#19314)
Signed-off-by: dependabot[bot] <support@github.com>
2026-09-14 14:30:53 -05:00
dependabot[bot] 358e240d42 Bump github/codeql-action/init from 4.37.9 to 4.38.0 (#19315)
Signed-off-by: dependabot[bot] <support@github.com>
2026-09-14 14:30:14 -05:00
dependabot[bot] df481eab00 Bump astral-sh/setup-uv from 10.0.1 to 10.1.0 in /.github/actions/restore-python (#19313)
Signed-off-by: dependabot[bot] <support@github.com>
2026-09-14 19:28:05 +00:00
Kevin Ahrendt 54b8e2e6dc [audio] Update esp-audio-libs to 4.0.0 (#19300) 2026-09-14 13:56:14 +00:00
651863323b [issues] Add AI usage guidance to the bug report template (#19122)
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: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-09-14 22:40:00 +10:00
esphome[bot] 1e627a31f1 [ci] Refresh integration test durations (#19275) 2026-09-14 07:36:30 -05:00
J. Nick Koston e5eb577b49 [esp8266_pwm] Skip the frequency setter when it matches the default (#19224) 2026-09-14 18:05:27 +12:00
J. Nick Koston c378ea1300 [logger] Skip the hardware UART setter when it matches the default (#19230) 2026-09-14 16:53:41 +12:00
J. Nick Koston cba4f5bc05 [wifi] Skip setters that pass the default priority, timeouts, power save and auth mode (#19229) 2026-09-14 16:52:57 +12:00
J. Nick Koston 8109aa96f6 [light] Skip the flash transition setter and the empty effect list (#19228) 2026-09-14 16:51:42 +12:00
J. Nick Koston 4067f572cc [output] Skip the power limit setters when they match the defaults (#19225) 2026-09-14 16:50:56 +12:00
J. Nick Koston 1e22861d11 [web_server] Skip setters that pass the default port, log and include internal values (#19226) 2026-09-14 16:50:00 +12:00
J. Nick Koston 282be54d1e [number] Fix the default mode check so mode auto is no longer emitted (#19231) 2026-09-14 16:44:36 +12:00
Jesse Hills abadfbfd20 [core] Mark filters, manual_ip and interlock as advanced (#19272) 2026-09-14 16:32:31 +12:00
Jesse Hills 93b3dc1876 Merge branch 'beta' into dev 2026-09-14 15:27:22 +12:00
Jesse Hills e7c9c5ad0b Merge pull request #19270 from esphome/bump-2026.9.0b4
2026.9.0b4
2026-09-14 15:27:03 +12:00
J. Nick Koston 82b608706c [bluetooth_connection] Keep USE_BLUETOOTH_PROXY out of the shared host test binary (#19271) 2026-09-14 14:18:38 +12:00
J. Nick Koston 02648d3547 [bluetooth_connection] Keep USE_BLUETOOTH_PROXY out of the shared host test binary (#19271) 2026-09-14 14:17:58 +12:00
tronikos 41e34c19eb [template] Stop water heater republishing when a temperature is unknown (#19013) 2026-09-14 13:36:39 +12:00
Jeff Brown a0821c225a [pmsa003i] Fix read from uninitialized stack memory (#19053) 2026-09-14 13:18:52 +12:00
Jesse Hills 7fe0689fb8 Bump version to 2026.9.0b4 2026-09-14 13:12:27 +12:00
J. Nick Koston 93fa95c833 [api] Reuse overflow buffer storage instead of allocating per stalled write (#19093) 2026-09-14 13:12:26 +12:00
Jesse Hills 501009073d [core] Clear loaded_platforms on CORE.reset() (#19268) 2026-09-14 13:12:26 +12:00
J. Nick Koston 81a54ea9db [ota] Allocate the signature block through RAMAllocator (#19251) 2026-09-14 13:12:26 +12:00
J. Nick Koston e28b4eb2a0 [ethernet] Keep the W5500 SPI context in a static instance instead of the heap (#19248) 2026-09-14 13:12:26 +12:00
J. Nick Koston ebe72c3cef [core] Resolve file paths against the YAML file that declares them (#19259) 2026-09-14 13:12:26 +12:00
J. Nick Koston 977542061d [esphome] Allocate the OTA noise session and auth buffer through RAMAllocator (#19249) 2026-09-14 13:12:26 +12:00
J. Nick Koston b0b75f705a [nextion] Allocate queue components through RAMAllocator and free entries the way they were allocated (#19246) 2026-09-14 13:12:25 +12:00
J. Nick Koston be2c3dda94 [esp32_ble_tracker] Revert coexistence preference to balanced when OTA starts (#19082) 2026-09-14 13:12:25 +12:00
Kevin Ahrendt 64fd87f436 [i2s_audio][router] Loop thread controls all state changes (#19089) 2026-09-14 13:12:25 +12:00
J. Nick Koston cd5d4ff254 [core] Add RAMAllocator::make_unique for objects whose allocation may fail (#19245) 2026-09-14 13:12:25 +12:00
J. Nick Koston 1a555d5848 [esp32_ble_tracker] Re-register GATT clients after ble.disable and ble.enable (#19068) 2026-09-14 13:12:25 +12:00
J. Nick Koston b17cd89469 [wifi] Drop a scan instead of aborting when its results cannot be allocated, filter ESP32 scans by SSID in the driver (#19254) 2026-09-14 13:12:25 +12:00
esphome[bot] 9064bfcc85 Bump bundled esphome-device-builder to 1.14.9 (#19263) 2026-09-14 13:12:25 +12:00
J. Nick Koston 0803d7b37c [core] Add FixedVector::try_init so callers can handle an exhausted heap (#19253) 2026-09-14 13:12:25 +12:00
J. Nick Koston 9814966fe7 [noise] Bump noise-c to 0.1.30 and libsodium to 1.10021.11 (#19062) 2026-09-14 13:12:25 +12:00
esphome[bot] 3eda3060b8 Bump bundled esphome-device-builder to 1.14.8 (#19250) 2026-09-14 13:12:25 +12:00
Clyde Stubbs 6cc2b9bf17 [lvgl] Fix crash when using lvgl.list.add (#19177) 2026-09-14 13:12:25 +12:00
esphome[bot] 1975b17eac Bump bundled esphome-device-builder to 1.14.7 (#19096) 2026-09-14 13:12:25 +12:00
J. Nick Koston 3b499ecb3e [core] Support set_internal() during setup, log error after setup (#19069) 2026-09-14 13:12:25 +12:00
esphome[bot] 6bd6603d52 Bump bundled esphome-device-builder to 1.14.6 (#19072) 2026-09-14 13:12:25 +12:00
Robin ThoniandJesse Hills ad4ee1d34e [network] Improve network::is_connected() to better handle multiple interfaces (#18999)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2026-09-14 13:12:25 +12:00
Remco van Essen ccec6e72bf [sendspin] Fix codec enum codegen when codecs is not set (#19055) 2026-09-14 13:12:25 +12:00
J. Nick Koston 91b1a82a66 [api] Reuse overflow buffer storage instead of allocating per stalled write (#19093) 2026-09-14 13:03:00 +12:00
Jesse Hills 7801cf4a8a [core] Clear loaded_platforms on CORE.reset() (#19268) 2026-09-14 00:55:13 +00:00
J. Nick Koston e431bfcb38 [spi] Send ESP8266 writes through transferBytes instead of a heap copy (#19265) 2026-09-13 19:28:24 -05:00
J. Nick Koston f5afd141de [ota] Allocate the signature block through RAMAllocator (#19251) 2026-09-14 10:37:04 +12:00
J. Nick Koston e38ee343b4 [ethernet] Keep the W5500 SPI context in a static instance instead of the heap (#19248) 2026-09-14 10:35:38 +12:00
J. Nick Koston cf398ea8b2 [core] Resolve file paths against the YAML file that declares them (#19259) 2026-09-13 17:27:03 -05:00
J. Nick Koston bc1841c1b5 [esphome] Allocate the OTA noise session and auth buffer through RAMAllocator (#19249) 2026-09-13 17:26:28 -05:00
J. Nick Koston ed5a570e17 [nextion] Allocate queue components through RAMAllocator and free entries the way they were allocated (#19246) 2026-09-13 17:26:14 -05:00
J. Nick Koston 16df92212e [esp32_ble_tracker] Revert coexistence preference to balanced when OTA starts (#19082) 2026-09-14 10:02:42 +12:00
Kevin Ahrendt 67871bcf55 [i2s_audio][router] Loop thread controls all state changes (#19089) 2026-09-14 10:01:58 +12:00
J. Nick Koston c51020dbaf [core] Add RAMAllocator::make_unique for objects whose allocation may fail (#19245) 2026-09-14 09:56:50 +12:00
J. Nick Koston eb1ea4aefe [esp32_ble_tracker] Re-register GATT clients after ble.disable and ble.enable (#19068) 2026-09-14 09:49:23 +12:00
J. Nick Koston b1bfc512ac [wifi] Drop a scan instead of aborting when its results cannot be allocated, filter ESP32 scans by SSID in the driver (#19254) 2026-09-14 09:49:13 +12:00
esphome[bot] ce8fad1435 Bump bundled esphome-device-builder to 1.14.9 (#19263) 2026-09-13 21:44:03 +00:00
J. Nick Koston 4f0faac148 [core] Add FixedVector::try_init so callers can handle an exhausted heap (#19253) 2026-09-14 09:16:37 +12:00
J. Nick Koston fc8611a212 [noise] Bump noise-c to 0.1.30 and libsodium to 1.10021.11 (#19062) 2026-09-14 09:14:26 +12:00
6b08aa60e6 [bluetooth_proxy] Add an advertisement filter hook (#19220)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-09-13 16:25:27 +00:00
esphome[bot] eea66fc32b Bump bundled esphome-device-builder to 1.14.8 (#19250) 2026-09-13 08:06:35 -05:00
Clyde Stubbs 5bbfe12e4f [core] Isolate contextvars per task in the coroutine runner (#19238) 2026-09-13 13:38:27 +10:00
Clyde Stubbs 533002c41e [lvgl] Fix crash when using lvgl.list.add (#19177) 2026-09-13 06:12:08 +10:00
ebb9037ea1 [bridge] New component and cdc_acm_uart platform (#11689)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-09-11 22:08:23 -05:00
Clyde StubbsandClaude Sonnet 5 eecea15f4f [core][lvgl] Migrate codegen helpers from LVGL to core code (#19105)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-11 23:42:08 +00:00
J. Nick Koston ff9b2a1c83 [remote_receiver] Size the RMT ring buffer from receive symbols by default (#19100) 2026-09-11 18:20:40 -05:00
esphome[bot] 9f9df85aa0 Bump aioesphomeapi from 46.4.0 to 46.4.1 (#19104) 2026-09-11 23:04:48 +00:00
puddly 2b71d5496d [const] Centralize definition of CONF_MANUFACTURER (#19098) 2026-09-11 15:45:02 -05:00
esphome[bot] 2578f17dc8 Bump bundled esphome-device-builder to 1.14.7 (#19096) 2026-09-11 14:25:41 -05:00
J. Nick Koston 37e9b2b7af [remote_base] Make protocol methods non-virtual and size receiver lists from codegen (#19084) 2026-09-11 14:12:40 -05:00
Jesse Hills 6a21ab4ea7 [esp32] Trim mbedTLS to client-only defaults and stub vasprintf on the C6 (#19088) 2026-09-11 08:30:51 +00:00
Keith BurzinskiandClaude Fable 5.1 380938177c [uart] Add apply_settings_live() for in-place ESP-IDF reconfiguration (#19087)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-11 03:25:52 -05:00
J. Nick Koston f66ef23256 [core] Support set_internal() during setup, log error after setup (#19069) 2026-09-11 03:20:30 -05:00
3213 changed files with 97692 additions and 36228 deletions
+116
View File
@@ -0,0 +1,116 @@
---
name: code-review
description: Review guidance for ESPHome pull requests. Use this when reviewing a pull request that changes ESPHome Python, C++, or component code, to check it against the project's coding conventions, embedded-systems memory rules, testing requirements, and breaking-change policy.
---
# Reviewing ESPHome pull requests
ESPHome parses YAML into C++ firmware for memory-constrained microcontrollers
(ESP32, ESP8266, RP2040, LibreTiny). Review changes with that in mind: RAM and
flash are scarce, and code runs unattended for months.
`AGENTS.md` in the repository root is the full contributor guide and the
authority when it disagrees with this summary. The developer documentation at
https://developers.esphome.io explains the component lifecycle and the reasoning
behind these rules. This skill lists the concrete things worth flagging in a
review; read `AGENTS.md` for the detail behind any item.
Only raise findings that the diff actually introduces or changes. Do not ask for
drive-by cleanup of pre-existing code the PR did not touch.
## Memory and embedded constraints (highest value)
Heap allocation after `setup()` is treated as a reliability bug, not a
performance nit, because it fragments a small shared heap. Flag:
- New heap allocation on a hot path or after setup that could be avoided.
- `std::vector` where the size is known at compile time (use `std::array`, or
`StaticVector<T, N>` when a `push_back` API is needed) or fixed at runtime
init (use `FixedVector<T>`).
- Listener / child-entity registration lists stored as `std::vector`; these have
a compile-time-known count and should use `cg.slot_counter()` plus
`StaticVector`.
- `std::vector<uint8_t>` for a byte buffer that never grows: prefer
`std::unique_ptr<uint8_t[]>` or `std::array`.
- `std::map` / `std::set` / `std::unordered_map` for small datasets (1-16
elements): a `std::vector` of a small struct with linear search is lighter.
- `std::deque` anywhere: it allocates 512-byte blocks and should be avoided.
- `std::string` storing a value set once from config: prefer `StringRef` (the
literal already lives in flash).
- `std::string` / `std::to_string` / string-returning helpers on hot paths where
a buffer or view API exists.
## C++ conventions
- Include what you use: a file referencing a symbol must include the header that
declares it, even if it currently arrives transitively. New or changed uses of
a symbol need the matching include.
- Prefix all member access with `this->`.
- Naming: `lower_snake_case` for functions/methods/variables, `UpperCamelCase`
for classes/structs/enums, `UPPER_SNAKE_CASE` for namespace-scope constants,
trailing underscore on protected/private fields.
- `enum class` values must be prefixed with the enum name in `UPPER_SNAKE_CASE`
(e.g. `UARTFlushResult::UART_FLUSH_RESULT_SUCCESS`). Bare names like `SUCCESS`,
`FAIL`, or `OK` collide with SDK macros on some platforms and break the build.
- Prefer `const`/`enum` over `#define`; `#define` is only for conditional
compilation and code-generation sizes.
- Never call `millis()` in a `loop()` body; use
`App.get_loop_component_start_time()`. A rate-limit gate below ~16 ms (the loop
period) does nothing.
- Pick the timing primitive by cadence: gated `loop()` under 250 ms,
`set_interval` at 500 ms and above.
- Do not override a base method to return the value it already returns (e.g.
`get_setup_priority()` returning `setup_priority::DATA`).
- Wrap string literals passed as printf `%s` args in `LOG_STR_LITERAL()`.
- Required, invariant dependencies should be constructor parameters, not setters.
- Callback registration methods must be templated (`template<typename F>`), not
typed as `std::function`, so lightweight forwarders avoid a heap allocation.
- Two-space indent, `using` over `typedef`, wrap at 120 columns.
## Python conventions
- Type-annotate every new function signature (params and return), new dataclass
fields, and new module-level variables. Import `ConfigType` from
`esphome.types`.
- Use the walrus operator to avoid a double lookup, e.g.
`if (blah := config.get(CONF_BLAH)) is not None:`.
- Reuse existing validators from `config_validation.py` (`cv.rename_key`,
`cv.has_exactly_one_key`, etc.) via `cv.All(...)` instead of hand-rolling.
- `esphome/const.py` is frozen: no new `CONF_` constants there. Define them in
the component's own `.py`, or in `esphome/components/const/__init__.py` when
shared. The same constant defined in three or more component files fails CI.
- State that must persist during code generation goes in `CORE.data` namespaced
under the component `DOMAIN` (a `@dataclass`), not module-level mutable globals.
- Prefer callback-based triggers via `build_callback_automation()`; only use a
`Trigger<Ts...>` subclass when the forwarder needs mutable state.
## Testing and coverage
- New and changed lines and branches need test coverage, including defensive
early-returns, error paths, and no-op guards. A mocked-out function is not
covered; exercise the real call path too.
- Component YAML tests live in `tests/components/<component>/`. Never define
buses (uart, i2c, spi, modbus) directly in a test file: pull them from
`tests/test_build_components/common/` through dict-style `packages:` so CI can
group builds. List-style packages or top-level merge keys block grouping.
- Config-only checks use the `validate.*.yaml` prefix; compiled checks use
`test.*.yaml`.
## Breaking changes and public API
- Base classes under `esphome/core/` and documented config options are public
API. Undocumented `public` members of a component are internal.
- A breaking change needs justification, a migration path in the PR description,
and a deprecation window where feasible (`ESPDEPRECATED` in C++,
`cv.rename_key(..., removed_in=...)` in Python). Changing a codegen-injected
lambda signature is not a breaking change.
## Process and PR hygiene
- PR titles start with a `[tag]` prefix: the component name (e.g. `[uart] ...`)
or `[core]` for shared code.
- Prose in docs, comments, and commit messages should be plain English. Keep
inline comments short and only where the code is not self-explanatory; do not
restate what the code says.
- Verify the PR fills out `.github/PULL_REQUEST_TEMPLATE.md` and adds
`CODEOWNERS` entries for a new component.
+1
View File
@@ -0,0 +1 @@
../.agents/skills
-3
View File
@@ -31,7 +31,4 @@ RUN \
platformio settings set enable_telemetry No \
&& platformio settings set check_platformio_interval 1000000
COPY script/platformio_install_deps.py platformio.ini ./
RUN ./platformio_install_deps.py platformio.ini --libraries --platforms --tools
WORKDIR /workspaces
+23
View File
@@ -9,6 +9,29 @@ body:
If you have a feature request or enhancement, please [request them here instead][fr].
[fr]: https://github.com/orgs/esphome/discussions
- type: markdown
attributes:
value: |
## Use of AI in bug reports
AI tools are good at carrying out well-defined tasks, but they are not good at troubleshooting.
Please do NOT paste an AI-generated wall of text into the issue template - if the AI hasn't solved
your problem, its wild guesses are not likely to help.
Please DO include your own words and observations, compile/boot logs, and
especially a minimal reproducible example of your YAML configuration that demonstrates the problem.
It is however quite acceptable to use AI to translate your *own* report,
if you aren't a competent English speaker.
If you really think it will be useful to include an AI's analysis, preferably wrap it in a `<details>` block which will be collapsed by default.
If you are using AI to help solve a problem, rather than asking it to speculate about what the problem is,
it can be more useful to ask it to create a step-by-step troubleshooting procedure.
AI is also useful for generating boilerplate code, such as a minimal reproducible example of your YAML
configuration that demonstrates the problem.
Used properly, AI can be a useful tool to help you solve your problem, but don't let it get in the way.
- type: textarea
validations:
required: true
+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@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.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@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
env:
DOCKER_BUILD_SUMMARY: false
DOCKER_BUILD_RECORD_UPLOAD: false
@@ -0,0 +1,39 @@
name: Cache Arduino ESP8266
description: >
Resolve the pinned Arduino core and xtensa toolchain versions and cache the
native ESP8266 install (~110 MB framework + toolchain; no ccache store, the
seed job saves before any compile runs). Exports
ESPHOME_ARDUINO8266_PREFIX to the job so every later step installs into
the cached path; the Python venv must already be restored. Mirrors
cache-esp-idf: only dev-branch pushes write the shared cache, everything
else restores.
runs:
using: composite
steps:
- name: Resolve the native toolchain cache key
# Versions are pinned in code, not a hashable file; resolve them so a
# bump changes the cache key. Assignment form so errexit catches a
# resolver failure.
id: version
shell: bash
run: |
# One owner for the install prefix: exported here and referenced by
# the cache steps below via env, so the caller's install and the
# cached path cannot diverge.
echo "ESPHOME_ARDUINO8266_PREFIX=$HOME/.esphome-arduino8266" >> "$GITHUB_ENV"
. venv/bin/activate
key=$(python -c 'from esphome.components.esp8266 import RECOMMENDED_ARDUINO_FRAMEWORK_VERSION as f; from esphome.arduino8266.framework import FRAMEWORK_RELEASES, TOOLCHAIN_VERSION as t; print(f"{FRAMEWORK_RELEASES[f].tag}-{t}")')
[ -n "$key" ] || exit 1
echo "key=$key" >> "$GITHUB_OUTPUT"
- name: Cache the native toolchain (write on dev)
if: github.ref == 'refs/heads/dev'
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ${{ env.ESPHOME_ARDUINO8266_PREFIX }}
key: ${{ runner.os }}-esp8266-native-${{ steps.version.outputs.key }}
- name: Restore the native toolchain (off dev)
if: github.ref != 'refs/heads/dev'
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ${{ env.ESPHOME_ARDUINO8266_PREFIX }}
key: ${{ runner.os }}-esp8266-native-${{ steps.version.outputs.key }}
+1 -1
View File
@@ -32,7 +32,7 @@ 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@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
enable-cache: true
# Pull request saves land in per-PR scopes nothing else can
+1 -1
View File
@@ -53,7 +53,7 @@ module.exports = async ({ github, context }) => {
});
const currentLabels = currentLabelsData.map(label => label.name);
const managedLabels = currentLabels.filter(label =>
label.startsWith('component: ') || MANAGED_LABELS.includes(label)
label.startsWith('component: ') || label.startsWith('platform: ') || MANAGED_LABELS.includes(label)
);
// Check for mega-PR early - if present, skip most automatic labeling
+1
View File
@@ -0,0 +1 @@
../.agents/skills
+1 -1
View File
@@ -29,7 +29,7 @@ jobs:
- 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@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
enable-cache: true
# Pull-request-only workflow: a save could never be shared and
+5 -4
View File
@@ -12,15 +12,14 @@ on:
- ".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/build_helpers/**"
- "esphome/espidf/**"
- "esphome/platformio/**"
- "esphome/components/bk72xx/**"
@@ -67,7 +66,7 @@ jobs:
with:
python-version: "3.12"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
uses: docker/setup-buildx-action@594f3bf4285d9ea8dc53c9a0c9c4092420091003 # v4.4.0
- name: Determine tag and whether to push
id: tag
@@ -159,7 +158,7 @@ jobs:
with:
python-version: "3.12"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
uses: docker/setup-buildx-action@594f3bf4285d9ea8dc53c9a0c9c4092420091003 # v4.4.0
- name: Log in to the GitHub container registry
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
@@ -197,6 +196,7 @@ jobs:
# the default.
id:
- esp8266-arduino
- esp8266-arduino-native
- esp32-arduino-platformio
- esp32-arduino-esp-idf
- esp32-idf-platformio
@@ -219,5 +219,6 @@ jobs:
run: |
docker run --rm \
-v "${{ github.workspace }}/docker/test_configs:/config" \
-e ESPHOME_LDGEN_STRICT=1 \
"ghcr.io/esphome/esphome-amd64:${{ needs.check-docker.outputs.tag }}" \
compile "${{ matrix.id }}.yaml"
+122 -42
View File
@@ -49,7 +49,7 @@ 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@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
enable-cache: true
# Pull request saves land in per-PR scopes nothing else can
@@ -102,6 +102,8 @@ jobs:
device-builder: ${{ steps.determine.outputs.device-builder }}
esp32-platformio: ${{ steps.determine.outputs.esp32-platformio }}
esp32-platformio-components: ${{ steps.determine.outputs.esp32-platformio-components }}
esp8266-native: ${{ steps.determine.outputs.esp8266-native }}
esp8266-native-components: ${{ steps.determine.outputs.esp8266-native-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 }}
@@ -165,6 +167,8 @@ jobs:
echo "device-builder=$(echo "$output" | jq -r '.device_builder')" >> $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 "esp8266-native=$(echo "$output" | jq -r '.esp8266_native')" >> $GITHUB_OUTPUT
echo "esp8266-native-components=$(echo "$output" | jq -r '.esp8266_native_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
@@ -183,6 +187,33 @@ jobs:
path: .temp/components_graph.json
key: components-graph-${{ hashFiles('esphome/components/**/*.py') }}
seed-esp8266-native-cache:
name: Seed the esp8266 native toolchain cache
runs-on: ubuntu-24.04
needs:
- common
# PR-branch cache saves are invisible to other PRs, so dev pushes seed
# the shared entry the component matrix, the memory impact jobs and
# test-esp8266-native restore. Only dev: the composite action saves
# nowhere else, so a beta/release push would download the toolchain and
# discard it.
if: github.event_name == 'push' && github.ref == 'refs/heads/dev'
timeout-minutes: 15
steps:
- name: Check out code from GitHub
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Restore Python
uses: ./.github/actions/restore-python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Cache the native toolchain
uses: ./.github/actions/cache-arduino8266
- name: Install the native toolchain
run: |
. venv/bin/activate
python -c "from esphome.arduino8266.framework import check_and_install; from esphome.components.esp8266 import RECOMMENDED_ARDUINO_FRAMEWORK_VERSION; check_and_install(RECOMMENDED_ARDUINO_FRAMEWORK_VERSION)"
ci-custom:
name: Run script/ci-custom
runs-on: ubuntu-24.04
@@ -255,7 +286,7 @@ jobs:
fi
echo "version=$version" >> "$GITHUB_OUTPUT"
- name: Run prek
uses: j178/prek-action@4e14d07f9231acabce116ccfca13b13dd9755ece # v3.0.0
uses: j178/prek-action@1382ad72dea72f9d53832f2bcb65b3e87a947204 # v3.0.1
with:
prek-version: ${{ steps.prek.outputs.version }}
# This job only runs on pull requests, so nothing ever populates
@@ -268,7 +299,7 @@ jobs:
# Pushes any fixes the hooks made back to the pull request. This step
# must keep its default name: the GitHub App that performs the push
# locates the workflow run by that name.
- uses: pre-commit-ci/lite-action@5d6cc0eb514c891a40562a58a8e71576c5c7fb43 # v1.1.0
- uses: pre-commit-ci/lite-action@062bca0919bc9d6e66755cc05074b70c77e111fc # v1.2.0
if: always()
with:
msg: apply automatic formatting fixes
@@ -321,7 +352,7 @@ jobs:
. venv/bin/activate
pytest -vv --cov-report=xml --tb=native --durations=30 -n auto tests --ignore=tests/integration/
- name: Upload coverage to Codecov
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
- name: Save Python virtual environment cache
if: github.ref == 'refs/heads/dev'
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
@@ -350,7 +381,7 @@ jobs:
- name: Check out code from GitHub
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Report empty upload to Codecov
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
with:
run_command: empty-upload
force: true
@@ -369,14 +400,9 @@ jobs:
matrix:
bucket: ${{ fromJson(needs.determine-jobs.outputs.integration-test-buckets) }}
env:
# What the cache steps persist; libdeps is excluded (keyed per xdist
# worker and env, it never crosses runs).
INTEGRATION_PIO_CACHE_PATH: |
~/.esphome-integration-tests/platformio/platforms
~/.esphome-integration-tests/platformio/packages
~/.esphome-integration-tests/platformio/appstate.json
~/.esphome-integration-tests/platformio/.cache
~/.esphome-integration-tests/platformio/.esphome.pio.stamp.json
# Registry libraries (noise-c, libsodium, ArduinoJson, lvgl) the host builds
# download, shared per xdist worker by tests/integration/conftest.py
INTEGRATION_LIBRARY_CACHE_PATH: ~/.esphome-integration-tests/pio_components
steps:
- name: Check out code from GitHub
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
@@ -396,14 +422,13 @@ jobs:
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.13"
- name: Restore integration PlatformIO cache
# Native platform + toolchain installed by shared_platformio_cache in
# tests/integration/conftest.py; a miss self-heals, so no restore-keys.
id: pio-cache
- name: Restore integration library cache
# A miss or a changed pin self-heals with a download, so no restore-keys
id: library-cache
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ${{ env.INTEGRATION_PIO_CACHE_PATH }}
key: integration-pio-v1-${{ runner.os }}-py${{ steps.python.outputs.python-version }}-${{ hashFiles('requirements.txt', 'tests/integration/fixtures/cache_init.yaml', 'esphome/components/host/__init__.py') }}
path: ${{ env.INTEGRATION_LIBRARY_CACHE_PATH }}
key: integration-libraries-v1-${{ runner.os }}-${{ hashFiles('esphome/components/json/__init__.py', 'esphome/components/noise/__init__.py', 'esphome/components/lvgl/__init__.py', 'esphome/components/improv_base/__init__.py') }}
- name: Restore Python virtual environment
id: cache-venv
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
@@ -413,7 +438,7 @@ jobs:
- 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@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
enable-cache: true
# Pull request saves land in per-PR scopes nothing else can
@@ -471,16 +496,16 @@ jobs:
# A full cron period of margin for the weekly refresh
retention-days: 14
- name: Print ccache statistics
# esphome stores the PlatformIO ccache under the machine-global cache
# dir (see _ccache_env() in esphome/platformio/toolchain.py).
run: CCACHE_DIR="$HOME/.cache/esphome/platformio-ccache" ccache -s
- name: Save integration PlatformIO cache
# esphome stores the host build's ccache under the machine-global
# cache dir (see get_build_env() in esphome/host/toolchain.py).
run: CCACHE_DIR="$HOME/.cache/esphome/host/ccache" ccache -s
- name: Save integration library cache
# Bucket 0 only; the others would race the same immutable key.
if: success() && (github.ref == 'refs/heads/dev' || contains(github.event.pull_request.labels.*.name, 'ci-cache-write')) && strategy.job-index == 0 && steps.pio-cache.outputs.cache-hit != 'true'
if: success() && (github.ref == 'refs/heads/dev' || contains(github.event.pull_request.labels.*.name, 'ci-cache-write')) && strategy.job-index == 0 && steps.library-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ${{ env.INTEGRATION_PIO_CACHE_PATH }}
key: ${{ steps.pio-cache.outputs.cache-primary-key }}
path: ${{ env.INTEGRATION_LIBRARY_CACHE_PATH }}
key: ${{ steps.library-cache.outputs.cache-primary-key }}
import-time:
name: Check import esphome.__main__ time
@@ -601,7 +626,7 @@ jobs:
apt-get install -y libc6-dbg
- name: Run CodSpeed benchmarks
uses: CodSpeedHQ/action@373d6868929f444bc08d901fd0eb0ad52a8875ea # v5.2.1
uses: CodSpeedHQ/action@c4fd08a3a159bd0cc208da1e0edf32b8c47d75e5 # v5.4.0
with:
run: |
. venv/bin/activate
@@ -1097,6 +1122,11 @@ jobs:
uses: ./.github/actions/cache-sdk-nrf
with:
restore-only: true
- name: Cache the native ESP8266 toolchain
# Only batches whose test platforms include esp8266; never saves
# here, it reuses the install the dev seed job cached.
if: matrix.batch.needs_arduino8266
uses: ./.github/actions/cache-arduino8266
- name: Validate and compile components with intelligent grouping
run: |
. venv/bin/activate
@@ -1195,9 +1225,28 @@ jobs:
if [ -n "$compile_csv" ]; then
# Run compilation with grouping and isolation
python3 script/test_build_components.py -e compile -c "$compile_csv" -f --isolate "$directly_changed_csv"
# The bootloader has no ESPHome code and these builds never
# flash; the check_idf_py batch keeps the full build so the
# native sub-build and the equivalence check stay covered.
skip_flag="--skip-bootloader"
if [[ "${{ matrix.batch.check_idf_py }}" == "true" ]]; then
skip_flag=""
fi
python3 script/test_build_components.py -e compile -c "$compile_csv" -f --isolate "$directly_changed_csv" $skip_flag
if [[ "${{ matrix.batch.check_idf_py }}" == "true" ]]; then
# The real idf.py must find nothing to configure or build in a
# tree built above; catches drift on ESP-IDF bumps.
echo "Checking the native ESP-IDF build matches idf.py"
python3 script/check_idf_py_equivalence.py
fi
else
echo "All components in this batch are validate-only -- skipping compile stage."
if [[ "${{ matrix.batch.check_idf_py }}" == "true" ]]; then
# determine-jobs and this step disagree on what compiles; fail
# rather than let the check run nowhere.
echo "::error::This batch was picked for the idf.py check but compiled nothing"
exit 1
fi
fi
- name: Print ccache statistics
@@ -1237,7 +1286,7 @@ jobs:
# 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
python3 script/test_build_components.py -e compile -t esp32-idf -c "$TEST_COMPONENTS" -f --toolchain platformio --fail-on-no-tests
echo ""
echo "ESP-IDF-via-PlatformIO build passed! Starting Arduino smoke test..."
@@ -1246,6 +1295,40 @@ jobs:
# 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
test-esp8266-native:
name: Test esp8266 components with the native toolchain
runs-on: ubuntu-24.04
needs:
- common
- determine-jobs
if: github.event_name == 'pull_request' && needs.determine-jobs.outputs.esp8266-native == 'true'
env:
# Computed by script/determine-jobs.py (ESP8266_NATIVE_TEST_COMPONENTS)
TEST_COMPONENTS: ${{ needs.determine-jobs.outputs.esp8266-native-components }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Restore Python
uses: ./.github/actions/restore-python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Cache the native toolchain
uses: ./.github/actions/cache-arduino8266
- name: Run native toolchain compile test
run: |
. venv/bin/activate
echo "Testing components: $TEST_COMPONENTS"
echo ""
# ESP8266 Arduino built directly (no PlatformIO); compile validates
# config first, so a separate config pass is redundant.
python3 script/test_build_components.py -e compile -t esp8266-ard -c "$TEST_COMPONENTS" -f --toolchain arduino --fail-on-no-tests
device-builder:
name: Test downstream esphome/device-builder
runs-on: ubuntu-24.04
@@ -1274,7 +1357,7 @@ 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@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
enable-cache: true
# Pull request saves land in per-PR scopes nothing else can
@@ -1415,12 +1498,9 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- 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@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.platformio
key: platformio-memory-${{ fromJSON(needs.determine-jobs.outputs.memory_impact).platform }}-${{ hashFiles('platformio.ini') }}
- name: Cache the native ESP8266 toolchain
if: steps.check-script.outputs.skip != 'true' && steps.check-tests.outputs.skip != 'true' && steps.cache-memory-analysis.outputs.cache-hit != 'true' && fromJSON(needs.determine-jobs.outputs.memory_impact).needs_arduino8266
uses: ./.github/actions/cache-arduino8266
- name: Build, compile, and analyze memory
if: steps.check-script.outputs.skip != 'true' && steps.check-tests.outputs.skip != 'true' && steps.cache-memory-analysis.outputs.cache-hit != 'true'
@@ -1505,11 +1585,9 @@ jobs:
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Cache platformio
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') }}
- name: Cache the native ESP8266 toolchain
if: fromJSON(needs.determine-jobs.outputs.memory_impact).needs_arduino8266
uses: ./.github/actions/cache-arduino8266
- name: Build, compile, and analyze memory
id: extract
run: |
@@ -1608,6 +1686,7 @@ jobs:
needs:
- common
- seed-apt-cache
- seed-esp8266-native-cache
- determine-jobs
- ci-custom
- pylint
@@ -1623,6 +1702,7 @@ jobs:
- clang-tidy-esp32-variants
- test-build-components-split
- test-esp32-platformio
- test-esp8266-native
- device-builder
- memory-impact-target-branch
- memory-impact-pr-branch
+2 -2
View File
@@ -56,7 +56,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
uses: github/codeql-action/init@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
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@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9
uses: github/codeql-action/analyze@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
with:
category: "/language:${{matrix.language}}"
+14 -8
View File
@@ -125,14 +125,20 @@ jobs:
}
async function getEsphomeAndComponentChanges(github, owner, repo, prNumber) {
const changedFiles = await github.rest.pulls.listFiles({
owner: owner,
repo: repo,
pull_number: prNumber,
});
const changedFiles = await github.paginate(
github.rest.pulls.listFiles,
{
owner: owner,
repo: repo,
pull_number: prNumber,
per_page: 100,
}
);
const esphomeChanges = changedFiles.data
.filter(file => file.filename !== "esphome/core/defines.h" && file.filename.startsWith('esphome/'))
// Files used only for development and CI, which do not affect use as an external component
const ignoredFiles = ["esphome/core/defines.h", "esphome/idf_component.yml"];
const esphomeChanges = changedFiles
.filter(file => !ignoredFiles.includes(file.filename) && file.filename.startsWith('esphome/'))
.map(file => {
const match = file.filename.match(/esphome\/([^/]+)/);
return match ? match[1] : null;
@@ -144,7 +150,7 @@ jobs:
}
const uniqueEsphomeChanges = [...new Set(esphomeChanges)];
const componentChanges = changedFiles.data
const componentChanges = changedFiles
.filter(file => file.filename.startsWith('esphome/components/'))
.map(file => {
const match = file.filename.match(/esphome\/components\/([^/]+)\//);
+1 -1
View File
@@ -14,4 +14,4 @@ jobs:
permissions:
issues: write # issues.lock on closed issues
pull-requests: write # issues.lock on closed pull requests
uses: esphome/workflows/.github/workflows/lock.yml@0fdd5e311b7e744069166696072a1a9cbc5fbeb6 # 2026.8.1
uses: esphome/workflows/.github/workflows/lock.yml@cc3e76de337dc59bc1cba8da58d963cd23b873f1 # 2026.9.0
+2 -2
View File
@@ -123,7 +123,7 @@ jobs:
python-version: "3.12"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
uses: docker/setup-buildx-action@594f3bf4285d9ea8dc53c9a0c9c4092420091003 # v4.4.0
- name: Log in to docker hub
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
@@ -202,7 +202,7 @@ jobs:
merge-multiple: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
uses: docker/setup-buildx-action@594f3bf4285d9ea8dc53c9a0c9c4092420091003 # v4.4.0
- name: Log in to docker hub
if: matrix.registry == 'dockerhub'
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
# No GITHUB_TOKEN permissions: the reusable workflow mints an ESPHome
# GitHub App token so the labels, comments and closures come from
# esphome[bot] instead of github-actions[bot].
uses: esphome/workflows/.github/workflows/stale.yml@a1c1485ab46ef41a84a6a9d8abd7fa4b7628fd70 # main
uses: esphome/workflows/.github/workflows/stale.yml@cc3e76de337dc59bc1cba8da58d963cd23b873f1 # main
secrets:
ESPHOME_GITHUB_APP_PRIVATE_KEY: ${{ secrets.ESPHOME_GITHUB_APP_PRIVATE_KEY }}
with:
@@ -7,10 +7,6 @@ on:
permissions:
pull-requests: read # issues.listLabelsOnIssue to detect blocking labels (needs-docs, needs-developer-docs, merge-after-release, chained-pr)
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
check:
name: Check blocking labels
@@ -15,6 +15,8 @@ on:
# base branch checkout, so fork code never runs with the token.
pull_request_target:
types: [opened, synchronize, reopened]
branches:
- dev
paths:
- requirements_dev.txt
- requirements_test.txt
+1 -1
View File
@@ -47,7 +47,7 @@ jobs:
# setup-python interpreter so subsequent ``prek`` /
# ``script/run-in-env.py`` steps find the deps without a
# ``uv run`` prefix.
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
enable-cache: true
# Pin uv version so the action does not have to fetch the
+2 -2
View File
@@ -10,7 +10,7 @@ ci:
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.16.6
rev: v0.16.10
hooks:
# Run the linter.
- id: ruff
@@ -18,7 +18,7 @@ repos:
# Run the formatter.
- id: ruff-format
- repo: https://github.com/PyCQA/flake8
rev: 7.3.0
rev: 7.4.1
hooks:
- id: flake8
additional_dependencies:
+65 -3
View File
@@ -322,6 +322,25 @@ file does, and it is the authority when they disagree. The most useful starting
var = await switch.new_switch(config)
```
- **Optional child entities of a hub:** bind the config once with `sensor.sub_sensors(config)` (or
`sub_binary_sensors`, `sub_text_sensors`, `sub_buttons`, `sub_switches`, `sub_numbers`,
`sub_selects` in their domains), adding `parent=hub` for entities that derive from `Parented<T>`,
then make one call per key, even when there is only one. A call creates the entity only when its key
is configured, passes it to the setter and returns it (or `None`); extra arguments such as
`min_value` or `options` go on the call. Always name the setter explicitly on the object that owns
it, never with `getattr` and an f-string, and keep that variable short (`var` for the component
itself, `hub` for one fetched with `cg.get_variable`) so the calls fit on one line. Loops whose
setter also takes an index, such as `set_gate_threshold(x, n)`, stay as they are.
```python
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
sensors = sensor.sub_sensors(config)
await sensors(CONF_TEMPERATURE, var.set_temperature_sensor)
await sensors(CONF_HUMIDITY, var.set_humidity_sensor)
buttons = button.sub_buttons(config, parent=var)
await buttons(CONF_RESTART, var.set_restart_button)
```
* **Automations (Triggers, Actions, Conditions):**
Automations have three building blocks: **Triggers** (fire when something happens), **Actions** (do something), and **Conditions** (check if something is true).
@@ -431,7 +450,31 @@ file does, and it is the authority when they disagree. The most useful starting
MyComponent *parent_;
};
```
Register with `@automation.register_action("my_component.do_something", MyAction, schema, synchronous=True)`. Use `synchronous=True` for actions that run to completion inside `play()` without deferring. Use `synchronous=False` if the action may suspend/defer execution (e.g. `delay`, `wait_until`, `script.wait`) or store trigger arguments for later use.
Register it without writing a builder:
```python
automation.register_simple_action(
"my_component.do_something", MyAction, schema, synchronous=True
)
```
The constructor receives the object named by `config[CONF_ID]`. Use `register_bare_action` for a
no-argument constructor, `register_parented_action` for a class deriving from `Parented<T>`, and
the `@automation.register_action(...)` decorator only when the builder must also set fields.
Use `synchronous=True` for actions that run to completion inside `play()` without deferring. Use `synchronous=False` if the action may suspend/defer execution (e.g. `delay`, `wait_until`, `script.wait`) or store trigger arguments for later use.
**Actions that only forward templatable values to their parent need no C++ class.** Register them
with `register_apply_action`; do not write a `TEMPLATABLE_VALUE` class or a builder for this shape.
```python
automation.register_apply_action(
"my_component.set_gains",
schema,
automation.ApplyField(CONF_KP, "set_kp", cg.float_),
automation.ApplyField(CONF_KI, "set_ki", cg.float_),
)
```
The `ApplyField`, `ApplyCall` and `register_apply_action` docstrings in `esphome/automation.py` cover
the rest; `cover.control` and `cover.template.publish` are in-tree examples. `TEMPLATABLE_VALUE` with
`cg.templatable` stays for actions whose `play()` has real logic beyond forwarding values.
* **Conditions:**
```cpp
@@ -443,7 +486,21 @@ file does, and it is the authority when they disagree. The most useful starting
MyComponent *parent_;
};
```
Register with `@automation.register_condition("my_component.is_active", MyCondition, schema)`.
Register with `automation.register_simple_condition("my_component.is_active", MyCondition, schema)`;
`register_bare_condition`, `register_parented_condition` and the decorator follow the action rules.
**Conditions that only test their parent need no C++ class either.** Register them with
`register_apply_condition`; the expression is applied to the parent, and an `ApplyCall` compares
against config values.
```python
automation.register_apply_condition("my_component.is_active", schema, "is_active()")
automation.register_apply_condition(
"my_component.state_is",
schema,
automation.ApplyCall("state == {}", ((CONF_STATE, cg.bool_),)),
)
```
`cover.is_open`, `rtttl.is_playing` and `component.is_idle` are in-tree examples.
* **Type Hints:** Type-hint all function signatures, including test functions and config validators (e.g. `def validate_x(config: ConfigType) -> ConfigType:`, `def test_x() -> None:`). Import `ConfigType` from `esphome.types`.
@@ -629,6 +686,9 @@ file does, and it is the authority when they disagree. The most useful starting
_request_listener_slot()
cg.add(hub.register_listener(var))
```
When several instances each own a list declared at the same size (one per hub of a
`MULTI_CONF` component), pass the owning object as the key, `_request_listener_slot(str(hub))`;
the define is then the largest count any one key requested instead of the total.
```cpp
#ifdef MY_COMPONENT_LISTENER_COUNT
void register_listener(MyComponentListener *listener);
@@ -696,7 +756,9 @@ file does, and it is the authority when they disagree. The most useful starting
6. **Avoid `std::deque`:** It allocates in 512-byte blocks regardless of element size, guaranteeing at least 512 bytes of RAM usage immediately. This is a major source of crashes on memory-constrained devices.
7. **Detection:** Look for these patterns in compiler output:
7. **Never use `new (std::nothrow)`:** On ESP-IDF exceptions are disabled, so a failed nothrow allocation aborts instead of returning `nullptr`. Use `RAMAllocator` from `esphome/core/helpers.h`; CI rejects `std::nothrow`.
8. **Detection:** Look for these patterns in compiler output:
- Large code sections with STL symbols (vector, map, set)
- `alloc`, `realloc`, `dealloc` in symbol names
- `_M_realloc_insert`, `_M_default_append` (vector reallocation)
+27 -1
View File
@@ -100,6 +100,7 @@ esphome/components/bmp581_i2c/* @danielkent-net @kahrendt
esphome/components/bmp581_spi/* @danielkent-net @kahrendt
esphome/components/bp1658cj/* @Cossid
esphome/components/bp5758d/* @Cossid
esphome/components/bridge/* @kbx81
esphome/components/bthome_mithermometer/* @nagyrobi
esphome/components/button/* @esphome/core
esphome/components/bytebuffer/* @clydebarrow
@@ -111,6 +112,8 @@ esphome/components/captive_portal/* @esphome/core
esphome/components/cc1101/* @gabest11 @lygris
esphome/components/ccs811/* @habbie
esphome/components/cd74hc4067/* @asoehlke
esphome/components/cdc_acm_uart/* @kbx81
esphome/components/cdc_acm_uart/bridge/* @kbx81
esphome/components/ch422g/* @clydebarrow @jesterret
esphome/components/ch423/* @dwmw2
esphome/components/chsc6x/* @kkosik20
@@ -122,6 +125,7 @@ esphome/components/combination/* @Cat-Ion @kahrendt
esphome/components/const/* @esphome/core
esphome/components/coolix/* @glmnet
esphome/components/copy/* @OttoWinter
esphome/components/counter/* @clydebarrow
esphome/components/cover/* @esphome/core
esphome/components/cs5460a/* @balrog-kun
esphome/components/cse7761/* @berfenger
@@ -179,15 +183,16 @@ esphome/components/esp32_camera_web_server/* @ayufan
esphome/components/esp32_can/* @Sympatron
esphome/components/esp32_hosted/* @swoboda1337
esphome/components/esp32_hosted/update/* @swoboda1337
esphome/components/esp32_improv/* @jesserockz
esphome/components/esp32_rmt/* @jesserockz
esphome/components/esp32_rmt_led_strip/* @jesserockz
esphome/components/esp8266/* @esphome/core
esphome/components/esp_ldo/* @clydebarrow
esphome/components/espectre/* @francescopace
esphome/components/espnow/* @jesserockz
esphome/components/espnow/packet_transport/* @EasilyBoredEngineer
esphome/components/ethernet_info/* @gtjadsonsantos
esphome/components/event/* @nohat
esphome/components/exponential_moving_average/* @clydebarrow
esphome/components/exposure_notifications/* @OttoWinter
esphome/components/ezo/* @ssieb
esphome/components/ezo_pmp/* @carlos-sarmiento
@@ -242,8 +247,11 @@ esphome/components/hmac_md5/* @dwmw2
esphome/components/hmac_sha256/* @dwmw2
esphome/components/hoermann_hcp/* @zweckj
esphome/components/homeassistant/* @esphome/core @OttoWinter
esphome/components/homeassistant/button/* @jesserockz
esphome/components/homeassistant/number/* @landonr
esphome/components/homeassistant/select/* @jesserockz
esphome/components/homeassistant/switch/* @Links2004
esphome/components/homeassistant/text/* @jesserockz
esphome/components/honeywell_hih_i2c/* @Benichou34
esphome/components/honeywellabp/* @RubyBailey
esphome/components/honeywellabp2_i2c/* @jpfaff
@@ -263,8 +271,10 @@ esphome/components/i2s_audio/* @jesserockz
esphome/components/i2s_audio/microphone/* @jesserockz
esphome/components/i2s_audio/speaker/* @jesserockz @kahrendt
esphome/components/iaqcore/* @yozik04
esphome/components/icnt86/* @danepowell
esphome/components/ili9xxx/* @clydebarrow @nielsnl68
esphome/components/improv_base/* @esphome/core
esphome/components/improv_ble/* @jesserockz
esphome/components/improv_serial/* @esphome/core
esphome/components/ina226/* @latonita @Sergio303
esphome/components/ina260/* @mreditor97
@@ -277,6 +287,7 @@ esphome/components/inkplate/* @jesserockz @JosipKuci
esphome/components/integration/* @OttoWinter
esphome/components/internal_temperature/* @Mat931
esphome/components/interval/* @esphome/core
esphome/components/ir_rf_base/* @bdraco @kbx81
esphome/components/ir_rf_proxy/* @kbx81
esphome/components/it8951/* @koosoli @limengdu @Passific
esphome/components/jsn_sr04t/* @Mafus1
@@ -418,6 +429,7 @@ esphome/components/pn7150_i2c/* @jesserockz @kbx81
esphome/components/pn7160/* @jesserockz @kbx81
esphome/components/pn7160_i2c/* @jesserockz @kbx81
esphome/components/pn7160_spi/* @jesserockz @kbx81
esphome/components/pn71xx/* @jesserockz @kbx81
esphome/components/power_supply/* @esphome/core
esphome/components/preferences/* @esphome/core
esphome/components/provisioning/* @esphome/core
@@ -425,6 +437,7 @@ esphome/components/psram/* @esphome/core
esphome/components/pulse_meter/* @cstaahl @stevebaxter @TrentHouliston
esphome/components/pvvx_mithermometer/* @pasiz
esphome/components/pylontech/* @functionpointer
esphome/components/pzem6l24/* @nuttytree
esphome/components/qmi8658/* @clydebarrow
esphome/components/qmp6988/* @andrewpc
esphome/components/qr_code/* @wjtje
@@ -453,6 +466,7 @@ esphome/components/rtl87xx/* @kuba2k2
esphome/components/rtttl/* @glmnet @ximex
esphome/components/runtime_image/* @clydebarrow @guillempages @kahrendt
esphome/components/runtime_stats/* @bdraco
esphome/components/rx8025t/* @remcom
esphome/components/rx8130/* @beormund
esphome/components/safe_mode/* @jsuanet @kbx81 @paulmonigatti
esphome/components/scd4x/* @martgras @sjtrny
@@ -474,6 +488,7 @@ esphome/components/sendspin/image/* @kahrendt
esphome/components/sendspin/media_player/* @kahrendt
esphome/components/sendspin/media_source/* @kahrendt
esphome/components/sendspin/sensor/* @kahrendt
esphome/components/sendspin/switch/* @kahrendt
esphome/components/sendspin/text_sensor/* @kahrendt
esphome/components/sensirion_common/* @martgras
esphome/components/sensor/* @esphome/core
@@ -532,6 +547,7 @@ esphome/components/st7735/* @SenexCrenshaw
esphome/components/st7789v/* @kbx81
esphome/components/st7920/* @marsjan155
esphome/components/statsd/* @Links2004
esphome/components/stcc4/* @j9brown
esphome/components/stts22h/* @B48D81EFCC
esphome/components/substitutions/* @esphome/core
esphome/components/sun/* @OttoWinter
@@ -542,11 +558,15 @@ esphome/components/sx126x/* @swoboda1337
esphome/components/sx127x/* @swoboda1337
esphome/components/sy6970/* @linkedupbits
esphome/components/syslog/* @clydebarrow
esphome/components/systa_bus/* @Mat931
esphome/components/t6615/* @tylermenezes
esphome/components/tas2780/* @remcom
esphome/components/tas58xx/* @mrtoy-me @remcom
esphome/components/tc74/* @sethgirvan
esphome/components/tca9548a/* @andreashergert1984
esphome/components/tca9555/* @mobrembski
esphome/components/tcl112/* @glmnet
esphome/components/tcp_uart/* @Bascht74
esphome/components/tee501/* @Stock-M
esphome/components/teleinfo/* @0hax
esphome/components/tem3200/* @bakerkj
@@ -555,6 +575,7 @@ esphome/components/template/datetime/* @rfdarter
esphome/components/template/event/* @nohat
esphome/components/template/fan/* @ssieb
esphome/components/text/* @mauritskorse
esphome/components/tfluna/* @candrews
esphome/components/thermopro_ble/* @sittner
esphome/components/thermostat/* @kbx81
esphome/components/time/* @esphome/core
@@ -586,11 +607,14 @@ esphome/components/uart/* @esphome/core
esphome/components/uart/button/* @ssieb
esphome/components/uart/event/* @eoasmxd
esphome/components/uart/packet_transport/* @clydebarrow
esphome/components/uart_mux/* @kbx81
esphome/components/uart_tcp/* @Bascht74
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/unicode/* @esphome/core
esphome/components/update/* @jesserockz
esphome/components/uponor_smatrix/* @kroimon
esphome/components/usb_cdc_acm/* @kbx81
@@ -630,9 +654,11 @@ esphome/components/wts01/* @alepee
esphome/components/x9c/* @EtienneMD
esphome/components/xdb401/* @RT530
esphome/components/xgzp68xx/* @gcormier
esphome/components/xiaomi_body_scale/* @dckiller51
esphome/components/xiaomi_hhccjcy10/* @fariouche
esphome/components/xiaomi_lywsd02mmc/* @juanluss31
esphome/components/xiaomi_lywsd03mmc/* @ahpohl
esphome/components/xiaomi_mccgq02hl/* @ahpohl @morph027
esphome/components/xiaomi_mhoc303/* @drug123
esphome/components/xiaomi_mhoc401/* @vevsvevs
esphome/components/xiaomi_rtcgq02lm/* @jesserockz
+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.10.0-dev
PROJECT_NUMBER = 2026.11.0-dev
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
+1 -4
View File
@@ -22,16 +22,13 @@ RUN \
-r /requirements.txt
# Install the ESPHome Device Builder dashboard.
RUN uv pip install --no-cache-dir esphome-device-builder==1.14.6
RUN uv pip install --no-cache-dir esphome-device-builder==1.22.0
RUN \
platformio settings set enable_telemetry No \
&& platformio settings set check_platformio_interval 1000000 \
&& mkdir -p /piolibs
COPY script/platformio_install_deps.py platformio.ini /
RUN /platformio_install_deps.py /platformio.ini --libraries
ARG BUILD_VERSION
LABEL \
+6 -2
View File
@@ -21,10 +21,14 @@ export PLATFORMIO_PLATFORMS_DIR="${pio_cache_base}/platforms"
export PLATFORMIO_PACKAGES_DIR="${pio_cache_base}/packages"
export PLATFORMIO_CACHE_DIR="${pio_cache_base}/cache"
# Keep the native toolchain installs on the persistent cache root, not the
# container's ephemeral user cache dir (re-downloaded on every restart).
# Keep the native toolchain installs and compiler caches on the persistent
# cache root, not the container's user cache dir: it is lost on every
# restart, and not writable when the container runs as a non-root user.
export ESPHOME_ESP_IDF_PREFIX="$(dirname "${pio_cache_base}")/idf"
export ESPHOME_SDK_NRF_PREFIX="$(dirname "${pio_cache_base}")/sdk-nrf"
export ESPHOME_ARDUINO8266_PREFIX="$(dirname "${pio_cache_base}")/arduino8266"
export ESPHOME_HOST_PREFIX="$(dirname "${pio_cache_base}")/host"
export ESPHOME_PLATFORMIO_CCACHE_DIR="$(dirname "${pio_cache_base}")/platformio-ccache"
# If /build is mounted, use that as the build path
# otherwise use path in /config (so that builds aren't lost on container restart)
@@ -4,7 +4,6 @@
# Home Assistant Add-on: ESPHome
# Sends discovery information to Home Assistant.
# ==============================================================================
declare config
declare port
# We only disable it when disabled explicitly
@@ -19,14 +18,17 @@ port=$(bashio::addon.ingress_port)
# Wait for the ESPHome Device Builder to become available
bashio::net.wait_for "${port}" "127.0.0.1" 300
config=$(\
bashio::var.json \
host "127.0.0.1" \
port "^${port}" \
)
# Send one discovery message; the config is a JSON string built with bashio::var.json.
send_discovery() {
local service=$1
local config=$2
if bashio::discovery "${service}" "${config}" > /dev/null; then
bashio::log.info "Successfully send ${service} discovery information to Home Assistant."
else
bashio::log.error "${service} discovery message to Home Assistant failed!"
fi
}
if bashio::discovery "esphome" "${config}" > /dev/null; then
bashio::log.info "Successfully send discovery information to Home Assistant."
else
bashio::log.error "Discovery message to Home Assistant failed!"
fi
send_discovery "esphome" "$(bashio::var.json host "127.0.0.1" port "^${port}")"
# The Device Builder MCP server, consumed by Home Assistant's mcp integration.
send_discovery "mcp" "$(bashio::var.json url "http://127.0.0.1:${port}/api/mcp")"
@@ -15,10 +15,14 @@ export PLATFORMIO_PLATFORMS_DIR="${pio_cache_base}/platforms"
export PLATFORMIO_PACKAGES_DIR="${pio_cache_base}/packages"
export PLATFORMIO_CACHE_DIR="${pio_cache_base}/cache"
# Keep the native toolchain installs on the persistent /data volume, not the
# container's ephemeral user cache dir (wiped on every add-on update/restart).
# Keep the native toolchain installs and compiler caches 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
export ESPHOME_ARDUINO8266_PREFIX=/data/cache/arduino8266
export ESPHOME_HOST_PREFIX=/data/cache/host
export ESPHOME_PLATFORMIO_CCACHE_DIR=/data/cache/platformio-ccache
if bashio::config.true 'leave_front_door_open'; then
export DISABLE_HA_AUTHENTICATION=true
+12
View File
@@ -5,3 +5,15 @@ bk72xx:
board: generic-bk7231n-qfn32-tuya
logger:
wifi:
ssid: MySSID
password: password1
ap:
# mqtt and captive_portal together pull in AsyncTCP and ESPAsyncWebServer;
# a stray ESP32 AsyncTCP copy on the library search path breaks this build
captive_portal:
mqtt:
broker: 192.168.178.84
@@ -0,0 +1,8 @@
esphome:
name: docker-test-esp8266-native
esp8266:
board: d1_mini
toolchain: arduino
logger:
+2
View File
@@ -3,5 +3,7 @@ esphome:
esp8266:
board: d1_mini
# The PlatformIO path stays covered whatever the default is
toolchain: platformio
logger:
+157 -212
View File
@@ -7,6 +7,7 @@ import importlib
import logging
import os
from pathlib import Path
import platform
import re
import sys
import time
@@ -16,8 +17,8 @@ from typing import TYPE_CHECKING, Protocol
# cause them to be loaded before external components are processed, resulting
# in the built-in version being used instead of the external component one.
from esphome import const, platform_hooks
from esphome.build_helpers.native import analysis_backend, native_backend
from esphome.const import (
ALLOWED_NAME_CHARS,
ARGUMENT_HELP_DEVICE,
BUNDLE_EXTENSION,
CONF_API,
@@ -35,7 +36,6 @@ from esphome.const import (
CONF_LOGGER,
CONF_MDNS,
CONF_MQTT,
CONF_NAME,
CONF_NAME_ADD_MAC_SUFFIX,
CONF_OTA,
CONF_PASSWORD,
@@ -55,12 +55,13 @@ from esphome.const import (
)
from esphome.core import CORE, EsphomeError, coroutine
from esphome.enum import StrEnum
from esphome.helpers import get_bool_env, indent, is_ip_address
from esphome.helpers import IS_MACOS, get_bool_env, indent, is_ip_address
from esphome.log import AnsiFore, color, setup_log
from esphome.stacktrace import LogLineProcessor
from esphome.types import ConfigType
from esphome.upload_targets import PortType, get_port_type
from esphome.util import (
ESPHOME_COMMAND,
PICOTOOL_PACKAGE,
FlashImage,
detect_rp2040_bootsel,
@@ -84,7 +85,6 @@ if TYPE_CHECKING:
_LOGGER = logging.getLogger(__name__)
ESPHOME_COMMAND = [sys.executable, "-m", "esphome"]
# Maximum buffer size for serial log reading to prevent unbounded memory growth
SERIAL_BUFFER_MAX_SIZE = 65536
@@ -149,6 +149,7 @@ class ArgsProtocol(Protocol):
file: str | None
no_logs: bool
only_generate: bool
skip_bootloader: bool
show_secrets: bool
dashboard: bool
configuration: str
@@ -817,7 +818,9 @@ def write_cpp_file() -> int:
from esphome.build_gen import espidf
espidf.write_project()
else:
elif not CORE.using_native_toolchain:
# Other native builds generate their project at compile time;
# never write a platformio.ini for them
from esphome.build_gen import platformio
platformio.write_project()
@@ -826,6 +829,14 @@ def write_cpp_file() -> int:
def compile_program(args: ArgsProtocol, config: ConfigType) -> int:
if CORE.skip_bootloader and not (CORE.is_esp32 and CORE.using_toolchain_esp_idf):
# Debug only: an orchestrator cannot see YAML toolchain overrides,
# so this is its expected no-op, and a full build is safe.
_LOGGER.debug(
"--skip-bootloader ignored: only supported on ESP32 with the "
"esp-idf toolchain"
)
CORE.skip_bootloader = False
# Keep this gate here, NOT in config validation: device-builder needs
# `esphome config` to keep succeeding with placeholders so onboarding can run.
if CONF_WIFI in config:
@@ -835,7 +846,7 @@ def compile_program(args: ArgsProtocol, config: ConfigType) -> int:
# 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:
if CORE.is_esp8266 and CORE.using_toolchain_platformio:
from esphome.components.esp8266 import check_rosetta
check_rosetta()
@@ -856,23 +867,20 @@ def compile_program(args: ArgsProtocol, config: ConfigType) -> int:
return rc
# Create factory.bin, ota.bin, and firmware.elf copy
toolchain.create_factory_bin()
if not toolchain.create_factory_bin():
# A build whose factory image could not be produced must not
# exit 0; downloads would serve an image from an older build.
return 1
toolchain.create_ota_bin()
toolchain.create_elf_copy()
from esphome.build_helpers.idedata import IDEDATA_BEST_EFFORT_ERRORS
from esphome.build_helpers.idedata import warn_if_idedata_missing
try:
if toolchain.get_idedata() is None:
_LOGGER.warning("No idedata was generated for this build")
except IDEDATA_BEST_EFFORT_ERRORS as err:
# The firmware already built; an idedata failure must not fail
# a successful build.
_LOGGER.warning(
"Could not generate idedata: %s (IDE, clang-tidy, and "
"memory-analysis data will be unavailable for this build)",
err,
)
_LOGGER.debug("Idedata failure detail", exc_info=True)
warn_if_idedata_missing(toolchain.get_idedata)
elif CORE.using_native_toolchain:
raise EsphomeError(
f"Toolchain '{CORE.toolchain.value}' resolved but no platform "
"backend claimed the build"
)
else:
from esphome.platformio import toolchain
@@ -975,12 +983,16 @@ def upload_using_esptool(
if file is not None:
flash_images = [FlashImage(path=file, offset="0x0")]
elif CORE.using_toolchain_esp_idf:
from esphome.espidf import toolchain
flash_images = [
FlashImage(path=toolchain.get_factory_firmware_path(), offset="0x0")
]
elif (native := native_backend()) is not None:
# Every native backend supplies its own 0x0 flash image (bootloader
# and partitions included where the target needs them)
image = native.get_factory_firmware_path()
if not image.is_file():
hint = getattr(native, "missing_image_hint", lambda: None)()
raise EsphomeError(
hint or f"{image} does not exist; compile the configuration first"
)
flash_images = [FlashImage(path=image, offset="0x0")]
else:
from esphome.platformio import toolchain
@@ -1289,10 +1301,9 @@ def _choose_ota_platform(config: ConfigType, requested: str | None) -> str:
The native API uses challenge-response auth with MD5/SHA256 hashing of a
server-issued nonce, so the password is never sent over the wire; the
``web_server`` path uses HTTP Basic auth which transmits credentials in
cleartext over the LAN. (The native path also supports gzip compression
on ESP8266, where flash space is tight; on ESP32/RP2040/LibreTiny the
backend reports ``supports_compression() == false`` and the firmware is
sent uncompressed regardless of which platform is used.) Falls back to
cleartext over the LAN. (The native path also compresses the upload:
gzip on ESP8266 and RP2040, which inflate it at reboot, and a deflate
stream on ESP32/LibreTiny, which inflate it as it arrives.) Falls back to
``web_server`` only when that is the only available platform.
"""
# Use a dict (insertion-ordered) instead of a list so error messages and
@@ -1301,9 +1312,9 @@ def _choose_ota_platform(config: ConfigType, requested: str | None) -> str:
# platform's final-validate hook merges duplicates anyway.
available: dict[str, None] = {}
for ota_item in config.get(CONF_OTA, []):
platform = ota_item.get(CONF_PLATFORM)
if platform in (CONF_ESPHOME, CONF_WEB_SERVER):
available[platform] = None
ota_platform = ota_item.get(CONF_PLATFORM)
if ota_platform in (CONF_ESPHOME, CONF_WEB_SERVER):
available[ota_platform] = None
if not available:
raise EsphomeError(
@@ -1343,8 +1354,12 @@ def _upload_via_native_api(
# fall back to a plaintext upload
noise_psk = None
plaintext_fallback = False
allow_plaintext_upload = False
if (encryption_conf := ota_conf.get(CONF_ENCRYPTION)) is not None:
noise_psk = encryption_conf.get(CONF_KEY)
allow_plaintext_upload = bool(
encryption_conf.get(espota2.CONF_ALLOW_PLAINTEXT_UPLOAD)
)
if not noise_psk:
raise EsphomeError(
"OTA encryption is configured but no key was resolved; "
@@ -1377,6 +1392,12 @@ def _upload_via_native_api(
ota_type = espota2.OTA_TYPE_UPDATE_PARTITION_TABLE
elif getattr(args, "bootloader", False):
check_partition_access("--bootloader")
if (
getattr(args, "file", None) is None
and (native := native_backend())
and (hint := getattr(native, "missing_image_hint", lambda: None)())
):
raise EsphomeError(hint)
binary = CORE.bootloader_bin
ota_type = espota2.OTA_TYPE_UPDATE_BOOTLOADER
if getattr(args, "file", None) is not None:
@@ -1395,6 +1416,7 @@ def _upload_via_native_api(
ota_type,
noise_psk,
plaintext_fallback=plaintext_fallback,
allow_plaintext_upload=allow_plaintext_upload,
)
@@ -1709,26 +1731,12 @@ def command_compile(args: ArgsProtocol, config: ConfigType) -> int | None:
if exit_code != 0:
return exit_code
if CORE.is_host:
_LOGGER.info(
"Successfully compiled program to path '%s'", _host_program_path(config)
)
_LOGGER.info("Successfully compiled program to path '%s'", CORE.firmware_bin)
else:
_LOGGER.info("Successfully compiled program.")
return 0
def _host_program_path(config: ConfigType) -> str:
"""Return the compiled host ELF path."""
if CORE.using_toolchain_esp_idf:
from esphome.espidf import toolchain
return str(toolchain.get_elf_path())
from esphome.platformio.toolchain import get_idedata
# Memoized by compile_program's own call; this is a dict lookup
return str(get_idedata(config).firmware_elf_path)
def command_upload(args: ArgsProtocol, config: ConfigType) -> int | None:
# Get devices, resolving special identifiers like OTA
devices = choose_upload_log_host(
@@ -1765,6 +1773,18 @@ def command_logs(args: ArgsProtocol, config: ConfigType) -> int | None:
def command_run(args: ArgsProtocol, config: ConfigType) -> int | None:
if (
CORE.skip_bootloader
and CORE.is_esp32
and CORE.using_toolchain_esp_idf
and any(
get_port_type(device) == PortType.SERIAL for device in (args.device or [])
)
):
# Fail before the compile: the result could never flash over serial.
# Elsewhere the flag is ignored, so serial stays fine there.
_LOGGER.error("--skip-bootloader builds cannot be flashed over serial")
return 1
exit_code = write_cpp(config)
if exit_code != 0:
return exit_code
@@ -1773,7 +1793,7 @@ def command_run(args: ArgsProtocol, config: ConfigType) -> int | None:
return exit_code
_LOGGER.info("Successfully compiled program.")
if CORE.is_host:
program_path = _host_program_path(config)
program_path = str(CORE.firmware_bin)
_LOGGER.info("Running program from path '%s'", program_path)
return run_external_process(program_path)
@@ -1926,7 +1946,10 @@ def run_multiple_configs(
safe_print()
cmd = command_builder(f)
rc = run_external_process(*cmd)
# The parent already logged the Intel macOS warning; children skip it.
rc = run_external_process(
*cmd, env={**os.environ, _INTEL_MACOS_WARNED_ENV: "1"}
)
if rc == 0:
print_bar(f"[{color(AnsiFore.BOLD_GREEN, 'SUCCESS')}] {str(f)}")
@@ -1965,12 +1988,12 @@ def command_update_all(args: ArgsProtocol) -> int | None:
def command_idedata(args: ArgsProtocol, config: ConfigType) -> int:
import json
if CORE.using_toolchain_esp_idf:
# Native ESP-IDF derives idedata from the build's compile_commands.json,
# so the configuration must already be compiled.
from esphome.espidf import toolchain as espidf_toolchain
native_toolchain = native_backend()
idedata = espidf_toolchain.get_idedata()
if native_toolchain is not None:
# Native toolchains derive idedata from the build's
# compile_commands.json, so the configuration must already be compiled.
idedata = native_toolchain.get_idedata()
if idedata is None:
_LOGGER.error(
"No idedata available; compile the configuration first",
@@ -2009,6 +2032,22 @@ def command_analyze_memory(args: ArgsProtocol, config: ConfigType) -> int:
from esphome.analyze_memory.cli import MemoryAnalyzerCLI
from esphome.analyze_memory.ram_strings import RamStringsAnalyzer
# Refuse an unsupported toolchain before paying for a full compile
analysis_toolchain = analysis_backend()
if analysis_toolchain is None and not CORE.using_toolchain_platformio:
_LOGGER.error(
"analyze-memory is not supported with the '%s' toolchain on %s; "
"re-run with --toolchain platformio",
CORE.toolchain.value if CORE.toolchain else "unresolved",
CORE.target_platform,
)
return 1
if (
check_supported := getattr(analysis_toolchain, "check_analysis_supported", None)
) is not None:
# Raises with the reason; before the compile, not after it
check_supported()
# Always compile to ensure fresh data (fast if no changes - just relinks)
exit_code = write_cpp(config)
if exit_code != 0:
@@ -2020,13 +2059,30 @@ def command_analyze_memory(args: ArgsProtocol, config: ConfigType) -> int:
# Get idedata for analysis
idedata = None
if CORE.using_toolchain_esp_idf:
from esphome.espidf import toolchain
if analysis_toolchain is not None:
objdump = analysis_toolchain.get_objdump_path()
readelf = analysis_toolchain.get_readelf_path()
for tool in (objdump, readelf):
if not tool.is_file():
# The analyzer would silently fall back to host
# binutils, which cannot read the target ELF
_LOGGER.error(
"%s is missing; the toolchain install may be incomplete "
"(recompile, or run 'esphome clean-all' if it persists)",
tool,
)
return 1
objdump_path = str(objdump)
readelf_path = str(readelf)
objdump_path = str(toolchain.get_objdump_path())
readelf_path = str(toolchain.get_readelf_path())
firmware_elf = toolchain.get_elf_path()
firmware_elf = analysis_toolchain.get_elf_path()
if not firmware_elf.is_file():
# The analyzer swallows tool failures, so a missing ELF would
# produce an exit-0 zeroed report
_LOGGER.error(
"%s is missing; compile the configuration first", firmware_elf
)
return 1
else:
from esphome.platformio import toolchain
@@ -2080,155 +2136,9 @@ def command_analyze_memory(args: ArgsProtocol, config: ConfigType) -> int:
def command_rename(args: ArgsProtocol, config: ConfigType) -> int | None:
from esphome import yaml_util
from esphome.cli.rename import command_rename as run
new_name = args.name
for c in new_name:
if c not in ALLOWED_NAME_CHARS:
safe_print(
color(
AnsiFore.BOLD_RED,
f"'{c}' is an invalid character for names. Valid characters are: "
f"{ALLOWED_NAME_CHARS} (lowercase, no spaces)",
)
)
return 1
# Load existing yaml file
raw_contents = CORE.config_path.read_text(encoding="utf-8")
yaml = yaml_util.load_yaml(CORE.config_path)
if CONF_ESPHOME not in yaml or CONF_NAME not in yaml[CONF_ESPHOME]:
safe_print(
color(
AnsiFore.BOLD_RED, "Complex YAML files cannot be automatically renamed."
)
)
return 1
old_name = yaml[CONF_ESPHOME][CONF_NAME]
match = re.match(r"^\$\{?([a-zA-Z0-9_]+)\}?$", old_name)
if match is None:
# Only swap the ``name:`` line that sits directly under the
# top-level ``esphome:`` block. A naked ``re.sub`` would
# also clobber any other ``name:`` line whose value happens
# to match (e.g. a sensor / output / wifi entry sharing the
# device's hostname), silently rewriting unrelated user
# configuration. The pattern anchors:
# - at the start of the line so ``friendly_name:``,
# ``device_name:`` etc. don't match the trailing ``name:``
# substring; and
# - at the end of the value (lookahead for whitespace +
# comment + EOL) so ``old_name`` doesn't match as a
# prefix of a longer value (``kitchen`` vs ``kitchen2``).
name_pattern = re.compile(
rf"^(\s*)name:\s+[\"']?{re.escape(old_name)}[\"']?(?=\s*(?:#|$))"
)
out_lines: list[str] = []
in_esphome_block = False
for line in raw_contents.splitlines(keepends=True):
if line and not line[0].isspace() and line.strip():
in_esphome_block = line.lstrip().startswith("esphome:")
out_lines.append(line)
continue
if in_esphome_block:
line = name_pattern.sub(rf'\1name: "{new_name}"', line, count=1)
out_lines.append(line)
new_raw = "".join(out_lines)
else:
old_name = yaml[CONF_SUBSTITUTIONS][match.group(1)]
if (
len(
re.findall(
rf"^\s+{match.group(1)}:\s+[\"']?{old_name}[\"']?",
raw_contents,
flags=re.MULTILINE,
)
)
> 1
):
safe_print(
color(AnsiFore.BOLD_RED, "Too many matches in YAML to safely rename")
)
return 1
new_raw = re.sub(
rf"^(\s+{match.group(1)}):\s+[\"']?{old_name}[\"']?",
f'\\1: "{new_name}"',
raw_contents,
flags=re.MULTILINE,
)
# ``new_name == old_name`` (after substitution resolution) is
# a no-op rewrite that would still queue a pointless re-flash.
# Catch it before the path-equality check below — covers the
# case where the config filename doesn't match the device name
# (e.g. ``weird-file.yaml`` whose ``esphome.name`` is
# ``kitchen``; running ``esphome rename weird-file.yaml kitchen``
# would otherwise just re-flash the same hostname).
if new_name == old_name:
safe_print(
color(
AnsiFore.BOLD_RED,
f"'{new_name}' is already the device's name.",
)
)
return 1
new_path: Path = CORE.config_dir / (new_name + ".yaml")
if new_path.resolve() == CORE.config_path.resolve():
safe_print(
color(
AnsiFore.BOLD_RED,
f"'{new_name}' is already the device's name.",
)
)
return 1
if new_path.exists():
safe_print(
color(
AnsiFore.BOLD_RED,
f"Cannot rename: {new_path} already exists. "
"Refusing to overwrite an existing configuration.",
)
)
return 1
safe_print(
f"Updating {color(AnsiFore.CYAN, str(CORE.config_path))} to {color(AnsiFore.CYAN, str(new_path))}"
)
print()
new_path.write_text(new_raw, encoding="utf-8")
rc = run_external_process(*ESPHOME_COMMAND, "config", str(new_path))
if rc != 0:
safe_print(color(AnsiFore.BOLD_RED, "Rename failed. Reverting changes."))
new_path.unlink()
return 1
cli_args = [
"run",
str(new_path),
"--no-logs",
"--device",
CORE.address,
]
if args.dashboard:
cli_args.insert(0, "--dashboard")
try:
rc = run_external_process(*ESPHOME_COMMAND, *cli_args)
except KeyboardInterrupt:
rc = 1
if rc != 0:
new_path.unlink()
return 1
if CORE.config_path != new_path:
CORE.config_path.unlink()
safe_print(color(AnsiFore.BOLD_GREEN, "SUCCESS"))
print()
return 0
return run(args, config)
PRE_CONFIG_ACTIONS = {
@@ -2263,6 +2173,15 @@ SIMPLE_CONFIG_ACTIONS = [
]
def _add_skip_bootloader_arg(parser: argparse.ArgumentParser) -> None:
parser.add_argument(
"--skip-bootloader",
help="Do not build the bootloader or the factory image; "
"the result can only be flashed over OTA.",
action="store_true",
)
def _add_states_args(parser: argparse.ArgumentParser) -> None:
"""Add mutually exclusive ``--states``/``--no-states`` flags to a parser.
@@ -2343,7 +2262,8 @@ def parse_args(argv):
metavar="{" + ",".join(t.value for t in Toolchain) + "}",
help=(
"Select toolchain for compiling. Overrides '<platform>.toolchain' in YAML. "
f"Default: {Toolchain.PLATFORMIO.value}."
"Default: the platform's native toolchain where it has one, else "
f"{Toolchain.PLATFORMIO.value}."
),
)
@@ -2403,6 +2323,7 @@ def parse_args(argv):
help="Only generate source code, do not compile.",
action="store_true",
)
_add_skip_bootloader_arg(parser_compile)
parser_upload = subparsers.add_parser(
"upload",
@@ -2466,7 +2387,7 @@ def parse_args(argv):
"-r",
action="store_true",
help="Reset the device before starting serial logs.",
default=os.getenv("ESPHOME_SERIAL_LOGGING_RESET"),
default=get_bool_env("ESPHOME_SERIAL_LOGGING_RESET"),
)
_add_states_args(parser_logs)
@@ -2499,6 +2420,7 @@ def parse_args(argv):
parser_run.add_argument(
"--no-logs", help="Disable starting logs.", action="store_true"
)
_add_skip_bootloader_arg(parser_run)
_add_states_args(parser_run)
@@ -2507,7 +2429,7 @@ def parse_args(argv):
"-r",
action="store_true",
help="Reset the device before starting serial logs.",
default=os.getenv("ESPHOME_SERIAL_LOGGING_RESET"),
default=get_bool_env("ESPHOME_SERIAL_LOGGING_RESET"),
)
parser_run.add_argument(
"--ota-platform",
@@ -2696,6 +2618,27 @@ def _warn_if_source_tree_mismatch() -> None:
)
_INTEL_MACOS_REMOVAL = "2027.6.0"
_INTEL_MACOS_WARNED_ENV = "ESPHOME_INTEL_MACOS_WARNED"
def _warn_if_intel_macos() -> None:
"""Warn that Intel (x86_64) Python on macOS loses support by _INTEL_MACOS_REMOVAL."""
if (
not IS_MACOS
or platform.machine() != "x86_64"
or _INTEL_MACOS_WARNED_ENV in os.environ
):
return
_LOGGER.warning(
"Support for Intel Macs will end in ESPHome %s or earlier. The Python "
"packages ESPHome depends on have stopped publishing Intel macOS builds, "
"so future releases will not install on this machine. On an Apple "
"Silicon Mac, switch to a native arm64 Python.",
_INTEL_MACOS_REMOVAL,
)
def run_esphome(argv):
from esphome.address_cache import AddressCache
@@ -2715,6 +2658,7 @@ def run_esphome(argv):
setup_log(log_level=args.log_level)
_warn_if_source_tree_mismatch()
_warn_if_intel_macos()
if args.command in PRE_CONFIG_ACTIONS:
try:
@@ -2758,6 +2702,7 @@ def run_esphome(argv):
CORE.config_path = conf_path
CORE.dashboard = args.dashboard
CORE.skip_bootloader = getattr(args, "skip_bootloader", False)
if args.toolchain is not None:
# CLI toolchain wins over esp32.toolchain in YAML.
CORE.toolchain = args.toolchain
+9 -2
View File
@@ -148,6 +148,13 @@ class AddressCache:
continue
hostname, ips = arg.split("=", 1)
# Normalize hostname for consistent lookups
normalized = normalize_hostname(hostname)
cache[normalized] = [ip.strip() for ip in ips.split(",")]
normalized = normalize_hostname(hostname.strip())
addresses = [ip for value in ips.split(",") if (ip := value.strip())]
if not normalized or not addresses:
_LOGGER.warning(
"Invalid cache entry: %s (hostname and at least one address are required)",
arg,
)
continue
cache[normalized] = addresses
return cache
+5 -1
View File
@@ -37,7 +37,7 @@ def find_elf_path(build_path: Path) -> Path | None:
"""
name = build_path.name
for candidate in (
# Native ESP-IDF: idf.py writes build/<name>.elf, which ESPHome copies
# Native ESP-IDF: the build writes build/<name>.elf, which ESPHome copies
# to build/firmware.elf (see espidf.toolchain.create_elf_copy)
build_path / "build" / "firmware.elf",
# PlatformIO
@@ -68,12 +68,16 @@ def idedata_candidates(build_path: Path) -> list[Path]:
The candidate idedata JSON paths, most specific first
"""
name = build_path.name
data_dir = build_path.parent.parent / "idedata"
# Native backends suffix the cache by toolchain (<name>.arduino.json)
suffixed = sorted(data_dir.glob(f"{name}.*.json")) if data_dir.is_dir() else []
return [
# In .pioenvs for test builds
build_path / ".pioenvs" / name / "idedata.json",
# Both toolchains cache it in the data dir, which holds this build dir:
# <data_dir>/idedata/<name>.json next to <data_dir>/build/<name>
build_path.parent.parent / "idedata" / f"{name}.json",
*suffixed,
# Regular builds, invoked from the config dir or from anywhere
Path.cwd() / ".esphome" / "idedata" / f"{name}.json",
Path.home() / ".esphome" / "idedata" / f"{name}.json",
+33 -15
View File
@@ -2,7 +2,9 @@
Bundled names build straight from the framework tree; everything else goes
through ``esphome.platformio.library``. Mirrors ``lib_ldf_mode=off``: each
library builds its own archive; all include dirs join one global path.
library builds its own archive; all include dirs join one global path. The
host build reuses it without a framework tree: nothing is bundled there and
every name resolves from the registry.
Deviations from PlatformIO: flat-layout libraries get the recursive default
source filter; ``dot_a_linkage`` is honored; bundled libraries never run a
@@ -342,13 +344,24 @@ def _check_unfulfilled_provides(
def resolve_libraries(
framework_path: Path, *, pio_platform: str, board_mcu: str, cache_key: str
framework_path: Path | None,
*,
pio_platform: str,
board_mcu: str,
cache_key: str,
framework: str | None = "arduino",
manifest_optional: bool = False,
) -> list[ArduinoLibrary]:
"""Resolve every ``cg.add_library()`` entry into an :class:`ArduinoLibrary`.
``pio_platform``/``board_mcu`` filter manifests the way PlatformIO would
for that core (e.g. ``espressif8266``/``esp8266``); ``cache_key`` keys the
shared converter's download cache.
shared converter's download cache. ``framework`` is the manifest
framework token the compatibility check warns about; None skips it.
A None ``framework_path`` means no core-bundled libraries exist (the
host build): every name resolves from the registry.
``manifest_optional`` accepts libraries without a manifest, built with
PlatformIO's default layout.
The returned list is not topologically sorted, so the caller must link
the archives inside one ``--start-group``/``--end-group`` pair (the
@@ -359,18 +372,22 @@ def resolve_libraries(
# PlatformIO's lib_ignore covers framework-bundled libraries too; the
# shared converter only filters the registry/git ones.
lib_ignore = lib_ignore_set()
# Exact directory names keep membership case-sensitive everywhere
# (an is_dir() probe would match "wire" on macOS/Windows and build
# the bundled Wire twice)
libraries_dir = framework_path / "libraries"
if not libraries_dir.is_dir():
# A registry fallback would fail later with a misleading
# package-not-found error per bundled name
raise EsphomeError(
f"{libraries_dir} is missing; the framework install may be "
"incomplete (run 'esphome clean-all')"
bundled_dir_names: frozenset[str] = frozenset()
if framework_path is not None:
# Exact directory names keep membership case-sensitive everywhere
# (an is_dir() probe would match "wire" on macOS/Windows and build
# the bundled Wire twice)
libraries_dir = framework_path / "libraries"
if not libraries_dir.is_dir():
# A registry fallback would fail later with a misleading
# package-not-found error per bundled name
raise EsphomeError(
f"{libraries_dir} is missing; the framework install may be "
"incomplete (run 'esphome clean-all')"
)
bundled_dir_names = frozenset(
p.name for p in libraries_dir.iterdir() if p.is_dir()
)
bundled_dir_names = frozenset(p.name for p in libraries_dir.iterdir() if p.is_dir())
def _provided(name: object) -> bool:
return _is_safe_library_name(name) and name in bundled_dir_names
@@ -497,12 +514,13 @@ def resolve_libraries(
backend = LibraryBackend(
platform=pio_platform,
framework="arduino",
framework=framework,
emit=_emit,
cache_key=cache_key,
# The walk must not resolve bundled names from the registry;
# _add_bundled_dependencies adds them after emit
provides=_provided,
manifest_optional=manifest_optional,
)
if external:
convert_libraries(external, backend)
+107 -59
View File
@@ -3,12 +3,12 @@
Artifacts land in a machine-global cache (shared across projects, like the
ESP-IDF install in ``esphome.espidf.framework``):
<cache>/arduino8266/frameworks/<version>/ framework-arduinoespressif8266
<cache>/arduino8266/toolchains/<version>/ toolchain-xtensa (gcc 10.3)
<cache>/arduino8266/frameworks/<tag>/ the Arduino core
<cache>/arduino8266/toolchains/<tag>/ xtensa-lx106-elf gcc 10.3
Packages come from the PlatformIO registry (identical bits to the PlatformIO
backend); ``ESPHOME_ARDUINO8266_*_MIRRORS`` overrides the URLs. ninja comes
from PATH or the ninja PyPI wheel.
Both come from esphome-libs releases pinned below;
``ESPHOME_ARDUINO8266_*_MIRRORS`` overrides the URLs, with ``{VERSION}``
standing for the release tag. ninja comes from PATH or the ninja PyPI wheel.
"""
from __future__ import annotations
@@ -17,18 +17,74 @@ import os
from pathlib import Path
from typing import NamedTuple
from esphome.build_helpers.ccache import ccache_defaults_env
from esphome.build_helpers.ccache import ccache_env
from esphome.build_helpers.ninja import find_ninja
from esphome.build_helpers.tools_cache import ARDUINO8266_TOOLS_CACHE, tools_cache_path
from esphome.core import EsphomeError, Version
from esphome.framework_helpers import str_to_lst_of_str
from esphome.platformio.registry import install_package, prefetch_packages
from esphome.platformio.registry import (
Download,
PackageSpec,
Resolver,
get_systype,
install_packages,
prefetch_packages,
)
FRAMEWORK_PACKAGE = "framework-arduinoespressif8266"
TOOLCHAIN_PACKAGE = "toolchain-xtensa"
FRAMEWORK_PACKAGE = "arduino-esp8266"
_FRAMEWORK_RELEASES = "https://github.com/esphome-libs/arduino-esp8266/releases/"
class FrameworkRelease(NamedTuple):
tag: str
sha256: str
size: int
def download(self) -> Download:
archive = f"{FRAMEWORK_PACKAGE}-{self.tag}.tar.gz"
url = f"{_FRAMEWORK_RELEASES}download/{self.tag}/{archive}"
return Download(url, self.sha256, self.size)
# Arduino core version -> its build in esphome-libs/arduino-esp8266
FRAMEWORK_RELEASES: dict[Version, FrameworkRelease] = {
Version(3, 1, 2): FrameworkRelease(
"3.1.2-esphome.2",
"9377d63f569c524823199b1897ed7fde2063e50e2676dd519a0d758ab73678f7",
37192128,
),
}
TOOLCHAIN_PACKAGE = "toolchain-xtensa-lx106-elf"
# gcc 10.3, the toolchain Arduino core 3.x builds with; the build
# generator's compile flags are tuned to it.
TOOLCHAIN_VERSION = "2.100300.220621"
TOOLCHAIN_VERSION = "10.3.0-esphome.3"
_TOOLCHAIN_RELEASES = (
"https://github.com/esphome-libs/xtensa-lx106-elf-toolchain/releases/"
)
# Registry system tag -> (sha256, size) of that host's archive
TOOLCHAIN_BUILDS: dict[str, tuple[str, int]] = {
"darwin_arm64": (
"ad0ea929238d9b527c43e0d20257acc2a5680bdb0d2b1cf8f2c99169786217e9",
60747260,
),
"darwin_x86_64": (
"997090cac80c8eb59604ec52b73fb3568e5f2d9b2971f83c3f2a5c545c9c949c",
64065752,
),
"linux_aarch64": (
"403f14d0d5755682fcd6eaea1e4c457a1d8cad30690e52fb0c804d8db266b908",
67487067,
),
"linux_x86_64": (
"64f21fa3ba736b363dc8c234dbbb39e005e574e33de08a78e9a00c55a64ae706",
68343668,
),
"windows_amd64": (
"1e62abf8df041ce5158f5770dffef955f2f981ce7f7adee88fcd5a8197705aa6",
67536405,
),
}
ESPHOME_ARDUINO8266_FRAMEWORK_MIRRORS = str_to_lst_of_str(
os.environ.get("ESPHOME_ARDUINO8266_FRAMEWORK_MIRRORS", "")
@@ -44,36 +100,40 @@ def get_arduino8266_tools_path() -> Path:
return tools_cache_path(*ARDUINO8266_TOOLS_CACHE)
# 3.1.1 rather than 3.1.0: the registry has no packages for 3.0.0, 3.0.1 or 3.1.0
MIN_FRAMEWORK_VERSION = Version(3, 1, 1)
def framework_package_version(ver: Version) -> str:
"""Map an Arduino core version to its registry package version (3.1.2 ->
3.30102.0; the leading 3 is the package major).
Exact registry names for 3.x cores; callers floor at MIN_FRAMEWORK_VERSION.
"""
if ver.major > 3:
def framework_release(version: Version) -> FrameworkRelease:
if (release := FRAMEWORK_RELEASES.get(version)) is None:
raise EsphomeError(
f"Arduino core {ver} is not supported yet; "
"the newest known core series is 3.x"
f"'toolchain: arduino' has no build of Arduino core {version}; "
f"available: {', '.join(str(v) for v in FRAMEWORK_RELEASES)}. "
"Use one of those or 'toolchain: platformio'"
)
if ver.major < 3:
raise EsphomeError(
f"Arduino core {ver} is not supported; ESPHome requires core 3.x"
)
return f"3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0"
return release
def get_framework_path(package_version: str) -> Path:
return get_arduino8266_tools_path() / "frameworks" / package_version
def get_framework_path(tag: str) -> Path:
return get_arduino8266_tools_path() / "frameworks" / tag
def get_toolchain_path() -> Path:
return get_arduino8266_tools_path() / "toolchains" / TOOLCHAIN_VERSION
def toolchain_download() -> Download:
"""The toolchain archive for the current host."""
systype = get_systype()
if (build := TOOLCHAIN_BUILDS.get(systype)) is None:
raise EsphomeError(
f"There is no ESP8266 toolchain for this system ({systype}); "
f"supported systems are {', '.join(sorted(TOOLCHAIN_BUILDS))}. "
"Either set 'toolchain: platformio' under 'esp8266:', or point "
"ESPHOME_ARDUINO8266_TOOLCHAIN_MIRRORS at a toolchain archive"
)
sha256, size = build
archive = f"{TOOLCHAIN_PACKAGE}-{TOOLCHAIN_VERSION}-{systype}.tar.gz"
url = f"{_TOOLCHAIN_RELEASES}download/{TOOLCHAIN_VERSION}/{archive}"
return Download(url, sha256, size)
class InstalledPaths(NamedTuple):
"""Locations of the installed framework, toolchain, and ninja binary."""
@@ -84,29 +144,22 @@ class InstalledPaths(NamedTuple):
def check_and_install(framework_version: Version) -> InstalledPaths:
"""Ensure framework, toolchain, and ninja are installed; return their paths."""
if framework_version < MIN_FRAMEWORK_VERSION:
# Config validation enforces this too; keep the module honest when
# called directly.
raise EsphomeError(
f"The native toolchain requires the Arduino core "
f">= {MIN_FRAMEWORK_VERSION}, got {framework_version}"
)
release = framework_release(framework_version)
# Probe the cheap local dependency before ~110 MB of downloads
ninja_path = find_ninja()
package_version = framework_package_version(framework_version)
framework_path = get_framework_path(package_version)
framework_path = get_framework_path(release.tag)
downloads_dir = get_arduino8266_tools_path() / "downloads"
toolchain_path = get_toolchain_path()
# One spec per package: the prefetch and the installs must agree
specs = (
(
PackageSpec(
FRAMEWORK_PACKAGE,
package_version,
release.tag,
framework_path,
ESPHOME_ARDUINO8266_FRAMEWORK_MIRRORS,
("cores/esp8266", "tools/sdk", "libraries"),
),
(
PackageSpec(
TOOLCHAIN_PACKAGE,
TOOLCHAIN_VERSION,
toolchain_path,
@@ -115,10 +168,17 @@ def check_and_install(framework_version: Version) -> InstalledPaths:
("bin", "xtensa-lx106-elf"),
),
)
# Fetch both archives at once; the installs below verify and extract
prefetch_packages([spec[:4] for spec in specs], downloads_dir)
for name, version, dest, mirrors, expect in specs:
install_package(name, version, dest, mirrors, downloads_dir, expect=expect)
# Resolved only when a download is needed, so an installed toolchain
# keeps working on a host without a build; a mirror override wins
resolvers: dict[str, Resolver] = {}
if not ESPHOME_ARDUINO8266_FRAMEWORK_MIRRORS:
resolvers[FRAMEWORK_PACKAGE] = release.download
if not ESPHOME_ARDUINO8266_TOOLCHAIN_MIRRORS:
resolvers[TOOLCHAIN_PACKAGE] = toolchain_download
# Fetch both archives at once; the installs verify and extract them.
# One spec list for both, so the two phases cannot drift.
prefetch_packages(specs, downloads_dir, resolvers)
install_packages(specs, downloads_dir, resolvers)
return InstalledPaths(
framework=framework_path, toolchain=toolchain_path, ninja=ninja_path
)
@@ -143,17 +203,5 @@ def get_build_env(toolchain_path: Path, ccache: str | None) -> dict[str, str]:
*filter(None, env.get("PATH", "").split(os.pathsep)),
]
env["PATH"] = os.pathsep.join(parts)
env.update(ccache_env(ccache))
env.update(ccache_env(ccache, ARDUINO8266_TOOLS_CACHE))
return env
def ccache_env(ccache: str | None) -> dict[str, str]:
"""Return ccache settings for the build subprocess (not os.environ).
``ccache`` is the pre-resolved binary (resolve_ccache_path), or None
when disabled. Values the user already set in the environment are
respected.
"""
if ccache is None:
return {}
return ccache_defaults_env(get_arduino8266_tools_path() / "ccache")
+212
View File
@@ -0,0 +1,212 @@
"""Native Arduino ESP8266 build driver (the PlatformIO ``run`` equivalent)."""
from __future__ import annotations
import logging
from pathlib import Path
import subprocess
from typing import TYPE_CHECKING
from esphome.build_helpers.ccache import resolve_absolute_ccache_path
from esphome.build_helpers.native import warn_ignored_platformio_options
from esphome.build_helpers.ninja import refresh_compile_commands
from esphome.const import (
CONF_COMPILE_PROCESS_LIMIT,
CONF_ESPHOME,
KEY_CORE,
KEY_FRAMEWORK_VERSION,
)
from esphome.core import CORE
from esphome.types import ConfigType
if TYPE_CHECKING:
from esphome.arduino8266.framework import InstalledPaths
_LOGGER = logging.getLogger(__name__)
# ESP8266 user RAM (matches upload.maximum_ram_size in every board manifest)
_MAX_RAM_SIZE = 81920
_RAM_SECTIONS = (".data", ".rodata", ".bss")
_FLASH_SECTIONS = (".irom0.text", ".text", ".text1", ".data", ".rodata")
def get_build_dir() -> Path:
return CORE.relative_pioenvs_path(CORE.name)
def get_elf_path() -> Path:
return get_build_dir() / "firmware.elf"
def _toolchain_tool(name: str) -> Path:
# Imported here, not at module scope: the serial upload/logs fast path
# resolves this module for its artifact paths alone, and framework
# pulls in the whole package-download stack
from esphome.arduino8266 import framework
return framework.toolchain_tool(framework.get_toolchain_path(), name)
def get_factory_firmware_path() -> Path:
"""The image to serial-flash at 0x0 (same bytes as firmware.bin: the
8266 factory copy exists for artifact-contract parity, not content)."""
return get_build_dir() / "firmware.factory.bin"
def get_addr2line_path() -> Path:
return _toolchain_tool("addr2line")
def get_objdump_path() -> Path:
return _toolchain_tool("objdump")
def get_readelf_path() -> Path:
return _toolchain_tool("readelf")
def run_compile(config: ConfigType, verbose: bool) -> int:
from esphome.arduino8266 import framework
from esphome.build_gen import arduino8266 as build_gen
from esphome.core.config import NATIVE_ARDUINO_CONSUMED_PIO_OPTIONS
warn_ignored_platformio_options(NATIVE_ARDUINO_CONSUMED_PIO_OPTIONS)
paths = framework.check_and_install(CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION])
# Resolved once: the probe is not free and three consumers need it
ccache = resolve_absolute_ccache_path()
ninja_changed = build_gen.write_project(paths, ccache)
build_dir = get_build_dir()
env = framework.get_build_env(paths.toolchain, ccache)
refresh_compile_commands(paths.ninja, build_dir, env, ninja_changed)
cmd = [str(paths.ninja)]
if verbose:
cmd.append("-v")
if jobs := config[CONF_ESPHOME].get(CONF_COMPILE_PROCESS_LIMIT):
cmd += ["-j", str(jobs)]
# Explicit targets: a generator defect that drops them fails loudly
# instead of a green no-op run leaving stale artifacts in place
targets = ["firmware.factory.bin", "firmware.ota.bin"]
cmd += targets
# cwd, not -C: drops ninja's "Entering directory" banner
_LOGGER.debug("Running: %s", " ".join(cmd))
rc = subprocess.run(
cmd, cwd=build_dir, env=env, check=False, close_fds=False
).returncode
if rc != 0:
return rc
# ninja already refused missing targets; existence covers a rule that
# ran but wrote elsewhere
build_dir_artifacts = (
get_elf_path(),
build_dir / "firmware.bin",
get_factory_firmware_path(),
build_dir / "firmware.ota.bin",
)
for artifact in build_dir_artifacts:
if not artifact.is_file():
_LOGGER.error("Build produced no %s", artifact)
return 1
if not _print_size_summary(build_dir, paths):
# Cause already warned; name the consequence for CI harnesses
_LOGGER.warning("Firmware size summary unavailable for this build")
from esphome.build_helpers.idedata import warn_if_idedata_missing
warn_if_idedata_missing(lambda: get_idedata(ccache))
return 0
def _parse_app_size(build_dir: Path, paths: InstalledPaths) -> int | None:
"""Read the app flash budget (irom0_0_seg length) from the linker script."""
from esphome.build_gen.arduino8266 import get_flash_ld_path
from esphome.components.esp8266.build_surgery import segment_length
# Warnings, not debug: without the app size the Flash summary line is
# dropped and CI's memory-impact extraction loses its flash metric.
ld_path = get_flash_ld_path(build_dir, paths)
try:
ld_text = ld_path.read_text(encoding="utf-8")
except (OSError, UnicodeDecodeError) as err:
# A corrupt script degrades the same way, never aborts the build
_LOGGER.warning("Cannot read linker script for the Flash summary: %s", err)
return None
if not (app_size := segment_length(ld_text, "irom0_0_seg")):
_LOGGER.warning("No usable irom0_0_seg in %s; skipping Flash summary", ld_path)
return None
return app_size
def _print_size_summary(build_dir: Path, paths: InstalledPaths) -> bool:
"""Print the RAM/Flash lines ``ci_memory_impact_extract.py`` parses;
False when skipped."""
from esphome.arduino8266.framework import toolchain_tool
from esphome.build_helpers.size_summary import print_size_line
try:
result = subprocess.run(
[
str(toolchain_tool(paths.toolchain, "size")),
"-A",
"-d",
str(build_dir / "firmware.elf"),
],
capture_output=True,
text=True,
check=True,
close_fds=False,
)
except (OSError, subprocess.CalledProcessError) as err:
# The summary is a bonus artifact like idedata; a truncated
# toolchain extraction must not discard an already-linked build
_LOGGER.warning("Could not summarize firmware size: %s", err)
return False
# -d prints decimal sizes; anything else trips the missing-sections guard
sections = {
parts[0]: int(parts[1])
for line in result.stdout.splitlines()
if (parts := line.split())[:1] and parts[0].startswith(".") and len(parts) >= 2
if parts[1].isdigit()
}
if missing := set(_RAM_SECTIONS + _FLASH_SECTIONS) - set(sections):
# A defaulted 0 would print a confidently wrong total for CI's metric
_LOGGER.warning(
"Size output is missing section(s) %s; skipping the size summary",
", ".join(sorted(missing)),
)
return False
# Resolve the flash budget before printing: a RAM line without its
# Flash line would skew CI's memory-impact extraction
app_size = _parse_app_size(build_dir, paths)
if not app_size:
return False
ram = sum(sections[s] for s in _RAM_SECTIONS)
flash = sum(sections[s] for s in _FLASH_SECTIONS)
print_size_line("RAM", ram, _MAX_RAM_SIZE)
print_size_line("Flash", flash, app_size)
return True
def get_idedata(ccache: str | None = None) -> dict | None:
"""Derive idedata from the build's compile_commands.json (same
contract as ``espidf.toolchain.get_idedata``)."""
from esphome.build_helpers.idedata import load_or_build_idedata
# A disabled ccache resolves to None without spawning anything, so
# re-resolving here costs nothing when the caller has no answer
launcher = ccache or resolve_absolute_ccache_path()
return load_or_build_idedata(
get_build_dir() / "compile_commands.json",
get_elf_path(),
# Suffixed so a platformio->arduino->platformio round trip on one
# config never serves the other toolchain's cache shape
CORE.relative_internal_path("idedata", f"{CORE.name}.arduino.json"),
# The compile DB's commands carry the same ccache prefix the ninja
# rules were generated with
launcher=str(launcher) if launcher else None,
)
+437 -45
View File
@@ -1,5 +1,8 @@
from collections.abc import Callable
from dataclasses import dataclass, field
import logging
import string
from typing import Any
import esphome.codegen as cg
import esphome.config_validation as cv
@@ -18,18 +21,46 @@ from esphome.const import (
CONF_TYPE_ID,
CONF_UPDATE_INTERVAL,
)
from esphome.core import ID, Lambda
from esphome.core import CORE, ID, EsphomeError, HexInt, Lambda
from esphome.cpp_generator import (
FlashStringLiteral,
LambdaExpression,
MockObj,
MockObjClass,
TemplateArgsType,
call_lambda,
)
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
from esphome.types import ConfigType
from esphome.types import ConfigType, SafeExpType
from esphome.util import Registry
def progmem_bytes(name: str, data: bytes | list[int]) -> MockObj:
"""Shared PROGMEM table for constant bytes; equal payloads share one, empty is nullptr."""
if not data:
return cg.nullptr
return cg.shared_progmem_array(
name, cg.uint8, cg.ArrayInitializer(*(HexInt(x) for x in data))
)
async def templatable_bytes(
value: Any,
args: TemplateArgsType,
set_template: MockObj,
set_static: MockObj,
table_name: str,
) -> None:
"""Set a TemplatableBytes: a lambda via set_template, constant bytes via set_static."""
if cg.is_template(value):
fn = await cg.templatable(value, args, cg.std_vector.template(cg.uint8))
cg.add(set_template(fn))
elif len(value) > 0xFFFF:
raise EsphomeError(f"Byte payload is {len(value)} bytes; the maximum is 65535")
else:
cg.add(set_static(progmem_bytes(table_name, value), len(value)))
def maybe_simple_id(*validators):
"""Allow a raw ID to be specified in place of a config block.
If the value that's being validated is a dictionary, it's passed as-is to the specified validators. Otherwise, it's
@@ -57,6 +88,7 @@ def maybe_conf(conf, *validators):
with cv.remove_prepend_path([conf]):
return validator({conf: value})
validate.inner_schema = validator
return validate
@@ -102,6 +134,101 @@ def register_condition(name: str, condition_type: MockObjClass, schema: cv.Schem
return CONDITION_REGISTRY.register(name, condition_type, schema)
async def _build_with_parent(
config: ConfigType,
automation_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
parent = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(automation_id, template_arg, parent)
async def _build_without_parent(
config: ConfigType,
automation_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
return cg.new_Pvariable(automation_id, template_arg)
async def _build_parented(
config: ConfigType,
automation_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
var = cg.new_Pvariable(automation_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
return var
def register_simple_action(
name: str,
action_type: MockObjClass,
schema: cv.Schema,
*,
synchronous: bool,
) -> None:
"""Register an action whose constructor takes the object named by ``config[CONF_ID]``.
Use the ``register_action`` decorator instead when the builder must also set fields.
"""
register_action(name, action_type, schema, synchronous=synchronous)(
_build_with_parent
)
def register_simple_condition(
name: str, condition_type: MockObjClass, schema: cv.Schema
) -> None:
"""Condition counterpart of ``register_simple_action``."""
register_condition(name, condition_type, schema)(_build_with_parent)
def register_bare_action(
name: str,
action_type: MockObjClass,
schema: cv.Schema,
*,
synchronous: bool,
) -> None:
"""Register an action whose constructor takes no arguments."""
register_action(name, action_type, schema, synchronous=synchronous)(
_build_without_parent
)
def register_bare_condition(
name: str, condition_type: MockObjClass, schema: cv.Schema
) -> None:
"""Condition counterpart of ``register_bare_action``."""
register_condition(name, condition_type, schema)(_build_without_parent)
def register_parented_action(
name: str,
action_type: MockObjClass,
schema: cv.Schema,
*,
synchronous: bool,
) -> None:
"""Register an action deriving from ``Parented<T>``.
The object is constructed without arguments and ``set_parent()`` receives the object
named by ``config[CONF_ID]``.
"""
register_action(name, action_type, schema, synchronous=synchronous)(_build_parented)
def register_parented_condition(
name: str, condition_type: MockObjClass, schema: cv.Schema
) -> None:
"""Condition counterpart of ``register_parented_action``."""
register_condition(name, condition_type, schema)(_build_parented)
Action = cg.esphome_ns.class_("Action")
Trigger = cg.esphome_ns.class_("Trigger")
ACTION_REGISTRY = Registry()
@@ -112,6 +239,308 @@ validate_action_list = cv.validate_registry("action", ACTION_REGISTRY)
validate_condition = cv.validate_registry_entry("condition", CONDITION_REGISTRY)
validate_condition_list = cv.validate_registry("condition", CONDITION_REGISTRY)
ApplyAction = cg.esphome_ns.class_("ApplyAction", Action)
ApplyCondition = cg.esphome_ns.class_("ApplyCondition", Condition)
def flash_string(config: ConfigType, value: str) -> str:
"""Default renderer for ``std::string`` constants; copies the literal out of flash on ESP8266."""
if CORE.is_esp8266:
return f"progmem_string({FlashStringLiteral(value)})"
return str(cg.safe_exp(value))
def literal_with_length(config: ConfigType, value: str) -> str:
"""Renderer for a ``(const char *, size_t)`` target: a plain literal plus its byte length.
The target compares or copies the bytes in place, so it needs the RAM literal rather than
the PROGMEM rendering on ESP8266, and the length saves a strlen.
"""
return f"{cg.safe_exp(value)}, {len(value.encode('utf-8'))}"
@dataclass(frozen=True)
class ApplyCall:
"""One statement from config keys, e.g. ``"set_range({}, {})"`` with ``((CONF_LOW, cg.float_), ...)``.
Each arg is ``(conf_key, type_)`` or ``(conf_key, type_, const_fn)``. A ``conf_key`` may be a
path into nested sections. A plain ``str`` ``type_`` is raw C++ type text and may use
``{parent}``. ``const_fn(config, value)`` renders a constant's argument text; a lambda or an
id bypasses it. The statement is skipped when none of its keys is set, always emitted when it
has no keys, and a partial set is a config error.
"""
target: str
args: tuple[tuple[Any, ...], ...] = ()
def __post_init__(self) -> None:
fields = [
f for _, f, _, _ in string.Formatter().parse(self.target) if f is not None
]
if any(fields):
raise ValueError(
f"apply target {self.target!r}: only bare {{}} placeholders"
)
if len(fields) != len(self.args):
raise ValueError(
f"apply target {self.target!r} has {len(fields)} "
f"placeholder(s) for {len(self.args)} config key(s)"
)
if any(len(arg) not in (2, 3) for arg in self.args):
raise ValueError(
f"apply target {self.target!r}: each arg is (conf_key, type_[, const_fn])"
)
@property
def members(self) -> list[tuple[Any, Any, Any]]:
"""Each arg as ``(conf_key, type_, const_fn or None)``."""
return [
(arg[0], arg[1], arg[2] if len(arg) == 3 else None) for arg in self.args
]
@dataclass(frozen=True)
class ApplyField:
"""One config key forwarded as ``target(value)``, or as statement ``target`` when it has ``{}``.
Double a literal brace in a template. ``conf_key`` may be a path into nested sections.
``type_`` may be a C++ type string using ``{parent}`` when the type is only known per
instance. ``const_fn(config, value)`` renders a constant's argument text when ``cg.safe_exp``
is not the right spelling (unit conversion belongs in the validator); a lambda or an id
bypasses it, so the target must also take a plain ``type_``. An absent key emits nothing.
"""
conf_key: str | tuple[str, ...]
target: str
type_: SafeExpType
const_fn: Callable[[ConfigType, Any], str] | None = None
def call(self) -> ApplyCall:
target = self.target if "{}" in self.target else f"{self.target}({{}})"
return ApplyCall(target, ((self.conf_key, self.type_, self.const_fn),))
def _config_lookup(config: ConfigType, key: str | tuple[str, ...]) -> Any:
if isinstance(key, str):
return config.get(key)
for part in key:
if (config := config.get(part)) is None:
return None
return config
def _dict_schema(schema: Any) -> Any:
"""The dict-backed cv.Schema inside cv.All and maybe_* wrappers, or None; cv.Any is not inspected."""
if isinstance(schema, dict):
return cv.Schema(schema)
if isinstance(getattr(schema, "schema", None), dict):
return schema
if isinstance(schema, cv.All):
inner = schema.validators
else:
inner = (
getattr(schema, "inner_schema", None),
) # maybe_conf / maybe_simple_value
for candidate in inner:
if candidate is not None and (found := _dict_schema(candidate)) is not None:
return found
return None
def _check_key_in_schema(
name: str, schema: Any, conf_key: str | tuple[str, ...]
) -> None:
"""Reject a key path the schema does not have; a typo would otherwise be a silent no-op.
Only dict-backed schemas, also inside cv.All and maybe_* wrappers, can be checked.
"""
for part in (conf_key,) if isinstance(conf_key, str) else conf_key:
if (schema := _dict_schema(schema)) is None:
return
markers = {
getattr(marker, "schema", marker): marker for marker in schema.schema
}
if part not in markers:
raise ValueError(f"{name}: config key {part!r} is not in the schema")
schema = schema.schema[markers[part]]
async def _apply_parent(config: ConfigType, id_key: str = CONF_ID) -> str:
# Global-scope qualified so a trigger arg named like the id cannot shadow it.
return f"::{await cg.get_variable(config[id_key])}"
def _apply_lambda_args(args: TemplateArgsType) -> TemplateArgsType:
# The generated function's parameters; a std::string arg is never copied.
return [
(cg.RawExpression(f"const std::remove_cvref_t<{cg.safe_exp(t)}> &"), arg)
for t, arg in args
]
def _apply_function(
id_: ID,
return_type: SafeExpType,
template_arg: cg.TemplateArguments,
lambda_args: TemplateArgsType,
statements: list[str],
) -> MockObj:
"""Emit the generated function and declare ``id_`` as the ``ApplyAction`` or
``ApplyCondition`` templated on it, so ``play()`` calls it directly."""
fn = cg.static_function(
f"esphome__{id_.id}__fn", return_type, lambda_args, statements
)
return cg.new_Pvariable(id_, cg.TemplateArguments(fn, *template_arg))
async def _render_values(
name: str,
target: str,
members: list[tuple[Any, Any, Any]],
values: list[Any],
config: ConfigType,
parent: str,
lambda_args: TemplateArgsType,
compare: bool = False,
) -> list[str]:
"""Render the argument text of one statement; every key must be present.
``compare``: an inlined lambda expression is parenthesized so it binds as a whole
beside an operator.
"""
if any(value is None for value in values):
keys = [key for key, _, _ in members]
raise EsphomeError(f"{name}: {target!r} needs all of {keys}")
exprs: list[str] = []
for (_, type_, const_fn), value in zip(members, values, strict=True):
if isinstance(value, Lambda):
if isinstance(type_, str):
type_ = cg.RawExpression(type_.format(parent=parent))
inner = await cg.process_lambda(value, lambda_args, return_type=type_)
expr = call_lambda(inner)
bare = compare and isinstance(expr, cg.RawExpression)
exprs.append(f"({expr})" if bare else str(expr))
elif isinstance(value, ID):
# Qualified like the parent, so a trigger arg named like the id cannot shadow it.
exprs.append(f"::{await cg.get_variable(value)}")
elif const_fn is not None:
exprs.append(const_fn(config, value))
else:
exprs.append(str(cg.safe_exp(value)))
return exprs
def _apply_values(config: ConfigType, members: list[tuple[Any, Any, Any]]) -> list[Any]:
return [_config_lookup(config, key) for key, _, _ in members]
def register_apply_action(
name: str,
schema: cv.Schema,
*fields: ApplyField | ApplyCall,
call: str | None = None,
id_key: str = CONF_ID,
) -> None:
"""Register an action that only forwards config values to its parent, with no C++ class.
Generates one static function with the parent (read from ``id_key``) and constants baked
in, lambdas called inline with the trigger args, and an ``ApplyAction`` templated on it.
A constant that is an id (``cv.use_id`` under ``cv.templatable``) is the object it names.
With ``call`` every statement targets the call object ``auto apply_call = parent->call()``,
and ``apply_call.perform()`` is appended.
"""
# An action stores the value, so a std::string constant stays in flash on ESP8266.
statements_spec = [
(
c.target,
[
(key, t, fn or (flash_string if t is cg.std_string else None))
for key, t, fn in c.members
],
)
for c in (f if isinstance(f, ApplyCall) else f.call() for f in fields)
]
_check_key_in_schema(name, schema, id_key)
for _, members in statements_spec:
for conf_key, _, _ in members:
_check_key_in_schema(name, schema, conf_key)
async def builder(
config: ConfigType,
action_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
parent = await _apply_parent(config, id_key)
lambda_args = _apply_lambda_args(args)
receiver = "apply_call." if call else f"{parent}->"
statements: list[str] = []
for target, members in statements_spec:
values = _apply_values(config, members)
if members and all(value is None for value in values):
continue
exprs = await _render_values(
name, target, members, values, config, parent, lambda_args
)
statements.append(f"{receiver}{target.format(*exprs)};")
if call:
statements = [
f"auto apply_call = {parent}->{call}();",
*statements,
"apply_call.perform();",
]
return _apply_function(
action_id, cg.void, template_arg, lambda_args, statements
)
register_action(name, ApplyAction, schema, synchronous=True)(builder)
def register_apply_condition(
name: str, schema: cv.Schema, check: str | ApplyCall, id_key: str = CONF_ID
) -> None:
"""Register a condition that is one expression on its parent, with no C++ class.
``check`` is applied to the parent: ``"is_playing()"`` becomes ``parent->is_playing()``; an
``ApplyCall`` such as ``ApplyCall("state == {}", ((CONF_STATE, cg.bool_),))`` compares
against config values, all of which must be present. Write ``== false`` to negate.
String constants are plain literals, so compare a ``std::string`` or ``StringRef`` member.
Generates one static predicate and an ``ApplyCondition`` templated on it.
"""
call = check if isinstance(check, ApplyCall) else ApplyCall(check)
members = call.members
_check_key_in_schema(name, schema, id_key)
for conf_key, _, _ in members:
_check_key_in_schema(name, schema, conf_key)
async def builder(
config: ConfigType,
condition_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
parent = await _apply_parent(config, id_key)
lambda_args = _apply_lambda_args(args)
exprs = await _render_values(
name,
call.target,
members,
_apply_values(config, members),
config,
parent,
lambda_args,
compare=True,
)
return _apply_function(
condition_id,
cg.bool_,
template_arg,
lambda_args,
[f"return {parent}->{call.target.format(*exprs)};"],
)
register_condition(name, ApplyCondition, schema)(builder)
def validate_potentially_and_condition(value):
if isinstance(value, list):
@@ -359,28 +788,15 @@ async def for_condition_to_code(
return var
@register_condition(
register_apply_condition(
"component.is_idle",
LambdaCondition,
maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(cg.Component),
}
),
"is_idle()",
)
async def component_is_idle_condition_to_code(
config: ConfigType,
condition_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
comp = await cg.get_variable(config[CONF_ID])
lambda_ = await cg.process_lambda(
Lambda(f"return {comp}->is_idle();"), args, return_type=bool
)
return new_lambda_pvariable(
condition_id, lambda_, StatelessLambdaCondition, template_arg
)
@register_action(
@@ -534,44 +950,20 @@ async def lambda_action_to_code(
return new_lambda_pvariable(action_id, lambda_, StatelessLambdaAction, template_arg)
@register_action(
register_simple_action(
"component.update",
UpdateComponentAction,
maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(cg.PollingComponent),
}
),
maybe_simple_id({cv.Required(CONF_ID): cv.use_id(cg.PollingComponent)}),
synchronous=True,
)
async def component_update_action_to_code(
config: ConfigType,
action_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
comp = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, comp)
@register_action(
register_simple_action(
"component.suspend",
SuspendComponentAction,
maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(cg.PollingComponent),
}
),
maybe_simple_id({cv.Required(CONF_ID): cv.use_id(cg.PollingComponent)}),
synchronous=True,
)
async def component_suspend_action_to_code(
config: ConfigType,
action_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
comp = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, comp)
@register_action(
File diff suppressed because it is too large Load Diff
+258 -6
View File
@@ -3,7 +3,10 @@
import json
import logging
from pathlib import Path
import re
import textwrap
from esphome.build_helpers import pch
from esphome.components.esp32 import (
get_esp32_variant,
get_excluded_builtin_components,
@@ -18,7 +21,7 @@ from esphome.framework_helpers import (
get_project_cxx_compile_flags,
get_project_link_flags,
)
from esphome.helpers import mkdir_p, write_file_if_changed
from esphome.helpers import get_bool_env, mkdir_p, write_file_if_changed
_LOGGER = logging.getLogger(__name__)
@@ -33,6 +36,83 @@ list(FILTER esphome_cxx_compile_options EXCLUDE REGEX "^-std=")
list(APPEND esphome_cxx_compile_options "-std={standard}")
idf_build_set_property(CXX_COMPILE_OPTIONS "${{esphome_cxx_compile_options}}")"""
# Drops the app archive from ldgen's inputs so app-only edits skip the
# sections.ld regeneration. Safe: no mapping fragment references it
# (run_compile re-checks each build). Filters only the top-level call;
# the prior definition stays reachable with an underscore prefix.
_LDGEN_OVERRIDE = """\
if(COMMAND __ldgen_get_lib_deps_of_target)
set_property(GLOBAL PROPERTY ESPHOME_LDGEN_ARMED 1)
function(__ldgen_get_lib_deps_of_target target out_list_var)
if(NOT COMMAND ___ldgen_get_lib_deps_of_target)
message(FATAL_ERROR "ESPHome ldgen override lost the original "
"implementation; set ESPHOME_LDGEN_FULL_DEPS=1 and rebuild.")
endif()
___ldgen_get_lib_deps_of_target(${target} ${out_list_var})
if(out_list_var STREQUAL "ldgen_libraries")
set_property(GLOBAL PROPERTY ESPHOME_LDGEN_FILTERED 1)
list(LENGTH ${out_list_var} esphome_ldgen_before)
list(REMOVE_ITEM ${out_list_var} idf::src __idf_src)
list(LENGTH ${out_list_var} esphome_ldgen_after)
if(esphome_ldgen_before EQUAL esphome_ldgen_after)
message(@SEVERITY@ "ESPHome ldgen app archive exclusion matched "
"nothing; app edits will regenerate sections.ld.")
endif()
endif()
set(${out_list_var} "${${out_list_var}}" PARENT_SCOPE)
endfunction()
else()
message(@MISSING@ "ESPHome ldgen override target not found; "
"app edits will regenerate sections.ld.")
endif()"""
# lwip sources that compile to empty objects with the option off (their own
# #if guard). (option, regex valid for both Python and CMake); a source is
# only dropped when its option is defined and off, so a renamed option
# keeps it.
LWIP_EMPTY_SOURCES: tuple[tuple[str, str], ...] = (
("CONFIG_LWIP_PPP_SUPPORT", "/netif/ppp/"),
("CONFIG_LWIP_IPV6", "/core/ipv6/"),
("CONFIG_LWIP_AUTOIP", "/core/ipv4/autoip[.]c$"),
("CONFIG_LWIP_STATS", "/core/stats[.]c$"),
)
# Drift guard only: keep every lwip source.
LWIP_FULL_SOURCES_ENV = "ESPHOME_LWIP_FULL_SOURCES"
# Drops the empty objects after project(), once the lwip target exists.
_LWIP_EMPTY_SOURCES_FILTER = f"""\
idf_build_get_property(esphome_build_components BUILD_COMPONENTS)
if(lwip IN_LIST esphome_build_components AND NOT DEFINED ENV{{{LWIP_FULL_SOURCES_ENV}}})
idf_component_get_property(esphome_lwip_lib lwip COMPONENT_LIB)
get_target_property(esphome_lwip_srcs ${{esphome_lwip_lib}} SOURCES)
@FILTERS@
set_property(TARGET ${{esphome_lwip_lib}} PROPERTY SOURCES ${{esphome_lwip_srcs}})
endif()"""
def lwip_empty_source_gate(option: str, regex: str) -> str:
return (
f" if(DEFINED {option} AND NOT {option})\n"
f' list(FILTER esphome_lwip_srcs EXCLUDE REGEX "{regex}")\n'
" endif()"
)
def _lwip_empty_sources_filter() -> str:
gates = "\n".join(lwip_empty_source_gate(*entry) for entry in LWIP_EMPTY_SOURCES)
return _LWIP_EMPTY_SOURCES_FILTER.replace("@FILTERS@", gates)
# Runs after project() so the walk has happened; catches the remaining
# silent path where the top-level out-var was renamed.
_LDGEN_OVERRIDE_CHECK = """\
get_property(esphome_ldgen_armed GLOBAL PROPERTY ESPHOME_LDGEN_ARMED)
get_property(esphome_ldgen_filtered GLOBAL PROPERTY ESPHOME_LDGEN_FILTERED)
if(esphome_ldgen_armed AND NOT esphome_ldgen_filtered)
message(@SEVERITY@ "ESPHome ldgen override never filtered the app "
"archive; app edits will regenerate sections.ld.")
endif()"""
def get_available_components() -> list[str] | None:
"""List the built-in ESP-IDF components from ``project_description.json``.
@@ -78,6 +158,85 @@ def _cmake_quote(value: str) -> str:
return f'"{escaped}"'
# CONFIG_APP_BUILD_BOOTLOADER is hidden and force-selected, so it can only be
# cleared at the CMake level (the same state IDF's RAM-app build type uses).
# The macro is IDF's __build_process_project_includes plus a few added lines;
# the flag is ignored and the bootloader builds as usual if IDF changes it.
IDF_BOOTLOADER_OVERRIDE = """\
# ESPHome bootloader skip switch; see esphome/espidf/toolchain.py.
if(ESPHOME_SKIP_BOOTLOADER)
macro(__build_process_project_includes)
idf_build_get_property(sdkconfig_cmake SDKCONFIG_CMAKE)
include(${sdkconfig_cmake})
set(CONFIG_APP_BUILD_BOOTLOADER "")
# bt's CMakeLists reads the lowercase idf_target that the (now
# skipped) bootloader project_include leaks; keep it defined, or
# its empty TARGET_SRC_NAME sends file(GLOB_RECURSE) across /.
idf_build_get_property(idf_target IDF_TARGET)
# partition_table's V1 ECDSA signing reads this key, which the
# skipped bootloader project_include also sets.
get_filename_component(SECURE_BOOT_SIGNING_KEY "${CONFIG_SECURE_BOOT_SIGNING_KEY}" ABSOLUTE BASE_DIR "${project_dir}")
idf_build_get_property(build_properties __BUILD_PROPERTIES)
foreach(build_property ${build_properties})
idf_build_get_property(val ${build_property})
set(${build_property} "${val}")
endforeach()
idf_build_get_property(build_component_targets __BUILD_COMPONENT_TARGETS)
foreach(component_target ${build_component_targets})
__component_get_property(dir ${component_target} COMPONENT_DIR)
__component_get_property(_name ${component_target} COMPONENT_NAME)
set(COMPONENT_NAME ${_name})
set(COMPONENT_DIR ${dir})
set(COMPONENT_PATH ${dir})
if(EXISTS ${COMPONENT_DIR}/project_include.cmake)
include(${COMPONENT_DIR}/project_include.cmake)
endif()
endforeach()
endmacro()
endif()
"""
# The lines the override adds to IDF's macro; idf_macro_matches() below
# strips them before comparing with the live macro.
BOOTLOADER_OVERRIDE_ADDED_LINES = (
'set(CONFIG_APP_BUILD_BOOTLOADER "")',
"idf_build_get_property(idf_target IDF_TARGET)",
(
"get_filename_component(SECURE_BOOT_SIGNING_KEY"
' "${CONFIG_SECURE_BOOT_SIGNING_KEY}" ABSOLUTE BASE_DIR "${project_dir}")'
),
)
_MACRO = re.compile(
r"macro\(__build_process_project_includes\)(.*?)endmacro\(\)", re.DOTALL
)
def _normalized_macro(text: str) -> list[str] | None:
"""The macro body as comment-free, whitespace-collapsed lines."""
if (match := _MACRO.search(text)) is None:
return None
return [
re.sub(r"\s+", " ", line)
for raw in match.group(1).splitlines()
if (line := raw.split("#", 1)[0].strip())
]
_EXPECTED_MACRO = [
line
for line in _normalized_macro(IDF_BOOTLOADER_OVERRIDE)
if line not in BOOTLOADER_OVERRIDE_ADDED_LINES
]
def idf_macro_matches(idf_path: Path) -> bool:
"""Whether IDF's macro still matches the copy the override replays."""
build_cmake = idf_path / "tools" / "cmake" / "build.cmake"
live = _normalized_macro(build_cmake.read_text(encoding="utf-8"))
return live == _EXPECTED_MACRO
def get_project_cmakelists(
minimal: bool = False, builtin_components: list[str] | None = None
) -> str:
@@ -90,9 +249,10 @@ def get_project_cmakelists(
"""
idf_target = variant_to_idf_target(get_esp32_variant())
# esp_idf_size 2.x (bundled with IDF >=6.0) made NG the default and
# removed the --ng flag; on 1.x (IDF 5.5) --ng is required to get
# --format=raw because the legacy mode doesn't support it.
# esp_idf_size 2.x (IDF >=6.0) made NG the default and removed --ng;
# 1.x (IDF 5.5) needs --ng for --format=json2. 1.x json2 also lacks
# total_size, hence the ELF fallback in espidf/size_summary.py; both
# go away together when 1.x support is dropped.
size_ng_flag = "--ng" if idf_version() < cv.Version(6, 0, 0) else ""
# Project-wide compile options: -D defines and -W warning flags (skip
@@ -122,6 +282,22 @@ def get_project_cmakelists(
else ""
)
# Stops the ~3s sections.ld regeneration on app-only edits; see
# _LDGEN_OVERRIDE. ESPHOME_LDGEN_FULL_DEPS=1 restores stock behavior;
# ESPHOME_LDGEN_STRICT=1 (CI) fails the configure when an IDF bump
# breaks the override instead of degrading to stock deps.
if get_bool_env("ESPHOME_LDGEN_FULL_DEPS"):
ldgen_override = ""
ldgen_override_check = ""
else:
strict = get_bool_env("ESPHOME_LDGEN_STRICT")
severity = "FATAL_ERROR" if strict else "WARNING"
missing = "FATAL_ERROR" if strict else "STATUS"
ldgen_override = _LDGEN_OVERRIDE.replace("@SEVERITY@", severity).replace(
"@MISSING@", missing
)
ldgen_override_check = _LDGEN_OVERRIDE_CHECK.replace("@SEVERITY@", severity)
# CMake variables registered via cg.add_cmake_arg(). Emitted before
# include(project.cmake) so values like EXCLUDE_COMPONENTS are already
# set when project.cmake seeds the component list, and on minimal
@@ -199,6 +375,9 @@ set(EXTRA_COMPONENT_DIRS ${{CMAKE_SOURCE_DIR}}/src)
include($ENV{{IDF_PATH}}/tools/cmake/project.cmake)
{IDF_BOOTLOADER_OVERRIDE}
{ldgen_override}
{cpp_standard_options}
{cxx_compile_options}
@@ -211,12 +390,22 @@ include($ENV{{IDF_PATH}}/tools/cmake/project.cmake)
project({CORE.name})
# Emit raw JSON size data for ESPHome to read post-build.
{ldgen_override_check}
{_lwip_empty_sources_filter()}
# Emit per-memory-type JSON size data for ESPHome to read post-build.
# json2 stays small; raw dumps every symbol (~2s on a large map) and
# this command runs inside the link edge, blocking everything downstream.
# The map is a BYPRODUCT so ninja knows the link writes it; IDF's size
# target depends on the map and can then be built in the same run as all.
# IDF's cmakev2 declares the map itself, so drop this line on that switch.
add_custom_command(
TARGET ${{CMAKE_PROJECT_NAME}}.elf POST_BUILD
COMMAND ${{PYTHON}} -m esp_idf_size {size_ng_flag} --format=raw
COMMAND ${{PYTHON}} -m esp_idf_size {size_ng_flag} --format=json2
-o ${{CMAKE_BINARY_DIR}}/esp_idf_size.json
${{CMAKE_PROJECT_NAME}}.map
BYPRODUCTS ${{CMAKE_BINARY_DIR}}/${{CMAKE_PROJECT_NAME}}.map
WORKING_DIRECTORY ${{CMAKE_BINARY_DIR}}
VERBATIM
)
@@ -279,9 +468,72 @@ idf_component_register(
target_link_options(${{COMPONENT_LIB}} PUBLIC
{link_opts_str}
)
{_pch_cmake_block()}"""
# Where CMake puts the .gch of the src component; ccache reads the checksum
# next to it in place of the .gch
_PCH_SUM_PATH = "build/esp-idf/src/CMakeFiles/__idf_src.dir/cmake_pch.hxx.gch.sum"
# Where the Windows gate records its choice
_PCH_CHOICE_VAR = "ESPHOME_PCH"
def _pch_cmake_block() -> str:
"""The CMake block that precompiles the core headers for the C++ sources
of the src component; empty when disabled."""
if not pch.pch_enabled():
return ""
headers = "\n".join(
f' "$<$<COMPILE_LANGUAGE:CXX>:${{CMAKE_CURRENT_SOURCE_DIR}}/{header}>"'
for header in pch.PCH_DEFAULT_HEADERS
)
block = f"""target_precompile_headers(${{COMPONENT_LIB}} PRIVATE
{headers}
)"""
if not pch.pch_needs_gcc_check():
return f"\n# ESPHome precompiled header\n{block}\n"
# Before the first configure only CMake knows the compiler version
return f"""
# ESPHome precompiled header, unless GCC bug 14940 keeps it from loading
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND ({pch.PCH_WINDOWS_CMAKE_OLD_GCC}))
message(STATUS "ESPHome: GCC ${{CMAKE_CXX_COMPILER_VERSION}} cannot load a precompiled header on Windows; compiling without it")
set({_PCH_CHOICE_VAR} OFF CACHE BOOL "ESPHome precompiled header in use" FORCE)
else()
set({_PCH_CHOICE_VAR} ON CACHE BOOL "ESPHome precompiled header in use" FORCE)
{textwrap.indent(block, " ")}
endif()
"""
def _read_if_exists(path: Path) -> str:
return path.read_text(encoding="utf-8") if path.is_file() else ""
def write_pch_checksum() -> None:
"""Write the checksum ccache uses in place of the .gch: the core headers,
the framework version, the sdkconfig and the managed component versions."""
from esphome.espidf.toolchain import get_cmake_cache_value
if not pch.pch_enabled():
return
# The gate's choice, cached by configure
if pch.pch_needs_gcc_check() and get_cmake_cache_value(_PCH_CHOICE_VAR) != "ON":
return
pch.log_pch_in_use()
checksum = pch.pch_checksum(
CORE.relative_src_path(),
pch.PCH_DEFAULT_HEADERS,
(
str(idf_version()),
_read_if_exists(CORE.relative_build_path(f"sdkconfig.{CORE.name}")),
_read_if_exists(CORE.relative_build_path("dependencies.lock")),
),
)
path = CORE.relative_build_path(_PCH_SUM_PATH)
path.parent.mkdir(parents=True, exist_ok=True)
write_file_if_changed(path, checksum + "\n")
def write_project(
minimal: bool = False, builtin_components: list[str] | None = None
) -> None:
+303
View File
@@ -0,0 +1,303 @@
"""Native ninja build generator for the host platform.
Emits ``build.ninja`` under ``.pioenvs/<name>/``: every source in the
generated ``src/`` tree plus the resolved registry libraries compiles with
the machine's compiler and links into ``program``, the name PlatformIO's
native platform produced. Build flags route the way SCons's ``ParseFlags``
did under PlatformIO: ``-D``/``-I``/``-std=``/``-W`` shapes reach the
compile lines only, ``-l``/``-L``/``-Wl,`` the link line only, everything
else both.
"""
from __future__ import annotations
from collections.abc import Iterable
import logging
import os
from pathlib import Path
import subprocess
import sys
from typing import TYPE_CHECKING
from esphome.build_helpers.ninja import escape as _e, quote_path as _q, shell_token
from esphome.build_helpers.ninja_gen import (
PATH_ARG_FLAGS,
Flag,
anchor_path_flag,
ar_rule_lines,
collect_sources,
compile_edges,
compile_rule_lines,
library_edges,
pch_edges,
pch_rule_lines,
tool_lines,
)
from esphome.build_helpers.pch import PCH_DEFAULT_HEADERS, pch_enabled
from esphome.core import CORE, EsphomeError
from esphome.framework_helpers import get_project_cxx_compile_flags
from esphome.helpers import mkdir_p, write_file_if_changed
from esphome.host.toolchain import PROGRAM_NAME, HostCompilers, find_tool, get_build_dir
from esphome.platformio.library import lex_build_flags
if TYPE_CHECKING:
from esphome.arduino.library import ArduinoLibrary
_LOGGER = logging.getLogger(__name__)
# The PlatformIO platform the host built under; registry manifests declare
# compatibility against it, as lib_compat_mode=strict checked before
PIO_PLATFORM = "native"
# Namespaces the shared library download cache (pio_components/host/)
LIBRARY_CACHE_KEY = "host"
# Flag shapes that only the compiler understands; dropped from the link line
_COMPILE_ONLY_PREFIXES = ("-D", "-U", "-I", "-std=", "-W", *PATH_ARG_FLAGS)
# Flag shapes that only the linker consumes; inert on a -c compile line
_LINK_ONLY_PREFIXES = ("-l", "-L", "-Wl,")
# Link-only flags whose argument is the next token
_LINK_ONLY_ARG_FLAGS = ("-framework", "-Xlinker", "-z")
def parse_flags(entries: Iterable[str], owner: str) -> list[Flag]:
"""Lex build flag entries into flags, each with its argument.
Entries are a set, so their order is not the user's: a flag and the
argument it takes as the next token must share one entry.
"""
flags: list[Flag] = []
for entry in entries:
it = iter(lex_build_flags(entry, owner))
for tok in it:
if tok not in PATH_ARG_FLAGS and tok not in _LINK_ONLY_ARG_FLAGS:
flags.append((tok,))
continue
arg = next(it, None)
# A path never starts with "-"; that is the next flag
if arg is None or (tok in PATH_ARG_FLAGS and arg.startswith("-")):
raise EsphomeError(
f"{owner} build flags have '{tok}' with no argument; write "
f"the flag and its argument as one entry"
)
flags.append((tok, arg))
return flags
def split_flags(flags: list[Flag]) -> tuple[list[Flag], list[Flag]]:
"""Route build flags to the compile and link lines."""
compile_flags: list[Flag] = []
link_flags: list[Flag] = []
for flag in flags:
name = flag[0]
if len(flag) > 1:
(compile_flags if name in PATH_ARG_FLAGS else link_flags).append(flag)
elif name.startswith(_LINK_ONLY_PREFIXES):
# Checked before the compile prefixes: -Wl, would match -W
link_flags.append(flag)
elif name.startswith(_COMPILE_ONLY_PREFIXES):
compile_flags.append(flag)
else:
# -g, -O, -f*, -m*, -pthread, --coverage: both lines, as SCons
compile_flags.append(flag)
link_flags.append(flag)
return compile_flags, link_flags
def _is_std(flag: Flag) -> bool:
return flag[0].startswith("-std=")
def _is_cxx_std(flag: Flag) -> bool:
return _is_std(flag) and "++" in flag[0]
def _anchored_flags(entries: Iterable[str], owner: str) -> list[Flag]:
build_path = Path(CORE.build_path)
return [
anchor_path_flag(flag, build_path)
for flag in parse_flags(sorted(entries), owner)
]
def _flag_lists() -> tuple[list[str], list[str], list[str]]:
"""The C, C++, and link flag lists (raw tokens), build_unflags applied.
``cg.set_cpp_standard`` wins over any ``-std=`` in the build flags for
C++ compiles, as PlatformIO's unflag of every other standard did; C
compiles never see a C++ standard.
"""
# The funnel warns and drops empty glued arguments (-D "") itself
compile_flags, link_flags = split_flags(
_anchored_flags(CORE.build_flags, "esphome")
)
cflags = [f for f in compile_flags if not _is_cxx_std(f)]
cxx_std = CORE.cpp_standard
cxxflags = [f for f in compile_flags if not (cxx_std and _is_std(f))]
if cxx_std:
cxxflags.insert(0, (f"-std={cxx_std}",))
cxxflags += [(tok,) for tok in get_project_cxx_compile_flags()]
# A flag is removed whole, with its argument, as PlatformIO did
unflags = set(_anchored_flags(CORE.build_unflags, "esphome build_unflags"))
# An unflag that hits nothing (a typo, or -DUSE_FOO against
# -DUSE_FOO=1) must be visible, since the user believes the flag is
# gone while it still drives the build
if unmatched := sorted(unflags - set(cflags) - set(cxxflags) - set(link_flags)):
_LOGGER.warning(
"build_unflags entries matched no build flag: %s",
", ".join(" ".join(flag) for flag in unmatched),
)
def keep(flags: list[Flag]) -> list[str]:
return [tok for flag in flags if flag not in unflags for tok in flag]
return keep(cflags), keep(cxxflags), keep(link_flags)
def _resolve_host_libraries() -> list[ArduinoLibrary]:
"""Every ``cg.add_library()`` entry, fetched from the registry.
The host has no framework, so nothing is bundled and no framework
compatibility check applies; the platform check keeps the strict
manifest gate PlatformIO's native platform enforced. Manifest-less
libraries (a bare git checkout) build with PlatformIO's default
layout, as they did under its native platform.
"""
if not CORE.platformio_libraries:
return []
from esphome.arduino.library import resolve_libraries
return resolve_libraries(
None,
pio_platform=PIO_PLATFORM,
board_mcu="host",
cache_key=LIBRARY_CACHE_KEY,
framework=None,
manifest_optional=True,
)
def _file_macro_maps(build_dir: Path) -> list[str]:
"""Flags that keep ``__FILE__`` relative to the build path.
PlatformIO compiled ``src/x.cpp`` from the build path, and tools name
things after that spelling (CodSpeed's benchmark ids). Here a source
reaches the compiler by its absolute path, or relative to the build
directory when ccache rewrites it.
"""
build_path = Path(CORE.build_path)
prefixes = (build_path, Path(os.path.relpath(build_path, build_dir)))
return [
shell_token(f"-fmacro-prefix-map={prefix}{os.sep}=", force=True)
for prefix in prefixes
]
def _compiler_version(cxx: tuple[str, ...]) -> str:
"""What the compiler says it is: its path can stay the same across an
update (the macOS shims in /usr/bin)."""
result = subprocess.run(
[*cxx, "--version"], capture_output=True, text=True, check=False
)
return result.stdout
def write_project(compilers: HostCompilers, ccache: str | None) -> bool:
"""Write the ninja build for the current configuration.
``ccache`` is the caller's already-resolved binary (None when disabled).
Returns True when ``build.ninja`` changed, so the caller can skip work
derived purely from it (the compile database) on unchanged builds.
"""
build_dir = get_build_dir()
mkdir_p(build_dir)
src_dir = CORE.relative_src_path()
if not src_dir.is_dir():
# Generated project state, not install state: clean-all would not help
raise EsphomeError(f"Generated source directory {src_dir} is missing")
cflags, cxxflags, link_flags = _flag_lists()
libraries = _resolve_host_libraries()
include_dirs = [src_dir]
for lib in libraries:
include_dirs += lib.include_dirs
includes = [f"-I{_q(d)}" for d in include_dirs]
includes += _file_macro_maps(build_dir)
# SCons's link line: $LINKFLAGS $SOURCES $_LIBDIRFLAGS $_LIBFLAGS, so
# -L and -l trail the objects while every other link token leads
lib_dirs = [Path(t[2:]) for t in link_flags if t.startswith("-L")]
libs = [t for t in link_flags if t.startswith("-l")]
linkflags = [shell_token(t) for t in link_flags if not t.startswith(("-L", "-l"))]
for lib in libraries:
lib_dirs += lib.link_dirs
libs += [f"-l{name}" for name in lib.link_libs]
linkflags += [shell_token(f) for f in lib.link_flags]
# PlatformIO's ASPPCOM passes only -D/-I user flags to assembly
asflags = [t for t in cflags if t.startswith(("-D", "-I"))]
lines = [
*tool_lines(compilers.cc, compilers.cxx, ccache),
*compile_rule_lines(),
*pch_rule_lines(),
"rule link",
" command = $cxx -o $out $linkflags @$out.rsp $archives $libdirflags $libflags",
" rspfile = $out.rsp",
" rspfile_content = $in_newline",
" description = LINK $out",
]
if any(lib.sources and lib.lib_archive for lib in libraries):
# Resolved only when an archive is built, so a system without
# binutils still links a library-free configuration
lines += ar_rule_lines(find_tool("AR", ("ar",)))
lines += [
"",
f"cflags = {' '.join([*map(shell_token, cflags), *includes])}",
f"cxxflags = {' '.join([*map(shell_token, cxxflags), *includes])}",
f"asflags = {' '.join([*map(shell_token, asflags), *includes])}",
f"linkflags = {' '.join(linkflags)}",
f"libdirflags = {' '.join(f'-L{_q(d)}' for d in lib_dirs)}",
f"libflags = {' '.join(shell_token(lib) for lib in libs)}",
"",
]
archives, direct_objs = library_edges(lines, libraries)
src_cxx_override = pch_edges(
lines,
build_dir,
src_dir,
PCH_DEFAULT_HEADERS,
# The arguments of a CXX override come before the flags
[*compilers.cxx[1:], *cxxflags],
(),
(compilers.cxx[0], _compiler_version(compilers.cxx)) if pch_enabled() else (),
compilers.cxx,
)
src_objs = compile_edges(
lines,
collect_sources(src_dir),
src_dir,
"src",
cxx_override=src_cxx_override,
)
if not src_objs:
raise EsphomeError(f"No source files found under {src_dir}")
# Archives are not topologically sorted; GNU ld needs the group to
# resolve references between them. ld64 loads archives iteratively and
# rejects the option, so macOS lists them bare.
archive_tokens = [shell_token(a) for a in archives]
if archive_tokens and sys.platform != "darwin":
archive_tokens = ["-Wl,--start-group", *archive_tokens, "-Wl,--end-group"]
lines.append(
f"build {PROGRAM_NAME}: link {' '.join(src_objs + direct_objs)} | "
f"{' '.join(_e(a) for a in archives)}"
)
lines.append(f" archives = {' '.join(archive_tokens)}")
lines.append(f"default {PROGRAM_NAME}")
lines.append("")
return write_file_if_changed(build_dir / "build.ninja", "\n".join(lines))
+6
View File
@@ -1,6 +1,8 @@
from esphome.build_helpers.pch import pch_script_enabled
from esphome.const import __version__
from esphome.core import CORE
from esphome.helpers import mkdir_p, read_file, write_file_if_changed
from esphome.platformio.toolchain import copy_pch_script
from esphome.writer import find_begin_end
INI_AUTO_GENERATE_BEGIN = "; ========== AUTO GENERATED CODE BEGIN ==========="
@@ -62,6 +64,8 @@ def get_ini_content():
# Add extra script for C++ flags
CORE.add_platformio_option("extra_scripts", [f"pre:{CXX_FLAGS_FILE_NAME}"])
if pch_script_enabled():
CORE.add_platformio_option("extra_scripts", ["post:pch.py"])
# Add CMake args. A user-supplied value (str or list) is deliberately
# replaced; this option was always overwritten at FINAL priority.
@@ -106,6 +110,8 @@ def write_project():
# Write extra script for C++ specific flags
write_cxx_flags_script()
if pch_script_enabled():
copy_pch_script()
CXX_FLAGS_FILE_NAME = "cxx_flags.py"
+41 -2
View File
@@ -68,6 +68,15 @@ def resolve_ccache_path() -> str | None:
return ccache
def resolve_absolute_ccache_path() -> str | None:
"""``resolve_ccache_path`` for the ninja backends, which run their
commands from the build directory, where a relative path is lost."""
from esphome.build_helpers.ninja import absolute_tool
ccache = resolve_ccache_path()
return absolute_tool(ccache) if ccache else None
def ccache_defaults_env(cache_dir: Path) -> dict[str, str]:
"""Default ``CCACHE_*`` values for a build subprocess (not os.environ).
@@ -84,9 +93,39 @@ def ccache_defaults_env(cache_dir: Path) -> dict[str, str]:
"CORE.build_path must be set before constructing the build environment"
)
defaults = {
"CCACHE_DIR": str(cache_dir),
# ccache expands $VAR in its settings; $$ is a literal $
"CCACHE_DIR": str(cache_dir).replace("$", "$$"),
"CCACHE_NOHASHDIR": "true",
"CCACHE_DEPEND": "1",
"CCACHE_BASEDIR": str(Path(CORE.build_path).resolve()),
"CCACHE_BASEDIR": str(Path(CORE.build_path).resolve()).replace("$", "$$"),
}
return {k: v for k, v in defaults.items() if k not in os.environ}
def effective_ccache_basedir() -> str:
"""The prefix ccache strips from hashed paths: a usable user
CCACHE_BASEDIR, else the resolved build path."""
from esphome.core import CORE
raw = os.environ.get("CCACHE_BASEDIR")
if raw is not None and Path(raw).is_absolute() and len(Path(raw).parts) > 1:
return raw
return str(Path(CORE.build_path).resolve())
def ccache_env(ccache: str | None, tools_cache: tuple[str, str]) -> dict[str, str]:
"""The ccache settings for a build subprocess (not os.environ).
``ccache`` is the pre-resolved binary (resolve_ccache_path), or None when
disabled; ``tools_cache`` is the backend's tools cache spec, which holds
its ccache dir. The pch settings include ``time_macros``: a cached
object can keep an older ``__DATE__`` or ``__TIME__``.
"""
if ccache is None:
return {}
from esphome.build_helpers.pch import ccache_pch_env
from esphome.build_helpers.tools_cache import tools_cache_path
env = ccache_defaults_env(tools_cache_path(*tools_cache) / "ccache")
env.update(ccache_pch_env())
return env
+27 -1
View File
@@ -11,6 +11,7 @@ consumers (IDE integration, clang-tidy) expect:
from __future__ import annotations
from collections.abc import Callable
import json
import logging
import os
@@ -21,6 +22,8 @@ import subprocess
from esphome.core import EsphomeError
from esphome.helpers import write_file
_LOGGER = logging.getLogger(__name__)
# Everything idedata generation may raise after a successful link; idedata
# is a bonus artifact, so consumers warn instead of failing the build
IDEDATA_BEST_EFFORT_ERRORS = (
@@ -31,7 +34,21 @@ IDEDATA_BEST_EFFORT_ERRORS = (
ValueError,
)
_LOGGER = logging.getLogger(__name__)
def warn_if_idedata_missing(get_idedata: Callable[[], dict | None]) -> None:
"""Run an idedata generator, downgrading any failure to a warning:
the firmware already built."""
try:
if get_idedata() is None:
_LOGGER.warning("No idedata was generated for this build")
except IDEDATA_BEST_EFFORT_ERRORS as err:
_LOGGER.warning(
"Could not generate idedata: %s (IDE, clang-tidy, and "
"memory-analysis data will be unavailable for this build)",
err,
)
_LOGGER.debug("Idedata failure detail", exc_info=True)
# C++ translation-unit suffixes used to identify ESPHome source files.
_CXX_SUFFIXES = (".cpp", ".cc")
@@ -135,6 +152,15 @@ def _is_launcher(token: str) -> bool:
return Path(token).stem.lower() in _LAUNCHER_STEMS
def is_joined_include(tok: str) -> bool:
"""The joined ``-includefoo.h`` spelling; excludes clang's -include-pch."""
return (
tok.startswith("-include")
and tok != "-include"
and not tok.startswith("-include-")
)
def parse_entry(
entry: dict, launcher: str | None = None
) -> tuple[str, list[str], list[str], list[str]]:
+74
View File
@@ -0,0 +1,74 @@
"""Resolution of the native (non-PlatformIO) build backend for a config.
Kept deliberately light: the serial upload and logs fast path resolves a
backend for its artifact paths alone, so importing this must not pull in a
platform component package or the backend itself.
"""
from __future__ import annotations
from collections.abc import Collection
import importlib
import logging
from types import ModuleType
from esphome.const import Toolchain
from esphome.core import CORE, EsphomeError
_LOGGER = logging.getLogger(__name__)
# Native build backend per (target platform, toolchain)
NATIVE_TOOLCHAIN_MODULES = {
("esp32", Toolchain.ESP_IDF): "esphome.espidf.toolchain",
("esp8266", Toolchain.ARDUINO): "esphome.arduino8266.toolchain",
("host", Toolchain.HOST): "esphome.host.toolchain",
}
def native_backend() -> ModuleType | None:
"""The native build backend module for the resolved toolchain."""
if not CORE.using_native_toolchain:
return None
key = (CORE.target_platform, CORE.toolchain)
if (module_path := NATIVE_TOOLCHAIN_MODULES.get(key)) is None:
# Degrading to the PlatformIO path would build with the wrong backend
raise EsphomeError(
f"Toolchain '{CORE.toolchain.value}' has no native build backend "
f"module for platform {CORE.target_platform}"
)
return importlib.import_module(module_path)
# Binutils and the linked image for memory analysis, for toolchains that build
# without PlatformIO but have no native build backend (which supplies them)
ANALYSIS_TOOLCHAIN_MODULES = {
("nrf52", Toolchain.SDK_NRF): "esphome.components.nrf52.toolchain",
}
def analysis_backend() -> ModuleType | None:
"""The module giving objdump, readelf and the ELF of a non-PlatformIO build.
None means PlatformIO's idedata supplies them (or nothing can).
"""
if (native := native_backend()) is not None:
return native
module_path = ANALYSIS_TOOLCHAIN_MODULES.get((CORE.target_platform, CORE.toolchain))
return importlib.import_module(module_path) if module_path else None
def warn_ignored_platformio_options(consumed: Collection[str]) -> None:
"""Warn for component-added platformio options a native build drops.
User-supplied keys were already routed or warned about by
``core/config.py``; what survives into ``CORE.platformio_options`` came
from ``cg.add_platformio_option`` calls in components.
"""
for key in sorted(CORE.platformio_options or {}):
if key not in consumed:
_LOGGER.warning(
"platformio_options->%s is ignored when building with the "
"native '%s' toolchain",
key,
CORE.toolchain.value,
)
+93 -3
View File
@@ -2,14 +2,17 @@
from __future__ import annotations
import json
import logging
import os
from pathlib import Path
import re
import shutil
import subprocess
from esphome.core import EsphomeError
from esphome.framework_helpers import strip_win_long_path_prefix, tool_version_runs
from esphome.helpers import write_file_if_changed
_LOGGER = logging.getLogger(__name__)
@@ -23,11 +26,26 @@ def _ninja_runs(binary: str) -> bool:
)
# Compile rule names the generators emit; ninja's compdb tool is asked for
# exactly these, so a renamed rule fails the build instead of stranding idedata
COMPILE_RULES = ("c", "cxx", "aspp", "asm")
def absolute_tool(tool: str | Path) -> str:
"""A tool path that still resolves from the build directory.
``shutil.which`` returns a relative path for a relative PATH entry, and
ninja runs the commands from ``.pioenvs/<name>``. Symlinks are kept:
ccache's compiler links depend on the name they are called by.
"""
return strip_win_long_path_prefix(str(Path(tool).absolute()))
def find_ninja() -> Path:
"""Locate the ninja binary: a runnable PATH hit first, else the ninja
PyPI wheel."""
if binary := shutil.which("ninja"):
binary = strip_win_long_path_prefix(binary)
binary = absolute_tool(binary)
if _ninja_runs(binary):
return Path(binary)
import_error: ImportError | None = None
@@ -49,8 +67,18 @@ def find_ninja() -> Path:
def escape(value: Path | str) -> str:
"""Escape a path or token for a ninja file."""
return str(value).replace("$", "$$").replace(":", "$:").replace(" ", "$ ")
"""Escape a path or token for a ninja file.
ninja has no escape for ``|`` or a line break in a path, so those fail
here by name instead of producing a build file ninja misreads.
"""
text = str(value)
if bad := next((c for c in "|\r\n" if c in text), None):
raise EsphomeError(
f"Path {text!r} contains {bad!r}, which a ninja build file cannot "
"express; rename the file or directory"
)
return text.replace("$", "$$").replace(":", "$:").replace(" ", "$ ")
def quote_arg(tok: str) -> str:
@@ -90,3 +118,65 @@ def shell_token(tok: str, force: bool = False) -> str:
def quote_path(value: Path | str) -> str:
"""Force-quote a path for the ninja command line (shell/CreateProcess)."""
return shell_token(str(value), force=True)
def refresh_compile_commands(
ninja_path: Path, build_dir: Path, env: dict[str, str], ninja_changed: bool
) -> None:
"""Regenerate the compile DB (a pure function of build.ninja) when stale.
Freshness rides a stamp: the DB itself is written through
write_file_if_changed (its mtime feeds the idedata cache), so a
regeneration with identical content would stay "stale" forever. An
interrupted previous run may have rewritten the manifest without
regenerating the DB, hence the mtime comparison.
"""
compdb = build_dir / "compile_commands.json"
compdb_stamp = build_dir / ".compile_commands.stamp"
ninja_file = build_dir / "build.ninja"
if (
ninja_changed
or not compdb.is_file()
or not compdb_stamp.is_file()
or compdb_stamp.stat().st_mtime < ninja_file.stat().st_mtime
):
write_compile_commands(ninja_path, build_dir, env)
compdb_stamp.touch()
def write_compile_commands(
ninja_path: Path, build_dir: Path, env: dict[str, str]
) -> None:
compdb = build_dir / "compile_commands.json"
result = subprocess.run(
[str(ninja_path), "-C", str(build_dir), "-t", "compdb", *COMPILE_RULES],
env=env,
capture_output=True,
text=True,
check=False,
close_fds=False,
)
if result.returncode != 0:
# Drop any stale database so consumers (IDE integration, clang-tidy,
# the memory analyzer) can't silently read outdated data
compdb.unlink(missing_ok=True)
raise EsphomeError(f"Could not generate compile_commands.json: {result.stderr}")
try:
entries = json.loads(result.stdout)
except ValueError as err:
compdb.unlink(missing_ok=True)
raise EsphomeError(
f"ninja produced an unparsable compile database: {err} "
f"(output starts {result.stdout[:120]!r})"
) from err
if not entries:
# compdb exits 0 with [] for unknown rule names; a renamed compile
# rule must fail the build, not silently strand every consumer
compdb.unlink(missing_ok=True)
raise EsphomeError(
"ninja produced an empty compile database; the generator's rule "
"names no longer match"
)
# write_file_if_changed keeps the mtime stable on no-op builds so the
# idedata cache stays valid
write_file_if_changed(compdb, result.stdout)
+281
View File
@@ -0,0 +1,281 @@
"""The parts of a ``build.ninja`` every native backend emits the same way.
Rule names match ``SOURCE_KIND_FOR_SUFFIX`` values (c, cxx, asm, aspp) and
``ninja.COMPILE_RULES``, which the compile database is asked for.
"""
from __future__ import annotations
from collections.abc import Collection, Sequence
import logging
import os
from pathlib import Path
import sys
from typing import TYPE_CHECKING
from esphome.build_helpers.idedata import is_joined_include
from esphome.build_helpers.ninja import (
escape as _e,
quote_path as _q,
shell_token as _shell_token,
)
from esphome.build_helpers.pch import (
PCH_GCH_NAME,
PCH_SUM_NAME,
log_pch_in_use,
pch_consumer_flags,
pch_identity,
pch_usable,
write_pch_headers,
)
from esphome.framework_helpers import strip_win_long_path_prefix
from esphome.helpers import write_file_if_changed
from esphome.platformio.library import SOURCE_KIND_FOR_SUFFIX
if TYPE_CHECKING:
from esphome.arduino.library import ArduinoLibrary
_LOGGER = logging.getLogger(__name__)
_BUILD_TOOL = Path(__file__).parent.parent / "build_gen" / "build_tool.py"
def collect_sources(root: Path, exclude: Collection[str] = frozenset()) -> list[Path]:
return sorted(
p
for p in root.rglob("*")
if p.suffix in SOURCE_KIND_FOR_SUFFIX and p.name not in exclude
)
def common_parent(paths: list[Path]) -> Path:
return Path(os.path.commonpath([str(p.parent) for p in paths]))
def _command(words: Sequence[Path | str]) -> str:
program, *args = words
return " ".join([_q(program), *(_shell_token(str(a)) for a in args)])
def tool_lines(
cc: Sequence[Path | str], cxx: Sequence[Path | str], ccache: str | None
) -> list[str]:
"""The file header: the compilers and the helper tools as variables.
A compiler is its program followed by any arguments it always takes.
"""
return [
"# Auto-generated by ESPHome",
"ninja_required_version = 1.5",
f"cc = {_command(cc)}",
f"cxx = {_command(cxx)}",
# The NSIS launcher starts Python with a \\?\ extended-length path
# that cmd.exe cannot spawn; same strip every other emitted binary
# path gets
f"python = {_q(strip_win_long_path_prefix(sys.executable))}",
f"buildtool = {_q(_BUILD_TOOL)}",
f"ccache = {_q(ccache) if ccache else ''}",
"",
]
def compile_rule_lines() -> list[str]:
"""The compile rules; ``$own_includes`` is empty unless an edge sets it."""
return [
"rule c",
" command = $ccache $cc -MMD -MF $out.d $own_includes $cflags $flags -c $in -o $out",
" depfile = $out.d",
" deps = gcc",
" description = CC $out",
"rule cxx",
" command = $ccache $cxx -MMD -MF $out.d $own_includes $cxxflags $flags -c $in -o $out",
" depfile = $out.d",
" deps = gcc",
" description = CXX $out",
"rule aspp",
" command = $ccache $cc -MMD -MF $out.d -x assembler-with-cpp $own_includes $asflags $flags -c $in -o $out",
" depfile = $out.d",
" deps = gcc",
" description = AS $out",
# Plain assembler, as SCons's ASCOM: no preprocessor, so no
# depfile and no $flags (defines/includes) either
"rule asm",
" command = $ccache $cc -x assembler $asflags -c $in -o $out",
" description = AS $out",
]
def pch_rule_lines() -> list[str]:
"""The precompiled header rule, for a generator that emits one."""
return [
# No $ccache: the .gch embeds build dir paths
"rule pch",
" command = $cxx -MMD -MF $out.d -x c++-header $cxxflags $flags -c $in -o $out",
" depfile = $out.d",
" deps = gcc",
" description = PCH $out",
]
def pch_edges(
lines: list[str],
build_dir: Path,
src_dir: Path,
headers: Sequence[str],
cxxflags: Sequence[str],
src_flags: Sequence[str],
identity: Sequence[str],
cxx: Sequence[Path | str],
) -> tuple[str, str] | None:
"""Emit the precompiled header for the C++ src edges.
``headers`` are folded into one prefix header, ``src_flags`` are the
flags every src edge carries, ``identity`` names what else the compile
depends on, the compiler included, and ``cxx`` is what the host rule
asks. Returns the ``cxx_override`` for ``compile_edges``, or None
without a pch.
"""
if not pch_usable(cxx):
return None
if any(
tok == "-include" or tok.startswith("--include") or is_joined_include(tok)
for tok in cxxflags
):
# $cxxflags expands first and GCC only loads a .gch for the first
# -include
_LOGGER.warning(
"A -include in the compiler flags prevents the precompiled header from "
"loading; compiling without it"
)
return None
log_pch_in_use()
source = write_pch_headers(build_dir, headers)
write_file_if_changed(
build_dir / PCH_SUM_NAME,
pch_identity([*cxxflags, *src_flags], src_dir, tuple(headers), identity) + "\n",
)
gch = _e(PCH_GCH_NAME)
# The checksum file changes with anything the .gch depends on
lines.append(f"build {gch}: pch {_e(source)} | {_e(PCH_SUM_NAME)}")
if src_flags:
lines.append(f" flags = {' '.join(src_flags)}")
lines.append(f"srccxxflags = {' '.join([*src_flags, *pch_consumer_flags()])}")
return ("$srccxxflags", gch)
def ar_rule_lines(ar: Path | str) -> list[str]:
return [
"rule ar",
f" command = $python $buildtool ar {_q(ar)} $out $out.rsp",
" rspfile = $out.rsp",
" rspfile_content = $in_newline",
" description = AR $out",
]
def compile_edges(
lines: list[str],
sources: list[Path],
root: Path,
group: str,
flags: str = "",
own_includes: str = "",
cxx_override: tuple[str, str] | None = None,
) -> list[str]:
"""Emit compile edges for ``sources``; return the object paths.
``cxx_override`` is a (flags, implicit-dep) pair applied to C++ edges
only, replacing ``flags`` (used for the precompiled header).
"""
objects = []
for src in sources:
rel = src.relative_to(root).as_posix()
obj = f"obj/{group}/{rel}.o"
escaped_obj = _e(obj)
kind = SOURCE_KIND_FOR_SUFFIX[src.suffix]
override = cxx_override if kind == "cxx" else None
implicit = f" | {override[1]}" if override else ""
lines.append(f"build {escaped_obj}: {kind} {_e(src)}{implicit}")
if own_includes:
lines.append(f" own_includes = {own_includes}")
if edge_flags := override[0] if override else flags:
lines.append(f" flags = {edge_flags}")
# Escaped once here: the returned paths only ever appear in build
# statements (archive/link inputs), which use ninja escaping
objects.append(escaped_obj)
return objects
def library_edges(
lines: list[str], libraries: list[ArduinoLibrary], extra_flags: str = ""
) -> tuple[list[str], list[str]]:
"""Emit every library's compile and archive edges.
``extra_flags`` (raw ninja text) precede each library's own flags.
Returns the archive names and the objects that link directly. A
library's own include dirs lead its compile lines, as PlatformIO searched
them first: the include path is one global list, so another library's
header of the same name would shadow them.
"""
archives: list[str] = []
direct_objs: list[str] = []
for lib in libraries:
if not lib.sources:
# Header-only libraries are legitimate; the log makes an empty
# srcFilter or broken tree traceable before link errors do
_LOGGER.debug(
"Library %s has no source files; contributing includes only",
lib.name,
)
continue
objs = compile_edges(
lines,
lib.sources,
common_parent(lib.sources),
f"lib/{lib.name}",
flags=" ".join(filter(None, [extra_flags, *map(_shell_token, lib.flags)])),
own_includes=" ".join(f"-I{_q(d)}" for d in lib.include_dirs),
)
if not lib.lib_archive:
# libArchive: false / dot_a_linkage=false: hand the objects to
# the linker directly so unreferenced-but-required symbols
# (exception handlers, weak overrides) survive
direct_objs.extend(objs)
continue
archive = f"lib{lib.name}.a"
lines.append(f"build {_e(archive)}: ar {' '.join(objs)}")
archives.append(archive)
return archives, direct_objs
# One build flag: the flag, plus its argument when that is a separate token
Flag = tuple[str, ...]
# Flags whose path operand is the next token; gcc also takes it glued on
PATH_ARG_FLAGS = ("-include", "-imacros", "-isystem", "-iquote", "-idirafter")
# Flags whose path operand is glued on
PATH_PREFIXES = ("-I", "-L", *PATH_ARG_FLAGS)
def _anchor(path: str, base: Path) -> str:
if not path or Path(path).is_absolute():
return path
return str(base / path)
def anchor_path_flag(flag: Flag, base: Path) -> Flag:
"""Anchor a flag's relative path operand at ``base``.
PlatformIO ran the compiler from the build path; ninja runs it from
``.pioenvs/<name>``, where a relative operand would point elsewhere.
"""
name, *args = flag
if args:
if name in PATH_ARG_FLAGS:
return (name, _anchor(args[0], base))
return flag
for prefix in PATH_PREFIXES:
if name.startswith(prefix):
return (prefix + _anchor(name[len(prefix) :], base),)
return flag
+248
View File
@@ -0,0 +1,248 @@
"""Shared precompiled header policy for the build backends."""
from __future__ import annotations
from collections.abc import Iterable, Sequence
import hashlib
import logging
import os
from pathlib import Path
import posixpath
import re
import subprocess
import sys
from esphome.build_helpers.ccache import effective_ccache_basedir, parse_enable_env
from esphome.const import PLATFORM_NRF52
from esphome.helpers import write_file_if_changed
_LOGGER = logging.getLogger(__name__)
# The header and its sidecars live in the build directory
PCH_HEADER_NAME = "esphome_pch.h"
PCH_GCH_NAME = f"{PCH_HEADER_NAME}.gch"
# ccache hashes this instead of the .gch; also the freshness stamp
PCH_SUM_NAME = f"{PCH_GCH_NAME}.sum"
# The include list the .gch is compiled from
PCH_SOURCE_NAME = "esphome_pch_src.h"
# GCC can skip a .gch without a diagnostic and read the header of the same
# name, so that header is an error. Other tools get the include list.
PCH_GUARD_TEXT = f"""\
#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTELLISENSE__)
#error "The precompiled header was not loaded"
#else
#include "{PCH_SOURCE_NAME}"
#endif
"""
# The cc1plus wrapper the PlatformIO script writes on arm64 macOS
PCH_CC1_DIR = "pch_cc1"
# What the PlatformIO script leaves in the project root, for cleanup
PCH_ARTIFACT_NAMES = (PCH_HEADER_NAME, PCH_GCH_NAME, PCH_SUM_NAME, PCH_SOURCE_NAME)
PCH_ARTIFACT_DIRS = (PCH_CC1_DIR,)
# The core headers every backend precompiles
PCH_DEFAULT_HEADERS = ("esphome/core/pch_prefix.h",)
# PlatformIO platforms that do not take the pch script
PCH_SCRIPT_EXCLUDED_PLATFORMS = frozenset(
{
PLATFORM_NRF52,
}
)
# What ccache needs to cache compiles that load a .gch
_CCACHE_PCH_SLOPPINESS = ("pch_defines", "time_macros")
# Both include forms: an angle include resolving under src/ enters the digest
_INCLUDE_RE = re.compile(rb'^\s*#\s*include\s+["<]([^">]+)[">]', re.MULTILINE)
def pch_enabled() -> bool:
"""Precompiled-header knob: default on, ``ESPHOME_PCH_ENABLE=0`` opts out."""
return parse_enable_env("ESPHOME_PCH_ENABLE") is not False
def pch_forced() -> bool:
"""``ESPHOME_PCH_ENABLE=1``: wanted even where the host rule says no."""
return parse_enable_env("ESPHOME_PCH_ENABLE") is True
# GCC bug 14940: before these releases the Windows loader maps a .gch only
# at its saved address. First fixed release per major, 16 on always fixed;
# PCH_WINDOWS_CMAKE_OLD_GCC and the pch_usable message spell the same table
PCH_WINDOWS_GCC_FIXED = {14: (14, 4), 15: (15, 3)}
PCH_WINDOWS_GCC_FIXED_DEFAULT = (16, 0)
# The same rule for CMake, which alone knows the version before configure
PCH_WINDOWS_CMAKE_OLD_GCC = (
"CMAKE_CXX_COMPILER_VERSION VERSION_LESS 14.4 OR "
"(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 15 AND "
"CMAKE_CXX_COMPILER_VERSION VERSION_LESS 15.3)"
)
def gcc_relocates_pch_on_windows(version: Sequence[int]) -> bool:
"""Whether a GCC of this version loads a .gch on Windows."""
if not version:
return False
fixed = PCH_WINDOWS_GCC_FIXED.get(version[0], PCH_WINDOWS_GCC_FIXED_DEFAULT)
return tuple(version[:2]) >= fixed
# GCC ends the first --version line with its version; clang names itself
_VERSION_RE = re.compile(r"\d+(?:\.\d+)+")
def gcc_version(cxx: Sequence[Path | str]) -> tuple[int, ...] | None:
"""The GCC version from ``--version``: () when it cannot be read, None
for a compiler that is not GCC."""
try:
result = subprocess.run(
[*cxx, "--version"], capture_output=True, text=True, check=False
)
except OSError as err:
_LOGGER.debug("Cannot run %s: %s", cxx[0], err)
return ()
banner = result.stdout.partition("\n")[0]
if "clang" in banner.lower():
return None
found = _VERSION_RE.findall(banner)
return tuple(int(part) for part in found[-1].split(".")) if found else ()
def pch_needs_gcc_check() -> bool:
"""Windows host with the knob unset: the compiler version decides."""
return sys.platform == "win32" and parse_enable_env("ESPHOME_PCH_ENABLE") is None
def pch_usable(cxx: Sequence[Path | str]) -> bool:
"""The knob plus the host rule; ``ESPHOME_PCH_ENABLE=1`` skips the rule."""
if not pch_enabled():
return False
if not pch_needs_gcc_check():
return True
version = gcc_version(cxx)
if version is None or gcc_relocates_pch_on_windows(version):
return True
_LOGGER.info(
"GCC %s cannot load a precompiled header on Windows (GCC bug 14940, "
"fixed in 14.4, 15.3 and 16); compiling without it "
"(set ESPHOME_PCH_ENABLE=1 to force)",
".".join(map(str, version)) or "of unknown version",
)
return False
def pch_consumer_flags() -> list[str]:
"""Flags a C++ src compile loads the pch with. The -include stays
relative: an absolute path would enter the ccache key."""
return ["-Winvalid-pch", "-Werror=invalid-pch", "-include", PCH_HEADER_NAME]
def ccache_pch_env() -> dict[str, str]:
"""What ccache needs to cache compiles that load a .gch, added to what
the user already set."""
if not pch_enabled():
return {}
sloppiness = [
item.strip()
for item in os.environ.get("CCACHE_SLOPPINESS", "").split(",")
if item.strip()
]
sloppiness += [item for item in _CCACHE_PCH_SLOPPINESS if item not in sloppiness]
env = {"CCACHE_SLOPPINESS": ",".join(sloppiness)}
if "CCACHE_PCH_EXTSUM" not in os.environ:
env["CCACHE_PCH_EXTSUM"] = "true"
return env
def pch_script_enabled() -> bool:
"""Whether this PlatformIO build takes the pch script."""
from esphome.core import CORE
return pch_enabled() and CORE.target_platform not in PCH_SCRIPT_EXCLUDED_PLATFORMS
def pch_header_text(include_headers: Iterable[str]) -> str:
"""The prefix-header source: exactly these includes, in order."""
return "".join(f'#include "{name}"\n' for name in include_headers)
def write_pch_headers(build_dir: Path, include_headers: Iterable[str]) -> Path:
"""Write the guard header and the include list; return the latter,
which is what the .gch compiles from."""
write_file_if_changed(build_dir / PCH_HEADER_NAME, PCH_GUARD_TEXT)
source = build_dir / PCH_SOURCE_NAME
write_file_if_changed(source, pch_header_text(include_headers))
return source
def _include_closure(src_dir: Path, roots: Iterable[str]) -> dict[str, bytes]:
"""Include closure of ``roots``: src-relative name -> contents.
Resolution mirrors the compiler (includer's dir, then src root). No
#ifdef evaluation: including too much is the safe direction. Headers
outside ``src_dir`` are covered by the version strings of the caller.
"""
seen: dict[str, bytes] = {}
stack: list[tuple[str, str]] = [(name, "") for name in roots]
while stack:
name, from_dir = stack.pop()
for candidate in (f"{from_dir}/{name}" if from_dir else name, name):
rel = posixpath.normpath(candidate)
if not rel.startswith("..") and (src_dir / rel).is_file():
break
else:
continue
if rel in seen:
continue
data = seen[rel] = (src_dir / rel).read_bytes()
parent = posixpath.dirname(rel)
stack.extend((inc.decode(), parent) for inc in _INCLUDE_RE.findall(data))
return seen
def pch_checksum(
src_dir: Path, include_headers: Iterable[str], extra: Iterable[str]
) -> str:
"""Digest of the prefix header's include closure plus ``extra``."""
digest = hashlib.sha256()
closure = _include_closure(src_dir, include_headers)
for name in sorted(closure):
digest.update(name.encode())
digest.update(closure[name])
digest.update(b"\0")
for item in extra:
digest.update(item.encode())
digest.update(b"\0")
return digest.hexdigest()
def pch_identity(
tokens: Iterable[str],
src_dir: Path,
include_headers: tuple[str, ...],
extra: Iterable[str],
) -> str:
"""The .sum digest: include closure, header text, ``extra`` and the
compile flags with the build path stripped, as ccache does."""
from esphome.core import CORE
flags = (
" ".join(tokens)
.replace(str(CORE.build_path), "")
.replace(effective_ccache_basedir(), "")
)
# The closure is sorted, so header order only enters via the text
return pch_checksum(
src_dir, include_headers, (pch_header_text(include_headers), *extra, flags)
)
_DISABLE_HINT = " (set ESPHOME_PCH_ENABLE=0 to disable)"
def log_pch_in_use() -> None:
_LOGGER.info("Compiling with a precompiled header%s", _DISABLE_HINT)
+180
View File
@@ -0,0 +1,180 @@
"""Run a native build tool (cmake, ninja) and relay its output.
Output is read from a pipe so it can be filtered here: a child that inherits
our stdout writes straight to the file descriptor, past any Python wrapper.
"""
from __future__ import annotations
import codecs
from contextlib import suppress
import logging
import os
from pathlib import Path
import re
import shutil
import subprocess
import sys
from typing import Any, TextIO
from esphome.util import ANSI_ESCAPE, RedirectText, shlex_quote
_LOGGER = logging.getLogger(__name__)
# Windows code page identifier for UTF-8, as used by ``chcp 65001``.
UTF8_CODEPAGE = 65001
# Same pattern idf.py uses to spot ninja status lines (``is_progression``).
_PROGRESS = re.compile(r"^\[\d+/\d+\]|.*\(\d+ \%\)$")
_READ_SIZE = 65536
def _get_kernel32() -> Any | None:
"""Return the Windows kernel32 module, or None on any other platform."""
if sys.platform != "win32":
return None
import ctypes
return ctypes.windll.kernel32
class Utf8Console:
"""Keep an attached Windows console on UTF-8 while a build tool runs.
esp_idf_size draws its table with Unicode box characters, and CMake
re-decodes a child's output with the console code page, which garbles the
table on any page but UTF-8. A console already on UTF-8 is left alone so
an overlapping build never records UTF-8 as the page to go back to.
"""
def __init__(self, kernel32: Any | None) -> None:
self._kernel32 = kernel32
self._codepages: tuple[int, int] | None = None
def __enter__(self) -> None:
kernel32 = self._kernel32
if kernel32 is None:
return
old_in = kernel32.GetConsoleCP()
old_out = kernel32.GetConsoleOutputCP()
# Both calls return 0 when no console is attached.
if not old_in or not old_out:
return
if old_in == UTF8_CODEPAGE and old_out == UTF8_CODEPAGE:
return
# Record first so a switch that fails part way is still undone.
self._codepages = (old_in, old_out)
kernel32.SetConsoleCP(UTF8_CODEPAGE)
kernel32.SetConsoleOutputCP(UTF8_CODEPAGE)
def __exit__(self, *exc_info: object) -> None:
if self._codepages is None:
return
old_in, old_out = self._codepages
self._codepages = None
self._kernel32.SetConsoleCP(old_in)
self._kernel32.SetConsoleOutputCP(old_out)
def _fit_terminal(text: str) -> str:
"""Elide the middle of ``text`` to fit the terminal, as idf.py does.
A width of 0 (a pipe, the dashboard) leaves the text whole.
"""
width = shutil.get_terminal_size((0, 0)).columns
if not width:
return text
if width <= 3:
return "." * width
if len(text) >= width:
keep = (width - 3) // 2
return f"{text[:keep]}...{text[len(text) - keep :]}"
return text
class ToolOutput(RedirectText):
"""RedirectText that can collapse ninja status lines into one line.
With ``progress`` each ``[n/m]`` line overwrites the previous one, the
way idf.py shows a build.
"""
def __init__(
self, out: TextIO, filter_lines: list[str] | None, progress: bool
) -> None:
super().__init__(out, filter_lines=filter_lines)
self._progress = progress
self._on_progress_line = False
def _splits_lines(self) -> bool:
return self._progress or super()._splits_lines()
def _emit_line(self, line: str) -> None:
if self._progress and _PROGRESS.match(line):
if not self._is_filtered(line):
text = _fit_terminal(line.strip("\r\n"))
self._write_color_replace(f"\r{text}\x1b[K")
self._on_progress_line = True
return
self._end_progress_line()
super()._emit_line(line)
def _end_progress_line(self) -> None:
if self._on_progress_line:
self._on_progress_line = False
self._write_color_replace(os.linesep)
def drain(self) -> None:
super().drain()
# Called from cleanup, so a broken stream must not hide the exit code.
with suppress(OSError, ValueError):
self._end_progress_line()
self._out.flush()
def run_build_tool(
cmd: list[str],
*,
cwd: Path,
env: dict[str, str],
filter_lines: list[str] | None = None,
progress: bool = False,
log_path: Path | None = None,
) -> int:
"""Run ``cmd`` and relay stdout and stderr, merged, to our stdout.
``log_path`` also gets the full, unfiltered output without color codes, as
idf.py wrote its logs (its hint patterns expect plain text). Returns the
exit code.
"""
_LOGGER.debug("Running: %s", " ".join(shlex_quote(arg) for arg in cmd))
_LOGGER.debug(" in directory: %s", cwd)
output = ToolOutput(sys.stdout, filter_lines, progress)
decoder = codecs.getincrementaldecoder("utf-8")(errors="replace")
if log_path is not None:
log_path.parent.mkdir(parents=True, exist_ok=True)
with (
Path(log_path or os.devnull).open("w", encoding="utf-8", newline="") as log,
Utf8Console(_get_kernel32()),
subprocess.Popen(
cmd,
cwd=cwd,
env=env,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
close_fds=False,
) as proc,
):
try:
# read1 returns as soon as anything is available, so output
# streams while the tool runs.
while chunk := proc.stdout.read1(_READ_SIZE):
text = decoder.decode(chunk)
log.write(ANSI_ESCAPE.sub("", text))
output.write(text)
if tail := decoder.decode(b"", final=True):
log.write(ANSI_ESCAPE.sub("", tail))
output.write(tail)
finally:
output.drain()
return proc.returncode
+13 -3
View File
@@ -16,8 +16,8 @@ def tools_cache_path(env_var: str, subdir: str) -> Path:
from esphome.helpers import get_str_env
if prefix := get_str_env(env_var, "").strip():
# resolve(): symlinked prefixes otherwise trip idf.py's
# venv-mismatch warning on every build
# resolve(): a symlinked prefix would otherwise record a second
# spelling of the same paths in the build tree
return Path(prefix).expanduser().resolve()
# appauthor=False keeps the Windows path short (no vendor segment);
# deep IDF trees run into MAX_PATH otherwise
@@ -33,4 +33,14 @@ def tools_cache_path(env_var: str, subdir: str) -> Path:
IDF_TOOLS_CACHE = ("ESPHOME_ESP_IDF_PREFIX", "idf")
SDK_NRF_TOOLS_CACHE = ("ESPHOME_SDK_NRF_PREFIX", "sdk-nrf")
ARDUINO8266_TOOLS_CACHE = ("ESPHOME_ARDUINO8266_PREFIX", "arduino8266")
TOOLS_CACHE_SPECS = (IDF_TOOLS_CACHE, SDK_NRF_TOOLS_CACHE, ARDUINO8266_TOOLS_CACHE)
# The host backend installs nothing; the entry only holds its ccache dir
HOST_TOOLS_CACHE = ("ESPHOME_HOST_PREFIX", "host")
# PlatformIO installs into its own dirs; the entry is its ccache dir itself
PLATFORMIO_CCACHE = ("ESPHOME_PLATFORMIO_CCACHE_DIR", "platformio-ccache")
TOOLS_CACHE_SPECS = (
IDF_TOOLS_CACHE,
SDK_NRF_TOOLS_CACHE,
ARDUINO8266_TOOLS_CACHE,
HOST_TOOLS_CACHE,
PLATFORMIO_CCACHE,
)
+2
View File
@@ -0,0 +1,2 @@
"""Commands of the esphome command line, one module each, imported by
__main__ only when they run so that startup stays light."""
+164
View File
@@ -0,0 +1,164 @@
"""``esphome rename``."""
from __future__ import annotations
import argparse
from pathlib import Path
import re
from esphome import yaml_edit, yaml_util
from esphome.const import (
ALLOWED_NAME_CHARS,
CONF_ESPHOME,
CONF_NAME,
CONF_SUBSTITUTIONS,
)
from esphome.core import CORE, EsphomeError
from esphome.log import AnsiFore, color
from esphome.types import ConfigType
from esphome.util import ESPHOME_COMMAND, run_external_process, safe_print
def _revert(new_path: Path, why: str) -> int:
"""Say why the rename stopped and take the new file back; an orphan the
next attempt would trip over is reported."""
safe_print(color(AnsiFore.BOLD_RED, f"Rename failed: {why}"))
try:
new_path.unlink(missing_ok=True)
except OSError as err:
safe_print(color(AnsiFore.BOLD_RED, f"Could not remove {new_path}: {err}"))
return 1
def command_rename(args: argparse.Namespace, config: ConfigType) -> int | None:
"""Rename the device: a new file with the name line rewritten, validated
and installed, then the old file removed."""
new_name = args.name
for c in new_name:
if c not in ALLOWED_NAME_CHARS:
safe_print(
color(
AnsiFore.BOLD_RED,
f"'{c}' is an invalid character for names. Valid characters are: "
f"{ALLOWED_NAME_CHARS} (lowercase, no spaces)",
)
)
return 1
yaml = yaml_util.load_yaml(CORE.config_path)
def name_edit() -> tuple[str, yaml_edit.LineEdit]:
"""The name and the line to rewrite: the name's own line, or the
substitution's line it comes from, as a plain value in this file."""
esphome_conf = yaml.get(CONF_ESPHOME)
if not isinstance(esphome_conf, dict) or CONF_NAME not in esphome_conf:
raise EsphomeError(f"no '{CONF_ESPHOME}: {CONF_NAME}:' in the file")
old_name = str(esphome_conf[CONF_NAME])
mapping, field = esphome_conf, CONF_NAME
if match := re.match(r"^\$\{?([a-zA-Z0-9_]+)\}?$", old_name):
mapping, field = yaml.get(CONF_SUBSTITUTIONS), match.group(1)
if not isinstance(mapping, dict) or field not in mapping:
raise EsphomeError(f"the substitution '{field}' is not in the file")
old_name = str(mapping[field])
# Only read here; the rewritten text goes to a new file, so the
# source may live anywhere the config path points to
source = yaml_edit.source_of(mapping, field)
if source is None or source[0].resolve() != CORE.config_path.resolve():
raise EsphomeError(f"'{field}' was not read from {CORE.config_path}")
doc, line_no = source
text = yaml_edit.line_at(doc, line_no)
if (line_match := yaml_edit.field_line_re(field, old_name).match(text)) is None:
raise EsphomeError(f"'{field}' is not a plain value on {doc}:{line_no + 1}")
# The new value is always quoted, whatever the old line had
return old_name, yaml_edit.LineEdit(
doc, line_no, text, yaml_edit.rewrite(line_match, new_name, '"')
)
try:
old_name, edit = name_edit()
except EsphomeError as err:
safe_print(
color(
AnsiFore.BOLD_RED,
f"Complex YAML files cannot be automatically renamed: {err}",
)
)
return 1
# ``new_name == old_name`` (after substitution resolution) is
# a no-op rewrite that would still queue a pointless re-flash.
# Catch it before the path-equality check below — covers the
# case where the config filename doesn't match the device name
# (e.g. ``weird-file.yaml`` whose ``esphome.name`` is
# ``kitchen``; running ``esphome rename weird-file.yaml kitchen``
# would otherwise just re-flash the same hostname).
if new_name == old_name:
safe_print(
color(
AnsiFore.BOLD_RED,
f"'{new_name}' is already the device's name.",
)
)
return 1
new_path: Path = CORE.config_dir / (new_name + ".yaml")
if new_path.resolve() == CORE.config_path.resolve():
safe_print(
color(
AnsiFore.BOLD_RED,
f"'{new_name}' is already the device's name.",
)
)
return 1
if new_path.exists():
safe_print(
color(
AnsiFore.BOLD_RED,
f"Cannot rename: {new_path} already exists. "
"Refusing to overwrite an existing configuration.",
)
)
return 1
safe_print(
f"Updating {color(AnsiFore.CYAN, str(CORE.config_path))} to {color(AnsiFore.CYAN, str(new_path))}"
)
print()
try:
yaml_edit.write_keeping_mode(
new_path,
yaml_edit.rewritten_text(yaml_edit.read_text(CORE.config_path), [edit]),
like=CORE.config_path,
)
except EsphomeError as err:
return _revert(new_path, str(err))
if run_external_process(*ESPHOME_COMMAND, "config", str(new_path)) != 0:
return _revert(new_path, "the new configuration does not validate")
cli_args = [
"run",
str(new_path),
"--no-logs",
"--device",
CORE.address,
]
if args.dashboard:
cli_args.insert(0, "--dashboard")
try:
rc = run_external_process(*ESPHOME_COMMAND, *cli_args)
except KeyboardInterrupt:
rc = 1
if rc != 0:
return _revert(
new_path,
"the install did not finish; the device may already run the new name",
)
CORE.config_path.unlink()
safe_print(color(AnsiFore.BOLD_GREEN, "SUCCESS"))
print()
return 0
+3 -1
View File
@@ -31,6 +31,7 @@ from esphome.cpp_generator import ( # noqa: F401
add_global,
add_library,
add_platformio_option,
extern_progmem_array,
get_variable,
get_variable_with_full_id,
is_template,
@@ -40,8 +41,10 @@ from esphome.cpp_generator import ( # noqa: F401
progmem_array,
safe_exp,
set_cpp_standard,
shared_progmem_array,
statement,
static_const_array,
static_function,
templatable,
variable,
with_local_variable,
@@ -64,7 +67,6 @@ from esphome.cpp_types import ( # noqa: F401
Application,
Component,
ComponentPtr,
Controller,
EntityBase,
EntityCategory,
ESPTime,
+1
View File
@@ -6,5 +6,6 @@ See the component-alias section of esphome/loader.py.
# alias -> (canonical component, removal version or None)
COMPONENT_ALIASES: dict[str, tuple[str, str | None]] = {
"esp32_improv": ("improv_ble", "2027.4.0"),
"rp2040": ("rp2", "2027.7.0"),
}
+1 -1
View File
@@ -6,7 +6,7 @@
namespace esphome::a01nyub {
static const char *const TAG = "a01nyub.sensor";
ESPHOME_LOG_TAG(TAG, "a01nyub.sensor");
void A01nyubComponent::loop() {
uint8_t data;
+1 -1
View File
@@ -6,7 +6,7 @@
namespace esphome::a02yyuw {
static const char *const TAG = "a02yyuw.sensor";
ESPHOME_LOG_TAG(TAG, "a02yyuw.sensor");
void A02yyuwComponent::loop() {
uint8_t data;
+1 -1
View File
@@ -3,7 +3,7 @@
namespace esphome::a4988 {
static const char *const TAG = "a4988.stepper";
ESPHOME_LOG_TAG(TAG, "a4988.stepper");
void A4988::setup() {
if (this->sleep_pin_ != nullptr) {
@@ -3,7 +3,7 @@
namespace esphome::absolute_humidity {
static const char *const TAG{"absolute_humidity.sensor"};
ESPHOME_LOG_TAG(TAG, "absolute_humidity.sensor");
void AbsoluteHumidityComponent::setup() {
this->temperature_sensor_->add_on_state_callback([this](float state) {
+1 -1
View File
@@ -14,7 +14,7 @@
namespace esphome::ac_dimmer {
static const char *const TAG = "ac_dimmer";
ESPHOME_LOG_TAG(TAG, "ac_dimmer");
// Global array to store dimmer objects
static AcDimmerDataStore *all_dimmers[32]; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
@@ -9,7 +9,7 @@
#include "esp_clk_tree.h"
#include "soc/clk_tree_defs.h"
static const char *const TAG = "hw_timer_esp_idf";
ESPHOME_LOG_TAG(TAG, "hw_timer_esp_idf");
namespace esphome::ac_dimmer {
@@ -3,7 +3,7 @@
namespace esphome::adalight {
static const char *const TAG = "adalight_light_effect";
ESPHOME_LOG_TAG(TAG, "adalight_light_effect");
static const uint32_t ADALIGHT_ACK_INTERVAL = 1000;
static const uint32_t ADALIGHT_RECEIVE_TIMEOUT = 1000;
+1 -1
View File
@@ -94,7 +94,7 @@ class ADCSensor final : public sensor::Sensor, public PollingComponent, public v
/// - SamplingMode::MIN: Use the lowest sample value
/// - SamplingMode::MAX: Use the highest sample value
/// @param sampling_mode The desired sampling mode to use for aggregating ADC samples.
void set_sampling_mode(SamplingMode sampling_mode);
void set_sampling_mode(SamplingMode sampling_mode) { this->sampling_mode_ = sampling_mode; }
/// Perform a single ADC sampling operation and return the measured value.
/// This function handles raw readings, calibration, and averaging as needed.
+1 -3
View File
@@ -3,7 +3,7 @@
namespace esphome::adc {
static const char *const TAG = "adc";
ESPHOME_LOG_TAG(TAG, "adc");
const LogString *sampling_mode_to_str(SamplingMode mode) {
switch (mode) {
@@ -76,6 +76,4 @@ void ADCSensor::set_sample_count(uint8_t sample_count) {
}
}
void ADCSensor::set_sampling_mode(SamplingMode sampling_mode) { this->sampling_mode_ = sampling_mode; }
} // namespace esphome::adc
+1 -1
View File
@@ -6,7 +6,7 @@
namespace esphome::adc {
static const char *const TAG = "adc";
ESPHOME_LOG_TAG(TAG, "adc");
adc_oneshot_unit_handle_t ADCSensor::shared_adc_handles[2] = {nullptr, nullptr};
@@ -13,7 +13,7 @@ ADC_MODE(ADC_VCC)
namespace esphome::adc {
static const char *const TAG = "adc";
ESPHOME_LOG_TAG(TAG, "adc");
void ADCSensor::setup() {
#ifndef USE_ADC_SENSOR_VCC
@@ -5,7 +5,7 @@
namespace esphome::adc {
static const char *const TAG = "adc";
ESPHOME_LOG_TAG(TAG, "adc");
void ADCSensor::setup() {
#ifndef USE_ADC_SENSOR_VCC
+1 -1
View File
@@ -17,7 +17,7 @@
namespace esphome::adc {
static const char *const TAG = "adc";
ESPHOME_LOG_TAG(TAG, "adc");
// The on-die temperature sensor sits on the last ADC channel: input 4 on RP2040
// and RP2350A, but input 8 on RP2350B, which has eight external channels rather
+1 -1
View File
@@ -7,7 +7,7 @@
namespace esphome::adc {
static const char *const TAG = "adc";
ESPHOME_LOG_TAG(TAG, "adc");
void ADCSensor::setup() {
if (!adc_is_ready_dt(this->channel_)) {
+1 -1
View File
@@ -3,7 +3,7 @@
namespace esphome::adc128s102 {
static const char *const TAG = "adc128s102";
ESPHOME_LOG_TAG(TAG, "adc128s102");
float ADC128S102::get_setup_priority() const { return setup_priority::HARDWARE; }
@@ -4,7 +4,7 @@
namespace esphome::adc128s102 {
static const char *const TAG = "adc128s102.sensor";
ESPHOME_LOG_TAG(TAG, "adc128s102.sensor");
ADC128S102Sensor::ADC128S102Sensor(uint8_t channel) : channel_(channel) {}
@@ -3,7 +3,7 @@
namespace esphome::addressable_light {
static const char *const TAG = "addressable_light.display";
ESPHOME_LOG_TAG(TAG, "addressable_light.display");
int AddressableLightDisplay::get_width_internal() { return this->width_; }
int AddressableLightDisplay::get_height_internal() { return this->height_; }
+1 -1
View File
@@ -15,7 +15,7 @@
namespace esphome::ade7880 {
static const char *const TAG = "ade7880";
ESPHOME_LOG_TAG(TAG, "ade7880");
void IRAM_ATTR ADE7880Store::gpio_intr(ADE7880Store *arg) { arg->reset_done = true; }
@@ -5,7 +5,7 @@
namespace esphome::ade7953_base {
static const char *const TAG = "ade7953";
ESPHOME_LOG_TAG(TAG, "ade7953");
constexpr uint16_t CONFIG_DEFAULT = 0x8004u;
constexpr uint16_t CONFIG_LOCK_BIT = 0x8000u;
@@ -4,7 +4,7 @@
namespace esphome::ade7953_i2c {
static const char *const TAG = "ade7953";
ESPHOME_LOG_TAG(TAG, "ade7953");
void AdE7953I2c::dump_config() {
ESP_LOGCONFIG(TAG, "ADE7953_i2c:");
@@ -4,7 +4,7 @@
namespace esphome::ade7953_spi {
static const char *const TAG = "ade7953";
ESPHOME_LOG_TAG(TAG, "ade7953");
// Datasheet requires at least 1.2µs after clearing CONFIG LOCK_BIT before raising CS
constexpr uint8_t CONFIG_LOCK_SETTLE_US = 2;
+1 -1
View File
@@ -4,7 +4,7 @@
namespace esphome::ads1115 {
static const char *const TAG = "ads1115";
ESPHOME_LOG_TAG(TAG, "ads1115");
static const uint8_t ADS1115_REGISTER_CONVERSION = 0x00;
static const uint8_t ADS1115_REGISTER_CONFIG = 0x01;
@@ -4,7 +4,7 @@
namespace esphome::ads1115 {
static const char *const TAG = "ads1115.sensor";
ESPHOME_LOG_TAG(TAG, "ads1115.sensor");
float ADS1115Sensor::sample() {
return this->parent_->request_measurement(this->multiplexer_, this->gain_, this->resolution_, this->samplerate_);
+1 -1
View File
@@ -4,7 +4,7 @@
namespace esphome::ads1118 {
static const char *const TAG = "ads1118";
ESPHOME_LOG_TAG(TAG, "ads1118");
static const uint8_t ADS1118_DATA_RATE_860_SPS = 0b111;
void ADS1118::setup() {
@@ -4,7 +4,7 @@
namespace esphome::ads1118 {
static const char *const TAG = "ads1118.sensor";
ESPHOME_LOG_TAG(TAG, "ads1118.sensor");
void ADS1118Sensor::dump_config() {
LOG_SENSOR(" ", "ADS1118 Sensor", this);
+15 -1
View File
@@ -4,7 +4,7 @@
#include <cinttypes>
namespace esphome::ags10 {
static const char *const TAG = "ags10";
ESPHOME_LOG_TAG(TAG, "ags10");
// Data acquisition.
static const uint8_t REG_TVOC = 0x00;
@@ -106,6 +106,20 @@ bool AGS10Component::set_zero_point_with_factory_defaults() { return this->set_z
bool AGS10Component::set_zero_point_with_current_resistance() { return this->set_zero_point_with(ZP_CURRENT); }
void AGS10Component::set_zero_point(AGS10SetZeroPointActionMode mode, uint16_t value) {
switch (mode) {
case FACTORY_DEFAULT:
this->set_zero_point_with_factory_defaults();
break;
case CURRENT_VALUE:
this->set_zero_point_with_current_resistance();
break;
case CUSTOM_VALUE:
this->set_zero_point_with(value);
break;
}
}
bool AGS10Component::set_zero_point_with(uint16_t value) {
std::array<uint8_t, 5> data{0x00, 0x0C, (uint8_t) ((value >> 8) & 0xFF), (uint8_t) (value & 0xFF), 0};
data[4] = crc8(data.data(), 4, 0xFF, 0x31, true);
+14 -36
View File
@@ -2,11 +2,19 @@
#include "esphome/components/i2c/i2c.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
namespace esphome::ags10 {
enum AGS10SetZeroPointActionMode {
// Zero-point reset.
FACTORY_DEFAULT,
// Zero-point calibration with current resistance.
CURRENT_VALUE,
// Zero-point calibration with custom resistance.
CUSTOM_VALUE,
};
class AGS10Component final : public PollingComponent, public i2c::I2CDevice {
public:
/**
@@ -47,6 +55,11 @@ class AGS10Component final : public PollingComponent, public i2c::I2CDevice {
*/
bool set_zero_point_with_current_resistance();
/**
* Sets zero-point by mode; the value is only used for CUSTOM_VALUE.
*/
void set_zero_point(AGS10SetZeroPointActionMode mode, uint16_t value);
/**
* Sets zero-point with the value.
*/
@@ -100,39 +113,4 @@ class AGS10Component final : 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 final : public Action<Ts...>, public Parented<AGS10Component> {
public:
TEMPLATABLE_VALUE(uint8_t, new_address)
void play(const Ts &...x) override { this->parent_->new_i2c_address(this->new_address_.value(x...)); }
};
enum AGS10SetZeroPointActionMode {
// Zero-point reset.
FACTORY_DEFAULT,
// Zero-point calibration with current resistance.
CURRENT_VALUE,
// Zero-point calibration with custom resistance.
CUSTOM_VALUE,
};
template<typename... Ts> class AGS10SetZeroPointAction final : public Action<Ts...>, public Parented<AGS10Component> {
public:
TEMPLATABLE_VALUE(uint16_t, value)
TEMPLATABLE_VALUE(AGS10SetZeroPointActionMode, mode)
void play(const Ts &...x) override {
switch (this->mode_.value(x...)) {
case FACTORY_DEFAULT:
this->parent_->set_zero_point_with_factory_defaults();
break;
case CURRENT_VALUE:
this->parent_->set_zero_point_with_current_resistance();
break;
case CUSTOM_VALUE:
this->parent_->set_zero_point_with(this->value_.value(x...));
break;
}
}
};
} // namespace esphome::ags10
+11 -51
View File
@@ -17,8 +17,6 @@ from esphome.const import (
UNIT_OHM,
UNIT_PARTS_PER_BILLION,
)
from esphome.core import ID
from esphome.cpp_generator import MockObj, TemplateArgsType
from esphome.types import ConfigType
CONF_RESISTANCE = "resistance"
@@ -28,12 +26,6 @@ DEPENDENCIES = ["i2c"]
ags10_ns = cg.esphome_ns.namespace("ags10")
AGS10Component = ags10_ns.class_("AGS10Component", cg.PollingComponent, i2c.I2CDevice)
# Actions
AGS10NewI2cAddressAction = ags10_ns.class_(
"AGS10NewI2cAddressAction", automation.Action
)
AGS10SetZeroPointAction = ags10_ns.class_("AGS10SetZeroPointAction", automation.Action)
CONFIG_SCHEMA = (
cv.Schema(
{
@@ -70,16 +62,10 @@ async def to_code(config: ConfigType) -> None:
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
sens = await sensor.new_sensor(config[CONF_TVOC])
cg.add(var.set_tvoc(sens))
if version_config := config.get(CONF_VERSION):
sens = await sensor.new_sensor(version_config)
cg.add(var.set_version(sens))
if resistance_config := config.get(CONF_RESISTANCE):
sens = await sensor.new_sensor(resistance_config)
cg.add(var.set_resistance(sens))
sensors = sensor.sub_sensors(config)
await sensors(CONF_TVOC, var.set_tvoc)
await sensors(CONF_VERSION, var.set_version)
await sensors(CONF_RESISTANCE, var.set_resistance)
AGS10_NEW_I2C_ADDRESS_SCHEMA = cv.maybe_simple_value(
@@ -91,24 +77,11 @@ AGS10_NEW_I2C_ADDRESS_SCHEMA = cv.maybe_simple_value(
)
@automation.register_action(
automation.register_apply_action(
"ags10.new_i2c_address",
AGS10NewI2cAddressAction,
AGS10_NEW_I2C_ADDRESS_SCHEMA,
synchronous=True,
automation.ApplyField(CONF_ADDRESS, "new_i2c_address", cg.uint8),
)
async def ags10newi2caddress_to_code(
config: ConfigType,
action_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
address = await cg.templatable(config[CONF_ADDRESS], args, cg.uint8)
cg.add(var.set_new_address(address))
return var
AGS10SetZeroPointActionMode = ags10_ns.enum("AGS10SetZeroPointActionMode")
AGS10_SET_ZERO_POINT_ACTION_MODE = {
@@ -128,24 +101,11 @@ AGS10_SET_ZERO_POINT_SCHEMA = cv.Schema(
)
@automation.register_action(
automation.register_apply_action(
"ags10.set_zero_point",
AGS10SetZeroPointAction,
AGS10_SET_ZERO_POINT_SCHEMA,
synchronous=True,
automation.ApplyCall(
"set_zero_point({}, {})",
((CONF_MODE, AGS10SetZeroPointActionMode), (CONF_VALUE, cg.uint16)),
),
)
async def ags10setzeropoint_to_code(
config: ConfigType,
action_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
var = cg.new_Pvariable(action_id, template_arg)
await cg.register_parented(var, config[CONF_ID])
mode = await cg.templatable(
config.get(CONF_MODE), args, AGS10SetZeroPointActionMode
)
cg.add(var.set_mode(mode))
value = await cg.templatable(config[CONF_VALUE], args, cg.uint16)
cg.add(var.set_value(value))
return var
+1 -1
View File
@@ -19,7 +19,7 @@
namespace esphome::aht10 {
static const char *const TAG = "aht10";
ESPHOME_LOG_TAG(TAG, "aht10");
static const uint8_t AHT10_INITIALIZE_CMD[] = {0xE1, 0x08, 0x00};
static const uint8_t AHT20_INITIALIZE_CMD[] = {0xBE, 0x08, 0x00};
static const uint8_t AHT10_MEASURE_CMD[] = {0xAC, 0x33, 0x00};
+3 -7
View File
@@ -57,10 +57,6 @@ async def to_code(config: ConfigType) -> None:
await i2c.register_i2c_device(var, config)
cg.add(var.set_variant(config[CONF_VARIANT]))
if temperature := config.get(CONF_TEMPERATURE):
sens = await sensor.new_sensor(temperature)
cg.add(var.set_temperature_sensor(sens))
if humidity := config.get(CONF_HUMIDITY):
sens = await sensor.new_sensor(humidity)
cg.add(var.set_humidity_sensor(sens))
sensors = sensor.sub_sensors(config)
await sensors(CONF_TEMPERATURE, var.set_temperature_sensor)
await sensors(CONF_HUMIDITY, var.set_humidity_sensor)
+1 -1
View File
@@ -6,7 +6,7 @@
namespace esphome::aic3204 {
static const char *const TAG = "aic3204";
ESPHOME_LOG_TAG(TAG, "aic3204");
#define ERROR_CHECK(err, msg) \
if (!(err)) { \
+2 -19
View File
@@ -4,8 +4,6 @@ from esphome.components import i2c
from esphome.components.audio_dac import AudioDac
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_MODE
from esphome.core import ID
from esphome.cpp_generator import MockObj, TemplateArgsType
from esphome.types import ConfigType
CODEOWNERS = ["@kbx81"]
@@ -14,7 +12,6 @@ DEPENDENCIES = ["i2c"]
aic3204_ns = cg.esphome_ns.namespace("aic3204")
AIC3204 = aic3204_ns.class_("AIC3204", AudioDac, cg.Component, i2c.I2CDevice)
SetAutoMuteAction = aic3204_ns.class_("SetAutoMuteAction", automation.Action)
CONFIG_SCHEMA = (
cv.Schema(
@@ -36,25 +33,11 @@ SET_AUTO_MUTE_ACTION_SCHEMA = cv.maybe_simple_value(
)
@automation.register_action(
automation.register_apply_action(
"aic3204.set_auto_mute_mode",
SetAutoMuteAction,
SET_AUTO_MUTE_ACTION_SCHEMA,
synchronous=True,
automation.ApplyField(CONF_MODE, "set_auto_mute_mode", cg.uint8),
)
async def aic3204_set_volume_to_code(
config: ConfigType,
action_id: ID,
template_arg: cg.TemplateArguments,
args: TemplateArgsType,
) -> MockObj:
paren = await cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
template_ = await cg.templatable(config.get(CONF_MODE), args, cg.uint8)
cg.add(var.set_auto_mute_mode(template_))
return var
async def to_code(config: ConfigType) -> None:
-21
View File
@@ -1,21 +0,0 @@
#pragma once
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
#include "aic3204.h"
namespace esphome::aic3204 {
template<typename... Ts> class SetAutoMuteAction final : public Action<Ts...> {
public:
explicit SetAutoMuteAction(AIC3204 *aic3204) : aic3204_(aic3204) {}
TEMPLATABLE_VALUE(uint8_t, auto_mute_mode)
void play(const Ts &...x) override { this->aic3204_->set_auto_mute_mode(this->auto_mute_mode_.value(x...)); }
protected:
AIC3204 *aic3204_;
};
} // namespace esphome::aic3204
@@ -4,7 +4,7 @@
namespace esphome::airthings_ble {
static const char *const TAG = "airthings_ble";
ESPHOME_LOG_TAG(TAG, "airthings_ble");
bool AirthingsListener::parse_device(const ble_device_base::ESPBTDevice &device) {
for (auto &it : device.get_manufacturer_datas()) {
@@ -85,20 +85,11 @@ async def wave_base_to_code(var: MockObj, config: ConfigType) -> None:
await ble_client.register_ble_node(var, config)
if config_humidity := config.get(CONF_HUMIDITY):
sens = await sensor.new_sensor(config_humidity)
cg.add(var.set_humidity(sens))
if config_temperature := config.get(CONF_TEMPERATURE):
sens = await sensor.new_sensor(config_temperature)
cg.add(var.set_temperature(sens))
if config_pressure := config.get(CONF_PRESSURE):
sens = await sensor.new_sensor(config_pressure)
cg.add(var.set_pressure(sens))
if config_tvoc := config.get(CONF_TVOC):
sens = await sensor.new_sensor(config_tvoc)
cg.add(var.set_tvoc(sens))
if config_battery_voltage := config.get(CONF_BATTERY_VOLTAGE):
sens = await sensor.new_sensor(config_battery_voltage)
cg.add(var.set_battery_voltage(sens))
sensors = sensor.sub_sensors(config)
await sensors(CONF_HUMIDITY, var.set_humidity)
await sensors(CONF_TEMPERATURE, var.set_temperature)
await sensors(CONF_PRESSURE, var.set_pressure)
await sensors(CONF_TVOC, var.set_tvoc)
await sensors(CONF_BATTERY_VOLTAGE, var.set_battery_voltage)
if config_battery_update_interval := config.get(CONF_BATTERY_UPDATE_INTERVAL):
cg.add(var.set_battery_update_interval(config_battery_update_interval))
@@ -8,7 +8,7 @@
namespace esphome::airthings_wave_base {
static const char *const TAG = "airthings_wave_base";
ESPHOME_LOG_TAG(TAG, "airthings_wave_base");
void AirthingsWaveBase::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
esp_ble_gattc_cb_param_t *param) {
@@ -4,7 +4,7 @@
namespace esphome::airthings_wave_mini {
static const char *const TAG = "airthings_wave_mini";
ESPHOME_LOG_TAG(TAG, "airthings_wave_mini");
void AirthingsWaveMini::read_sensors(uint8_t *raw_value, uint16_t value_len) {
auto *value = (WaveMiniReadings *) raw_value;
@@ -4,7 +4,7 @@
namespace esphome::airthings_wave_plus {
static const char *const TAG = "airthings_wave_plus";
ESPHOME_LOG_TAG(TAG, "airthings_wave_plus");
void AirthingsWavePlus::read_sensors(uint8_t *raw_value, uint16_t value_len) {
auto *value = (WavePlusReadings *) raw_value;
@@ -87,16 +87,9 @@ async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await airthings_wave_base.wave_base_to_code(var, config)
if config_radon := config.get(CONF_RADON):
sens = await sensor.new_sensor(config_radon)
cg.add(var.set_radon(sens))
if config_radon_long_term := config.get(CONF_RADON_LONG_TERM):
sens = await sensor.new_sensor(config_radon_long_term)
cg.add(var.set_radon_long_term(sens))
if config_co2 := config.get(CONF_CO2):
sens = await sensor.new_sensor(config_co2)
cg.add(var.set_co2(sens))
if config_illuminance := config.get(CONF_ILLUMINANCE):
sens = await sensor.new_sensor(config_illuminance)
cg.add(var.set_illuminance(sens))
sensors = sensor.sub_sensors(config)
await sensors(CONF_RADON, var.set_radon)
await sensors(CONF_RADON_LONG_TERM, var.set_radon_long_term)
await sensors(CONF_CO2, var.set_co2)
await sensors(CONF_ILLUMINANCE, var.set_illuminance)
cg.add(var.set_device_type(config[CONF_DEVICE_TYPE]))
@@ -41,18 +41,6 @@ StateAnyForwarder = alarm_control_panel_ns.class_("StateAnyForwarder")
StateEnterForwarder = alarm_control_panel_ns.class_("StateEnterForwarder")
AlarmControlPanelState = alarm_control_panel_ns.enum("AlarmControlPanelState")
ArmAwayAction = alarm_control_panel_ns.class_("ArmAwayAction", automation.Action)
ArmHomeAction = alarm_control_panel_ns.class_("ArmHomeAction", automation.Action)
ArmNightAction = alarm_control_panel_ns.class_("ArmNightAction", automation.Action)
DisarmAction = alarm_control_panel_ns.class_("DisarmAction", automation.Action)
PendingAction = alarm_control_panel_ns.class_("PendingAction", automation.Action)
TriggeredAction = alarm_control_panel_ns.class_("TriggeredAction", automation.Action)
ChimeAction = alarm_control_panel_ns.class_("ChimeAction", automation.Action)
ReadyAction = alarm_control_panel_ns.class_("ReadyAction", automation.Action)
AlarmControlPanelCondition = alarm_control_panel_ns.class_(
"AlarmControlPanelCondition", automation.Condition
)
_ALARM_CONTROL_PANEL_SCHEMA = (
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
@@ -196,125 +184,38 @@ async def new_alarm_control_panel(config, *args):
return var
@automation.register_action(
"alarm_control_panel.arm_away",
ArmAwayAction,
ALARM_CONTROL_PANEL_ACTION_SCHEMA,
synchronous=True,
)
async def alarm_action_arm_away_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 code_config := config.get(CONF_CODE):
templatable_ = await cg.templatable(code_config, args, cg.std_string)
cg.add(var.set_code(templatable_))
return var
@automation.register_action(
"alarm_control_panel.arm_home",
ArmHomeAction,
ALARM_CONTROL_PANEL_ACTION_SCHEMA,
synchronous=True,
)
async def alarm_action_arm_home_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 code_config := config.get(CONF_CODE):
templatable_ = await cg.templatable(code_config, args, cg.std_string)
cg.add(var.set_code(templatable_))
return var
@automation.register_action(
"alarm_control_panel.arm_night",
ArmNightAction,
ALARM_CONTROL_PANEL_ACTION_SCHEMA,
synchronous=True,
)
async def alarm_action_arm_night_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 CONF_CODE in config:
templatable_ = await cg.templatable(config[CONF_CODE], args, cg.std_string)
cg.add(var.set_code(templatable_))
return var
@automation.register_action(
"alarm_control_panel.disarm",
DisarmAction,
ALARM_CONTROL_PANEL_ACTION_SCHEMA,
synchronous=True,
)
async def alarm_action_disarm_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 code_config := config.get(CONF_CODE):
templatable_ = await cg.templatable(code_config, args, cg.std_string)
cg.add(var.set_code(templatable_))
return var
@automation.register_action(
"alarm_control_panel.pending",
PendingAction,
ALARM_CONTROL_PANEL_ACTION_SCHEMA,
synchronous=True,
)
async def alarm_action_pending_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, paren)
@automation.register_action(
"alarm_control_panel.triggered",
TriggeredAction,
ALARM_CONTROL_PANEL_ACTION_SCHEMA,
synchronous=True,
)
async def alarm_action_trigger_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, paren)
@automation.register_action(
"alarm_control_panel.chime",
ChimeAction,
ALARM_CONTROL_PANEL_ACTION_SCHEMA,
synchronous=True,
)
async def alarm_action_chime_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, paren)
@automation.register_action(
"alarm_control_panel.ready",
ReadyAction,
ALARM_CONTROL_PANEL_ACTION_SCHEMA,
synchronous=True,
)
@automation.register_condition(
"alarm_control_panel.ready",
AlarmControlPanelCondition,
ALARM_CONTROL_PANEL_CONDITION_SCHEMA,
)
async def alarm_action_ready_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, paren)
@automation.register_condition(
"alarm_control_panel.is_armed",
AlarmControlPanelCondition,
ALARM_CONTROL_PANEL_CONDITION_SCHEMA,
)
async def alarm_control_panel_is_armed_to_code(
config, condition_id, template_arg, args
# Mirrors AlarmControlPanel::arm_with_code_: arm first, set the code only when given.
for _name, _arm in (
("alarm_control_panel.arm_away", "arm_away()"),
("alarm_control_panel.arm_home", "arm_home()"),
("alarm_control_panel.arm_night", "arm_night()"),
("alarm_control_panel.disarm", "disarm()"),
):
paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(condition_id, template_arg, paren)
automation.register_apply_action(
_name,
ALARM_CONTROL_PANEL_ACTION_SCHEMA,
automation.ApplyCall(_arm),
automation.ApplyField(CONF_CODE, "set_code", cg.std_string),
call="make_call",
)
for _name, _call in (
("alarm_control_panel.pending", "pending()"),
("alarm_control_panel.triggered", "triggered()"),
):
automation.register_apply_action(
_name,
ALARM_CONTROL_PANEL_ACTION_SCHEMA,
automation.ApplyCall(_call),
call="make_call",
)
for _name in ("alarm_control_panel.ready", "alarm_control_panel.is_armed"):
automation.register_apply_condition(
_name, ALARM_CONTROL_PANEL_CONDITION_SCHEMA, "is_armed_pending_or_triggered()"
)
@coroutine_with_priority(CoroPriority.CORE)
@@ -10,7 +10,7 @@
namespace esphome::alarm_control_panel {
static const char *const TAG = "alarm_control_panel";
ESPHOME_LOG_TAG(TAG, "alarm_control_panel");
AlarmControlPanelCall AlarmControlPanel::make_call() { return AlarmControlPanelCall(this); }
@@ -130,6 +130,12 @@ class AlarmControlPanel : public EntityBase {
// is the state one of the armed states
bool is_state_armed(AlarmControlPanelState state);
/// Armed, pending (entry delay) or triggered; not ARMING (exit delay). Backs the is_armed and ready
/// conditions.
bool is_armed_pending_or_triggered() {
auto state = this->get_state();
return this->is_state_armed(state) || state == ACP_STATE_PENDING || state == ACP_STATE_TRIGGERED;
}
protected:
friend AlarmControlPanelCall;
@@ -138,11 +144,11 @@ class AlarmControlPanel : public EntityBase {
// in order to store last panel state in flash
ESPPreferenceObject pref_;
// current state
AlarmControlPanelState current_state_;
AlarmControlPanelState current_state_{ACP_STATE_DISARMED};
// the desired (or previous) state
AlarmControlPanelState desired_state_;
AlarmControlPanelState desired_state_{ACP_STATE_DISARMED};
// last time the state was updated
uint32_t last_update_;
uint32_t last_update_{0};
// the call control function
virtual void control(const AlarmControlPanelCall &call) = 0;
// state callback - passes the new state to listeners
@@ -6,7 +6,7 @@
namespace esphome::alarm_control_panel {
static const char *const TAG = "alarm_control_panel";
ESPHOME_LOG_TAG(TAG, "alarm_control_panel");
AlarmControlPanelCall::AlarmControlPanelCall(AlarmControlPanel *parent) : parent_(parent) {}
@@ -27,84 +27,4 @@ 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 final : public Action<Ts...> {
public:
explicit ArmAwayAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
TEMPLATABLE_VALUE(std::string, code)
void play(const Ts &...x) override { this->alarm_control_panel_->arm_away(this->code_.optional_value(x...)); }
protected:
AlarmControlPanel *alarm_control_panel_;
};
template<typename... Ts> class ArmHomeAction final : public Action<Ts...> {
public:
explicit ArmHomeAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
TEMPLATABLE_VALUE(std::string, code)
void play(const Ts &...x) override { this->alarm_control_panel_->arm_home(this->code_.optional_value(x...)); }
protected:
AlarmControlPanel *alarm_control_panel_;
};
template<typename... Ts> class ArmNightAction final : public Action<Ts...> {
public:
explicit ArmNightAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
TEMPLATABLE_VALUE(std::string, code)
void play(const Ts &...x) override { this->alarm_control_panel_->arm_night(this->code_.optional_value(x...)); }
protected:
AlarmControlPanel *alarm_control_panel_;
};
template<typename... Ts> class DisarmAction final : public Action<Ts...> {
public:
explicit DisarmAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
TEMPLATABLE_VALUE(std::string, code)
void play(const Ts &...x) override { this->alarm_control_panel_->disarm(this->code_.optional_value(x...)); }
protected:
AlarmControlPanel *alarm_control_panel_;
};
template<typename... Ts> class PendingAction final : public Action<Ts...> {
public:
explicit PendingAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
void play(const Ts &...x) override { this->alarm_control_panel_->make_call().pending().perform(); }
protected:
AlarmControlPanel *alarm_control_panel_;
};
template<typename... Ts> class TriggeredAction final : public Action<Ts...> {
public:
explicit TriggeredAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
void play(const Ts &...x) override { this->alarm_control_panel_->make_call().triggered().perform(); }
protected:
AlarmControlPanel *alarm_control_panel_;
};
template<typename... Ts> class AlarmControlPanelCondition final : public Condition<Ts...> {
public:
AlarmControlPanelCondition(AlarmControlPanel *parent) : parent_(parent) {}
bool check(const Ts &...x) override {
return this->parent_->is_state_armed(this->parent_->get_state()) ||
this->parent_->get_state() == ACP_STATE_PENDING || this->parent_->get_state() == ACP_STATE_TRIGGERED;
}
protected:
AlarmControlPanel *parent_;
};
} // namespace esphome::alarm_control_panel
+1 -1
View File
@@ -7,7 +7,7 @@
namespace esphome::alpha3 {
static const char *const TAG = "alpha3";
ESPHOME_LOG_TAG(TAG, "alpha3");
void Alpha3::dump_config() {
ESP_LOGCONFIG(TAG, "ALPHA3");

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