Compare commits

...
Author SHA1 Message Date
J. Nick Koston 3500ecc1c1 Merge remote-tracking branch 'origin/dev' into declcfg-migrate
# Conflicts:
#	esphome/pins.py
2026-10-18 14:51:26 -10:00
J. Nick Koston c793ab30a7 [core][gpio_expander] Add a shared expander pin class and pin codegen helpers (#20866) 2026-10-18 14:48:35 -10:00
Clyde StubbsandClaude Opus 5.5 1753c45e53 [core] Move CONF_PROVIDER and CONF_REMOTE_ID to components/const (#21096)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 14:47:37 -10:00
J. Nick Koston 1767e4b0be [sensor] Add SensorWithDedup for child sensors that skip repeated values (#20925) 2026-10-18 14:47:19 -10:00
J. Nick Koston 5575dd8928 [core] Add final_validate.get_declaring_config (#20912) 2026-10-18 14:46:57 -10:00
J. Nick Koston 8e4592fa87 Merge branch 'declcfg' into declcfg-migrate 2026-10-18 14:21:18 -10:00
J. Nick Koston 76addfec20 Raise TypeError when an ID is not declared in a config block 2026-10-18 14:21:15 -10:00
J. Nick Koston 704dd4b445 [components] Report communication failures with status_comm_result in the remaining components (#21079) 2026-10-19 00:17:57 +00:00
J. Nick Koston c7073b9a0b Drop unused CONF_ID import in dfrobot_sen0609 2026-10-18 14:13:48 -10:00
pre-commit-ci-lite[bot] 480577e2f4 apply automatic formatting fixes 2026-10-19 00:12:56 +00:00
J. Nick Koston 3808c767fd Merge branch 'declcfg' into declcfg-migrate
# Conflicts:
#	esphome/components/display/__init__.py
2026-10-18 14:10:51 -10:00
J. Nick Koston d6a9292c41 Merge remote-tracking branch 'origin/dev' into declcfg 2026-10-18 14:10:43 -10:00
e6ad995c8e [i2s_audio] Add ESP32-S31 support (#20115)
Co-authored-by: cosmicDustOfLightLength <cosmicDustOfLightLength@example.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-19 00:07:52 +00:00
J. Nick Koston 84ce39e493 [components] Use the codegen helpers in the remaining components (#21078) 2026-10-18 14:05:37 -10:00
J. Nick Koston d9242520e3 [components] Decode bytes with the shared helpers (#21080) 2026-10-18 20:00:48 -04:00
J. Nick Koston 03bf2de7df [components] Decode BLE fields with the shared helpers (#21082) 2026-10-18 19:59:19 -04:00
J. Nick Koston a70d75e469 [components] Read UART bytes with read_available and discard_available (#21084) 2026-10-18 13:59:09 -10:00
J. Nick Koston 2dcb97774d [tests] Add an esp32-s31-idf base file and shared i2c and i2s_audio packages (#21086) 2026-10-18 19:54:48 -04:00
Jacopo Labardi be9741c6de [const] Share CONF_VALUE_TYPE across components (#21062) 2026-10-18 13:02:22 -10:00
ab-tools b1c138c612 [audio] Compute the remaining AudioStreamInfo conversions in 64 bits (#21092) 2026-10-18 12:12:50 -10:00
ab-tools 9526233821 [audio] Compute frames_to_microseconds() in 64 bits (#21089) 2026-10-18 11:39:37 -10:00
Bonne EgglestonandJ. Nick Koston 512e43793b [api] Poll the outgoing dial every loop (#21085)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-18 20:47:02 +00:00
603f16623f [usb_host] Size the FIFO split on ESP32-S2/S3 from periodic packet requests (#21081)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-18 20:44:05 +00:00
218e696654 [globals] Add RTC memory support (#13954)
Co-authored-by: Copilot <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-10-18 10:40:55 -10:00
Clyde Stubbs 3e68e0f5e1 [epaper_spi] Add the UC8151D controller and the Waveshare 2.13" B V3 (#21022) 2026-10-19 07:40:16 +11:00
dd456ea0b2 [homeassistant] Add the light platform (#11354)
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-10-18 10:02:31 -10:00
Bonne Eggleston e54d66e620 [api] Poll the outgoing dial every loop on deep sleep builds (#21077) 2026-10-18 09:36:35 -10:00
J. Nick Koston a3bb17d2c6 [components] Create BLE and modbus devices with the new helpers (#21026) 2026-10-18 15:09:58 -04:00
71c0f46e35 [bme68x_bsec2] disable loop (#20994)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-18 08:33:17 -10:00
J. Nick Koston a153fcc8ee [bl0906][kamstrup_kmp][ufm01] Drop stale receive bytes with discard_available (#21071) 2026-10-18 14:30:10 -04:00
85724892cf [light] Add power estimation and current limiting for addressable LED strips (#15348)
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-18 08:08:31 -10:00
J. Nick Koston deea006b39 [components] Set referenced components with cg.add_variable and register_parented (a-l) (#21021) 2026-10-18 17:43:05 +00:00
J. Nick Kostonandpre-commit-ci-lite[bot] 619728b686 [components] Drop stale UART receive bytes with discard_available (#21030)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-10-18 07:23:06 -10:00
J. Nick Koston c35d98b622 [core] Add progmem_string_table_find() (#20790) 2026-10-18 07:19:14 -10:00
J. Nick Koston b2d6c78499 [light] Add LightState::set_external_state() for outputs that mirror a light elsewhere (#20787) 2026-10-18 07:18:47 -10:00
J. Nick KostonandJonathan Swoboda a801e414a0 [components] Set referenced components with cg.add_variable and register_parented (m-z) (#21023)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-10-18 13:32:06 +00:00
J. Nick Koston 22a36de4ae [components] Decode little-endian I2C values with the new helpers (#20939) 2026-10-18 09:20:58 -04:00
J. Nick Koston eca95348ac [components] Assemble 24-bit readings with encode_uint24 (#21016) 2026-10-18 09:16:50 -04:00
J. Nick KostonandJonathan Swoboda ed9616d512 [components] Set pins with cg.add_pin (#21019)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-10-18 09:10:22 -04:00
J. Nick Koston 0cd1ae01f1 [components] Use cv.uint8_t and cv.uint16_t for full byte ranges (#21015) 2026-10-18 08:52:14 -04:00
J. Nick Koston 2a416fbce6 [core] Document that hub entities go in platforms in AGENTS.md (#21003) 2026-10-18 08:45:21 -04:00
J. Nick Koston 270d989481 [core] Add cg.add_pin and cg.add_variable for optional pins and ids (#21018) 2026-10-18 08:41:33 -04:00
J. Nick Koston 3665bcc4e3 [ble_device_base][ble_client][modbus] Add new_ble_device_component, new_ble_node_component and new_modbus_client_component (#21025) 2026-10-18 08:40:35 -04:00
J. Nick Koston 0922df9bb3 [uart] Add discard_available to drop stale receive bytes (#21029) 2026-10-18 08:39:02 -04:00
J. Nick Koston 54a28b3e3b [bl0906] Read stale receive bytes instead of spinning on flush() (#21035) 2026-10-18 08:37:48 -04:00
Michele PorelliandJ. Nick Koston 032a2b7200 [core] Let entities choose flash or RTC storage for their preferences (#21017)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-18 06:58:40 +00:00
J. Nick Koston 0bb78186f2 [components] Set display lambdas with display.add_writer (#21028) 2026-10-17 18:34:38 -10:00
J. Nick Koston 2fe1333cba [display] Add add_writer for display lambdas (#21027) 2026-10-17 18:34:38 -10:00
J. Nick Koston 265754c467 [components] Drop get_setup_priority overrides that return the default (#21014) 2026-10-17 17:43:16 -10:00
6580f4f314 [micro_wake_word] Allow configurable capture buffer duration (#20942)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-17 16:43:07 -10:00
J. Nick Koston 5ad63e3bb6 [http_request] Only treat a URL as secure when its scheme is https (#20867) 2026-10-18 02:38:07 +00:00
J. Nick Koston a8ab035b59 [lock] Filter on_lock and on_unlock with when (#20858) 2026-10-17 16:33:05 -10:00
J. Nick Koston 968d57c56b [core][time] Add BCD helpers and a shared RTC BCD calendar codec (#20860) 2026-10-17 16:32:13 -10:00
J. Nick Koston 1307faef06 [core] Add Component::status_comm_result (#20863) 2026-10-17 16:31:17 -10:00
ee117886b9 [vevor_dtx] Add Vevor weather station component (#17224)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-17 15:42:55 -10:00
1dbad7b951 [airton] Add support to climate for Airton IR A/C units (#7753)
Co-authored-by: Arjan <44190435+vingerha@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-18 01:37:58 +00:00
J. Nick Koston 5df6c65cc2 [api] Test api.connected without options and with a lambda (#20835) 2026-10-17 15:23:35 -10:00
J. Nick Koston af5efabdc6 [light] Test light.toggle with a transition length (#20832) 2026-10-17 15:23:22 -10:00
J. Nick Koston a2754f655c [dfrobot_sen0609][http_request] Use ESPHOME_strstr_P (#20825) 2026-10-17 15:23:06 -10:00
J. Nick Koston 34e6d3ec9c [pylontech] Restore the CONF_ID import (#21013) 2026-10-17 15:21:36 -10:00
J. Nick Koston 21362f8c92 [ethernet] Test the ethernet.connected condition (#20844) 2026-10-17 15:21:18 -10:00
J. Nick Koston 626d84bc64 [sensor][number] Test in_range with every bound combination (#20847) 2026-10-17 15:20:52 -10:00
J. Nick Koston 68fa7b9847 [wifi] Test the wifi.connected and wifi.enabled conditions (#20841) 2026-10-17 15:20:00 -10:00
J. Nick Koston 32dc3b8535 [http_request] Test ota.http_request.flash with every field (#20829) 2026-10-17 15:19:24 -10:00
J. Nick Koston 74bcec34ad [i2c] Add I2CDevice::update_register for register read-modify-write (#20854) 2026-10-17 15:18:36 -10:00
J. Nick Koston c1687aba4c [components] Let core report communication failures in dump_config (5/5) (#20874) 2026-10-17 15:16:21 -10:00
J. Nick Koston ec642f928f [i2c] Add little-endian and signed byte decoding helpers (#20938) 2026-10-17 15:15:36 -10:00
J. Nick Koston ec9a207f42 [components] Create UART devices with new_uart_component (#20919) 2026-10-17 15:15:21 -10:00
J. Nick Koston 3b7847d951 [components] Create SPI devices with new_spi_component (#20920) 2026-10-17 21:13:49 -04:00
J. Nick Koston 1197f22f00 [fusb302b] Expose the sensors as platforms (#21004) 2026-10-17 15:09:49 -10:00
J. Nick Koston e034662dc6 [uart_tcp] Expose the connection entities as platforms (#21008) 2026-10-17 15:09:32 -10:00
J. Nick Koston 42e849967a [bq76972] Expose the sensors as a sensor platform (#21006) 2026-10-17 15:09:14 -10:00
J. Nick Koston e6f1ed8d51 [tcp_uart] Expose the connection entities as platforms (#21009) 2026-10-17 15:07:42 -10:00
Igor StadnykandJ. Nick Koston bb9067acd7 [tfluna] Recover from transient runtime reconfiguration failures (#20992)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-17 15:07:16 -10:00
Michele Porelli 928cf7d696 [libretiny] Emulate rtc preference storage on the Beken BK7231T, BK7231N and BK7238 with flash and the reset reason (#21007) 2026-10-17 14:59:40 -10:00
89afb111ad [mt6701] Add MT6701 (MagnTek magnetic encoder) component (#17643)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-17 14:06:46 -10:00
Brandon HarveyandJ. Nick Koston d1ec52e95a [sen6x] Add device status binary sensors (#18784)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-17 23:34:15 +00:00
Bonne Egglestonandclydebarrow 818d43f742 [epaper_spi] Run the loop continuously between the steps of an update (#20554)
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
2026-10-18 10:25:36 +11:00
1a28a9220b [uart] Add a binary sensor that fires on a byte sequence (#14032)
Co-authored-by: J. Nick Koston <nick+github@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-17 22:35:46 +00:00
Brandon HarveyandJ. Nick Koston 022733c46b [sen6x] Add PM number concentration sensors (#18782)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-17 12:16:45 -10:00
0f6fc503bd [sen6x] Add temperature compensation, acceleration and startup delay (#18781)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@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-17 11:01:14 -10:00
Bonne Egglestonandclydebarrow ae62960193 [epaper_spi] Add the UC8279 controller and the Xteink X4 Pro (#20545)
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
2026-10-18 07:51:07 +11:00
325c747dcd [sen6x] Add CO2 configuration options (#18780)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@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-17 10:41:13 -10:00
Brandon HarveyandJ. Nick Koston 5759c6b8e6 [deep_sleep] Honor nested wakeup_pin_mode on ESP32 single-pin configs (#16618)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-17 10:25:40 -10:00
c9ba6c355c [hoermann_hcp] Add an actuator error binary sensor (#20299)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-17 10:00:31 -10:00
Brandon HarveyandJ. Nick Koston d02da3c8de [wifi] Support hidden option in single-network shorthand (#18157)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-17 09:54:55 -10:00
Bascht74andJ. Nick Koston a576092f25 [remote_base] Send the 72-bit Jarolift TDEF group frame with transmit_keeloq (#20927)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-17 09:52:01 -10:00
Simonas Kazlauskas 792d650fb2 [remote_base] Add a protocol for 433MHz A-OK remotes (#20991) 2026-10-17 09:44:25 -10:00
f72857151a [pylontech] Add individual cell voltage and temperature sensors (#16852)
Co-authored-by: Michael <michael_e@gmx-topmail.com>
Co-authored-by: ritschys <117527311+ritschys@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-17 19:28:52 +00:00
5f8f075e76 [zephyr_coredump] Store a coredump in flash on nRF52 and print it to the next log reader (#13122)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-17 19:22:28 +00:00
Matias FernandezandJ. Nick Koston c2c8cb5eb3 [climate_ir_lg] Fix swing command to not include climate state (#17417)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-17 09:12:35 -10:00
J. Nick Koston 5c2b05d0eb [ci] Run the C++ unit tests on macOS without a sanitizer whose runtime aborts (#21000) 2026-10-17 09:00:51 -10:00
Igor StadnykandJ. Nick Koston e110613ef1 [core] Construct App once in the C++ unit test main (#21001)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-17 19:00:37 +00:00
luar123 4324b7d084 [zigbee] bump esp-zigbee-sdk to 2.0.5 (#20998) 2026-10-17 08:58:49 -10:00
37d6140193 [epaper_spi] Fix partial refresh and port waveshare 2.90inv2 (#18959)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick+github@koston.org>
Co-authored-by: luar123 <49960470+luar123@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-17 08:55:37 -10:00
4f192130f8 [water_heater] Hand the component to the core team (#20999)
Co-authored-by: J. Nick Koston <nick+github@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-17 18:51:06 +00:00
Hasan Khalilandpre-commit-ci-lite[bot] 4ef17cebdb [api] Pre-reserve Home Assistant state subscriptions vector (#20986)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-10-17 08:01:01 -10:00
35276a38bc [rtsp_server] Add an RTSP server that streams the esp32_camera picture (#17990)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-17 07:18:52 -10:00
J. Nick Koston 635df9c6ad [socket] Share the allow-list reject log and config lines through Ipv4Allow (#20983) 2026-10-17 06:51:29 -10:00
Jonathan Swoboda d16d8c111e [debug] Log uptime in dump_config (#20989) 2026-10-17 06:50:54 -10:00
Jonathan Swoboda 1ead61f11f [esp32] Remove trailing comment from ECDSA test signing key (#20988) 2026-10-17 06:50:11 -10:00
J. Nick Koston bd06d37d1a [components] Check I2C chip IDs with check_chip_id (#20943) 2026-10-17 06:49:46 -10:00
Jonathan Swoboda 7c6fac91ff [esp32] Bump recommended ESP-IDF to 5.5.6 (#20987) 2026-10-17 06:49:15 -10:00
J. Nick Koston 052a6096e6 [components] Log hex dumps with the ESP_LOG*_HEX macros (#20932) 2026-10-17 16:46:16 +00:00
J. Nick Koston 25b684f762 [pylontech] Parse each line as it arrives with one fixed buffer (#20959) 2026-10-17 06:44:41 -10:00
J. Nick Koston 62f8cc3436 [uart] Add read_available helpers to drain available bytes in chunks (#20947) 2026-10-17 06:41:51 -10:00
J. Nick Koston 34f9152d61 [i2c] Add write_registers for constant register tables (#20945) 2026-10-17 06:41:20 -10:00
luar123 73af6b0500 [core][scheduler] run process_to_add after component phase (#20993) 2026-10-17 06:40:39 -10:00
J. Nick Koston a384aae5b1 [button] Add factory reset, restart and query button schema presets (#20915) 2026-10-17 06:38:45 -10:00
J. Nick Koston 905267a9c3 [esp32_ble] Build ble.enabled, ble.enable and ble.disable with the apply helpers (#20839) 2026-10-17 06:38:25 -10:00
J. Nick Koston d08c845c65 [components] Create I2C devices with new_i2c_component (#20909) 2026-10-17 06:36:49 -10:00
J. Nick Koston 91dfb92c60 [esp32_ble] Test ble.enabled, ble.enable and ble.disable (#20838) 2026-10-17 09:22:29 -04:00
J. Nick Koston cef9418a0f [i2c][uart][spi] Add new_i2c_component, new_uart_component and new_spi_component (#20908) 2026-10-17 09:20:28 -04:00
J. Nick Koston c0a693dfce [components] Use xor_bytes and sum16_bytes for checksums (#20922) 2026-10-17 09:18:24 -04:00
Clyde Stubbs 1b4721d445 [core] Script improvements including choose (#20547) 2026-10-17 21:23:12 +11:00
f8d31c3e54 [dfrobot_sen0465] Add DFRobot SEN0465-SEN0476 electrochemical gas sensors (#20880)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-16 22:47:50 -10:00
eafcca8274 [mics_5524] Add datasheet model, diagnostics and advanced wiring (2/2) (#20521)
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-16 22:47:28 -10:00
Remco van Essen 06b585d373 [i2s_audio] Fix speaker restart loop with full_duplex (#20984) 2026-10-16 22:05:21 -10:00
HugoPeters1024andJ. Nick Koston 885f2fe5d8 [kamstrup_kmp] Fix Kamstrup KMP reads with echoing optical heads (#20897)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-16 22:04:01 -10:00
J. Nick Koston 4e27b2fc12 [components] Collect UART lines with LineReader (#20956) 2026-10-16 19:54:11 -10:00
J. Nick Koston 375900bfcd [core] Add LineReader to collect lines in a fixed buffer (#20952) 2026-10-16 19:54:11 -10:00
Zhaoqi Xu 6d664d3fef [web_server] Report numbers without a state as NA (#20973) 2026-10-16 19:15:04 -10:00
J. Nick Koston 4dc2b5911b [core] Move CONF_DRIVE_STRENGTH to the shared component constants (#20975) 2026-10-16 18:21:04 -10:00
tomaszduda23 687466b6cd [logger][usb_cdc_acm][core] Add CDC uart to NRF52 (#11634) 2026-10-16 18:14:51 -10:00
J. Nick Koston dcdc042a4b [host] Define a no-op InterruptLock (#20974) 2026-10-16 17:53:33 -10:00
d2a1f7ee31 [zephyr] Fix preference log levels and report settings read/save errors (#20810)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-17 03:02:54 +00:00
J. Nick Koston a5a883fd19 [core] Add xor_bytes and sum16_bytes checksum helpers (#20921) 2026-10-16 15:57:00 -10:00
J. Nick Koston 7671d4410c [i2c] Add check_chip_id helper (#20941) 2026-10-16 15:55:53 -10:00
J. Nick Koston 298bd5ec72 [core] Add ESP_LOGV_HEX, ESP_LOGVV_HEX and ESP_LOGD_HEX (#20931) 2026-10-16 15:55:28 -10:00
J. Nick Koston b8afcdfb3c [i2c] Write 16-bit registers without a heap allocation (#20907) 2026-10-16 15:54:53 -10:00
luar123andJ. Nick Koston 9031df757b [core][scheduler] add missing process_to_add call (#20961)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-17 01:33:29 +00:00
J. Nick Koston 107bebebad [components] Use sign_extend for raw sensor readings (#20934) 2026-10-16 15:24:45 -10:00
J. Nick Koston 1147b024d6 [core] Add sign_extend helper (#20933) 2026-10-16 21:17:59 -04:00
J. Nick Koston 36ba2f6106 [components] Build enum option dicts with options() (#20911) 2026-10-16 14:56:34 -10:00
J. Nick Koston bb0eabf241 [core] Add options() to C++ enums (#20910) 2026-10-16 14:56:34 -10:00
Frédéric MetrichandClaude d9969dd533 [teleinfo] Skip sensor values that are not numbers instead of publishing 0 (#20960)
Co-authored-by: Claude <noreply@anthropic.com>
2026-10-16 14:55:08 -10:00
Clyde Stubbs 51ae3bb642 [ci] Rename merge conflict label to has-conflicts (#20969) 2026-10-16 14:31:06 -10:00
b3ef5cf2f3 [online_image] Report an error when a download can never complete (#20594)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: guillempages <guillempages@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-16 23:58:12 +00:00
Hasan Khalil b3f5789dc6 [mipi_rgb] direct-to-PSRAM fast rotated blitting (#20905) 2026-10-17 10:39:44 +11:00
J. Nick Koston 1ddacb4931 [tests] Set i2c_id in the component tests that clash with tca9548a (#20963) 2026-10-16 13:24:34 -10:00
J. Nick Koston e38ee35cd8 [core] Use get_declaring_config in ID matching 2026-10-16 10:58:44 -10:00
J. Nick Koston 81fecf2106 [core] Use get_declaring_config for parent config lookups 2026-10-16 10:48:34 -10:00
J. Nick Koston 76e5b07416 [core] Add final_validate.get_declaring_config helper 2026-10-16 10:42:44 -10:00
Igor StadnykandJ. Nick Koston 10d3ced897 [core] Parse unit values as one decimal so listed floats match, fixing at581x power consumption (#20889)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-16 10:11:56 -10:00
Bascht74 6fa7b87e6a [lp5562] Add the current per channel and logarithmic dimming (#20903) 2026-10-16 10:04:45 -10:00
c0b7c5e590 [dfrobot_sen0395] Forward trigger arguments in settings actions (#20885)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-16 10:03:07 -10:00
dependabot[bot] 57dd09018e Bump yamlrocks from 0.6.1 to 0.7.0 (#20904)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-16 10:00:58 -10:00
Clyde Stubbs 04aa59563d [display] Drop the color order from draw_pixels_at() and ColorUtil (#20884) 2026-10-17 06:45:03 +11:00
9440012413 [mics_5524] Add MiCS-5524 gas sensor platform (1/2) (#19721)
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-16 09:14:06 -10:00
Bascht74 38db8c1554 [ci] Fail a hung integration test instead of waiting for the job limit (#20902) 2026-10-16 09:00:55 -10:00
ab0c32c2aa [haier_water_heater] Initial water-heater implementation tested with Haier VH3W (#20765)
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-10-16 08:50:13 -10:00
cd6a34401b [web_server] Do not match internal entities by URL unless include_internal is set (#20883)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-16 08:22:46 -10:00
Igor StadnykandJ. Nick Koston 5ab92ed363 [ld2450] Handle footer bytes in zone query responses (#20852)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-16 08:15:17 -10:00
1368ce4342 [dfrobot_sen0609] Add restart, factory reset and query buttons (#20877)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-16 17:31:58 +00:00
Remco van Essen dc0d79635b [ld2410][ld2412] Move CONF_QUERY_PARAMS to the shared constants (#20878) 2026-10-16 07:12:49 -10:00
J. Nick Koston 1ff6b29b0d [components] Let core report communication failures in dump_config (4/5) (#20873) 2026-10-16 08:41:52 -04:00
J. Nick Koston 49caeae780 [components] Let core report communication failures in dump_config (3/5) (#20872) 2026-10-16 08:40:54 -04:00
J. Nick Koston e1a6c615b1 [core] Document passing the failure reason to mark_failed in AGENTS.md (#20879) 2026-10-16 08:40:06 -04:00
J. Nick Koston e95e56f100 [components] Let core report communication failures in dump_config (2/5) (#20871) 2026-10-16 08:39:20 -04:00
J. Nick Koston 126dc8b0b2 [components] Let core report communication failures in dump_config (1/5) (#20870) 2026-10-16 08:38:55 -04:00
esphome[bot] 3298988f5d Bump bundled esphome-device-builder to 1.24.0 (#20851)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-10-15 23:11:26 -10:00
802c2633ed [image] Draw rows in the outer loop and hand opaque RGB565 and RGB blocks to the display (#20653)
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 18:38:58 +11:00
51b1fdcc8b [i2s_audio] Add full duplex support (#19959)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-15 21:35:46 -10:00
6555da2906 [display] Fall back per pixel when a bulk blit does not match the panel (#20652)
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 16:57:11 +11:00
J. Nick Koston 6dbd15b903 [pca9505] Validate interrupt_pin with the shared expander validator (#20853) 2026-10-15 15:02:26 -10:00
Jonathan Swoboda 0399379c0d [lp5562] Pin the test fixture to the shared I2C bus (#20856) 2026-10-16 00:56:40 +00:00
Kevin Ahrendt 6817960365 [audio] Deprecate AudioSourceTransferBuffer (#20801) 2026-10-15 13:38:03 -10:00
Kevin Ahrendt cfdaf958e9 [audio][i2s_audio] Fix 16-bit mono playback at 44.1 kHz on the original ESP32 (#20806) 2026-10-15 13:32:35 -10:00
1e89abdb13 [http_request] Allow a per-request timeout (#20821)
Co-authored-by: p1ngb4ck <kleinecke.oliver@googlemail.com>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-10-15 13:17:46 -10:00
J. Nick Koston c838d4826e [core] Add ESPHOME_strstr_P (#20811) 2026-10-15 23:09:02 +00:00
J. Nick Koston 69215deb58 [core] Make the YAML load listener hook public (#20805) 2026-10-15 12:54:34 -10:00
J. Nick Koston 5b234bcc20 [wifi] Leave the cooldown from the loop after a credential save, not from the caller's context (#20802) 2026-10-15 12:53:51 -10:00
Panoplianandclydebarrow c101b3c101 [mipi_spi] Honour show_test_card when pages or a lambda are set (#20807)
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
2026-10-15 22:38:28 +00:00
Anton ViktorovandJ. Nick Koston c0d8a0dfe9 [as734x] [3/3] Add illuminance, photopic irradiance, PAR, PPFD, colour temperature and RGB (#18012)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-15 11:38:15 -10:00
J. Nick Koston 27c6b7caf5 [ci] Add hardware-test agent skill (#20817) 2026-10-15 21:36:36 +00:00
699e3bd037 [usb_uart] Runtime modem pin control for USB and host (#20330)
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-15 11:03:27 -10:00
Anton ViktorovandJ. Nick Koston a58ce2d641 [as734x] [2/3] Add basic counts, calibration and saturation level (#18011)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-15 10:48:18 -10:00
Remco van EssenandClaude Opus 5.5 27d8d49b17 [dfrobot_sen0609] Add number, switch and select settings (#20813)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 10:41:20 -10:00
Remco van Essen 304627efd3 [ld2410][ld2412] Move CONF_OUT_PIN_LEVEL to the shared constants (#20820) 2026-10-15 10:34:00 -10:00
7ed06e0ada [as734x] [1/3] Add AS7341/AS7343/TCS3448 spectral sensor hub with counts sensors (#17038)
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-15 10:19:56 -10:00
dependabot[bot] 7e564c12dd Bump py7zr from 1.1.3 to 1.1.4 (#20816)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-15 09:58:07 -10:00
dependabot[bot] 6f6fdd0f8a Bump actions/create-github-app-token from 3.2.0 to 3.3.0 (#20815)
Signed-off-by: dependabot[bot] <support@github.com>
2026-10-15 09:55:47 -10:00
45ea5f4220 [nrf52] nrf52840dongle support (#19009)
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-15 09:03:53 -10:00
techdregsandJ. Nick Koston 108ed67d31 [rx8111] Add basic RX8111CE real-time clock support (#20785)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-15 08:30:25 -10:00
Remco van EssenandClaude Opus 5.5 6356458081 [dfrobot_sen0609] Add DFRobot SEN0609/SEN0610 radar (#20809)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 18:27:57 +00:00
Panoplian bf67b49104 [i2c] Let host unit tests with I2C build on macOS (#20738) 2026-10-15 07:47:52 -10:00
Panoplian 1947c3d355 [improv_ble] Start Improv again after a session has stopped (#20808) 2026-10-15 07:47:14 -10:00
J. Nick Koston cf4e69273c [shelly_dimmer] Keep the STM32 device table in flash (#20643) 2026-10-15 07:28:55 -10:00
J. Nick Koston c433e96734 [homeassistant] Share the Python list parser between the platforms (#20793) 2026-10-15 07:28:27 -10:00
J. Nick Koston 93dc80d895 [esp8266] Refuse two components that drive hardware timer 1 (#20794) 2026-10-15 07:28:07 -10:00
J. Nick Koston 7f342054fe [wifi] Retry a failed fallback AP once per ap_timeout and start the captive portal once (#20784) 2026-10-15 07:27:19 -10:00
Jeroenandjeroen85 d924d2daf7 [core] Extend fast value formatting to four decimal places (#20800)
Co-authored-by: jeroen85 <26403565+jeroen85@users.noreply.github.com>
2026-10-15 07:15:11 -10:00
Clyde Stubbs 49b12daa58 [epaper_spi] Ensure test card gets drawn (#20804) 2026-10-15 10:55:19 +00:00
Clyde Stubbs d3faafc578 [display] Always treat 888 pixel blocks as b,g,r in memory (#20799) 2026-10-15 20:49:50 +11:00
Remco van Essen 7b08e738d3 [fusb302b] Add sensors, contract text sensor and automations (#20796) 2026-10-14 22:13:58 -10:00
J. Nick Koston 62e3c25c83 [lvgl] Fix the menu test after lambdas gained a line reset (#20798) 2026-10-14 21:50:47 -10:00
6a50f5c166 [apc_proteous] Cover component for APC Proteous gate motors (#13403)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-15 17:46:40 +11:00
Clyde Stubbs bb0837d814 [lvgl] Implement menu widget (#18022) 2026-10-15 17:44:23 +11:00
Bonne Egglestonandclydebarrow ecd5980c5d [epaper_spi] Pass bits per pixel and plane count to the base constructor (#20683)
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
2026-10-15 17:41:37 +11:00
8d963bde5b [openthread] Add ESP32 OpenThread Border Router support (#18104)
Co-authored-by: Copilot App <223556219+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-10-14 20:02:33 -10:00
J. Nick Koston ea6d5499b3 [lsm6ds] Add the LSM6DSO and match all chips to their datasheets (#20733) 2026-10-14 19:24:00 -10:00
41e82b0295 [hdmi_cec] Add HDMI-CEC bus component (part 1: bus core) (#9189)
Co-authored-by: Stéphane Lepin <stephane.lepin@gmail.com>
Co-authored-by: Stéphane Lepin <Palakis@users.noreply.github.com>
Co-authored-by: Niko <nikokirste@hotmail.de>
Co-authored-by: Minims <github@minims.fr>
Co-authored-by: Nicholas Pace <segfly@users.noreply.github.com>
Co-authored-by: Nicholas Pace <3300476+segfly@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-10-15 04:16:09 +00:00
J. Nick Koston 39705a65e3 [bmi270] Keep the range name and scale tables in flash (#20715) 2026-10-14 17:44:46 -10:00
J. Nick Koston a522553e82 [lsm6ds] Keep the range and chip names in flash (#20718) 2026-10-14 17:43:54 -10:00
J. Nick Koston a07a1ab728 [qmi8658] Keep the range name tables in flash (#20714) 2026-10-14 17:43:34 -10:00
J. Nick Koston 9a5d2d9753 [api] Encode action strings straight from flash (#20425) 2026-10-14 17:38:39 -10:00
J. Nick Koston 3188e0d36f [ota] Fall back to a UDP data phase when the link is lossy (#20498) 2026-10-15 16:30:56 +13:00
Jonathan Swoboda f3eaf698d6 [mipi_rgb] Rotate pure RGB panels in software (#20757) 2026-10-14 22:05:01 -04:00
J. Nick Koston f7057e1270 [tests] Reset saved preferences before every C++ unit test (#20771) 2026-10-14 16:01:45 -10:00
J. Nick Koston 6edfbb594d [bluetooth_connection] Value-initialize the connection parameters for ESP-IDF 6.1 (#20774) 2026-10-14 22:00:07 -04:00
J. Nick Koston 22defcd704 [esp32_ble_client] Value-initialize the connection parameters for ESP-IDF 6.1 (#20776) 2026-10-14 21:59:56 -04:00
Jonathan Swoboda 0f408234d0 [core] Add ksz8851snl to the static analysis manifest for IDF 6 (#20788) 2026-10-14 21:53:17 -04:00
Jonathan Swoboda 92e0e6db6b [wifi] Call esp_supp_dpp_init without a callback on ESP-IDF 6 (#20789) 2026-10-15 01:40:07 +00:00
908c2f6778 [wifi] Add Wi-Fi Easy Connect (DPP) provisioning on the ESP32 (#17934)
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-14 14:29:47 -10:00
Keith BurzinskiandClaude Opus 5.5 af709ac9a5 [openthread] Fail instead of aborting when NVS is unavailable (#20717)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 13:30:36 -10:00
Panoplian fe57eef3f6 [core] Test that includes inside a package resolve next to the package (#20739) 2026-10-14 13:28:01 -10:00
d0b9f195b0 [wifi] Apply reboot_timeout with a fallback AP unless a station is connected to it (#9958)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-14 13:23:37 -10:00
eafeef7cb8 [fusb302b] Add FUSB302B driver (#20760)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-14 13:18:20 -10:00
471774cdbf [ecocomfort2] Fix the clock sync and read boost from the unit state (#20782)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-10-14 13:14:04 -10:00
Kevin AhrendtandJ. Nick Koston 4df6291feb [api][sendspin] Add the Sendspin client id to the capabilities message (#20775)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-14 22:56:51 +00:00
d8ca4a9460 [audio_dac] Add ES9038Q2M Katana platform (#15017)
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-14 22:50:41 +00:00
48a0a41cce [wifi] Implement access point WISP mode (#13633)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-14 12:49:37 -10:00
esphome[bot] 0e2c4cf5c2 Bump aioesphomeapi from 46.11.0 to 46.12.0 (#20781)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-10-14 12:43:07 -10:00
ea76073d86 [micro_wake_word] Allow selecting the inference task core (#20126)
Co-authored-by: n-IA-hane <n-IA-hane@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-14 12:42:24 -10:00
J. Nick Koston dcb943ded3 [noise] Initialize the protocol id's reserved field for ESP-IDF 6.1 (#20779) 2026-10-14 11:45:54 -10:00
dependabot[bot] 2c6f59738c Bump filelock from 4.1.0 to 4.1.1 (#20778)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-14 21:41:28 +00:00
puddlyandJ. Nick Koston 04715fb32b [usb_uart] Expose interface_class and port_number (#20659)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-14 21:41:12 +00:00
nullable.ethandJ. Nick Koston 3df9360876 [i2s_audio] Add keep_alive option to speaker to prevent pop/click on audio start (#15565)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-14 11:39:30 -10:00
dependabot[bot] 61f3de0bc0 Bump pylint from 4.1.2 to 4.1.3 (#20773)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-14 21:30:26 +00:00
dependabot[bot] cd97c9a9cb Bump platformdirs from 4.12.4 to 4.13.0 (#20772)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-14 21:29:17 +00:00
J. Nick Koston d3668a7186 [mqtt] Match subscription topics in a loop instead of recursing per character (#20767) 2026-10-14 11:13:32 -10:00
J. Nick Koston ac74796263 [friedrich] Clear saved preferences in the test fixture (#20770) 2026-10-14 10:52:13 -10:00
c56bb14037 [mqtt] Print crash info over MQTT (#17935)
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-10-14 10:39:55 -10:00
J. Nick Koston a4278b8dec [ci] Run benchmarks when a component the Python import benchmark imports changes (#20763) 2026-10-14 10:35:33 -10:00
Panoplian 6d173c7371 [bq25186] Set i2c_id in the component tests (#20742) 2026-10-14 10:35:01 -10:00
esphome[bot] 1779b011c9 Bump aioesphomeapi from 46.10.0 to 46.11.0 (#20766)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-10-14 20:21:45 +00:00
PanoplianandJ. Nick Koston 3c4cf50cb6 [core] Point compile errors after a lambda at the right line (#20740)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-14 10:02:51 -10:00
J. Nick Koston e869d1f7ab [ci] Give the BK72xx clang-tidy environment the static allocation flag the real build has (#20764) 2026-10-14 09:56:09 -10:00
e1a996435d [ld2450] Add polygon zones evaluated on the device (#20154)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-14 19:29:16 +00:00
Jonathan Swoboda c5a624a1f2 [tests] Count OTA key probe retries on a fake clock (#20761) 2026-10-14 15:21:35 -04:00
Remco van Essen 15fb7f775e [tmc2209] Add chopper and PWM tuning actions (#20753) 2026-10-14 08:59:21 -10:00
J. Nick Koston 3fa72e61c6 [mqtt] Build the mqtt triggers with build_trigger_callback (#20433) 2026-10-14 08:55:39 -10:00
Kevin Ahrendt 163b09a473 [microphone] Speed up MicrophoneSource audio conversion (#20755) 2026-10-14 08:47:25 -10:00
J. Nick Koston 7afc2768f8 [esp8266] Check that every component's saved values fit together (#20686) 2026-10-14 08:45:51 -10:00
56f1a2bf46 [midea_portasplit] Add Midea PortaSplit climate component (#18033)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-14 08:45:21 -10:00
Panoplian fee47fe2f1 [pcf85063] Convert the weekday to and from the chip's 0-6 range (#20736) 2026-10-14 08:43:23 -10:00
Panoplian b994e081c3 [bm8563] Convert the weekday to and from the chip's 0-6 range (#20737) 2026-10-14 08:42:47 -10:00
Jonathan Swoboda a1f740b71b [lvgl] Keep LV_CONF_PATH one argument when the build path has a space (#20758) 2026-10-14 08:36:17 -10:00
Remco van EssenandClaude Opus 5.5 7f144365ba [fusb302b] Add USB PD sink protocol (#20632)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 08:35:29 -10:00
Marcus VoßandJ. Nick Koston 928459d16f [cse7766] Keep last power reading between measurements at low load (#20631)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-14 08:33:11 -10:00
Jonathan Swoboda 9163359445 [core] Analyze the ethernet component under ESP-IDF (#20745) 2026-10-14 08:31:39 -10:00
Jonathan Swoboda 67307f0eeb [ci] Select the feature-gated files the platform clang-tidy jobs were missing (#20743) 2026-10-14 08:31:06 -10:00
91e0edc11d [pn532] Fix NDEF parsing greater than 255 bytes (#18188)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-14 08:15:48 -10:00
Jonathan Swoboda 9fe75e262f [ci] Add a host clang-tidy environment and job (#20747) 2026-10-14 08:09:53 -10:00
Kevin Ahrendt 0132f722a3 [audio] Bump esp-audio-libs to v4.1.0 (#20754) 2026-10-14 13:24:36 -04:00
Jonathan Swoboda 7d7a78308d [ci] Test tinyusb_keyboard in isolation (#20749) 2026-10-14 10:05:02 -04:00
J. Nick Koston 9c568525b0 [core] Add CodSpeed benchmarks for importing entity components (#20648) 2026-10-14 09:08:41 -04:00
J. Nick Koston 10f42e3e7c [shelly_dimmer] Build the STM32 firmware update path in CI (#20677) 2026-10-14 09:03:34 -04:00
J. Nick Koston 25d4b88605 [nfc] Add unit tests for the URI record prefix codes (#20724) 2026-10-14 09:01:59 -04:00
J. Nick Koston d6abf35138 [sx126x][sx127x] Stop the watchdog reset loop when a packet is sent before setup (#20727) 2026-10-14 09:01:10 -04:00
J. Nick Koston 66ea99ce65 [seeed_mr60fda2] Compile without binary sensors or selects (#20726) 2026-10-14 09:00:08 -04:00
J. Nick Koston d9298cb7b9 [seeed_mr60bha2] Compile without sensors or binary sensors (#20729) 2026-10-14 08:59:27 -04:00
J. Nick Koston 34e1cf96d5 [bmi270] Build the component on ESP8266 in CI (#20731) 2026-10-14 08:58:56 -04:00
56cc03c4d4 [haier] Haier Select component added (#16148)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Pavlo Dudnytskyi <Pavlo.Dudnytskyi@addsecure.com>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-13 23:27:18 -10:00
35b416c6c0 [web_server_idf] Keep active SSE sessions out of the LRU purge (#17383)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-13 22:19:06 -10:00
Petar Petrov 7f2c3ba3f5 [display] Let other components draw into a display (#20622) 2026-10-14 06:11:47 +00:00
Keith BurzinskiandClaude Opus 5.5 a97604d574 [esp32_ble] Rely on preferences for NVS init (#20708)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 20:10:51 -10:00
Petar Petrov 1146b77836 [socket] Add TcpClientLink::connecting() (#20722) 2026-10-13 19:19:17 -10:00
J. Nick Koston 1338902ac5 [heatpumpir] Link only the configured protocol classes (#20707) 2026-10-14 17:06:22 +13:00
60a1d70805 [core] Move deferred include processing to parse_yaml (#16344)
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-13 17:46:54 -10:00
Ryan LudwigandCursor 887d4c3a83 [esp32_can] Add NO_ACK mode (#20706)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 17:36:15 -10:00
J. Nick Koston c1fa73470e [display] Walk the blit fallback in frame buffer order and resolve the bitness once (#20651) 2026-10-13 17:31:13 -10:00
J. Nick Koston e41d16ba7e [haier] Keep the alarm message table in flash (#20641) 2026-10-14 03:23:28 +00:00
J. Nick Koston 5171113c87 [core] Let PROGMEM_STRING_TABLE hold more than 255 bytes (#20646) 2026-10-13 17:17:48 -10:00
techdregsandJ. Nick Koston 5718e4b779 [tmp102] Add text sensor for diagnostics (#20629)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-13 17:06:56 -10:00
J. Nick Koston 40e12cd0d8 [image] Add CodSpeed benchmarks for image, runtime image and display block draws (#20649) 2026-10-13 17:01:47 -10:00
Brandon HarveyandJ. Nick Koston 07029d0077 [sen6x] Add measurement control and maintenance actions (#18783)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-14 15:47:31 +13:00
Christopher Reimer bb1119d83f [esp32_can] Support 20 kbit/s on ESP32 revision 2 or later (#20426) 2026-10-13 22:34:03 -04:00
7b591b26d6 [sy6970] Service the I2C watchdog so charge/LED settings persist (#19424)
Co-authored-by: Willem Oldemans <>
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-10-13 16:19:58 -10:00
f45e6f0bf4 [midea] Add display_control and send the display toggle over UART without a transmitter (#18671)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-13 16:03:18 -10:00
Keith BurzinskiandClaude Opus 5.5 4b8af08155 [network] Notify listeners when the active interface changes (#20684)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 20:57:59 -05:00
613973b432 Bump ruff from 0.16.10 to 0.17.0 (#20694)
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>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-10-13 21:53:44 -04:00
dependabot[bot] 9ee4314bc2 Bump esptool from 5.4.0 to 5.5.0 (#20693)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-13 15:41:30 -10:00
fredkotzeandJ. Nick Koston 59f55dbe9e [epaper_spi] Add Waveshare 2.13in-G (JD79660) display models (#18141)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-13 15:40:28 -10:00
dependabot[bot] 8fce95454a Bump github/codeql-action/init from 4.38.2 to 4.38.3 (#20701)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-13 15:39:49 -10:00
dependabot[bot] 16134ee993 Bump astral-sh/setup-uv from 10.2.0 to 10.3.0 (#20704)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-13 15:39:38 -10:00
dependabot[bot] bc39d56bb4 Bump CodSpeedHQ/action from 5.4.0 to 5.4.2 (#20705)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-13 15:39:29 -10:00
dependabot[bot] c5522abc71 Bump github/codeql-action/analyze from 4.38.2 to 4.38.3 (#20699)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-13 21:39:11 -04:00
dependabot[bot] 7cb5906d2a Bump actions/upload-artifact from 7.0.1 to 7.0.2 (#20702)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-14 01:38:42 +00:00
dependabot[bot] d452dbce30 Bump actions/download-artifact from 8.0.1 to 8.0.2 (#20703)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-14 01:38:39 +00:00
dependabot[bot] 81e5b50087 Bump backports-zstd from 1.7.0 to 1.8.0 (#20695)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-13 15:38:35 -10:00
dependabot[bot] 426a4a2dce Bump filelock from 4.0.12 to 4.1.0 (#20696)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-13 15:38:26 -10:00
dependabot[bot]andesphome[bot] c95c17c4e7 Bump pyupgrade from 3.21.2 to 3.22.0 (#20698)
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-10-13 15:38:17 -10:00
dependabot[bot] 576d3325fe Bump astral-sh/setup-uv from 10.2.0 to 10.3.0 in /.github/actions/restore-python (#20700)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-13 15:37:47 -10:00
dependabot[bot] c9cf0ad92e Bump platformdirs from 4.12.3 to 4.12.4 (#20697)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-13 21:37:36 -04:00
Felipe Guedes f505212e64 [epaper_spi] Add Waveshare 2.66" e-Paper Module (B) support (#20546) 2026-10-13 15:03:05 -10:00
J. Nick Koston 52a5e6a007 [rpi_dpi_rgb] Set removal of the deprecated component for 2027.5.0 (#20691) 2026-10-14 13:32:18 +13:00
J. Nick Koston 3b261d2cf0 [st7789v] Set removal of the deprecated component for 2027.5.0 (#20689) 2026-10-14 13:30:52 +13:00
J. Nick Koston 9238fcc727 [st7735] Set removal of the deprecated component for 2027.5.0 (#20688) 2026-10-14 13:30:21 +13:00
J. Nick Koston 96902cc122 [qspi_dbi] Set removal of the deprecated component for 2027.5.0 (#20690) 2026-10-14 13:29:52 +13:00
J. Nick Koston 95711d83d1 [ili9xxx] Set removal of the deprecated component for 2027.5.0 (#20687) 2026-10-14 13:29:29 +13:00
Panoplian 3cdcf0417a [lvgl] Make the list_outside_block tests work offline (#20676) 2026-10-13 13:31:08 -10:00
J. Nick Koston c6e90de262 [ci] Catch out-of-class member log tags and keep midea's in flash (#20674) 2026-10-14 12:21:24 +13:00
J. Nick Koston 92f6ab8367 [ci] Lint log tags in headers and core (#20668) 2026-10-14 12:21:24 +13:00
J. Nick Koston f61955baa2 [core] Declare the remaining log tags with ESPHOME_LOG_TAG (#20666) 2026-10-14 12:21:23 +13:00
J. Nick Koston 36e1d831d3 [core] Add a decimal fast path to parse_number<float> (#20639) 2026-10-14 12:18:35 +13:00
J. Nick Koston 63616bf642 [rtttl] Play a configured song from static storage without copying it (#19569) 2026-10-14 12:06:27 +13:00
d275926f65 [mdns] Advertise the connected interface in the network TXT record (#20673)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-13 17:55:22 -05:00
Andrej Friesen f265c46c21 [PCA9505] Add PCA9505/PCA9506 40-channel GPIO expander (#20667) 2026-10-13 12:50:43 -10:00
Frédéric MetrichandClaude Opus 5.5 7d735f0677 [emontx] Lock the emonTx on reboot when its firmware supports it (#20621)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 12:37:09 -10:00
Remco van Essen 5c23a827b6 [tmc2209] Add current and diagnostic sensors (#20638) 2026-10-13 12:35:55 -10:00
Jonathan Swoboda e265c0c0fc [core] Sync the static analysis dependency pins with the code (#20681) 2026-10-13 12:30:42 -10:00
J. Nick Koston a0c7d754cb [syslog] Keep the log level to severity table in flash (#20662) 2026-10-13 12:22:35 -10:00
Jesse Hills a6dbd72c53 Merge branch 'beta' into dev 2026-10-14 11:15:09 +13:00
Jesse Hills 9d530f5c56 Merge pull request #20680 from esphome/bump-2026.10.0b4
2026.10.0b4
2026-10-14 11:14:49 +13:00
Panoplian 6562c49686 [thermostat] Allow a dry-only thermostat without cool_action (#20682) 2026-10-13 12:08:13 -10:00
guillempages a97ce3a201 [image] Fix pillow deprecation warning (#20679) 2026-10-13 11:58:21 -10:00
Jesse Hills 5a78f636b9 Bump version to 2026.10.0b4 2026-10-14 10:43:50 +13:00
tomaszduda23andClaude 53d504137b [nrf52] Accept S140 7.2.0 and 7.3.0 in the sd140_v7 DFU package (#20661)
Co-authored-by: Claude <noreply@anthropic.com>
2026-10-14 10:43:49 +13:00
bb0546fc0b [script] Keep the running script.wait item out of the queue while its chain runs (#20570)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-14 10:43:49 +13:00
emes30andJ. Nick Koston b43630cf8e [rp2_pio] Use pioasm 4.1.0 so PIO programs run on RP2350 (#19260)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-14 10:43:49 +13:00
Firat HajiyevandFirat17aze c9664d4e42 [wifi] Reject unsupported WPA3 on ESP8266 (#20592)
Co-authored-by: Firat17aze <187820330+Firat17aze@users.noreply.github.com>
2026-10-14 10:43:49 +13:00
Kevin Ahrendt 39ea6a3220 [audio] Bump microFLAC to v0.3.0 (#20591) 2026-10-14 10:43:49 +13:00
J. Nick Koston f4a6d6f18b [core] Create the PlatformIO HTTP cache dir before the prefetch resolve threads run (#20579) 2026-10-14 10:43:49 +13:00
esphome[bot] 6c24d4aa43 Bump aioesphomeapi from 46.9.0 to 46.10.0 (#20563) 2026-10-14 10:43:49 +13:00
J. Nick Koston 0dc1d3b5a1 [toshiba] Move the lookup tables to PROGMEM (#20636) 2026-10-13 11:25:18 -10:00
tomaszduda23andClaude 42341d05f9 [nrf52] Accept S140 7.2.0 and 7.3.0 in the sd140_v7 DFU package (#20661)
Co-authored-by: Claude <noreply@anthropic.com>
2026-10-13 11:12:08 -10:00
J. Nick Koston f941fee5ed [homeassistant] Add sensor platform benchmarks (#20637) 2026-10-13 11:10:14 -10:00
Bonne Eggleston 8721976066 [epaper_spi] Abandon an update when the busy pin never clears (#20555) 2026-10-14 07:47:09 +11:00
Bonne Eggleston 05bf7d3440 [epaper_spi] Write pixel blocks straight into mono buffers (#20552) 2026-10-14 07:40:56 +11:00
918b7d2553 [script] Keep the running script.wait item out of the queue while its chain runs (#20570)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-13 09:31:19 -10:00
rwrozelleandClaude Opus 5.5 417ab4d2e9 [openthread] Add core_options for OpenThread compile-time options (#20595)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 15:00:45 -04:00
Jonathan Swoboda 0821c84b7a [core] Let a nested repeat shadow the outer iteration variable (#20634) 2026-10-13 14:35:24 -04:00
Clyde Stubbs a827058928 [lvgl] Change deprecated calls to current version (#20620) 2026-10-13 14:27:50 -04:00
J. Nick Koston 3063b76210 [haier_tundra] Build the IR frame on the stack (#20635) 2026-10-13 14:25:36 -04:00
Remco van EssenandClaude Opus 5.5 55261ae7e5 [tmc2209] Add driver diagnostics and stall detection (#20618)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 08:17:25 -10:00
cociwebandJ. Nick Koston 1d8423edd8 [es8388] Add initial option configuration for DAC output and ADC input mic (#16342)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-13 08:14:43 -10:00
n-IA-haneandn-IA-hane d27ec8ec49 [voice_assistant] Make TTS playback start timeout configurable (#18707)
Co-authored-by: n-IA-hane <n-IA-hane@users.noreply.github.com>
2026-10-13 08:13:26 -10:00
Ivan KruglovandJ. Nick Koston 5acbd44067 [pcf8574] Add reset option to preserve outputs across MCU reboots (#20589)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-13 08:12:28 -10:00
40271fb597 [haier_tundra] New Climate IR component (#20469)
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-13 08:10:36 -10:00
95766ed3ad [icm20689] Add ICM-20689 6-axis accelerometer/gyroscope (SPI) (#19234)
Co-authored-by: Ludovic BOUÉ <938089+lboue@users.noreply.github.com>
Co-authored-by: Claude Sonnet 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@home-assistant.io>
2026-10-13 08:07:32 -10:00
4a80d785a8 [filter_lifetime] Add component with sensor/lambda support for is_on … (#13914)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-13 07:52:46 -10:00
J. Nick Koston c1c1d2b2ae [core] Add probe_ota_key, a Noise handshake with the device that sends no image (#20601) 2026-10-13 07:28:32 -10:00
Mat931andJonathan Swoboda 29a668af04 [esp32_camera] Fix compile warning (#20628)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-10-13 14:01:17 +00:00
J. Nick Koston fd541bca2c [core] Document constexpr constants, per-model schemas and isolated build tests in AGENTS.md (#20617) 2026-10-13 08:20:32 -04:00
J. Nick Koston 192ae31e45 [tests] Turn off the Hypothesis deadline for the MAC address tests (#20614) 2026-10-13 08:19:10 -04:00
Frédéric MetrichandClaude Opus 5.5 77c9fa2fa4 [emontx] Read the emonTx firmware version (#20619)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 22:43:02 -10:00
guillempagesandJ. Nick Koston 25843c5242 [image] Implement RGB111 format (#20114)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 22:16:44 -10:00
7881dfc5d8 [deep_sleep] Reject wakeup_pin configs that fail after validation (#18616)
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 15:29:42 +11:00
e289a94451 [friedrich] New Climate IR component (#9178)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 17:11:21 -10:00
5017165800 [selec_meter] Add EM4M (3-phase) support via model option (#17782)
Co-authored-by: Claude Sonnet 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@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-13 02:58:25 +00:00
8acf885241 [bq76972] Add BQ76972 BMS chip monitoring component (#17720)
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-12 16:43:51 -10:00
f829447b2e [tmp102] Add number entities for runtime thermostat thresholds (#20596)
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-12 16:25:49 -10:00
74e96b7da6 [INA3221] Add optional flags to expose all functionality (#16309)
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-10-12 16:21:00 -10:00
753c10e5ea [ld2410s] Add the distance sensor, firmware version and common parameters (Part 2) (#8486)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 16:06:45 -10:00
J. Nick Koston 98d3d23ce1 [ecocomfort2] Test the component in isolation (#20613) 2026-10-12 15:59:25 -10:00
Rodrigo MartínandJ. Nick Koston d6e5b9b03b [bq25186] Add the status sensors, buttons, PG/GPO switch and watchdog option (part 2) (#15736)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 15:50:12 -10:00
J. Nick Koston 02287fdf7b [sensor] Share filter_out and throttle_with_priority value tables in flash (#20078) 2026-10-12 15:48:27 -10:00
J. Nick Koston 2001f60954 [core] Keep string constants in apply conditions in flash on ESP8266 (#20107) 2026-10-12 15:48:05 -10:00
J. Nick Koston 5a39097b3a [light] Keep one copy of the color mode lookup table in flash (#20394) 2026-10-12 15:46:41 -10:00
J. Nick Koston fb14db267c [const] Move CONF_SERIAL_NUMBER to the shared component constants (#20611) 2026-10-13 01:45:52 +00:00
J. Nick Koston 36900e36f9 [esp32_power_management] Build the flash power-down path in a standalone test variant (#20612) 2026-10-12 15:45:09 -10:00
a035c17e04 [gt911] Fix the interrupt pin initialisation after reset (#15021)
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-12 15:42:24 -10:00
88fdf822cd [runtime_image] Speed up RGB565 JPEG decoding (#19454)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 15:41:30 -10:00
e7a7aa150d [yaml_util] Raise a clear error for a non-mapping defaults: in a package (#19425)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 15:36:57 -10:00
J. Nick Koston 54f228766c [stcc4] Use SUB_SENSOR for the output sensors (#20605) 2026-10-12 21:12:39 -04:00
Bascht74 58188305c4 [uart] Keep the swapped UART0 pins on an ESP8266 reload (#20463) 2026-10-12 15:09:32 -10:00
J. Nick Koston afedddafe2 [systa_bus] Use SUB_SENSOR and explicit sub_sensors calls for the sensors (#20610) 2026-10-12 20:57:01 -04:00
J. Nick Koston c3c235dea7 [pzem6l24] Use SUB_SENSOR and explicit sub_sensors calls for the sensors (#20609) 2026-10-12 20:56:58 -04:00
J. Nick Koston 39a7f4482a [bh1745] Use SUB_SENSOR for the sensors (#20606) 2026-10-12 20:52:16 -04:00
J. Nick Koston f3f3ca5a8a [runtime_image] Add CodSpeed benchmarks for JPEG decoding (#20603) 2026-10-12 14:50:24 -10:00
Jesse HillsandJ. Nick Koston bdd2dcd8c6 [weikai] Mark configurable classes as final (#17292)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-12 14:39:08 -10:00
J. Nick Koston a3e373b063 [noise] Add generate_encryption_key and use it in the wizard and dashboard_import (#20600) 2026-10-12 20:31:36 -04:00
dc9a7cffbd [ota] Refuse a second OTA while one is already running (#20588)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-13 00:28:38 +00:00
4cae63bbaa [ecocomfort2] Add the read-only sensors and clock sync (Part 2) (#15447)
Co-authored-by: gledian <gledina.kruja@gmail.com>
Co-authored-by: Claude Opus 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-10-12 14:27:16 -10:00
8aab968136 [aranet] Add Aranet BLE sensor support (#18263)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 14:11:10 -10:00
J. Nick Koston 80fa6efd1e [core] Document string tables, shared CRC helpers, NaN sentinels and polling start-up in AGENTS.md (#20599) 2026-10-12 13:44:49 -10:00
J. Nick Koston 3ad83ad02b [text_sensor] Share substitute and map filter tables in flash (#20076) 2026-10-12 13:40:38 -10:00
J. Nick Koston b66cde0270 [sensor] Share calibrate_linear and calibrate_polynomial tables in flash (#20074) 2026-10-12 13:39:38 -10:00
J. Nick Koston 331d12af2f [binary_sensor] Share multi click timing tables in flash and skip the default cooldown setter (#20071) 2026-10-12 13:39:10 -10:00
318e4b79c1 [apc1] Add support for the ScioSense APC1 air quality sensor (UART) (#19274)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2026-10-12 13:38:34 -10:00
J. Nick Koston 99a4ae1f51 [ota] Add old_key so an upload can retry with the previous key (#19398) 2026-10-12 13:35:27 -10:00
J. Nick Koston 72ca565a0e [core] Resume if and while from the end of their branch instead of an embedded continuation action (#20283) 2026-10-12 13:35:03 -10:00
J. Nick Koston cce435a31e [gpio] Keep each interlock group in one shared flash table (#20238) 2026-10-12 13:34:44 -10:00
J. Nick Koston 6c86a8903f [core] Report incomplete YAML discovery and record remote package sources (#20312) 2026-10-12 13:28:17 -10:00
b21354d6d9 [mistral_ir] Add climate component for Mistral MHFE1126A IR AC units (#17812)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 13:22:34 -10:00
Ilya EsinandJ. Nick Koston fa519cea64 [apds9930] Add ambient light and proximity sensor (#13125)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 13:14:31 -10:00
Remco van EssenandJ. Nick Koston 132e9dac8b [tmc2209] Add TMC2209 stepper driver (#20437)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 13:13:11 -10:00
0c43d689d7 [sh1122] Add OLED display driver (#15295)
Co-authored-by: Nicolas Reinecke <nr@das-labor.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 13:11:51 -10:00
73d5bb3c97 [remko_ar715] Add Remko AR-715 IR climate platform (#17173)
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-12 13:09:40 -10:00
81c275777d [bmi270] Add optional BMM150 magnetometer support via aux interface (#18436)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-12 13:08:41 -10:00
54fdeb7b1b [samsung] Add climate IR samsung support (#8004)
Co-authored-by: Georgi Filipov <georgi.filipov@uplink.bg>
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-12 13:01:22 -10:00
150e32c4be [renogy_inverter_ble] Add Renogy inverter BLE sensor component (#16754)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 12:56:50 -10:00
1e13b7f973 [tinyusb] Add tinyusb_keyboard component (#16079)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 12:56:28 -10:00
Rodrigo MartínandJ. Nick Koston d08d324a83 [bq25186] Add bq25186 1-Cell, 1A Linear Battery Charger component (part 1) (#15700)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 12:54:35 -10:00
88668f448e [max17048] Add MAX17048 Li+ Cell Fuel Gauge IC (#13585)
Co-authored-by: Rodrigo Martín <contact@rodrigomartin.dev>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 12:53:16 -10:00
c5baae35d5 [ezsp_proxy_tap] Add SiLabs EZSP auto-ACK implementation (#20304)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 12:51:48 -10:00
1c315c3bf2 [ns2009] Add Nsiway NS2009 touchscreen controller component (#10597)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 12:49:07 -10:00
f9f41ef16e [MAX31888] Add temperature sensor (#13567)
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-12 12:45:08 -10:00
a68cf669aa [tca8418] Add TCA8418 keypad component (#18521)
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-12 12:39:11 -10:00
3ddbf8b3ef [lp5562] Add LP5562 4-channel I2C LED driver output component (#18453)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 12:12:11 -10:00
Ryan GammonandJ. Nick Koston b88eafcd00 [bq27220] Add BQ27220 battery fuel gauge component (#17702)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 12:05:34 -10:00
d7dd25a1bc [ld2410s] Add HLK-LD2410S presence radar component (#10452)
Co-authored-by: NovakI <novaki@debian.ka11>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 11:57:16 -10:00
83e5e32e2b [i2s_clockless_led_strip] Drives LED strips using I2S and DMA (#20255)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 11:52:05 -10:00
584d4878eb [lis3dh] Add LIS3DH accelerometer component (#17393)
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>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 11:44:33 -10:00
93183c661e [ecocomfort2] Add component for Fantini Cosmi VMC units via BLE (Part 1: hub + fan) (#17092)
Co-authored-by: gledian <gledina.kruja@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 11:36:27 -10:00
Anton ViktorovandJ. Nick Koston 5a819f4b48 [bh1745] Add BH1745 colour sensor (#6349)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 11:34:04 -10:00
Anton ViktorovandJ. Nick Koston 2cd7c0eb36 [husb238] Add HUSB238 USB PD controller hub with binary sensors (#16572)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 11:31:16 -10:00
1c8808339a [power_management] Add the power_management domain with an esp32_power_management platform (#12325)
Co-authored-by: Kevin Ahrendt <kahrendt@gmail.com>
Co-authored-by: Kevin Ahrendt <kevin.ahrendt@openhomefoundation.org>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 11:23:55 -10:00
matt123pandpre-commit-ci-lite[bot] 6bf091104f [es7210] Add TDM support for future AEC support (#18954)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-10-12 11:22:17 -10:00
Martin KlingensmithandJ. Nick Koston 4e3845ba68 [pd_pioneer_ir] Add Pioneer mini-split IR climate component (#17680)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 11:20:13 -10:00
0b6856c052 [udp] Add IPv6 unicast and multicast (#20590)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-12 11:17:58 -10:00
Stephen CoxandJ. Nick Koston ffa849753a [ds3231] Add DS3231 real-time clock hub with temperature sensor (#18883)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 11:00:05 -10:00
0f087f425a [ld2460] Add HLK-LD2460 radar hub with target sensors (#19002)
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-12 10:52:04 -10:00
833c6634ad [lis2dw12] Add ST LIS2DW12 accelerometer component (#14780)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-13 09:44:51 +13:00
5a3bee638f [siemens_ira211] Add the Siemens IRA211 protocol and climate platform (#15154)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-13 09:33:33 +13:00
66ba258baa [udp] Wake the main loop when a datagram arrives (#20585)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-12 08:53:22 -10:00
216e1bee5c [socket] Read the LwIP interface list under the core lock (#20583)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 18:15:44 +00:00
emes30andJ. Nick Koston e0436e2600 [rp2_pio] Use pioasm 4.1.0 so PIO programs run on RP2350 (#19260)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 18:01:11 +00:00
ca69c33530 [whirlpool] Add the HISENSE_ARG model for Hisense and BGH units sold in Argentina (#17352)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-12 07:57:23 -10:00
J. Nick Koston aa0bd9af72 [core] Add YAML secret reference and registration helpers (#20343) 2026-10-12 07:55:38 -10:00
Jonathan Swoboda 9c35bb7426 [esp32] Check the TLS option the active IDF version writes in tests (#20584) 2026-10-12 07:48:33 -10:00
J. Nick Koston af8a0739f0 [web_server] Self contained AP mode: embedded interface served as a captive portal (#18517) 2026-10-12 07:46:11 -10:00
99b61760c3 [remote_base] Add the Pioneer WYT transmit protocol (#17175)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 07:37:31 -10:00
db9138c550 [bl0939] Add current transformer measurement mode support (#13350)
Co-authored-by: ShuishengPeng <63339353+ackPeng@users.noreply.github.com>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-10-12 07:34:25 -10:00
J. Nick Koston 5bb1f0f779 [statsd] Use the socket UDP API on ESP8266 and RP2040 (#20557) 2026-10-12 07:17:01 -10:00
J. Nick Koston 4c549d6e92 [e131] Use the socket UDP API on ESP8266 and RP2040 (#20559) 2026-10-12 07:16:47 -10:00
J. Nick Koston dc719b300b [udp] Use the socket UDP API on ESP8266 and RP2040 (#20558) 2026-10-12 07:16:29 -10:00
J. Nick Koston f854da4f8d [cli] Add --prompt-ota-key to present a different key for one upload (#19397) 2026-10-12 07:15:51 -10:00
Firat HajiyevandFirat17aze 2dad6e4f62 [wifi] Reject unsupported WPA3 on ESP8266 (#20592)
Co-authored-by: Firat17aze <187820330+Firat17aze@users.noreply.github.com>
2026-10-12 07:15:13 -10:00
J. Nick Koston 72ea9ae458 [remote_base] Generate the byte payload transmit actions with no C++ class (#20568) 2026-10-12 07:14:19 -10:00
Kevin Ahrendt 9e8845885b [audio] Bump microFLAC to v0.3.0 (#20591) 2026-10-12 11:22:59 -04:00
J. Nick Koston 00872d4336 [ethernet] Skip setters that pass the default PHY address, MDC, MDIO, clock and SPI interface (#20070) 2026-10-12 09:56:53 -04:00
J. Nick Koston 780ccfc462 [adc] Skip the output raw, sample count, attenuation and sampling mode setters when they match the defaults (#20068) 2026-10-12 09:56:24 -04:00
J. Nick Koston 9acc07a973 [remote_base] Generate transmit_pronto and transmit_rc6 with no C++ class (#20564) 2026-10-12 07:48:02 -04:00
J. Nick Koston 698988669d [remote_transmitter] Compile pronto, rc6, midea and lambda byte payloads in the test config (#20565) 2026-10-12 07:37:51 -04:00
J. Nick Koston bde67d83f2 [noise] Exclude the handshake write step from the loop blocking guard (#20573) 2026-10-12 07:33:19 -04:00
J. Nick Koston 057a0c5843 [api] Exclude the spare key refill from the loop blocking guard (#20574) 2026-10-12 07:31:15 -04:00
J. Nick Koston c212950df5 [wifi] Exclude the station connect from the loop blocking guard on LibreTiny (#20575) 2026-10-12 07:30:59 -04:00
J. Nick Koston 3c88d39192 [remote_base] List the hob2hood, nexus and onkyori protocol defines in defines.h (#20577) 2026-10-12 07:28:33 -04:00
J. Nick Koston c0d4287c6e [core] Create the PlatformIO HTTP cache dir before the prefetch resolve threads run (#20579) 2026-10-12 07:28:07 -04:00
BugHaverandJ. Nick Koston 3472ac3dcc [heatpumpir] Add Eco and Boost presets and fix fan speeds on Mitsubishi Heavy ZJ and ZMP (#16877)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-11 23:44:01 -10:00
5a90bceba0 [cse7766] Clamp a power factor above 1 instead of publishing NaN (#15307)
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-11 23:42:17 -10:00
af76f19ab4 [heatpumpir] Add ZMP IR receiver (#17099)
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-11 23:18:22 -10:00
e92d239996 [dsmr] add Switzerland channel 1-1 sensors (EWL Luzern AM550) (#17391)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-11 22:37:05 -10:00
602bf00435 [daikin_brc] Add sub_unit parameter for multi-unit addressing (#15234)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-11 22:21:26 -10:00
mfishma 1e05106a78 [mipi_spi] Add M5STACK-ATOMS3R-ST7735 as a display panel configuration (#18673) 2026-10-12 08:01:48 +00:00
marshnandJ. Nick Koston 061b27af02 [remote_base] Add a data option and checksum handling to the Drayton protocol (#13060)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-11 21:54:55 -10:00
Siddharth SinghandJ. Nick Koston d265ed2b9e [remote_base] Fix NEC command_repeats to send repeat frames (#17663)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-11 21:29:09 -10:00
20cdca6f18 [remote_base] Decode the full MagiQuest frame and check its checksum (#10622)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-12 07:01:12 +00:00
toupsandJ. Nick Koston c668ca516c [remote_base] updates to support 56 bit panasonic codes (#20260)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-11 20:48:55 -10:00
Frédéric MetrichandClaude Opus 5.5 7fbdcb0205 [emontx] Lock the emonTx across reboots (#20576)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:42:52 -10:00
Zach Isbach 92a4e283a9 [epaper_spi] Add Good Display GDEY0213F51 4-color e-paper support (#17991) 2026-10-12 17:40:59 +11:00
Frédéric MetrichandClaude Opus 5.5 ba9061e34e [nextion] Pass std::string to custom-protocol automations that defer (#20571)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:30:53 -10:00
0c645ac3a1 [remote_base] Adding Marantz RC5 Extended protocol (#13164)
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 06:24:50 +00:00
J. Nick Koston eb8a793f6e [core] Add a name truncation helper and keep the MAC suffix in the nRF52 BLE name (#19768) 2026-10-11 20:22:11 -10:00
36082b23c2 [remote_base] Add the Somfy RTS transmit protocol (#16502)
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>
2026-10-11 20:11:15 -10:00
esphome[bot] 8191cd18d2 [ci] Refresh integration test durations (#20578) 2026-10-12 06:02:07 +00:00
2692e0f189 [remote_base] Add Nexus RF protocol support (#13577)
Co-authored-by: zry98 <dev@zry.io>
Co-authored-by: Citizen <kvirbickas@gmail.com>
Co-authored-by: Citizen07 <34106434+Citizen07@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
Signed-off-by: zry98 <dev@zry.io>
2026-10-11 19:36:07 -10:00
Ivan KruglovandJ. Nick Koston 29db7b3f51 [bl0939] Add optional internal temperature sensor (#19415)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 05:33:44 +00:00
Firat Hajiyev 1c1b4d13ad [core] Mask integer-valued credentials without changing code generation (#20430) 2026-10-11 18:58:41 -10:00
J. Nick Koston f8076fe90b [core] Add UnavoidableBlockingScope for blocking that cannot be shortened (#19020) 2026-10-11 18:48:41 -10:00
19eeab0385 [wifi] Add DHCP lease cache (#17767)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-11 18:48:13 -10:00
3f21ddf6ff [lvgl] Add a color picker widget (#17883)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-12 15:12:22 +11:00
J. Nick Koston 5c1c946e88 [wake_on_lan] Use the socket UDP API on ESP8266 and RP2040 (#20556) 2026-10-11 18:09:11 -10:00
J. Nick Koston 698935c321 [seeed_mr24hpc1] Remove the duplicate startup delay constant left by the beta merge (#20569) 2026-10-11 17:38:41 -10:00
J. Nick Koston 0c6543c4a6 [remote_base] Name transmit as a template in ProtocolCall::perform (#20566) 2026-10-11 17:38:38 -10:00
4688df3b94 [mcp9808] Remove k7hpn as codeowner (#20561)
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-11 17:06:46 -10:00
esphome[bot] 32f416109e Bump aioesphomeapi from 46.9.0 to 46.10.0 (#20563) 2026-10-12 03:05:16 +00:00
J. Nick Koston 08a55c97b7 [remote_base] Generate the simple transmit actions with no C++ class (#20549) 2026-10-11 17:03:59 -10:00
d19bb7f4be [cse7761] Add current_input option for current transformer boards such as the Sonoff POW CT (#7836)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@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: Jesse Hills <3060199+jesserockz@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-11 17:00:16 -10:00
J. Nick Koston 6095d9d610 [improv_serial] Remove the duplicate timeout id left by the beta merge (#20562) 2026-10-11 16:56:56 -10:00
Jesse Hills 95d9b68ac9 Merge branch 'beta' into dev 2026-10-12 15:42:45 +13:00
Jesse Hills 2c4b1f3ed5 Merge pull request #20542 from esphome/bump-2026.10.0b3
2026.10.0b3
2026-10-12 15:42:25 +13:00
J. Nick KostonandClaude Opus 4.6 847be14d1e [socket] Add lwip raw UDP socket implementation (#14677)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-10-11 16:27:41 -10:00
J. Nick Koston 40606a6c08 [core] Do not search the same heap twice in RAMAllocator single-region requests (#19381) 2026-10-11 16:24:36 -10:00
J. Nick Koston 650a02beb0 [remote_transmitter] Compile the transmit actions the test config left out (#20550) 2026-10-11 22:13:15 -04:00
J. Nick Koston c6ed2069a5 [json][web_server] Link strings that outlive the document instead of copying them (#19380) 2026-10-11 15:55:51 -10:00
J. Nick Koston fa4bc28d3e [text] Build the on_value trigger with build_callback_automation (#19613) 2026-10-11 15:54:44 -10:00
J. Nick Koston 95de93bebb [fan] Build the on_state trigger with build_parent_callback_automation (#19612) 2026-10-11 15:54:25 -10:00
J. Nick Koston 028db614ca [select] Build the on_value trigger with build_callback_automation (#19609) 2026-10-11 15:54:09 -10:00
J. Nick Koston c38674bb7b [climate] Build the on_state and on_control triggers with build_callback_automation (#19611) 2026-10-11 15:53:55 -10:00
Clyde StubbsandClaude Opus 5.5 4ba5afaa1b [core] Move CONF_ON_STOP to the shared component constants (#20548)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 01:37:17 +00:00
J. Nick Koston 0cd239dfd1 [uart] Keep constant switch, button and write payloads in flash (#20082) 2026-10-12 01:16:34 +00:00
J. Nick Koston ed6ab4565d [core] Emit filter, action and condition lists one item at a time (#20116) 2026-10-11 15:12:59 -10:00
J. Nick Koston 5d64b65d72 [esp32_ble_server] Keep constant set_value payloads in shared flash tables (#20314) 2026-10-11 15:12:09 -10:00
J. Nick Koston 993fd8d7a2 [thermostat] Keep presets in shared flash tables (#20080) 2026-10-11 15:04:16 -10:00
J. Nick Koston 9295342c16 [event] Keep event types in a shared flash table (#20217) 2026-10-11 14:58:49 -10:00
J. Nick Koston e16b3a6245 [api] Share one flash string pool between API action and Home Assistant action strings (#20281) 2026-10-11 14:57:20 -10:00
J. Nick Koston da0c59a57c [http_request] Keep action request headers in a shared flash table (#20225) 2026-10-11 14:57:06 -10:00
J. Nick Koston 40425610d5 [remote_base] Keep constant abbwelcome payloads in shared flash tables (#20311) 2026-10-11 14:55:22 -10:00
Bascht74 643f29fae6 [uart] Let a UART say it has no line timing (#20227) 2026-10-11 14:49:19 -10:00
59d31505e9 [mipi_spi] Add LCDwiki ES3C35P model (ST77922 QSPI) (#19011)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 00:48:47 +00:00
J. Nick Koston e50a0d902e [core] Expose the preferences storage helpers to components (#20535) 2026-10-11 20:42:21 -04:00
Bascht74andpre-commit-ci-lite[bot] 22c17fe7e1 [remote_base] Add optional suffix bits to KeeLoq after the HCS301 word (#19891)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-10-11 14:36:27 -10:00
techdregs 826a7a252a [tmp102] Add comparator alert binary sensor (#20540) 2026-10-11 14:33:06 -10:00
J. Nick Koston ce19978cfd [ci] Tell too-big PR authors that review waits until the PR is under the cap (#20543) 2026-10-11 14:17:07 -10:00
J. Nick Koston 427e0c9064 [const] Move the colour channel config keys to the shared constants (#20544) 2026-10-11 13:50:50 -10:00
J. Nick Koston 0df7b0b366 [core] Move zstd_module from the api wizard to esphome.helpers (#20342) 2026-10-12 12:46:59 +13:00
Jesse Hills bd4b32873a Bump version to 2026.10.0b3 2026-10-12 11:52:33 +13:00
Bascht74 9e580db434 [uart] Reuse the ISR pin argument on an ESP8266 software serial reload (#20541) 2026-10-12 11:52:31 +13:00
Jesse Hills 87187c5391 [api] Let a wizard input pick multiple entities (#20537) 2026-10-12 11:52:31 +13:00
tomaszduda23andClaude 60402614cd [nrf52] fix zigbee time component (#20527)
Co-authored-by: Claude <noreply@anthropic.com>
2026-10-12 11:52:25 +13:00
puddly 2727d35655 [usb_uart] Fix invalid stop bits and parity enum mappings (#20408) 2026-10-12 11:51:44 +13:00
J. Nick Koston fb1ef29d54 [uart] Stop ESP8266 software serial RX ISR from blocking for a whole byte (#18519) 2026-10-12 11:51:39 +13:00
Frédéric MetrichandClaude Opus 5.5 7c0f63ea20 [core] Copy trigger arguments in wait_until and script.wait (#20536)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 11:46:44 +13:00
heikkikiiandJ. Nick Koston 5d75828ceb [seeed_mr24hpc1] Wait for the module to boot before sending UART traffic (#18860)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 11:46:44 +13:00
bd2e8d184d [gree] Keep display light on by default for YAN/YAA/YAC/YAC1FB9 (#18516)
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-12 11:46:44 +13:00
J. Nick Koston 3c7ce71d6f [uart_tcp] Prove the accept has run before checking what a new client receives (#20489) 2026-10-12 11:46:44 +13:00
Frédéric MetrichandClaude Opus 5.5 58e03a875f [teleinfo] Fix out-of-bounds read when splitting a frame into groups (#20513)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 11:46:44 +13:00
Clyde StubbsandClaude Opus 5.5 41a9fca881 [ci] Reset sdkconfig after ESP-IDF component discovery (#20511)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 11:46:44 +13:00
ec58e864f5 [canbus] Fix high frame rates causing crashes due to unbounded iteration (#14481)
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-12 11:46:44 +13:00
esphome[bot] 375fbd829e Bump bundled esphome-device-builder to 1.23.1 (#20497) 2026-10-12 11:46:44 +13:00
4bae89bd4a [nrf52] Fix BLE OTA/log device discovery & transport selection on macOS (#17101)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-12 11:46:43 +13:00
Bascht74andJ. Nick Koston 897b2f16a9 [tests] Close leftover server connections in the tcp_uart bridge and TCP client link tests (#20215)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-12 11:46:43 +13:00
tomaszduda23 93c8796ae8 [nrf52] fix build error on 3.4.0 with pch ON (#20474) 2026-10-12 11:46:43 +13:00
J. Nick Koston 654afb536e [core] Fix the native ESP8266 and host builds on Windows without ccache (#20468) 2026-10-12 11:46:43 +13:00
jabla 97154f9e5f [espidf] Clean the build when the IDF target changes (#20473) 2026-10-12 11:46:43 +13:00
c9e49b8e8f [teleinfo] Add predefined properties for main tags of TIC standard and historical (#9167)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-12 11:42:45 +13:00
21804a75de [openthread] add mesh_local_eid openthread_info sensor (#13924)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@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-11 12:41:14 -10:00
J. Nick Koston 3ae52e21cf [usb_host] Warn instead of failing when a device duplicates a usb_uart entry (#20373) 2026-10-12 11:38:38 +13:00
Zhaoqi Xu f2395f9001 [heatpumpir] Fix fan speeds for mitsubishi_msc protocol (#20460) 2026-10-12 11:38:38 +13:00
Ali Alaeiandtomaszduda23 3d248496ab [zephyr_mcumgr] Enable MCUMGR_GRP_OS for UART transport, not just BLE (#18319)
Co-authored-by: tomaszduda23 <tomaszduda23@gmail.com>
2026-10-12 11:38:38 +13:00
esphome[bot] 34eca948c4 Bump bundled esphome-device-builder to 1.23.0 (#20446) 2026-10-12 11:38:37 +13:00
J. Nick Koston 58815575ef [core] Pass a single condition to actions directly instead of wrapping it in a group (#19223) 2026-10-12 11:38:37 +13:00
J. Nick Koston 28b012389d [ci] Keep bus group partners on the same runner (#20431) 2026-10-12 11:38:37 +13:00
J. Nick Koston 54f86537ad [esp32][libretiny] Skip registering static destructors that never run (#20440) 2026-10-12 11:38:37 +13:00
J. Nick Koston 3284d93a33 [nrf52] Fake a larger flash in testing mode (#20439) 2026-10-12 11:38:37 +13:00
J. Nick Koston 01686a5f67 [improv_serial] Use a numeric scheduler id for the Wi-Fi connect timeout (#20360) 2026-10-12 11:38:37 +13:00
J. Nick Koston 3b0c485701 [mqtt] Use a numeric scheduler id for the deferred state publish (#20359) 2026-10-12 11:38:37 +13:00
J. Nick Koston e61d348f0c [wifi] Use numeric scheduler ids instead of string names (#20357) 2026-10-12 11:38:37 +13:00
J. Nick Koston 22e594bc62 [esp32_ble_client] Track conn_id per attempt and stop logging an error on a failed open (#20417) 2026-10-12 11:38:37 +13:00
J. Nick Koston 061733672f [ci] Refit component batch estimates to a full run (#20401) 2026-10-12 11:38:37 +13:00
J. Nick Koston b1f5da9561 [esp8266] Keep ArduinoJson's float tables in flash (Arduino core 3.1.2-esphome.3) (#20416) 2026-10-12 11:38:37 +13:00
J. Nick Koston db517059a3 [ota] Collapse duplicate CI test variants (#20407) 2026-10-12 11:38:37 +13:00
J. Nick Koston 0b40892775 [mcp23xxx_base] Keep the interrupt handler in IRAM on ESP8266 (#20409) 2026-10-12 11:38:37 +13:00
J. Nick Koston 8e6ed71e2a [ethernet] Collapse duplicate CI test variants (#20406) 2026-10-12 11:38:37 +13:00
J. Nick Koston cf569702c4 [ci] Stop the toolchain tests from installing ESP-IDF (#20413) 2026-10-12 11:38:37 +13:00
Bascht74 9a35c5229b [uart] Reuse the ISR pin argument on an ESP8266 software serial reload (#20541) 2026-10-11 22:36:06 +00:00
Jesse Hills 96eaf42336 [api] Let a wizard input pick multiple entities (#20537) 2026-10-12 11:22:29 +13:00
tomaszduda23andClaude 94894d60b7 [nrf52] fix zigbee time component (#20527)
Co-authored-by: Claude <noreply@anthropic.com>
2026-10-11 12:15:23 -10:00
puddly 4155b82f84 [usb_uart] Fix invalid stop bits and parity enum mappings (#20408) 2026-10-12 10:58:31 +13:00
J. Nick Koston 61d98534f6 [uart] Stop ESP8266 software serial RX ISR from blocking for a whole byte (#18519) 2026-10-12 10:56:21 +13:00
embeandJ. Nick Koston 8060dadd6c [micro_wake_word] Add opt-in internal RAM placement (#20529)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-11 11:49:49 -10:00
luar123andJ. Nick Koston 7d7db6bf90 [zigbee] add report action on esp32 (#20488)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-11 11:31:49 -10:00
Frédéric MetrichandClaude Opus 5.5 2ef95bbc28 [core] Copy trigger arguments in wait_until and script.wait (#20536)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 21:22:52 +00:00
luar123 3035a08b0a [zigbee] Improve basic cluster and add manufacturer and app version setting (#20519) 2026-10-11 11:19:43 -10:00
Frédéric MetrichandClaude Opus 5.5 9b34db14d4 [mqtt] Reject deferring actions in on_json_message (#20538)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 21:15:15 +00:00
heikkikiiandJ. Nick Koston 9998849009 [seeed_mr24hpc1] Wait for the module to boot before sending UART traffic (#18860)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-11 11:10:50 -10:00
Maciej KucharekandJ. Nick Koston f2db62adf2 [mipi_dsi] Add JC1060P470-V2 (#18695)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-11 11:08:02 -10:00
Frédéric MetrichandClaude 36fbf5bf22 [emontx] Add pause/resume actions to let another component take over the UART (#18756)
Co-authored-by: Claude <noreply@anthropic.com>
2026-10-11 21:07:39 +00:00
Fabio Pugliese OrnellasandJ. Nick Koston 5cdefa910f [cse7761] Add new sensors (#17531)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-11 11:06:05 -10:00
Frédéric MetrichandClaude Opus 5.5 eb56ce2b76 [haier] Reject deferring actions in on_status_message (#20539)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 11:05:09 -10:00
2ab06fa937 [pipsolar] Add QET/QLT total energy sensors (#17347)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-11 10:39:22 -10:00
Clyde StubbsandClaude bea0ef264a [lvgl] Migrate to 9.6.0 (#20257)
Co-authored-by: Claude <noreply@anthropic.com>
2026-10-12 07:36:32 +11:00
153c51e39f [gree] Keep display light on by default for YAN/YAA/YAC/YAC1FB9 (#18516)
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-11 10:24:50 -10:00
f562d7a6a4 [socket] Add join_multicast_group() and set_ipv6_multicast_if() helpers (#17045)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.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-10-11 09:59:44 -10:00
b73f151619 [pmsx003] add support for luftmy ld15 (#15296)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-11 09:49:06 -10:00
Frédéric MetrichandClaude Opus 5.5 da43b6292d [mk2pvrouter] Store the frame as bytes and read with read_byte() (#20530)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 09:41:58 -10:00
Frédéric MetrichandClaude Opus 5.5 b10eef537e [teleinfo] Log the tag name for an invalid timestamp (#20532)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 09:41:28 -10:00
Frédéric MetrichandClaude Opus 5.5 5c5737303f [teleinfo] Store the frame as bytes and read with read_byte() (#20531)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 09:41:02 -10:00
Frédéric MetrichandClaude Opus 5.5 16a474cce5 [core] Only treat action names as actions in has_non_synchronous_actions() (#20524)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 09:40:27 -10:00
86192d89f4 [ufm01] Improve stale recovery, add diagnostics and clear flow action (#18899)
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 5.5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-11 09:29:56 -10:00
J. Nick Koston c130e2808b [display] Build on_page_change with build_callback_automation and compile it out when unused (#20456) 2026-10-11 09:23:05 -10:00
J. Nick Koston 78c7cd725d [remote_base] Transmit constant Haier, Mirage and AEHA codes without a heap vector (#20287) 2026-10-11 09:22:39 -10:00
Frédéric MetrichandClaude Opus 5.5 eff3a6f9a2 [modbus_client] Use the core deferring-actions validators (#20526)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 09:17:39 -10:00
Frédéric MetrichandClaude Opus 5.5 8449820480 [emontx] Use the core validate_synchronous_automation() helper (#20525)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 09:17:18 -10:00
9355bdc3c5 [canbus] Bus error logging, Auto-recover bus-off on ESP32 (#10953)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-11 09:12:28 -10:00
J. Nick Koston eb1aa2bfd6 [web_server] Keep the cover, valve and alarm panel action tables in flash (#20397) 2026-10-11 09:07:06 -10:00
6322cef8e2 [tmp102] Add hardware configuration options (#20063)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-11 09:06:40 -10:00
Frédéric MetrichandClaude Opus 5.5 c8a300164b [core] Add validators that reject deferring actions in an automation (#20523)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 08:53:37 -10:00
J. Nick Koston 86af0af0e4 [core] Add sum_bytes() and use it for the additive byte checksums components hand-roll (#20507) 2026-10-11 08:48:47 -10:00
J. Nick Koston 8e8261c39e [core] Add this week's review lessons to AGENTS.md (#20522) 2026-10-11 18:14:56 +00:00
Mat931andpre-commit-ci-lite[bot] e9073f7298 [systa_bus] Add text_sensor (#20518)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-10-11 07:38:37 -10:00
Frédéric Metrich 5186728d53 [emontx] Pass on_json/on_data data without copies and reject deferring actions (#20516) 2026-10-11 06:52:41 -10:00
Bonne Eggleston 1312d5eec0 [modbus] Pass the request's address to client device callbacks (#20504) 2026-10-11 06:51:41 -10:00
Frédéric MetrichandClaude Opus 5.5 c5cbc4a4e0 [emontx] Test lines of exactly MAX_LINE_LENGTH bytes (#20517)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:50:42 +00:00
J. Nick Koston 7f7ae4c8fc [uart_tcp] Prove the accept has run before checking what a new client receives (#20489) 2026-10-11 08:47:12 -04:00
J. Nick Koston 493db0c2bb [esp32] Add allow_insecure_tls() for components that skip certificate verification (#20506) 2026-10-11 08:45:19 -04:00
6001cde234 [emontx] Discard the rest of a line that overflows the buffer (#20515)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-10-11 08:41:02 -04:00
Frédéric MetrichandClaude Opus 5.5 3df03e4be9 [emontx] Read UART bytes with read_byte() (#20514)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 08:31:07 -04:00
1402cb7b64 [tuya] Add forced minute boundary time sync (#18603)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-11 00:32:39 -10:00
Frédéric MetrichandClaude Opus 5.5 2681126986 [teleinfo] Fix out-of-bounds read when splitting a frame into groups (#20513)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 00:31:14 -10:00
J. Nick Koston 3e7fafb705 [daikin] Prefix the PendingComfort enumerators (#20512) 2026-10-11 00:09:55 -10:00
Clyde StubbsandClaude Opus 5.5 b6096c9437 [ci] Reset sdkconfig after ESP-IDF component discovery (#20511)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 10:04:57 +00:00
J. Nick Koston 681b481654 [tests] Reset shared App state before every host test (#20510) 2026-10-11 00:02:00 -10:00
84694b602a [ld2412] Fix crash when only some gate thresholds are configured (#18194)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-10 23:14:38 -10:00
cb54f6d6cc [daikin] Add presets and half-degree temperature steps (#17891)
Co-authored-by: James Nimmo <james@nimmo.net.nz>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-10 23:13:21 -10:00
TJ HornerandJ. Nick Koston 254690100c [hub75] add set_rotation and get_rotation methods (#13921)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-10 17:52:24 -10:00
6bc19d2a1a [espnow] Add support for ESPNow with ESP8266 (#14407)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-11 03:45:03 +00:00
fdf1752307 [toshiba] Add RAS-B10N3KV2 model support (#17550)
Co-authored-by: Claude Fable 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: Petr Helebrant <petr@helebrant.cz>
Co-authored-by: longerCZE <72692179+longerCZE@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Signed-off-by: Petr Helebrant <petr@helebrant.cz>
2026-10-10 17:35:42 -10:00
377a43c5be [toshiba] Add 'seiya' model support (#17830)
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>
Signed-off-by: Petr Helebrant <petr@helebrant.cz>
2026-10-10 17:17:22 -10:00
J. Nick Koston 70e4463644 [const] Move the adc_time and adc_averaging constants to the shared constants (#20503) 2026-10-10 16:56:35 -10:00
J. Nick Koston e6da33d3f0 [const] Move the verify_ssl constant to the shared constants (#20501) 2026-10-10 16:49:54 -10:00
Clyde StubbsandClaude Opus 5.5 eeaa8c78d8 [cli] Add line format error message option (#20470)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:33:18 +11:00
8af91c9663 [feedback] Support for overshoot_duration (#13186)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-11 01:23:31 +00:00
3be8c5b6b8 [canbus] Fix high frame rates causing crashes due to unbounded iteration (#14481)
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-10 15:07:36 -10:00
luar123 1763adf526 [zigbee] add support for SED on esp32 (#20487) 2026-10-10 13:24:13 -10:00
Paul Traina 924d7364ba [mipi_spi] ESP32-2432S028-9342 panel is RGB, not BGR (#19419) 2026-10-10 13:22:56 -10:00
esphome[bot] 5f43b8168e Bump bundled esphome-device-builder to 1.23.1 (#20497) 2026-10-10 12:00:39 -10:00
J. Nick Koston 642c3b7a9e [const] Add the milliampere hour unit to the shared constants (#20495) 2026-10-10 11:43:21 -10:00
78d3b9f5a1 [nrf52] Fix BLE OTA/log device discovery & transport selection on macOS (#17101)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-10 21:17:01 +00:00
6559ea470b [tca9548a] Allow separate I²C bus frequencies on each multiplexer port (#10422)
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-10 10:58:09 -10:00
J. Nick Koston ebbe3860ec [const] Move aqi to the shared constants (#20494) 2026-10-10 09:43:57 -10:00
Rupayon HaldarandJ. Nick Koston ff96530e4d [esp32] Honor sdkconfig CPU frequency options (#20158)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-10 09:13:05 -10:00
865863abf5 [cm1106] Add ABC function (#8894)
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: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-10-10 09:12:40 -10:00
17cbb75161 [mitsubishi] Control both vanes on multi-vane Mitsubishi minisplit heads (#12609)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-10 09:11:14 -10:00
f70b6a6b03 [mitsubishi] Add fahrenheit_compatibility mode (#18880)
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-10-10 09:04:06 -10:00
J. Nick Koston 2ac48d408f [const] Move automatic_baseline_calibration to the shared constants (#20492) 2026-10-10 08:56:49 -10:00
667dbd1ec3 [adalight] Add a component build test (#19103)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-10 08:56:16 -10:00
Jeff Brown a4ef8a6c90 [addressable_light] Clear white channel when setting HSV color (#20265) 2026-10-10 08:43:33 -10:00
Jonathan Swoboda ea7c277c08 [ci] Mark PRs with merge conflicts stale after 30 days (#20490) 2026-10-10 14:34:29 -04:00
Bascht74andJ. Nick Koston eb1afa4245 [tests] Close leftover server connections in the tcp_uart bridge and TCP client link tests (#20215)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-10 08:15:30 -10:00
J. Nick Koston 3447b68136 [ci] Compile an ESP8266 config with the native toolchain on Windows (#20467) 2026-10-10 08:00:40 -10:00
tomaszduda23 9f4a7cf5cc [nrf52] fix build error on 3.4.0 with pch ON (#20474) 2026-10-10 07:49:35 -10:00
J. Nick Koston 4ba0e1bfa2 [core] Fix the native ESP8266 and host builds on Windows without ccache (#20468) 2026-10-10 07:39:49 -10:00
jabla f4fbc3215d [espidf] Clean the build when the IDF target changes (#20473) 2026-10-10 07:28:40 -10:00
721228c1d2 [teleinfo] Add predefined properties for main tags of TIC standard and historical (#9167)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-10 08:31:06 -04:00
Clyde Stubbs f9a81869ef [it8951] Hold display enable pins off during deep-sleep (#20471) 2026-10-10 17:43:38 +11:00
J. Nick Koston d3e7bb119c [core] Deprecate the named status_momentary_warning/error overloads (#20369) 2026-10-10 02:39:21 +00:00
J. Nick Koston dd45f8a6f8 [usb_host] Warn instead of failing when a device duplicates a usb_uart entry (#20373) 2026-10-09 16:38:05 -10:00
ba7d3bd770 [gpio][esp32][uart] Add option to hold gpio during (deep)sleep and reboot (3/3) (#18964)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-10 02:37:46 +00:00
J. Nick Koston dc553aae55 [core] Use status_momentary_warning/error without a name in components (#20363) 2026-10-09 22:27:19 -04:00
J. Nick Koston f7b9646705 [improv_ble] Build the state triggers with build_callback_automation (#20444) 2026-10-09 16:06:14 -10:00
J. Nick Koston 0ae24f7536 [core] Catch heap allocating helper calls with explicit template arguments (#20391) 2026-10-10 02:00:41 +00:00
J. Nick Kostonandpre-commit-ci-lite[bot] 41cc96bbb8 [light] Keep the effect list in a flash table (#20204)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-10-09 15:55:09 -10:00
Zhaoqi Xu 6b03d68fac [heatpumpir] Fix fan speeds for mitsubishi_msc protocol (#20460) 2026-10-09 21:53:38 -04:00
J. Nick Koston 640d677913 [st7735][st7789v][waveshare_epaper] Declare log tags with ESPHOME_LOG_TAG (#20295) 2026-10-09 15:51:55 -10:00
J. Nick Koston 8567e69d46 [core] Add status_momentary_warning/error overloads without a name (#20362) 2026-10-09 15:51:38 -10:00
J. Nick Koston 4d187a2bb7 [valve] Build on_open and on_closed with build_callback_automation (#20442) 2026-10-09 15:43:22 -10:00
J. Nick Koston 073394b1eb [core] Let a trigger filter name its parent with {parent} (#20448) 2026-10-09 15:43:22 -10:00
J. Nick Koston 02bfa07819 [opentherm] Build before_send and before_process_response with build_callback_automation (#20450) 2026-10-09 15:42:55 -10:00
J. Nick Koston fdc163a30d [tuya] Build on_datapoint_update with build_trigger_callback (#20453) 2026-10-09 15:42:43 -10:00
J. Nick Koston bc44e8b550 [display] Test on_page_change with each from and to combination (#20457) 2026-10-09 21:40:40 -04:00
J. Nick Koston e894397503 [datetime] Build on_value with build_callback_automation (#20445) 2026-10-09 15:39:25 -10:00
J. Nick Koston f1a23a02c2 [time] Build on_time_sync with build_callback_automation (#20441) 2026-10-09 15:39:06 -10:00
J. Nick Koston 95dc9a749b [display_menu_base] Build the menu item triggers with build_parent_callback_automation (#20443) 2026-10-09 15:39:00 -10:00
J. Nick Koston 59963dd246 [espidf] Declare the map byproduct only when IDF has not (#20462) 2026-10-09 15:38:16 -10:00
J. Nick Koston 79fd5edc7f [core] Bind InternalGPIOPin to the platform pin class instead of a virtual base (#20455) 2026-10-09 15:37:10 -10:00
J. Nick Koston 5d2433f69a [graphical_display_menu] Build on_redraw with build_callback_automation (#20452) 2026-10-09 15:36:57 -10:00
J. Nick Koston 1b49e79bf8 [microphone] Build on_data with build_callback_automation (#20451) 2026-10-09 15:35:38 -10:00
Bascht74 4e4056d790 [uart] Share uart_tcp's write pacing and uart_id matcher (#20464) 2026-10-09 15:11:48 -10:00
Ali Alaeiandtomaszduda23 e5f5ca07c4 [zephyr_mcumgr] Enable MCUMGR_GRP_OS for UART transport, not just BLE (#18319)
Co-authored-by: tomaszduda23 <tomaszduda23@gmail.com>
2026-10-09 12:50:50 -10:00
a4a09043ec [deep_sleep][gpio][core][power_supply] Prepare for gpio hold (2/3) (#18966)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-09 11:32:24 -10:00
esphome[bot] a613073ee3 Bump bundled esphome-device-builder to 1.23.0 (#20446) 2026-10-09 10:06:10 -10:00
PolarGooseandJ. Nick Koston 8416a29862 [dlms_meter] Add support for "Landis+Gyr E450 HDLC Cyprus". Adapt the code for new API of dlms_parser (#16942)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-09 10:03:38 -10:00
J. Nick Koston 9ab0fa9669 [dlms_meter] Remove deprecated predefined sensor keys (#20386) 2026-10-09 09:36:28 -10:00
J. Nick Koston 05aeb2f094 [http_request] Test shared, lambda and trigger argument request headers (#20224) 2026-10-09 09:25:36 -10:00
J. Nick Koston c859c347cb [core] Pass a single condition to actions directly instead of wrapping it in a group (#19223) 2026-10-09 09:24:32 -10:00
J. Nick Koston 7ab24bf850 [nextion] Remove deprecated dump_device_info option (#20388) 2026-10-09 09:23:46 -10:00
J. Nick Koston cfbdd58e4a [lvgl] Remove deprecated update_on_release option (#20387) 2026-10-09 09:23:28 -10:00
8084916e30 [remote_base] Add Onkyo RI Wired Serial Support (#18595)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-09 19:23:10 +00:00
J. Nick Koston 9d02ab3734 [remote_base] Keep constant IR codes in flash (#20083) 2026-10-09 09:22:32 -10:00
J. Nick Koston 3442bb6624 [core] Remove deprecated esphome::RingBuffer alias (#20390) 2026-10-09 09:21:29 -10:00
J. Nick Koston 23d13d3a6e [core] Stop re-exporting alloc_helpers.h from helpers.h (#20389) 2026-10-09 09:21:16 -10:00
J. Nick Kostonandpre-commit-ci-lite[bot] 5cd7bf5993 [wireguard] Keep allowed IPs in a shared flash table (#20087)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-10-09 09:20:46 -10:00
J. Nick Koston f7b80e6d44 [dlms_meter] Remove deprecated provider option (#20385) 2026-10-09 09:20:17 -10:00
J. Nick Koston 588fde91b2 [template] Keep alarm codes in a shared flash table (#20084) 2026-10-09 09:19:35 -10:00
J. Nick Koston 94e6bd90d8 [sensor] Merge the radar timeout and throttle filters into one (#20118) 2026-10-09 09:19:22 -10:00
J. Nick Koston 456e3d8979 [ci] Keep bus group partners on the same runner (#20431) 2026-10-09 09:10:18 -10:00
J. Nick Koston 76ba7f0c88 [espidf] Cache the discovered component list once per target (#20432) 2026-10-09 09:10:06 -10:00
J. Nick Koston 0652e6c3fb [esp32][libretiny] Skip registering static destructors that never run (#20440) 2026-10-09 19:02:22 +00:00
luar123andJ. Nick Koston dbad1d4629 [core][zigbee] Wake main loop on defer/set_timeout from background task (#20356)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-10-09 18:58:33 +00:00
J. Nick Koston dd8315059c [core] Document the trigger callback helpers in AGENTS.md (#20435) 2026-10-09 08:56:50 -10:00
J. Nick Koston 822bbb85dc [core] Build trigger callbacks as stateless lambdas (#19606) 2026-10-09 08:50:32 -10:00
J. Nick Koston daea315b13 [nrf52] Fake a larger flash in testing mode (#20439) 2026-10-09 08:36:42 -10:00
J. Nick Koston 52b274c5e8 [improv_serial] Collapse duplicate CI test variants (#20405) 2026-10-09 07:34:34 -10:00
J. Nick Koston b5716a70c2 [ethernet] Remove deprecated clk_mode option (#20384) 2026-10-09 17:26:47 +00:00
J. Nick Koston 3eb8155ed2 [logger] Collapse duplicate CI test variants (#20402) 2026-10-09 07:22:48 -10:00
J. Nick Koston 11368a0b16 [adc] Collapse duplicate CI test variants (#20400) 2026-10-09 07:22:20 -10:00
J. Nick Koston cf0e64f84c [improv_serial] Use a numeric scheduler id for the Wi-Fi connect timeout (#20360) 2026-10-09 07:21:53 -10:00
J. Nick Koston d98ddeeb56 [mqtt] Use a numeric scheduler id for the deferred state publish (#20359) 2026-10-09 07:21:29 -10:00
J. Nick Koston 0701329ea7 [wifi] Use numeric scheduler ids instead of string names (#20357) 2026-10-09 07:20:59 -10:00
J. Nick Koston f5ce33a8de [esp32_ble_client] Track conn_id per attempt and stop logging an error on a failed open (#20417) 2026-10-09 07:16:17 -10:00
J. Nick Koston f89a603582 [esp8266] Keep switch lookup tables out of RAM (#20398) 2026-10-09 07:15:59 -10:00
J. Nick Koston 279f68c432 [tuya] Use numeric scheduler ids instead of string names (#20365) 2026-10-09 07:15:33 -10:00
J. Nick Koston b6ee8f8035 [power_supply] Use a numeric scheduler id for the power off timeout (#20367) 2026-10-09 07:15:14 -10:00
J. Nick Koston cea872be0b [modbus] Use a numeric scheduler id for the deferred server reply (#20368) 2026-10-09 07:14:59 -10:00
J. Nick Koston 558de5ebee [core] Use numeric scheduler ids instead of string names (5/5) (#20378) 2026-10-09 07:14:39 -10:00
J. Nick Koston aba0c5d7ee [dallas_temp] Use a numeric scheduler id and drop the cached address string (#20361) 2026-10-09 07:14:19 -10:00
J. Nick Koston 4c17ee7393 [core] Use numeric scheduler ids instead of string names (2/5) (#20375) 2026-10-09 07:13:46 -10:00
J. Nick Koston 24237a07c9 [ci] Refit component batch estimates to a full run (#20401) 2026-10-09 07:12:35 -10:00
J. Nick Koston d4d5ee87cb [ir_rf_proxy] Collapse duplicate CI test variants (#20399) 2026-10-09 07:12:14 -10:00
J. Nick Koston 56e6e332cb [ci] Skip the static analyzer on the generated protobuf dump file (#20414) 2026-10-09 07:11:45 -10:00
J. Nick Koston 7ac679aa04 [esp8266] Keep ArduinoJson's float tables in flash (Arduino core 3.1.2-esphome.3) (#20416) 2026-10-09 07:11:30 -10:00
J. Nick Koston 8a09e49e31 [ci] Skip the memory impact target build when the base test is missing (#20410) 2026-10-09 07:11:18 -10:00
J. Nick Koston 3485297e6e [core] Use numeric scheduler ids instead of string names (4/5) (#20377) 2026-10-09 07:11:01 -10:00
J. Nick Koston 541562451e [sprinkler] Key timers by index and keep them in a fixed array (#20379) 2026-10-09 07:10:48 -10:00
J. Nick Koston 824834f847 [core] Use numeric scheduler ids instead of string names (3/5) (#20376) 2026-10-09 07:10:19 -10:00
J. Nick Koston bc6fecfadb [core] Use numeric scheduler ids instead of string names (1/5) (#20374) 2026-10-09 07:10:04 -10:00
J. Nick Koston a2434f9aa3 [core] Document numeric scheduler ids in AGENTS.md (#20382) 2026-10-09 07:09:33 -10:00
J. Nick Koston 466081acc1 [core] Use numeric scheduler ids instead of string names (ESP32 2/2) (#20381) 2026-10-09 07:09:04 -10:00
J. Nick Koston d9b9ce2909 [core] Use numeric scheduler ids instead of string names (ESP32 1/2) (#20380) 2026-10-09 07:07:56 -10:00
J. Nick Koston a0f0949078 [mdns] Collapse duplicate CI test variants (#20403) 2026-10-09 06:42:00 -10:00
J. Nick Koston 6f4d0e9488 [debug] Collapse duplicate CI test variants (#20404) 2026-10-09 06:32:33 -10:00
J. Nick Koston 65e8345ab8 [ota] Collapse duplicate CI test variants (#20407) 2026-10-09 06:32:07 -10:00
J. Nick Koston de5c2cda09 [mcp23xxx_base] Keep the interrupt handler in IRAM on ESP8266 (#20409) 2026-10-09 06:31:42 -10:00
J. Nick Koston 9adb7e92e1 [ethernet] Collapse duplicate CI test variants (#20406) 2026-10-09 06:31:26 -10:00
J. Nick Koston c7d6a2e898 [sendspin] Fix duplicate definitions after the beta merge (#20424) 2026-10-08 22:13:45 -10:00
Jesse Hills 6188778538 Merge branch 'beta' into dev 2026-10-09 15:42:24 +13:00
Jesse Hills 2affc04e62 Merge pull request #20421 from esphome/bump-2026.10.0b2
2026.10.0b2
2026-10-09 15:42:04 +13:00
Clyde Stubbs 6513b176f4 [spd2010] Fix DOMAIN (#20422) 2026-10-09 02:23:56 +00:00
646f386f12 [spd2010] Add touchscreen platform (#19057)
Co-authored-by: clydebarrow <2366188+clydebarrow@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-09 12:50:09 +11:00
J. Nick Koston 6a463c801e [ci] Stop the toolchain tests from installing ESP-IDF (#20413) 2026-10-08 21:42:45 -04:00
Jesse Hills d9795399ad [core] Use component DOMAIN constants instead of name literals (#20419) 2026-10-09 13:50:29 +13:00
Jesse Hills a648822027 Bump version to 2026.10.0b2 2026-10-09 13:45:36 +13:00
J. Nick Koston c6244fa4b9 [esp32] Record the PC for cache error and interrupt watchdog crashes (#20415) 2026-10-09 13:45:35 +13:00
J. Nick Koston f6746b4977 [api] Fix C++ unit test link failure when api is only a dependency (#20418) 2026-10-09 13:45:35 +13:00
J. Nick Koston f1f1a77151 [ci] Deal clang-tidy files across five shards (#20411) 2026-10-09 13:45:35 +13:00
J. Nick Koston ba1af627fd [ci] Pin the shared I2C bus in test configs that left it implicit (#20412) 2026-10-09 13:45:35 +13:00
J. Nick Koston b82dfd7e64 [mdns] Bump espressif/mdns to 1.14.0 (#20383) 2026-10-09 13:45:35 +13:00
Kevin AhrendtandJ. Nick Koston 7de2bcb584 [api][sendspin] Add a request for the Sendspin pairing token (#20355)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-09 13:45:35 +13:00
esphome[bot] d5d11e1077 Bump aioesphomeapi from 46.8.0 to 46.9.0 (#20358) 2026-10-09 13:45:34 +13:00
Kevin Ahrendt ab66b20944 [sendspin] Add an unpaired access switch (#20350) 2026-10-09 13:45:34 +13:00
Kevin Ahrendt baf1682635 [sendspin] Add the dynamic pairing code (#20349) 2026-10-09 13:45:34 +13:00
Kevin Ahrendt eea11834f8 [sendspin] Add the static pairing code, pairing window and unpaired access option (#20348) 2026-10-09 13:45:34 +13:00
Kevin Ahrendt 1d86cc39e9 [sendspin] Update to sendspin-cpp v0.9.3 (#20347) 2026-10-09 13:45:34 +13:00
J. Nick Koston 686252f151 [esp8266] Fix Arduino core regressions that keep data in RAM and link the unused unwinder (#20337) 2026-10-09 13:45:23 +13:00
J. Nick Koston f5c15f66a5 [esp8266] Force-include the throw stubs into the core and libraries on the native toolchain (#20336) 2026-10-09 13:45:23 +13:00
J. Nick Koston 9d6b50320d [noise] Bump noise-c to 0.1.31 and libsodium to 1.10021.12 (#20345) 2026-10-09 13:45:23 +13:00
Jesse HillsandJ. Nick Koston b903c54be4 [combination] Use cv.rename_key for the deprecated coeffecient key (#20159)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-10-09 13:45:23 +13:00
esphome[bot] 63b4a59588 Bump bundled esphome-device-builder to 1.22.0 (#20334) 2026-10-09 13:45:23 +13:00
Jesse Hills adc6255fd4 [core] Let cv.rename_key pass non-dict input through (#20331) 2026-10-09 13:45:23 +13:00
J. Nick Koston b28e7a1357 [esp8266] Normalize misspelled boards and fall back to PlatformIO for unknown ones (#20322) 2026-10-09 13:45:23 +13:00
J. Nick Koston e7ba9a5726 [api] Add a progmem option for bytes fields kept in flash (#20318) 2026-10-09 13:45:22 +13:00
J. Nick Koston 2586e78127 [core] Warn on every run that Intel Mac support ends by 2027.6.0 (#20303) 2026-10-09 13:45:22 +13:00
J. Nick Koston 23f6dedb42 Pin smp to 4.1.0 on Intel macOS so cbor2 installs from a wheel (#20302) 2026-10-09 13:45:22 +13:00
David Buezas 10f30ddb59 [cc1101] Keep the remote_receiver interrupt on a shared GDO0 pin in begin_tx (#20296) 2026-10-09 13:45:22 +13:00
Keith BurzinskiandClaude Opus 5.5 96a66f3ddd [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-09 13:45:22 +13:00
esphome[bot] 5bf8ceac66 Bump aioesphomeapi from 46.7.0 to 46.8.0 (#20321) 2026-10-09 13:45:22 +13:00
mrtoy-me 4d42352ec5 [tas58xx] Improve TAS5805M audio performance and use GPIO1 for TAS5825M faults (#20319) 2026-10-09 13:45:22 +13:00
esphome[bot] 479232b6c0 Bump aioesphomeapi from 46.6.0 to 46.7.0 (#20317) 2026-10-09 13:45:22 +13:00
J. Nick Koston 0d596a20f8 [esp32] Record the PC for cache error and interrupt watchdog crashes (#20415) 2026-10-09 00:25:11 +00:00
Jesse Hills 5ca272b262 [mipi_dsi] Make config tests pass in isolation (#20420) 2026-10-09 13:20:52 +13:00
J. Nick Koston ff0d4347d6 [api] Fix C++ unit test link failure when api is only a dependency (#20418) 2026-10-08 14:05:18 -10:00
Jesse Hills e03db60a52 [core] Add a DOMAIN constant to every component (#20395) 2026-10-08 14:04:26 -10:00
J. Nick Koston b4bd95b2ac [ci] Deal clang-tidy files across five shards (#20411) 2026-10-08 13:48:03 -10:00
J. Nick Koston fe21e3ad16 [ci] Pin the shared I2C bus in test configs that left it implicit (#20412) 2026-10-08 13:14:00 -10:00
Jesse Hills 85108bfd84 [qspi_amoled] Remove legacy renamed component stub (#20396) 2026-10-08 12:58:20 -10:00
J. Nick Koston 92bfd0ea7e [mdns] Bump espressif/mdns to 1.14.0 (#20383) 2026-10-08 21:18:21 +00: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
esphome[bot] 3e3822e554 Bump bundled esphome-device-builder to 1.14.6 (#19072) 2026-09-10 16:22:30 +00:00
Kevin AhrendtandJ. Nick Koston 7564f5ff1a [sendspin] Add manufacturer, model, and firmware version options (#18792)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-09-10 11:06:58 -05:00
matt123p a807a8f945 [es7210] Fix 4 channel microphone support (#19034) 2026-09-10 11:11:43 -04:00
Keith Burzinskiandpuddly 280fac11e6 [serial_proxy] Add tap interface and port mode (#18955)
Co-authored-by: puddly <32534428+puddly@users.noreply.github.com>
2026-09-10 10:03:33 -05:00
esphome[bot] 9924148302 Bump aioesphomeapi from 46.3.0 to 46.4.0 (#19071) 2026-09-10 14:59:23 +00:00
luar123 66f829c760 [zigbee] wake loop on defer/set_timeout (#19050) 2026-09-10 08:08:07 -05:00
luar123 a88ec7d90b [logger] Flush uart before sleep in idf 6 (#18975) 2026-09-10 13:07:09 +00:00
luar123 54706e869c [deep_sleep] disable loop (#18962) 2026-09-10 07:18:15 -05:00
05f7d5e4f1 [mlx90614] pec validation (#6689)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2026-09-10 16:14:43 +12:00
Robin ThoniandJesse Hills ddbd89dd2a [network] Improve network::is_connected() to better handle multiple interfaces (#18999)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2026-09-10 04:06:44 +00:00
Jesse Hills 5ed59af920 [template] Surface value metadata on template entity forms (#17545) 2026-09-10 14:11:13 +12:00
Jesse Hills 4868b498cf [ci] Ask stale PR authors to merge dev instead of rebasing (#19064) 2026-09-10 14:09:58 +12:00
Jesse Hills c66fa81208 [core] Consolidate setup scripts into a cross-platform setup.py (#18856) 2026-09-10 14:09:49 +12:00
esphome[bot] 58ca345684 [ci] Refresh integration test durations (#19049) 2026-09-09 13:42:09 -04:00
Remco van Essen fd598057ef [sendspin] Fix codec enum codegen when codecs is not set (#19055) 2026-09-09 08:22:23 -04:00
Jesse Hills ef7279341f Merge branch 'beta' into dev 2026-09-09 12:18:55 +12:00
Jesse Hills 69f2a7d556 Merge pull request #19051 from esphome/bump-2026.9.0b3
2026.9.0b3
2026-09-09 12:18:36 +12:00
Jesse Hills 008677298a Bump version to 2026.9.0b3 2026-09-09 09:54:44 +12:00
Kevin AhrendtandJesse Hills d2bc056f0a [sendspin] Add codec preference list to the media source (#19047)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2026-09-09 09:54:42 +12:00
J. Nick Koston 42fffd16fe [esphome][core] Give a lost OTA chunk ack time to be retransmitted (#19041) 2026-09-09 09:54:42 +12:00
J. Nick Koston 9c16aba6f7 [noise] Bump noise-c to 0.1.26 and libsodium to 1.10021.8 (#19030) 2026-09-09 09:54:42 +12:00
Kevin Ahrendt ac79173f4a [i2s_audio] Fix spurious driver failure (#19045) 2026-09-09 09:54:42 +12:00
Kevin Ahrendt 0a1e2acbcb [audio][i2s_audio][micro_wake_word][microphone][mixer][resampler][speaker] Replace use_count() checks with lock and null test (#19046) 2026-09-09 09:54:42 +12:00
Kevin AhrendtandCopilot Autofix powered by AI 9b6facb20d [core] Fix use-after-free when deleting a running StaticTask (#19048)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-09-09 09:54:42 +12:00
esphome[bot] f89b9e704c Bump bundled esphome-device-builder to 1.14.5 (#19040) 2026-09-09 09:54:42 +12:00
Johnandpre-commit-ci-lite[bot] f8b2e53609 [atm90e32] Verify offset calibration writes (#18701)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-09-09 09:54:42 +12:00
raykholo 7660dd7fa7 [anova] Re-assert temperature unit on every poll cycle (#17141) 2026-09-09 09:54:42 +12:00
c3ce07755f [rf_bridge] Fix bucket sniffing with Portisch firmware (#17683)
Co-authored-by: Bryan Li <bryanli@Bryans-MacBook-Pro.local>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-09 09:54:42 +12:00
Gytis f8a4cfa945 [lvgl] Add missing label dependency to qrcode, keyboard and tabview (#18387) 2026-09-09 09:54:42 +12:00
J. Nick Koston 866ddb6e57 [core] Skip PlatformIO's private-package authorization probe (#18823) 2026-09-09 09:54:42 +12:00
Jonathan Swoboda ca864c22b4 [tuya] Build without a network component (#18948) 2026-09-09 09:54:42 +12:00
Jesse Hills c9729244af [udp] Use cv.invalid for relocated packet_transport options (#19032) 2026-09-09 09:54:42 +12:00
Davide D M 442e4a1ec2 [debug] Check reboot source pref on ESP_RST_WDT and guard against empty source (#17537) 2026-09-09 09:54:41 +12:00
Pieter ViljoenandJesse Hills 199acdf522 [ble_client] Report Established from nodes that never read services (#17920)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2026-09-09 09:54:41 +12:00
Samuel SiebandSamuel Sieb 9340863652 [dallas_temp] filter 85 temp from sensor reset (#17877)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2026-09-09 09:54:41 +12:00
AndreKR d5cff6e9df [logger] Fix garbled stack traces (#17939) 2026-09-09 09:54:41 +12:00
Ryan Ronnander e7f45a0d31 [mqtt] Restore brightness flag in light discovery (#18950) 2026-09-09 09:54:41 +12:00
mipa87andClaude 628ebe23ec [audio] Do not treat MP3_STREAM_INFO_CHANGED as a fatal decoder error (#19028)
Co-authored-by: Claude <noreply@anthropic.com>
2026-09-09 09:54:41 +12:00
823d79c948 [i2s_audio] Keep a start request that arrives while the speaker task stops (#19027)
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-09-09 09:54:41 +12:00
Kevin AhrendtandJesse Hills b947094f45 [sendspin] Add codec preference list to the media source (#19047)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2026-09-09 09:31:18 +12:00
J. Nick Koston 6c5ab89d5f [esphome][core] Give a lost OTA chunk ack time to be retransmitted (#19041) 2026-09-09 09:08:16 +12:00
J. Nick Koston 8f511a365a [noise] Bump noise-c to 0.1.26 and libsodium to 1.10021.8 (#19030) 2026-09-09 09:07:06 +12:00
Kevin Ahrendt 006f31af93 [i2s_audio] Fix spurious driver failure (#19045) 2026-09-09 09:05:02 +12:00
Kevin Ahrendt 5bb112f407 [audio][i2s_audio][micro_wake_word][microphone][mixer][resampler][speaker] Replace use_count() checks with lock and null test (#19046) 2026-09-09 09:04:34 +12:00
dependabot[bot] 4ab9298ab3 Bump esptool from 5.3.1 to 5.4.0 (#19023) 2026-09-08 22:11:36 +02:00
Kevin AhrendtandCopilot Autofix powered by AI 3926612281 [core] Fix use-after-free when deleting a running StaticTask (#19048)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-09-09 07:46:17 +12:00
J. Nick Koston 1ce0bed3f6 [core] Share compiled binaries across modbus integration tests (#18945) 2026-09-08 18:10:45 +02:00
esphome[bot] f91486305f Bump bundled esphome-device-builder to 1.14.5 (#19040) 2026-09-08 15:21:48 +02:00
Johnandpre-commit-ci-lite[bot] f191d5e0c3 [atm90e32] Verify offset calibration writes (#18701)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-09-08 20:02:49 +12:00
Jesse Hills 227ca90aad [core] Restore the shared git hooks after post-checkout runs script/setup (#19036) 2026-09-08 20:00:08 +12:00
Jesse Hills 1700a40b7c [core] Restore the shared git hooks after post-checkout runs script/setup (#19036) 2026-09-08 19:59:55 +12:00
raykholo 28588310e7 [anova] Re-assert temperature unit on every poll cycle (#17141) 2026-09-08 18:57:33 +12:00
10a9baff74 [rf_bridge] Fix bucket sniffing with Portisch firmware (#17683)
Co-authored-by: Bryan Li <bryanli@Bryans-MacBook-Pro.local>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-09-08 18:36:42 +12:00
Gytis a23f7bb569 [lvgl] Add missing label dependency to qrcode, keyboard and tabview (#18387) 2026-09-08 18:31:30 +12:00
J. Nick Koston 53075e4139 [core] Skip PlatformIO's private-package authorization probe (#18823) 2026-09-08 16:42:30 +12:00
Jonathan Swoboda 5722ccba37 [tuya] Build without a network component (#18948) 2026-09-08 16:36:04 +12:00
Jesse Hills 94e5c3839d [udp] Use cv.invalid for relocated packet_transport options (#19032) 2026-09-08 16:17:22 +12:00
Davide D M 574762f078 [debug] Check reboot source pref on ESP_RST_WDT and guard against empty source (#17537) 2026-09-08 13:59:05 +12:00
Pieter ViljoenandJesse Hills d34ffaf392 [ble_client] Report Established from nodes that never read services (#17920)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2026-09-08 01:57:50 +00:00
Samuel SiebandSamuel Sieb e6aa575f2e [dallas_temp] filter 85 temp from sensor reset (#17877)
Co-authored-by: Samuel Sieb <samuel@sieb.net>
2026-09-08 13:27:49 +12:00
AndreKR 639ce609bf [logger] Fix garbled stack traces (#17939) 2026-09-08 13:13:51 +12:00
Ryan Ronnander 62eafc477d [mqtt] Restore brightness flag in light discovery (#18950) 2026-09-08 13:09:02 +12:00
mipa87andClaude 56c3361b9a [audio] Do not treat MP3_STREAM_INFO_CHANGED as a fatal decoder error (#19028)
Co-authored-by: Claude <noreply@anthropic.com>
2026-09-08 12:13:03 +12:00
50ca381198 [i2s_audio] Keep a start request that arrives while the speaker task stops (#19027)
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-09-08 12:10:55 +12:00
dependabot[bot] 89a56298c2 Bump prek from 0.5.1 to 0.5.2 (#19021)
Signed-off-by: dependabot[bot] <support@github.com>
2026-09-08 00:00:28 +00:00
390742cf9b Bump ruff from 0.16.5 to 0.16.6 (#19022)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Signed-off-by: dependabot[bot] <support@github.com>
2026-09-07 23:47:46 +00:00
Jesse Hillsandpre-commit-ci-lite[bot] 5e37872da2 [ci] Sync pre-commit revs and prek version from requirements files (#19026)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-09-08 10:32:18 +12:00
Jesse Hills d34d3994e1 Merge branch 'beta' into dev 2026-09-07 12:50:44 +12:00
Jesse Hills 9a877a067c Merge pull request #19007 from esphome/bump-2026.9.0b2
2026.9.0b2
2026-09-07 12:50:25 +12:00
Jesse Hills 9ba4477ada Bump version to 2026.9.0b2 2026-09-07 10:46:22 +12:00
Jesse Hills 8434dc5474 [esp32_hosted] Add ESP-NOW-over-hosted shim for the ESP32-P4 (#17712) 2026-09-07 10:46:21 +12:00
J. Nick Koston e0e85db822 [core] Show the other downloader's progress while a prefetch job waits on its lock (#18983) 2026-09-07 10:46:21 +12:00
J. Nick Koston 5d2ddc658c [mdns] Guard LEAmDNS main loop calls against lwIP re-entrancy on ESP8266 (#18990) 2026-09-07 10:46:21 +12:00
J. Nick Koston c1aa41f276 [noise] Bump noise-c to 0.1.24 and libsodium to 1.10021.6 (#18989) 2026-09-07 10:46:21 +12:00
esphome[bot] 96b1a03ea4 Bump bundled esphome-device-builder to 1.14.4 (#19006) 2026-09-07 10:46:21 +12:00
J. Nick Koston 95ab3fb4f2 [ota] Offer encryption with the api key so enabling it works over OTA (#18979) 2026-09-07 10:46:21 +12:00
Ricardo Sanz 18220e0b39 [climate][template] New template climate component (#14455) 2026-09-07 10:46:21 +12:00
esphome[bot] 011497d6ee Bump bundled esphome-device-builder to 1.14.3 (#18996) 2026-09-07 10:46:20 +12:00
esphome[bot] 7089dae3b6 Bump bundled esphome-device-builder to 1.14.2 (#18988) 2026-09-07 10:46:20 +12:00
esphome[bot] 745eb30109 Bump bundled esphome-device-builder to 1.14.1 (#18981) 2026-09-07 10:46:20 +12:00
Keith Burzinski e36445fa5f [usb_uart] Keep the comm interface number valid when its claim fails (#18968) 2026-09-07 10:46:20 +12:00
Jesse Hills cb0c2bdaca [esp32_ble] Reference count BLE advertising (#18943) 2026-09-07 10:46:20 +12:00
J. Nick Kostonandpre-commit-ci-lite[bot] e47247486b [esp8266] Drop Arduino framework versions before 3.0.0 (#18917) to
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-09-07 10:46:20 +12:00
esphome[bot]andJonathan Swoboda 657116a213 Bump bundled esphome-device-builder to 1.14.0 (#18960)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-09-07 10:46:20 +12:00
Keith Burzinski 3321566cc0 [remote_transmitter] Fix BK7231N build by limiting the PWM path to BK7238 (#18958) 2026-09-07 10:46:20 +12:00
Jesse Hills d58b37faa1 [esp32_hosted] Add ESP-NOW-over-hosted shim for the ESP32-P4 (#17712) 2026-09-07 10:29:20 +12:00
J. Nick Koston 8966567be0 [core] Show the other downloader's progress while a prefetch job waits on its lock (#18983) 2026-09-07 10:28:31 +12:00
J. Nick Koston 20c7dcb1dd [mdns] Guard LEAmDNS main loop calls against lwIP re-entrancy on ESP8266 (#18990) 2026-09-07 10:26:44 +12:00
J. Nick Koston 688af60cbf [noise] Bump noise-c to 0.1.24 and libsodium to 1.10021.6 (#18989) 2026-09-07 10:12:52 +12:00
esphome[bot] 9c00f13606 Bump bundled esphome-device-builder to 1.14.4 (#19006) 2026-09-06 22:05:16 +00:00
J. Nick Koston 833dd0e812 [ota] Offer encryption with the api key so enabling it works over OTA (#18979) 2026-09-06 23:59:40 +02:00
Ricardo Sanz 8e1044e8ea [climate][template] New template climate component (#14455) 2026-09-06 14:03:07 -07:00
esphome[bot] e5200db6fd Bump bundled esphome-device-builder to 1.14.3 (#18996) 2026-09-06 09:28:02 +02:00
e3dd2f44a4 [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-05 21:08:21 +00:00
esphome[bot] 3ef7460fca Bump bundled esphome-device-builder to 1.14.2 (#18988) 2026-09-05 15:25:58 +00:00
ae187f81f2 [wifi] Allow a forced roam check (#17349)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-09-05 21:44:37 +10:00
esphome[bot] 84f78831f9 Bump bundled esphome-device-builder to 1.14.1 (#18981) 2026-09-05 13:07:15 +02:00
Keith Burzinski 13dbbcaa32 [usb_uart] Keep the comm interface number valid when its claim fails (#18968) 2026-09-05 13:00:25 +02:00
Clyde Stubbs b66822d9bd [ai] Advice to agents to limit verbiage (#18980) 2026-09-05 12:21:47 +02:00
dependabot[bot] d1829c495d Bump prek from 0.5.0 to 0.5.1 (#18977)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-04 19:05:36 -04:00
dependabot[bot] ce87bf9b17 Bump platformdirs from 4.11.5 to 4.11.7 (#18976)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-04 19:05:26 -04:00
Jesse Hills 51ea97deff [esp32_ble] Reference count BLE advertising (#18943) 2026-09-05 08:38:55 +12:00
dependabot[bot] ab800dc09d Bump filelock from 3.32.4 to 3.32.5 (#18963)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-03 17:19:29 -04:00
J. Nick Kostonandpre-commit-ci-lite[bot] f65ab5629e [esp8266] Drop Arduino framework versions before 3.0.0 (#18917) to
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-09-03 15:16:36 -04:00
esphome[bot]andJonathan Swoboda b84532d254 Bump bundled esphome-device-builder to 1.14.0 (#18960)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-09-03 12:15:06 +00:00
Keith Burzinski 6b11636491 [remote_transmitter] Fix BK7231N build by limiting the PWM path to BK7238 (#18958) 2026-09-03 08:12:36 -04:00
Jesse Hills 2bb98f2d64 Merge branch 'beta' into dev 2026-09-03 14:07:41 +12:00
Jesse Hills 25c29fd9e0 Merge pull request #18957 from esphome/bump-2026.9.0b1
2026.9.0b1
2026-09-03 14:07:19 +12:00
Jesse Hills f3c786c784 Bump version to 2026.10.0-dev 2026-09-03 13:07:21 +12:00
dependabot[bot] 2250430999 Bump zeroconf from 0.151.2 to 0.151.3 (#18951)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-02 20:56:51 -04:00
dependabot[bot] d1068d582f Bump ninja from 1.13.0 to 1.13.2 (#18952)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-02 20:56:42 -04:00
Jesse Hills 567a981078 Bump version to 2026.9.0b1 2026-09-03 12:16:25 +12:00
Jesse Hills 81ecb87253 Bump version to 2026.10.0-dev 2026-09-03 12:11:56 +12:00
f0e2eb96bd [snapshot][SDL] Display headless mode and snapshots (#17917)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2026-09-03 09:39:32 +12:00
Keith Burzinskiandpuddly 567f7f9196 [serial_proxy] Skip no-op reconfigure requests (#18953)
Co-authored-by: puddly <32534428+puddly@users.noreply.github.com>
2026-09-02 15:01:23 -05:00
Jesse Hills ecbde8ddf4 [uart] Migrate check_uart_settings to final validation (#18940) 2026-09-03 07:25:08 +12:00
esphome[bot] da16c01351 [ci] Refresh integration test durations (#18944) 2026-09-02 09:32:47 +00:00
Jesse Hills 6099ac7b53 [core] Document C++ conventions in AGENTS.md that reviews keep catching (#18941) 2026-09-02 11:17:22 +02:00
dependabot[bot] b8480b8424 Bump pylint from 4.0.7 to 4.0.8 (#18935)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-01 21:36:49 -04:00
dependabot[bot] 8bef5b22e1 Bump zeroconf from 0.150.4 to 0.151.2 (#18934)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-01 21:36:04 -04:00
379e077b5f [ds1603l] New sensor DS1603L V1.0 (#13133)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2026-09-02 10:05:56 +12:00
J. Nick KostonandJesse Hills 3f68930001 [ota] Add Noise encryption to the OTA platform (#18489)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2026-09-02 08:11:57 +12:00
0aff9e1c54 [d01] add D01 pm2.5 sensor support (#17788)
Co-authored-by: Andrej Walilko <awalilko@liquidweb.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2026-09-02 07:33:35 +12:00
ZebbleandClaude f9824ee83f [core] Move CONF_KEYS to the shared component constants (#18933)
Co-authored-by: Claude <noreply@anthropic.com>
2026-09-01 14:23:21 -04:00
68ffd5a773 [safe_mode] Uncover silent error in safe-mode (#18749)
Co-authored-by: Oliver Kleinecke <kleinecke.oliver@googlemail.com>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-09-01 09:12:49 -04:00
fe3788ff47 [esp32][mipi_rgb] Add ESP32-S31 support for execute_from_psram (#18929)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-09-01 08:56:03 -04:00
J. Nick Koston d6758377d1 [core] Clone git libraries in parallel in the library prefetch (#18836) 2026-09-01 14:25:58 +12:00
Jesse Hills 5dbc8ffe4c [epaper_spi] Add UC8179 mono driver and Seeed reTerminal E1001 model (#17568) 2026-09-01 14:17:52 +12:00
J. Nick Koston 0f982f03b2 [core] Prefetch tool-scons by PlatformIO's core spec (#18831) 2026-09-01 11:55:53 +12:00
Bonne Eggleston afb0022dd0 [core] Lint: require braces around single ESP_LOG control-statement bodies (#18727) 2026-09-01 11:53:39 +12:00
dependabot[bot]andJonathan Swoboda 081ef3d30d Bump prek from 0.4.14 to 0.5.0 (#18921)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-08-31 17:46:47 -04:00
Jonathan Swoboda ca44db107c [docker] Fix generate_tags.py formatting (#18928) 2026-08-31 17:33:52 -04:00
dependabot[bot] d437869373 Bump cryptography from 48.0.1 to 50.0.1 (#18922)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-31 17:23:29 -04:00
dependabot[bot] c20b619684 Bump platformdirs from 4.11.4 to 4.11.5 (#18923)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-31 17:20:43 -04:00
dependabot[bot] ebe6c2d049 Bump github/codeql-action/init from 4.37.8 to 4.37.9 (#18927)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-31 17:20:12 -04:00
dependabot[bot] 78beb75b3b Bump github/codeql-action/analyze from 4.37.8 to 4.37.9 (#18926)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-31 17:20:00 -04:00
dependabot[bot] fc9afcb201 Bump ruff from 0.16.4 to 0.16.5 (#18920)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-31 17:17:58 -04:00
dependabot[bot] 7a784d1135 Bump zeroconf from 0.150.0 to 0.150.4 (#18924)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-31 17:16:22 -04:00
Clyde Stubbs 24af3fd834 [lvgl] Fix user_ flags (#18902) 2026-09-01 07:13:36 +10:00
Clyde Stubbs 61e37cfc86 [docker] Use GITHUB_REPOSITORY instead of hardcoded esphome/esphome (#18916) 2026-09-01 07:01:53 +10:00
J. Nick Koston ea1e0c6f68 [core] Keep scheduler dump cancelled marker string in flash on ESP8266 (#18906) 2026-08-31 13:27:53 -05:00
J. Nick Koston 1ba1aebfa1 [ci] Balance integration test buckets by recorded durations (#18895) 2026-08-31 13:20:47 -05:00
J. Nick Koston 5d56517e14 [api] Keep action dropped warning strings in flash on ESP8266 (#18905) 2026-08-31 13:19:49 -05:00
J. Nick Koston ad69718ecc [core] Configure the platform again after prefetching its packages (#18830) 2026-08-31 13:01:13 -05:00
mfishmaandpre-commit-ci-lite[bot] 06d477bea7 [whynter] Fix truncating Fahrenheit temps that should be rounded (#18813)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-08-31 13:47:55 -04:00
Brandon Harvey bbe806f1e6 [sen6x] Add VOC/NOx algorithm tuning (#18779) 2026-08-31 13:45:38 -04:00
Jonathan Swoboda 813c000684 [adc] Add ESP32-S31 support (#18887) 2026-08-31 08:51:33 -04:00
2890afe0e5 [esp32] Fix S31 reserved pins (#18915)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-08-31 12:38:20 +00:00
Clyde Stubbs 5e58b312e2 [mipi_rgb] Add ESP32S31 support (#18914) 2026-08-31 07:20:20 -04:00
Jonathan Swoboda 575a540c9f [esp32] Fix ESP32-S31 GPIO validation (#18904) 2026-08-31 07:19:20 -04:00
J. Nick Koston 404da26a37 [core] Keep conditional log string literals in flash on ESP8266 (#18907) 2026-08-31 21:08:05 +12:00
Jesse Hills be54a5c403 Merge branch 'release' into dev 2026-08-31 14:23:06 +12:00
Jesse Hills 8b5c06add6 Merge pull request #18900 from esphome/bump-2026.8.2
2026.8.2
2026-08-31 14:22:42 +12:00
Keith BurzinskiandJ. Nick Koston 7b97c87390 [improv_serial] Support non-Wi-Fi network interfaces (#17598)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-08-30 20:43:59 -05:00
J. Nick Koston fbe306f00b [homeassistant] Add integration test for binary sensor initial state triggers (#18894) 2026-08-30 19:30:40 -05:00
Jesse Hills 0e4f460013 Bump version to 2026.8.2 2026-08-31 11:25:12 +12:00
esphome[bot] 4e0a5e5a5b Bump bundled esphome-device-builder to 1.13.1 (#18807) 2026-08-31 11:25:10 +12:00
J. Nick Koston 20d4fe1a41 [mdns] Skip MDNS.update() while the ESP8266 radio cannot transmit (#18785) 2026-08-31 11:25:10 +12:00
Clyde Stubbs 8929fc43d8 [mipi_spi] Fix dimensions for jc3636518v2 (#18786) 2026-08-31 11:25:10 +12:00
J. Nick KostonandCopilot Autofix powered by AI c75252d425 [http_request] Default watchdog_timeout from timeout on ESP32 (#18732)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-08-31 11:24:51 +12:00
Clyde StubbsandClaude Sonnet 5 ae460b430c [lvgl] Fix on_value/on_update triggers for LVGL select entities (#18778)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-31 11:22:13 +12:00
esphome[bot] 82ca5365c9 Bump bundled esphome-device-builder to 1.13.0 (#18743) 2026-08-31 11:22:13 +12:00
J. Nick Koston a5d8c45b67 [gpio] Fix one_wire reset busy-waiting with interrupts off when delay wraps (#18733) 2026-08-31 11:22:13 +12:00
J. Nick Koston 67f7532940 [espidf] Always reconfigure after component discovery (#18730) 2026-08-31 11:22:06 +12:00
J. Nick Koston fc977b7b5e [ci] Cache the integration test PlatformIO dir (#18896) 2026-08-30 17:34:32 -05:00
J. Nick Koston 2f1d3f8299 [core] Deduplicate the host program path lookup (#18897) 2026-08-30 17:34:16 -05:00
Bonne Eggleston cb54e57d84 [modbus] Yield the whole-millisecond part of the interframe wait (#18898) 2026-08-30 16:43:28 -05:00
Brandon HarveyandJ. Nick Koston fb65096ea3 [api] Add description and example metadata to user-defined actions (#18881)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-08-30 13:52:51 -05:00
esphome[bot] 0607c228f5 Bump aioesphomeapi from 46.2.1 to 46.3.0 (#18892) 2026-08-30 17:25:29 +00:00
Clyde Stubbs b9eda644cd [lvgl] Add radial and conical gradients (#18818) 2026-08-30 21:21:07 +10:00
Bonne Eggleston a811aa840c [modbus] Speed up the bus with microsecond-accurate timing (#12421) 2026-08-30 03:14:12 -05:00
J. Nick Koston cd28a8a03e [internal_temperature] Re-include esp_phy on the original ESP32 so the PHY blob links (#18884) 2026-08-29 23:25:25 -05:00
J. Nick Koston 6957576867 [esp32] Skip full rebuild on sdkconfig change with the esp-idf toolchain (#18876) 2026-08-29 03:09:12 +00:00
J. Nick Koston 3fea080ed8 [core] Hash downloaded file paths at the default data dir location (#18824) 2026-08-28 21:08:15 -05:00
Clyde Stubbs 06bc3d70c2 [mipi_rgb] Add Elecrow Crowpanel Advance 7 (#18810) 2026-08-28 22:02:27 -04:00
J. Nick Koston 2576a0e340 [ci] Drop picolibc from the cached ESP-IDF toolchains (#18871) 2026-08-29 01:50:24 +00:00
J. Nick Koston ce163b8258 [ci] Cache clang-tidy idedata and key ESP-IDF cache on Python version (#18868) 2026-08-28 20:41:57 -05:00
Bonne Eggleston bcec1d6cb8 [modbus] Decode by register address in the modbus sensor components (#18874) 2026-08-28 18:44:37 -05:00
Bonne Eggleston 1c44cec343 [modbus] Add value_at() and decode registers without the byte round-trip (#18873) 2026-08-28 17:44:00 -05:00
Clyde Stubbs c9848d8fa6 [light] Fix gamma table dead zone collapsing to 0 (#18845) 2026-08-29 08:37:20 +10:00
0dc0cf83de [mk2pvrouter] Add Mk2PVRouter component with sensor support (#8487)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.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: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick+github@koston.org>
2026-08-28 21:02:32 +00:00
dependabot[bot] ed3429d372 Bump resvg-py from 0.4.0 to 0.5.0 (#18864)
Signed-off-by: dependabot[bot] <support@github.com>
2026-08-28 15:43:52 -05:00
Bonne EgglestonandJ. Nick Koston dfac9e1f11 [pzemac] Use the typed modbus read callback with address-based extraction (#18855)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-08-28 15:35:10 -05:00
Bonne EgglestonandJ. Nick Koston 7255315ce2 [sdm_meter] Use the typed modbus read callback with address-based extraction (#18849)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-08-28 15:34:23 -05:00
Bonne EgglestonandJ. Nick Koston 346c250901 [selec_meter] Use the typed modbus read callback with address-based extraction (#18854)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-08-28 15:34:03 -05:00
Bonne EgglestonandJ. Nick Koston 5fc9bff371 [pzemdc] Use the typed modbus read callback with address-based extraction (#18853)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-08-28 15:27:45 -05:00
Bonne EgglestonandJ. Nick Koston 97f643574c [kuntze] Use the typed modbus read callback and queue all reads at once (#18852)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-08-28 15:27:01 -05:00
Bonne EgglestonandJ. Nick Koston e8d76b735b [havells_solar] Use the typed modbus read callback with address-based extraction (#18851)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-08-28 15:21:52 -05:00
J. Nick Koston 03147bc3b1 [core] Avoid double promotion in update interval and step formatting (#18825) 2026-08-28 15:12:50 -05:00
Bonne Eggleston d4348335dd [growatt_solar] Use the typed modbus read callback and drop the send-pacing state machine (#18850) 2026-08-28 15:11:42 -05:00
Bonne Eggleston 246670e22b [modbus] Add a compile-time register value decoder (#18863) 2026-08-28 14:54:31 -05:00
J. Nick Koston 0dce7f4845 [esp8266] Add the native library backend (#18558) 2026-08-28 13:50:52 -05:00
J. Nick Koston 1623fe0852 [esp32] Exclude the WiFi and Bluetooth stacks from builds that do not use them (#18599) 2026-08-28 18:49:21 +00:00
J. Nick Koston 4333870590 [core] Use uv for the ESP-IDF Python environment when available (#18838) 2026-08-28 13:43:48 -05:00
J. Nick Koston cc41fd0beb [core] Make rmtree tolerate missing paths and concurrent directory changes (#18846) 2026-08-28 13:42:42 -05:00
Bonne EgglestonandJ. Nick Koston 59397b4e28 [modbus] Build single-value register writes on a right-sized stack buffer (#18844)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-08-28 18:36:24 +00:00
Bonne Eggleston 768ab5b672 [modbus] Hub and helpers cleanup; tighten queue_pdu validation (#18847) 2026-08-28 13:16:41 -05:00
J. Nick Koston 8db07d0de5 [mdns] Bump espressif/mdns to 1.12.0 (#18861) 2026-08-28 12:57:45 -05:00
a1515ec662 [modbus_controller] Replace register_count/force_new_range with reuse_previous_range (#18085)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-08-28 11:54:06 -05:00
Jesse Hills 2031be0c23 [core] Make script/setup idempotent and prepare new worktrees (#18843) 2026-08-28 14:11:38 +12:00
esphome[bot] 708b7bfb53 Update webserver local assets to 20260824-021242 (#18704) 2026-08-28 11:39:30 +12:00
J. Nick Koston 7782bc11c6 [core] Remove make_name_with_suffix std::string overloads (#18828) 2026-08-27 15:29:43 -05:00
J. Nick Koston a9a66baeba [ci] Compress the compile-test image with zstd (#18812) 2026-08-27 15:29:23 -05:00
J. Nick Koston 16d0fa1165 [core] Add step_to_accuracy_decimals benchmarks (#18826) 2026-08-27 15:28:04 -05:00
25d5cd3e14 [modbus_controller] Poll through PollingDevice; deprecate ModbusCommandItem (#18071)
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-08-27 14:38:13 -05:00
dependabot[bot] 9598449b6b Bump CodSpeedHQ/action from 5.0.3 to 5.2.1 (#18834)
Signed-off-by: dependabot[bot] <support@github.com>
2026-08-27 14:31:11 -05:00
dependabot[bot] a8c7279b3b Bump platformdirs from 4.11.3 to 4.11.4 (#18835)
Signed-off-by: dependabot[bot] <support@github.com>
2026-08-27 14:30:53 -05:00
c3c69c730c [openthread] fix shutdown (#16332)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-27 12:59:17 -05:00
Bonne EgglestonandJ. Nick Koston a8c8827a5b [modbus_controller] Switch write_lambda return value is the wire value only (#18788)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-08-27 10:33:16 -05:00
Bonne EgglestonandJ. Nick Koston 18b7002604 [modbus_controller] Apply the write offset byte-accurately on switch and output (#18787)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-08-27 08:43:51 -05:00
51105a25db [tuya] Add water_heater platform (#17323)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2026-08-27 20:55:51 +12:00
J. Nick Koston fb132b4421 [improv_serial][esp32_improv] Use buffer based RPC response builder (#18793) 2026-08-27 01:01:35 -05:00
guillempages 20b38ac8b0 [runtime_image] Add support for QOI images (#16945) 2026-08-27 16:37:20 +12:00
Keith BurzinskiandJ. Nick Koston cdd892a526 [provisioning] Shut down the Wi-Fi AP and captive portal when the window closes (#17466)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-08-27 15:16:25 +12:00
J. Nick Koston b99e7f5ae2 [core] Install prefetched PlatformIO packages with parallel extraction (#18775) 2026-08-27 03:02:02 +00:00
J. Nick Koston e55a8aeabe [api] Drop connection instead of crashing when buffer allocation fails (#18803) 2026-08-27 14:49:12 +12:00
J. Nick Koston a8094ed548 [docker] Parallelize the image's PlatformIO library preinstall (#18777) 2026-08-27 14:45:47 +12:00
J. Nick Koston 2104096f02 [core] Prefetch PlatformIO packages in parallel (#18769) 2026-08-27 14:42:30 +12:00
J. Nick Koston e53870085b [improv_serial] Add uart bus support and host integration test (#18794) 2026-08-26 21:13:47 -05:00
J. Nick Koston b62305793d [improv_base] Bump Improv library to 1.2.7 (#18809) 2026-08-27 01:40:12 +00:00
Jonathan Swoboda a1b29b0d0b [external_components] Log when a source overrides built-in components (#18805) 2026-08-26 20:56:57 -04:00
n-IA-hane e458a38f89 [spi] Use PSRAM DMA for external buffers (#18699) 2026-08-27 10:55:39 +10:00
J. Nick Koston 39177402dd [api] Deprecate media player supports_pause field (#18801) 2026-08-26 19:46:28 -05:00
J. Nick Koston 3361d031de [api] Drop connection instead of crashing when overflow buffer allocation fails (#18802) 2026-08-26 19:46:13 -05:00
Bonne EgglestonandJ. Nick Koston 5df1c7f1d3 [modbus_controller] Writer entities as their own hub device; heap-free, byte-accurate write path (#18082)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-08-26 18:24:09 -05:00
guillempages 9508165797 [runtime_image] Add check for dimensions in BMP (#18800) 2026-08-26 16:36:47 -05:00
esphome[bot] 5878406918 Bump bundled esphome-device-builder to 1.13.1 (#18807) 2026-08-26 16:33:52 -05:00
esphome[bot] fedd46e648 Bump aioesphomeapi from 46.2.0 to 46.2.1 (#18804) 2026-08-26 20:55:29 +00:00
dependabot[bot] d28b17c619 Bump filelock from 3.32.3 to 3.32.4 (#18799)
Signed-off-by: dependabot[bot] <support@github.com>
2026-08-26 19:26:45 +00:00
Josef Zweck f17ef133f3 [hoermann_hcp] Add buttons to hoermann_hcp (#18544) 2026-08-26 14:27:16 -04:00
Leonardo Rivera 34e7453677 [climate] Don't restore a saved mode the device no longer supports (#18296) 2026-08-26 14:26:31 -04:00
Frédéric Metrich 8a89f3075f [emontx] Fix sensor storage initialization ordering (#18771) 2026-08-26 14:03:10 -04:00
MakerYuichianddoraemon2200 5328813814 [time] Silence compiler warning by initializing transit variables (#18715) (#18723)
Co-authored-by: doraemon2200 <106516578+doraemon2200@users.noreply.github.com>
2026-08-26 12:43:55 -04:00
Keith Burzinski 1cbe3a49b2 [remote_transmitter] ISR-driven transmission and non_blocking support on BK7231N/BK7238 (#18660) 2026-08-26 11:30:07 -05:00
guillempages 5c79c92c06 [runtime_image] Add FILTER_SOURCE_FILES (#18768) 2026-08-26 11:26:39 -05:00
J. Nick Koston 7b4894da03 [mdns] Skip MDNS.update() while the ESP8266 radio cannot transmit (#18785) 2026-08-26 15:16:44 +00:00
Clyde Stubbs 4eb85a24c2 [mipi_spi] Fix dimensions for jc3636518v2 (#18786) 2026-08-26 10:04:57 -05:00
Bonne Eggleston 1e7c48e2cf [modbus_controller] Add integration tests for register offset, response size and write buffer (#18741) 2026-08-26 08:35:46 -05:00
IreuN 6ef13cc8de [climate_ir_lg] advanced vert. swing, setting temp. in heat/cool mode, jet mode decoding, other fixes + refactor (#10875) 2026-08-25 22:26:50 -07:00
Keith Burzinski e37a540fb7 [esp32] Apply custom eFuse MAC as base MAC for all interfaces (#18452) 2026-08-25 23:59:37 -05:00
J. Nick Koston 9baff76520 [api] Treat homeassistant.event variables as lambdas (#18759) 2026-08-26 16:29:14 +12:00
J. Nick KostonandCopilot Autofix powered by AI 612d58ec37 [http_request] Default watchdog_timeout from timeout on ESP32 (#18732)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-08-26 16:11:26 +12:00
Keith Burzinski cb981c2930 [remote_transmitter] ISR-driven transmission and non_blocking support on RTL8720C (#18648) 2026-08-25 22:19:54 -05:00
Clyde StubbsandClaude Sonnet 5 8b8de0c9c6 [lvgl] Fix on_value/on_update triggers for LVGL select entities (#18778)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-26 13:33:39 +12:00
J. Nick Koston 9e4c52989c [esp8266] Add the native framework and toolchain installer (#18557) 2026-08-25 20:24:47 -05:00
J. Nick Koston 7ff56c62f9 [core] Add shared registry, ninja, and cache infrastructure for native toolchains (#18570) 2026-08-26 13:23:16 +12:00
J. Nick Koston 370e8fffed [core] Add the arduino toolchain seam and validate --toolchain on every platform (#18556) 2026-08-26 11:16:02 +12:00
c272c4c1a6 [lvgl] Add table widget (#18422)
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+github@koston.org>
2026-08-26 08:44:42 +10:00
J. Nick Koston 150f75d8f6 [esp8266] Add linker-script surgery and board build metadata for the native toolchain (#18555) 2026-08-26 10:29:57 +12:00
Jonathan Swoboda cf5ec2d277 [ci] Remove the remaining max-parallel caps (#18762) 2026-08-25 14:42:32 -04:00
J. Nick Koston 145373ed13 [api] Widen message type storage to uint16_t (#18526) 2026-08-25 18:04:41 +00:00
J. Nick Koston 8749c3d18d [esp32_ble_tracker] Warn when the scan window is above 600ms with wifi (#18725) 2026-08-25 12:40:57 -05:00
J. Nick Koston 688e833413 [core] Consolidate HTTP download paths (#18742) 2026-08-25 12:38:20 -05:00
J. Nick Kostonandpre-commit-ci-lite[bot] 19b434f99c [espidf] Cache the discovered component list to skip the discovery configure (#18752)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-08-25 12:36:57 -05:00
0b78800e3d [emontx] Add apparent power (AP) and frequency (F) sensor support (#18586)
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-08-25 11:07:18 -05:00
Jesse Hills 6a86ef0ee0 [ethernet] Add spi_id option to attach to an existing SPI bus (#18530) 2026-08-25 10:45:38 -05:00
Frédéric Metrich 4eba9ee41a [core] Fix false-positive readability-non-const-parameter in string_ref.h (#18758) 2026-08-25 10:36:00 -05:00
NoQuarrelandpre-commit-ci-lite[bot] b579751bdf [sfa40] Add SFA40 sensor support (#17815)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-08-25 07:55:19 -04:00
J. Nick Koston bc8d0840eb [time] Skip automation.cpp when no on_time or on_time_sync automation is configured (#18750) 2026-08-25 19:50:43 +12:00
J. Nick KostonandJesse Hills 3f615f87cf [esp32] Exclude esp_http_server and nvs_sec_provider from IDF builds by default (#18748)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2026-08-25 06:16:32 +00:00
J. Nick Koston c6d423159c [esp32] Only build the mbedTLS certificate bundle when a component needs it (#18747) 2026-08-25 17:26:18 +12:00
J. Nick Koston 791590c8a3 [uptime] Skip the timestamp sensor source unless a timestamp sensor is configured (#18746) 2026-08-25 17:24:20 +12:00
J. Nick Koston 036e5cda7e [core] Remove empty automation.cpp files from sensor, switch and output (#18745) 2026-08-25 17:24:14 +12:00
J. Nick Koston 44860ff512 [time] Skip posix_tz.cpp when no timezone is configured (#18680) 2026-08-25 17:22:41 +12:00
Edvard Filistovič 90927ba788 [rp2_ble_tracker] Automation triggers and scan actions (#18717) 2026-08-24 22:57:01 -05:00
Edvard Filistovič 169c055011 [wifi] libretiny: reset the STA state on synchronous connect failure (#18719) 2026-08-25 01:25:44 +00:00
esphome[bot] 8623b6836d Bump bundled esphome-device-builder to 1.13.0 (#18743) 2026-08-25 00:44:28 +00:00
J. Nick Koston b3d9aa2d84 [core] Log which source change triggered a rebuild (#18552) 2026-08-24 18:46:43 -05:00
acd3c4f156 [lvgl] Implement list widget (#18018)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-08-24 23:20:20 +00:00
luar123andJ. Nick Koston 37b78ad46a [zigbee][time] Add zigbee time component on esp32 (#18656)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-08-24 17:24:57 -05:00
esphome[bot] a15fae6a1e Bump aioesphomeapi from 46.1.0 to 46.2.0 (#18740) 2026-08-24 22:15:09 +00:00
guillempagesandJ. Nick Koston c866add320 [runtime_image] Fix include (#18738)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-08-24 16:23:07 -05:00
J. Nick Koston 2a05372a51 [gpio] Fix one_wire reset busy-waiting with interrupts off when delay wraps (#18733) 2026-08-24 16:20:25 -05:00
J. Nick Koston 19866c60ee [core] Parallelize registry and tool downloads (#18662) 2026-08-24 15:59:00 -05:00
J. Nick Koston 3f0ec0b157 [espidf] Always reconfigure after component discovery (#18730) 2026-08-25 08:56:59 +12:00
Clyde Stubbs 7c5105b16e [key_collector][mipi_rgb] Suppress compilation warnings (#18721) 2026-08-25 06:36:06 +10:00
Keith BurzinskiandJ. Nick Koston b97c877992 [api] Acknowledge proxy subscribe and configuration requests (#18312)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-08-24 20:27:24 +00:00
ca96365143 [runtime_image] Add MIME types to image formats (#16361)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-08-24 15:07:01 -05:00
esphome[bot] 3b6c6e3dad Bump aioesphomeapi from 46.0.0 to 46.1.0 (#18737) 2026-08-24 14:50:35 -05:00
dependabot[bot] ad508d79af Bump github/codeql-action/init from 4.37.7 to 4.37.8 (#18736)
Signed-off-by: dependabot[bot] <support@github.com>
2026-08-24 14:33:52 -05:00
dependabot[bot] 32a0a5c55c Bump github/codeql-action/analyze from 4.37.7 to 4.37.8 (#18735)
Signed-off-by: dependabot[bot] <support@github.com>
2026-08-24 14:33:39 -05:00
dependabot[bot] b103df1bb1 Bump ruff from 0.16.3 to 0.16.4 (#18734)
Signed-off-by: dependabot[bot] <support@github.com>
2026-08-24 14:33:24 -05:00
Bonne Eggleston c2a153a946 [modbus_controller] Add continuous polling option (#18080) 2026-08-24 13:26:26 -05:00
J. Nick Koston 49fc4be861 [ci] Run C++ unit tests when a component's Python or test override changes (#18706) 2026-08-24 12:18:17 -05:00
GafieltandJ. Nick Koston b8408e14b9 [uart] Delete ESP32 UART driver on shutdown to keep ROM output off the bus (#18684)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-08-24 11:36:23 -05:00
Boris Krivonog b11a34af5c [mitsubishi_cn105] Defer status requests after responses (#18227) 2026-08-24 10:51:09 -05:00
n-IA-haneandn-IA-hane 5ed20805ce [audio_http] Add persistent ring buffer option (#18708)
Co-authored-by: n-IA-hane <n-IA-hane@users.noreply.github.com>
2026-08-24 11:35:59 -04:00
Bonne Eggleston 7674477dd0 [modbus_controller] Brace poll-refused log to fix -Wempty-body warning (#18724) 2026-08-24 09:05:00 -05:00
2b11fbe164 [preferences] modify IntervalSyncer to be a PollingComponent (#14370)
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-23 22:50:19 -05:00
J. Nick Koston 5f6a910e2d [core] Harden the shared library converter (#18685) 2026-08-23 22:24:12 -05:00
J. Nick Koston 9974ad97fa [core] Extract the shared idedata and size-summary helpers into build_helpers (#18661) 2026-08-23 21:59:19 -05:00
Jesse Hills c8ebe461d1 [ci] Skip CodSpeed benchmarks on beta and release pull requests (#18703) 2026-08-24 14:11:35 +12:00
Jesse HillsandJ. Nick Koston de1361ac0e [core] Add type annotations to component Python (#18697)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-08-23 20:50:27 -05:00
J. Nick Koston d877fb021c [time] Remove C++ POSIX TZ string parser (#18383) 2026-08-24 13:24:00 +12:00
3c56b9e66e [wifi] Share the scan list dedupe helper with improv_serial (#18612)
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: Bluetooth Devices Bot <bluetooth@koston.org>
2026-08-24 01:05:35 +00:00
Bonne Eggleston 1ceaf699c2 [modbus_controller] Rename custom_command to custom_pdu; reject address 0; retire skip_updates (#18652) 2026-08-23 19:33:53 -05:00
J. Nick Koston d75f85b089 [network] Add tcp_send_buffer option (#18610) 2026-08-23 19:23:08 -05:00
J. Nick KostonandJesse Hills 34c1a009b6 [core] Use filter_source_files_from_defines in api, socket and ethernet (#18674)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
2026-08-24 00:20:00 +00:00
c9e91be35f [web_server_idf] Skip multipart parser sources when OTA upload is disabled (#18534)
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>
2026-08-23 23:45:26 +00:00
J. Nick Koston 31afd94a30 [noise] Declare libsodium as a direct dependency (#18692) 2026-08-23 23:35:45 +00:00
J. Nick Koston 6f7b9a1482 [gpio] Fix linker error when binary sensor only uses expander pins (#18698) 2026-08-24 11:31:48 +12:00
J. Nick Koston 6c23ad661d [bluetooth_proxy] Warn once per transaction when the TCP buffer is congested (#18605) 2026-08-24 11:30:11 +12:00
J. Nick Koston 41171e61d1 [core] Keep str_contains_ignore_case needle literals in flash on ESP8266 (#18574) 2026-08-24 11:29:05 +12:00
J. Nick Koston bdf8dec028 [i2s_audio] Skip SPDIF speaker sources when spdif_mode is off (#18676) 2026-08-24 11:16:03 +12:00
J. Nick Koston a872626fba [esp32_ble] Skip ble_advertising.cpp when advertising is not used (#18679) 2026-08-24 11:15:54 +12:00
J. Nick Koston 00bdbf8ed1 [uart] Skip uart_debugger.cpp when no debug block is configured (#18678) 2026-08-24 11:15:21 +12:00
J. Nick Koston 25fb3983cb [mqtt] Skip entity sources for entity types not in the config (#18677) 2026-08-24 11:14:40 +12:00
J. Nick Koston 4510f10491 [core] Treat a malformed build_info.json as stale instead of crashing (#18669) 2026-08-24 11:13:57 +12:00
J. Nick Koston 0ecc7045fa [core] Parse the common on/off spellings in boolean env vars (#18667) 2026-08-24 11:13:34 +12:00
J. Nick Koston 5cc1f001fa [light] Skip light_json_schema.cpp when json is not used (#18675) 2026-08-24 11:12:58 +12:00
J. Nick Koston 76caadc594 [noise] Add shared noise component and move api noise primitives (#18490) 2026-08-24 11:03:44 +12:00
Jesse Hills 7d34efecf2 Merge branch 'release' into dev 2026-08-24 10:37:28 +12:00
Jesse Hills c4096d44d8 Merge pull request #18696 from esphome/bump-2026.8.1
2026.8.1
2026-08-24 10:37:06 +12:00
Jesse Hills 68f3a6b9a5 Bump version to 2026.8.1 2026-08-24 09:19:29 +12:00
J. Nick Koston 662bf7d7f0 [esp32_ble_tracker] Scan at the default window while a GATT connection is active (#18609) 2026-08-24 09:19:28 +12:00
J. Nick Koston 0d71ab8efb [api] Fix loop stall that triggers the task watchdog when entity list sends block (#18577) 2026-08-24 09:19:28 +12:00
J. Nick Koston 4ce4768ebd [core] Retry transient network errors when downloading external files (#18538) 2026-08-24 09:19:28 +12:00
J. Nick Koston fb13327922 [esp32] Report abort and task watchdog panics correctly in crash handler (#18575) 2026-08-24 09:19:28 +12:00
J. Nick Koston 4c62420f1b [core] Dump the main.cpp config comment with sorted keys (#18653) 2026-08-24 09:19:28 +12:00
J. Nick Koston ca3f31643f [esp8266] Don't report stale crash state after hardware WDT resets (#18597) 2026-08-24 09:19:28 +12:00
J. Nick Koston d770004e0e [esp32_ble] Log connection parameter update results (#18607) 2026-08-24 09:19:28 +12:00
J. Nick Koston b28efcd545 [bk72xx_ble] Block BK7238 until the LibreTiny bonding partition fix lands (#18649) 2026-08-24 09:19:28 +12:00
esphome[bot] 6b6d27f905 Bump bundled esphome-device-builder to 1.12.4 (#18651) 2026-08-24 09:19:28 +12:00
esphome[bot] 603c3539a3 Bump bundled esphome-device-builder to 1.12.3 (#18601) 2026-08-24 09:19:28 +12:00
Jonathan Swoboda f248a85b51 [esp32_hosted] Fire on_update_available trigger when update is detected (#18591) 2026-08-24 09:19:28 +12:00
5a3d7e3292 [emontx] Fix sensor state_class defaults not being applied correctly (#17610)
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>
2026-08-24 09:19:28 +12:00
J. Nick Koston c6d329db64 [espnow] Fix dump_config crash when enable_on_boot is false (#18572) 2026-08-24 09:19:28 +12:00
esphome[bot] bdb203d742 Bump bundled esphome-device-builder to 1.12.2 (#18573) 2026-08-24 09:19:27 +12:00
J. Nick Koston f509516d60 [core] Keep templated !include filenames as strings so Windows path normalization cannot corrupt them (#18549) 2026-08-24 09:19:27 +12:00
J. Nick Koston 4c856949c2 [nrf52] Rebuild the Python env when its interpreter symlink dangles (#18540) 2026-08-24 09:19:27 +12:00
esphome[bot] c90a5f4cff Bump bundled esphome-device-builder to 1.12.1 (#18541) 2026-08-24 09:19:27 +12:00
Markus Häll 964fc1ef3f [wifi] Take the lwIP core lock around sntp_servermode_dhcp() (#18511) 2026-08-24 09:19:27 +12:00
J. Nick Koston 98f138f6b2 [esp32_ble_tracker] Scan at the default window while a GATT connection is active (#18609) 2026-08-24 09:16:01 +12:00
J. Nick Koston 9179fe26ce [api] Fix loop stall that triggers the task watchdog when entity list sends block (#18577) 2026-08-24 08:38:19 +12:00
J. Nick Koston e6c49e2bd9 [core] Retry transient network errors when downloading external files (#18538) 2026-08-24 08:25:39 +12:00
d84293931b [online_image] Support image format auto-detection (#16337)
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
2026-08-23 19:58:01 +00:00
Boris Krivonog 2c32ac2221 [mitsubishi_cn105] Mark configurable classes as final (#18272) 2026-08-23 19:40:08 +00:00
Bonne EgglestonandClaude dde6906f98 [modbus_client] Add continuous option to the read and send actions (#18542)
Co-authored-by: Claude <noreply@anthropic.com>
2026-08-23 13:01:34 -05:00
J. Nick Koston 4efd308345 [tests] Fix flaky pty log probe test on macOS (#18681) 2026-08-23 11:26:55 -05:00
esphome[bot] f741c274d5 Bump aioesphomeapi from 45.13.1 to 46.0.0 (#18683) 2026-08-23 15:55:42 +00:00
J. Nick Koston cf31c08a5c [core] Skip copying entity automation and filter sources when unused (#18602) 2026-08-23 09:05:04 -05:00
J. Nick Koston e7574a574b [ota] Restore lazy flash erase for ESP32 OTA with 64 KiB block erase (#18580) 2026-08-23 09:04:48 -05:00
J. Nick Koston 33484108a9 [core] Replace a damaged existing file in write_file_if_changed (#18665) 2026-08-23 09:04:24 -05:00
J. Nick Koston e697a40fda [core] Register the OTA component in dummy_main like its siblings (#18666) 2026-08-23 09:04:06 -05:00
J. Nick Koston d1f065671e [http_request] Abort OTA backend when update fails before first write (#18581) 2026-08-22 22:21:04 -05:00
J. Nick Koston 02da5c6484 [ethernet] Remove deprecated get_eth_mac_address_pretty() (#18379) 2026-08-22 22:02:41 -05:00
J. Nick Koston b2440cb655 [modbus] Remove deprecated waiting_for_response() (#18381) 2026-08-22 22:02:22 -05:00
J. Nick Koston 160d8b8f0c [web_server_idf] Remove deprecated AsyncWebServerRequest::url() (#18382) 2026-08-22 22:02:05 -05:00
J. Nick Koston 8899713ef9 [core] Remove deprecated gamma_correct and gamma_uncorrect (#18376) 2026-08-22 22:00:53 -05:00
J. Nick Koston f3cdefce21 [wifi] Remove deprecated wifi_ssid() (#18378) 2026-08-22 22:00:38 -05:00
J. Nick Koston b115813fbe [esp32] Report abort and task watchdog panics correctly in crash handler (#18575) 2026-08-22 22:00:17 -05:00
J. Nick Koston ab45ab316a [core] Remove deprecated entity_base getters (#18375) 2026-08-22 22:00:02 -05:00
J. Nick Koston 5b3a6c05bf [core] Remove deprecated esp_log_vprintf_ flash-string overload (#18377) 2026-08-22 21:59:47 -05:00
J. Nick Koston cd53681787 [wifi] Inline the remaining trivial WiFiAP and WiFiComponent accessors (#18617) 2026-08-23 02:51:39 +00:00
J. Nick Koston f0651e5c9b [radio_frequency] Inline the trivial make_call helper (#18645) 2026-08-22 21:07:34 -05:00
J. Nick Koston f24b731f95 [infrared] Inline the trivial make_call helper (#18644) 2026-08-22 21:07:14 -05:00
J. Nick Koston 01ad424d12 [datetime] Inline the trivial make_call helpers (#18643) 2026-08-22 21:07:03 -05:00
J. Nick Koston 435d522683 [text] Inline the trivial Text publish_state forwarding overloads (#18642) 2026-08-22 21:06:47 -05:00
J. Nick Koston a63c3bc0c7 [valve] Inline the trivial Valve and ValveCall accessors (#18641) 2026-08-22 21:06:31 -05:00
J. Nick Koston b83ce91528 [display] Inline the trivial DisplayPage setters and page navigation helpers (#18639) 2026-08-22 21:06:03 -05:00
J. Nick Koston a30238aab6 [wireguard] Inline the trivial Wireguard setters (#18638) 2026-08-22 21:05:51 -05:00
J. Nick Koston 47156c9a5b [mqtt] Inline the trivial MQTT client, component and sensor accessors (#18637) 2026-08-22 21:05:39 -05:00
J. Nick Koston 78240c9a46 [sprinkler] Inline the trivial Sprinkler accessors (#18636) 2026-08-22 21:04:57 -05:00
J. Nick Koston 5a9f06e584 [thermostat] Inline the trivial ThermostatClimate setters and getters (#18635) 2026-08-22 21:04:41 -05:00
J. Nick Koston ecb007da70 [deep_sleep] Inline the trivial DeepSleepComponent setters (#18634) 2026-08-22 21:04:23 -05:00
J. Nick Koston ad1a4fca36 [version] Inline the trivial VersionTextSensor setters (#18633) 2026-08-22 21:04:08 -05:00
J. Nick Koston 832a738588 [switch] Inline the trivial inverted accessors (#18632) 2026-08-22 21:03:51 -05:00
J. Nick Koston fedb3ac5c1 [fan] Inline the trivial Fan call helpers (#18631) 2026-08-22 21:03:38 -05:00
J. Nick Koston cb4e55e444 [cover] Inline the trivial Cover and CoverCall accessors (#18630) 2026-08-22 21:03:15 -05:00
J. Nick Koston 3ef5a8e6a4 [water_heater] Inline the trivial visual override setters (#18629) 2026-08-22 21:03:07 -05:00
J. Nick Koston 0e915e9b8b [core] Inline the ESPTime::strftime std::string overload (#18628) 2026-08-22 21:02:58 -05:00
J. Nick Koston dba3b287dd [api] Inline the trivial APIServer accessors (#18627) 2026-08-22 21:02:14 -05:00
J. Nick Koston ce019f508d [safe_mode] Inline the trivial set_safe_mode setters (#18626) 2026-08-22 21:02:04 -05:00
J. Nick Koston 5c2286cc4a [climate] Inline the trivial visual override setters (#18624) 2026-08-22 21:01:55 -05:00
J. Nick Koston efc0a94112 [text_sensor] Inline the trivial TextSensor forwarding overloads (#18623) 2026-08-22 21:01:42 -05:00
J. Nick Koston c60062c418 [sensor] Inline the trivial ExponentialMovingAverageFilter setters (#18622) 2026-08-22 21:01:28 -05:00
J. Nick Koston 763a1d9371 [select] Inline the trivial Select accessors (#18621) 2026-08-22 21:01:16 -05:00
J. Nick Koston 4db1666024 [light] Inline the trivial LightState accessors (#18620) 2026-08-22 21:00:59 -05:00
J. Nick Koston 0dc69aab1e [logger] Inline the trivial Logger accessors (#18619) 2026-08-22 21:00:49 -05:00
J. Nick Koston a282cb095e [ethernet] Inline the trivial EthernetComponent setters (#18618) 2026-08-22 21:00:39 -05:00
J. Nick Koston 259e7182a3 [esp32] Exclude esp_gdbstub from the build by default (#18604) 2026-08-22 20:59:55 -05:00
J. Nick Koston 74bdf275d2 [core] Dump the main.cpp config comment with sorted keys (#18653) 2026-08-22 20:58:28 -05:00
J. Nick Koston 14499223fd [esp8266] Don't report stale crash state after hardware WDT resets (#18597) 2026-08-22 20:58:13 -05:00
J. Nick Koston 6aab523dd9 [esp32_ble] Log connection parameter update results (#18607) 2026-08-22 20:57:58 -05:00
J. Nick Koston 1f31e51446 [esphome] Inline the trivial OTA port accessors (#18625) 2026-08-22 20:57:37 -05:00
J. Nick Koston c062d0c717 [ota] Log prepare, upload, and total OTA timing in espota2 (#18582) 2026-08-22 20:57:13 -05:00
J. Nick Koston dcabaedff1 [bk72xx_ble] Block BK7238 until the LibreTiny bonding partition fix lands (#18649) 2026-08-23 11:59:24 +12:00
Oliver Kleineckeandp1ngb4ck d119ad6c60 [usb_uart] Extract non-final USBUartChannelBase from USBUartChannel (#17472)
Co-authored-by: p1ngb4ck <p@local>
2026-08-22 10:47:46 -05:00
esphome[bot] 74fc2e367a Bump bundled esphome-device-builder to 1.12.4 (#18651) 2026-08-22 15:42:53 +00:00
Jesse HillsandJonathan Swoboda ea10f94376 [core] Add type annotations to component Python (10/11) (#18347)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-08-22 10:31:47 -05:00
ef1d77885d [captive_portal] Show each network once in the scan list (#17847)
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: Bluetooth Devices Bot <bluetooth@koston.org>
2026-08-22 05:04:08 +00:00
Keith Burzinski dccf55eadc [remote_transmitter] Use hardware PWM on rtl87xx to fix watchdog crash (#18579) 2026-08-21 23:53:16 -05:00
Boris KrivonogandJ. Nick Koston 65704e881f [mitsubishi_cn105] Add Fahrenheit support (#15488)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-08-21 23:52:18 -05:00
Brandon Harvey a30e82459f [deep_sleep] Reject wakeup_pin_mode at both levels on BK72xx (#18615) 2026-08-21 23:51:19 -05:00
Brandon Harvey 5a300e92f1 [wifi] Inline the trivial WiFiScanResult accessors (#18613) 2026-08-22 04:14:56 +00:00
Keith BurzinskiandJ. Nick Koston 8e9fb0f93c [remote_transmitter] Fix repeat gap timing on LibreTiny Beken (#18585)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-08-21 23:10:47 -05:00
esphome[bot] 11ea819bc7 Bump aioesphomeapi from 45.12.0 to 45.13.1 (#18600) 2026-08-21 14:36:47 -05:00
esphome[bot] abc9098bd8 Bump bundled esphome-device-builder to 1.12.3 (#18601) 2026-08-21 14:05:00 -05:00
Kevin Ahrendt 00cffa09a2 [sendspin] Convert tests to package-style includes (#18588) 2026-08-21 13:05:07 -05:00
Jesse Hills aa944456e0 [core] Add type annotations to component Python (6/11) (#18343) 2026-08-21 11:11:52 -05:00
Jonathan Swoboda 409d74a48d [esp32_hosted] Fire on_update_available trigger when update is detected (#18591) 2026-08-21 11:53:44 -04:00
7957808f00 [emontx] Fix sensor state_class defaults not being applied correctly (#17610)
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>
2026-08-21 09:57:58 -05:00
J. Nick Koston 52bfc0efb1 [espnow] Fix dump_config crash when enable_on_boot is false (#18572) 2026-08-20 18:06:23 -05:00
guillempagesandJ. Nick Koston 46f90d0c54 [core] Add portable strcasestr implementation named str_contains_ignore_case (#18497)
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-08-20 17:28:17 -05:00
5177972c04 [runtime_image] keep decoder allocated (#18488)
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-08-20 17:27:44 -05:00
esphome[bot] ece90ee97b Bump bundled esphome-device-builder to 1.12.2 (#18573) 2026-08-20 16:59:30 -05:00
dependabot[bot] 4cfa4893ef Bump docker/setup-buildx-action from 4.2.0 to 4.3.0 in the docker-actions group (#18564)
Signed-off-by: dependabot[bot] <support@github.com>
2026-08-20 15:24:28 -05:00
dependabot[bot] edd4a86d14 Bump prek from 0.4.13 to 0.4.14 (#18563)
Signed-off-by: dependabot[bot] <support@github.com>
2026-08-20 15:02:15 -05:00
Jesse Hills 44dcd82d78 [mipi_spi] Toggle D/C only while holding the SPI bus (#18529) 2026-08-21 07:58:11 +12:00
Jesse Hills 12da2140cf [core] Add type annotations to component Python (11/11) (#18348) 2026-08-21 07:57:14 +12:00
Jesse Hills c006e9804a [core] Add type annotations to component Python (9/11) (#18346) 2026-08-21 07:56:48 +12:00
Jesse HillsandJonathan Swoboda 6343c11873 [core] Add type annotations to component Python (5/11) (#18342)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-08-21 07:56:02 +12:00
David van 't Wout 2ab09e1a77 [core] Add add_cmake_arg (#18498) 2026-08-20 12:30:33 -05:00
J. Nick Koston a3ea77c2f1 [core] Keep templated !include filenames as strings so Windows path normalization cannot corrupt them (#18549) 2026-08-20 11:32:15 -05:00
J. Nick Koston 7fe4399b94 [esp32] Grow the default IDF component exclusion list (#18536) 2026-08-20 16:11:53 +00:00
Jesse Hills 545f762568 [core] Add type annotations to component Python (8/11) (#18345) 2026-08-20 12:08:26 -04:00
Jesse Hills e83439eaae [core] Add type annotations to component Python (7/11) (#18344) 2026-08-20 12:07:10 -04:00
Jesse Hills b6a9761dae [core] Add type annotations to component Python (4/11) (#18341) 2026-08-20 10:59:51 -04:00
Jesse Hills fbe4b39a16 [core] Add type annotations to component Python (3/11) (#18340) 2026-08-20 10:58:58 -04:00
Jesse HillsandJonathan Swoboda ecca240eef [core] Add type annotations to component Python (2/11) (#18339)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-08-20 14:26:16 +00:00
J. Nick Koston 347a6155f8 [uptime] Skip the timestamp sensor source when no time component is configured (#18535) 2026-08-20 10:07:17 -04:00
J. Nick Koston a8e721abeb [esp32] Apply IDF component exclusions to native toolchain builds (#18531) 2026-08-20 10:03:49 -04:00
J. Nick Koston 2d62ea78d2 [ota] Skip partition-access OTA sources when the feature is disabled (#18532) 2026-08-20 10:00:11 -04:00
J. Nick Koston 8ef0f38f4e [ethernet] Skip the custom W5500 SPI driver for other ethernet types (#18533) 2026-08-20 09:59:30 -04:00
Jesse Hills 3c47ab42d6 [core] Add type annotations to component Python (1/11) (#18338) 2026-08-20 09:55:15 -04:00
Alar Aun aafeca5859 [modbus_controller] Brace single-statement log bodies to fix -Wempty-body (#18543) 2026-08-20 09:54:28 -04:00
J. Nick Koston 132f494195 [nrf52] Rebuild the Python env when its interpreter symlink dangles (#18540) 2026-08-20 07:59:57 -04:00
esphome[bot] 5e9de7c94b Bump bundled esphome-device-builder to 1.12.1 (#18541) 2026-08-20 00:00:44 -05:00
Markus Häll b7d0b676fc [wifi] Take the lwIP core lock around sntp_servermode_dhcp() (#18511) 2026-08-19 23:31:27 -05:00
Jesse Hills 55b45fc6fb Merge branch 'release' into dev 2026-08-20 14:32:46 +12:00
Jesse Hills 6f8dbb6fbc Merge pull request #18527 from esphome/bump-2026.8.0
2026.8.0
2026-08-20 14:32:15 +12:00
J. Nick Koston ca97c86d65 [ci] Install requirements_dev.txt when the venv cache misses (#18502) 2026-08-20 13:12:33 +12:00
Jesse Hills 828eac90f3 Bump version to 2026.8.0 2026-08-20 11:51:59 +12:00
J. Nick Koston d9359a70c1 [tests] Keep PlatformIO libdeps per xdist worker to stop a compile race (#18524) 2026-08-20 11:51:57 +12:00
Jesse Hills 29404a782c Merge branch 'beta' into dev 2026-08-20 10:32:46 +12:00
Jesse Hills e75a7a61fa Merge pull request #18523 from esphome/bump-2026.8.0b6
2026.8.0b6
2026-08-20 10:32:26 +12:00
J. Nick Koston 185f12266a [tests] Keep PlatformIO libdeps per xdist worker to stop a compile race (#18524) 2026-08-19 17:29:10 -05:00
J. Nick Koston c455991962 [ci] Key PlatformIO cache on the Python version so a runner image bump does not serve a broken LibreTiny venv (#18512) 2026-08-20 09:38:44 +12:00
Jesse Hills f735dcadc0 Bump version to 2026.8.0b6 2026-08-20 09:33:05 +12:00
J. Nick Koston 78a65eabdc [ci] Stop jobs hanging on apt by restoring the cached apt action and bounding raw apt calls (#18518) 2026-08-20 09:33:03 +12:00
b3fda9973e [image] Restore defaults:/files: support for platform entries (#18032)
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@koston.org>
2026-08-20 09:33:03 +12:00
esphome[bot] 4a85c98285 Bump bundled esphome-device-builder to 1.12.0 (#18514) 2026-08-20 09:33:03 +12:00
esphome[bot] 2c92a2498e Bump bundled esphome-device-builder to 1.11.5 (#18507) 2026-08-20 09:33:03 +12:00
esphome[bot] 74e22b5ad7 Bump bundled esphome-device-builder to 1.11.4 (#18506) 2026-08-20 09:33:03 +12:00
esphome[bot] e9e77d02a0 Bump bundled esphome-device-builder to 1.11.3 (#18505) 2026-08-20 09:33:03 +12:00
Jonathan Swoboda 7418fcce8d [ci] Stop persisting the integration test ccache (#18504) 2026-08-20 09:33:03 +12:00
Jonathan Swoboda b768e2a1ce [esp32] Fix ESP32-P4 bootloop on rev3 (v3.x) chips when only variant is set (#18500) 2026-08-20 09:33:03 +12:00
J. Nick Koston 6084314cc9 [vscode] Report the origin of an unexpected exception during validation (#18494) 2026-08-20 09:33:03 +12:00
J. Nick Koston 2df953f3d7 [platformio] Give the ccache wrapper a cmd.exe safe path (#18495) 2026-08-20 09:33:03 +12:00
Bonne EgglestonandClaude Opus 4.8 10e592fa3a [modbus] CRC scan all unknown function codes (#18483)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-20 09:33:03 +12:00
J. Nick Koston a99a8f364e [api] Bump noise-c to 0.1.21 (#18484) 2026-08-20 09:33:03 +12:00
J. Nick Koston 200a1644a5 [ci] Fail the benchmark job when the C++ benchmark build fails (#18480) 2026-08-20 09:33:03 +12:00
J. Nick Koston 9daae377fc [api] Bump noise-c to 0.1.20 (#18482) 2026-08-20 09:33:02 +12:00
J. Nick Koston 0b1065feee [ci] Stop jobs hanging on apt by restoring the cached apt action and bounding raw apt calls (#18518) 2026-08-20 09:30:34 +12:00
Clyde Stubbs 47da743d11 [ai] Add instructions for concise comments (#18522) 2026-08-19 16:09:12 -05:00
470226ca03 [image] Restore defaults:/files: support for platform entries (#18032)
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@koston.org>
2026-08-19 12:30:12 -05:00
esphome[bot] 7b7107556f Bump bundled esphome-device-builder to 1.12.0 (#18514) 2026-08-19 12:21:58 -05:00
J. Nick Koston f90b776071 [ci] Key PlatformIO cache on the Python version so a runner image bump does not serve a broken LibreTiny venv (#18512) 2026-08-19 16:39:01 +00:00
esphome[bot] e26237e57d Bump bundled esphome-device-builder to 1.11.5 (#18507) 2026-08-19 00:42:05 -05:00
esphome[bot] d2c3f749ab Bump bundled esphome-device-builder to 1.11.4 (#18506) 2026-08-18 23:24:23 -05:00
esphome[bot] b7cc271219 Bump bundled esphome-device-builder to 1.11.3 (#18505) 2026-08-18 21:57:51 -05:00
dependabot[bot] 17eed7055b Bump resvg-py from 0.3.4 to 0.4.0 (#18460)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-18 22:22:51 -04:00
dependabot[bot] b7121940c8 Update wheel requirement from <0.48,>=0.43 to >=0.43,<0.49 (#18459)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-18 22:08:23 -04:00
Jonathan Swoboda 07fa16e2e7 [ci] Stop persisting the integration test ccache (#18504) 2026-08-18 21:38:18 -04:00
Jonathan Swoboda 8aa7db15e5 [esp32] Fix ESP32-P4 bootloop on rev3 (v3.x) chips when only variant is set (#18500) 2026-08-18 21:25:09 -04:00
J. Nick Koston 8b637b339b [vscode] Report the origin of an unexpected exception during validation (#18494) 2026-08-18 19:37:10 -05:00
J. Nick Koston 4f866c563b [platformio] Give the ccache wrapper a cmd.exe safe path (#18495) 2026-08-18 19:36:57 -05:00
J. Nick Koston 7a999f9a48 [ci] Install requirements_dev.txt when the venv cache misses (#18502) 2026-08-18 19:06:55 -05:00
esphome[bot] 0a88c81d95 Bump aioesphomeapi from 45.11.0 to 45.12.0 (#18501) 2026-08-18 18:38:23 -05:00
J. Nick Koston 804e8fb856 [socket] Remove constant duplicated by the beta merge (#18496) 2026-08-18 15:44:38 -05:00
Jesse Hills 43939a9199 Merge branch 'beta' into dev 2026-08-19 08:01:33 +12:00
Jesse Hills f414a07bcd Merge pull request #18481 from esphome/bump-2026.8.0b5
2026.8.0b5
2026-08-19 08:01:14 +12:00
esphome[bot] 5c9d050ebe Bump aioesphomeapi from 45.10.3 to 45.11.0 (#18493) 2026-08-18 17:02:54 +00:00
Bonne EgglestonandClaude Opus 4.8 285a508e09 [modbus] CRC scan all unknown function codes (#18483)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-18 11:09:12 -05:00
J. Nick Koston 92f55f721f [api] Bump noise-c to 0.1.21 (#18484) 2026-08-18 14:32:37 +00:00
Jonathan Swoboda 476d540065 [ci] Fall back to files API when PR diff exceeds GitHub line limit (#18486) 2026-08-18 10:22:28 -04:00
J. Nick Koston ae730d6357 [ci] Fail the benchmark job when the C++ benchmark build fails (#18480) 2026-08-18 09:03:33 -05:00
J. Nick Koston 9823205ef3 [api] Bump noise-c to 0.1.20 (#18482) 2026-08-18 09:03:14 -05:00
Jesse Hills d1391c2b10 Bump version to 2026.8.0b5 2026-08-18 16:04:49 +12:00
J. Nick Koston 8b888f31e0 [gpio_expander][pcf8574][pca9554][tca9555][pca6416a][pi4ioe5v6408][mcp23016][mcp23xxx_base] Reject unsupported interrupt_pin options (inverted, allow_other_uses) (#18472) 2026-08-18 16:04:47 +12:00
Jesse Hills 6a247dfe91 [light] Replace rgb_order/is_rgbw/is_wrgb with channel_colors (#18474) 2026-08-18 16:01:54 +12:00
J. Nick Koston 482869fbbe [socket] Fix multi-second TCP stalls on ESP8266 by yielding to the SYS context (#18455) 2026-08-18 14:27:54 +12:00
esphome[bot] 4dea147386 Bump bundled esphome-device-builder to 1.11.2 (#18477) 2026-08-18 14:27:54 +12:00
J. Nick Koston 1fd6337254 [api] Bump noise-c to 0.1.19 (#18473) 2026-08-18 14:27:54 +12:00
esphome[bot] 4ce6d59484 Bump bundled esphome-device-builder to 1.11.1 (#18475) 2026-08-18 14:27:54 +12:00
J. Nick Koston 014cc19902 [api] Bump noise-c to 0.1.18 (#18451) 2026-08-18 14:27:54 +12:00
esphome[bot] b9041566ea Bump aioesphomeapi from 45.10.2 to 45.10.3 (#18433)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-08-18 14:27:54 +12:00
J. Nick Koston 3a403c40d5 [ld2420] Drop the setup priority override so setup runs after the UART bus (#18428) 2026-08-18 14:27:54 +12:00
esphome[bot] 096e71bd67 Bump aioesphomeapi from 45.10.1 to 45.10.2 (#18357) 2026-08-18 14:27:54 +12:00
J. Nick Koston fffa902a1a [gpio_expander][pcf8574][pca9554][tca9555][pca6416a][pi4ioe5v6408][mcp23016][mcp23xxx_base] Reject unsupported interrupt_pin options (inverted, allow_other_uses) (#18472) 2026-08-18 13:32:03 +12:00
Jesse Hills 463e3833da [light] Replace rgb_order/is_rgbw/is_wrgb with channel_colors (#18474) 2026-08-18 13:19:48 +12:00
J. Nick Koston 4416aacebb [socket] Fix multi-second TCP stalls on ESP8266 by yielding to the SYS context (#18455) 2026-08-18 13:09:29 +12:00
esphome[bot] 15a626bcf3 Bump bundled esphome-device-builder to 1.11.2 (#18477) 2026-08-17 19:16:38 -05:00
J. Nick Koston a347a2e879 [api] Bump noise-c to 0.1.19 (#18473) 2026-08-17 18:58:11 -05:00
dependabot[bot] 7be4566b41 Bump platformdirs from 4.11.2 to 4.11.3 (#18468)
Signed-off-by: dependabot[bot] <support@github.com>
2026-08-17 18:33:13 -05:00
esphome[bot] 96e26c6a5f Bump bundled esphome-device-builder to 1.11.1 (#18475) 2026-08-17 17:54:23 -05:00
J. Nick Koston 7362c01c67 [core] Replace base64 lookup tables with arithmetic mapping (#18454) 2026-08-17 16:14:06 -05:00
Jesse Hills e45b4e4938 [core] Make FINAL_VALIDATE_SCHEMA functions return None (#18457) 2026-08-18 08:26:24 +12:00
J. Nick Koston e32329cc11 [core] Sync pre-commit ruff hook with requirements (0.16.3) (#18469) 2026-08-17 15:00:37 -05:00
dependabot[bot] 199e368fe2 Bump astral-sh/setup-uv from 9.0.0 to 10.0.1 (#18463)
Signed-off-by: dependabot[bot] <support@github.com>
2026-08-17 14:57:50 -05:00
dependabot[bot] 55726120db Bump esphome/workflows/.github/workflows/lock.yml from 2026.7.0 to 2026.8.1 (#18465)
Signed-off-by: dependabot[bot] <support@github.com>
2026-08-17 14:47:28 -05:00
dependabot[bot] 346ba7e831 Bump github/codeql-action/analyze from 4.37.6 to 4.37.7 (#18467)
Signed-off-by: dependabot[bot] <support@github.com>
2026-08-17 14:47:18 -05:00
dependabot[bot] 9518006724 Bump ruff from 0.16.2 to 0.16.3 (#18458)
Signed-off-by: dependabot[bot] <support@github.com>
2026-08-17 14:46:04 -05:00
dependabot[bot] 3d5f6f692f Bump filelock from 3.32.2 to 3.32.3 (#18461)
Signed-off-by: dependabot[bot] <support@github.com>
2026-08-17 14:45:41 -05:00
dependabot[bot] 4712c15c75 Bump github/codeql-action/init from 4.37.6 to 4.37.7 (#18466)
Signed-off-by: dependabot[bot] <support@github.com>
2026-08-17 14:42:24 -05:00
dependabot[bot] 9bc72529a6 Bump astral-sh/setup-uv from 9.0.0 to 10.0.1 in /.github/actions/restore-python (#18462)
Signed-off-by: dependabot[bot] <support@github.com>
2026-08-17 14:42:08 -05:00
dependabot[bot] 6d20943a9c Bump esphome/workflows/.github/workflows/stale.yml from 61fd37a044cad4e9aa4303027b2a61b6a34da855 to a1c1485ab46ef41a84a6a9d8abd7fa4b7628fd70 (#18464)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-17 15:38:06 -04:00
J. Nick Koston a3af82867b [api] Bump noise-c to 0.1.18 (#18451) 2026-08-17 14:00:39 -05:00
J. Nick Koston 27483a4101 [core] Retry gh CLI calls on transient network errors in CI scripts (#18292) 2026-08-17 13:15:31 -05:00
J. Nick Koston 6d20ebc66b [socket] Shorten lwip TAG string (#18450) 2026-08-17 17:18:43 +00:00
J. Nick Koston 031a038b49 [deep_sleep] Shorten bk72xx TAG string (#18445) 2026-08-17 11:56:37 -05:00
J. Nick Koston f6c7434b2a [i2c] Shorten platform backend TAG strings (#18440) 2026-08-17 11:54:55 -05:00
J. Nick Koston 3d9fecb562 [remote_receiver] Shorten esp32 TAG string (#18447) 2026-08-17 11:54:52 -05:00
J. Nick Koston ebe93e2c68 [bluetooth_connection] Shorten platform TAG strings (#18449) 2026-08-17 11:54:35 -05:00
J. Nick Koston 1f4fcead38 [nextion] Shorten upload TAG strings (#18448) 2026-08-17 11:54:29 -05:00
J. Nick Koston 1f000ba668 [mqtt] Shorten esp32 backend TAG string (#18446) 2026-08-17 11:54:17 -05:00
J. Nick Koston e0d28d7f5c [http_request] Shorten platform backend TAG strings (#18444) 2026-08-17 11:54:03 -05:00
J. Nick Koston 47a58dd799 [internal_temperature] Shorten platform TAG strings (#18443) 2026-08-17 11:53:56 -05:00
J. Nick Koston 37bea1c153 [spi] Shorten platform backend TAG strings (#18442) 2026-08-17 11:53:48 -05:00
J. Nick Koston d1a7b8df8b [adc] Shorten platform TAG strings (#18441) 2026-08-17 11:53:42 -05:00
J. Nick Koston c01f24553c [uart] Shorten platform backend TAG strings (#18439) 2026-08-17 11:53:29 -05:00
J. Nick Koston 07e8b303b9 [ota] Shorten platform backend TAG strings (#18438) 2026-08-17 11:52:56 -05:00
esphome[bot] ebb0923362 Bump aioesphomeapi from 45.10.2 to 45.10.3 (#18433)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-08-16 22:34:00 -05:00
J. Nick Koston e1c279718f [ld2420] Drop the setup priority override so setup runs after the UART bus (#18428) 2026-08-16 21:57:12 -05:00
J. Nick Koston cf764740cf [api] Create the camera image reader lazily (#18421) 2026-08-16 21:56:58 -05:00
J. Nick Koston 58d549ed4c [api] Move NoiseProtocolId off the connection object (#18420) 2026-08-16 21:56:37 -05:00
Jesse Hills 6b22d8068d Merge branch 'beta' into dev 2026-08-17 11:36:10 +12:00
Jesse Hills 443d8f1f28 Merge pull request #18424 from esphome/bump-2026.8.0b4
2026.8.0b4
2026-08-17 11:33:27 +12:00
Jesse Hills 1ec21a2245 Bump version to 2026.8.0b4 2026-08-17 10:22:41 +12:00
J. Nick Koston bb7d4c3630 [esp32] Split crash handler addr2line hint per core (#18418) 2026-08-17 10:22:39 +12:00
J. Nick Koston f42fe9af29 [core] Skip redundant ESP8266 main loop wake posts from ISR context (#18416) 2026-08-17 10:22:39 +12:00
J. Nick Koston 0bc2d71370 [bk72xx_ble] Fail early with a clear error on non BLE 5.x SoCs (#18406) 2026-08-17 10:22:39 +12:00
luar123 594c12b3d9 [zigbee] bump esp-zigbee-sdk to 2.0.4 (#18415) 2026-08-17 10:22:39 +12:00
Jonathan Swoboda bca72e9b6d [sensor] Pass NaN through the delta filter again (#18400) 2026-08-17 10:22:39 +12:00
J. Nick Koston ce09504c92 [platformio] Skip ccache when the binary on PATH fails to run (#18407) 2026-08-17 10:22:39 +12:00
esphome[bot] dda4566b9e Bump bundled esphome-device-builder to 1.11.0 (#18403) 2026-08-17 10:22:39 +12:00
Samuel Sieb 46a5665a66 [rotary_encoder] account for min and max value when resetting (#18197) 2026-08-17 10:22:39 +12:00
esphome[bot] 9161f74bb1 Bump bundled esphome-device-builder to 1.10.0 (#18389) 2026-08-17 10:22:39 +12:00
J. Nick Koston 3f01f9895f [esp32_hosted] Require ESP-IDF 5.3 or newer (#18417) 2026-08-16 12:44:49 -07:00
J. Nick Koston 801a1817b5 [esp32] Split crash handler addr2line hint per core (#18418) 2026-08-16 12:44:00 -07:00
J. Nick Koston 32c76ae828 [core] Skip redundant ESP8266 main loop wake posts from ISR context (#18416) 2026-08-16 12:43:27 -07:00
J. Nick Koston c664f5fc95 [bk72xx_ble] Fail early with a clear error on non BLE 5.x SoCs (#18406) 2026-08-16 11:06:22 -07:00
luar123 2bc4681fd6 [zigbee] bump esp-zigbee-sdk to 2.0.4 (#18415) 2026-08-16 13:33:25 -04:00
Jonathan Swoboda 646501b0ef [sensor] Pass NaN through the delta filter again (#18400) 2026-08-16 12:36:51 -04:00
J. Nick Koston de3e657d8b [platformio] Skip ccache when the binary on PATH fails to run (#18407) 2026-08-16 08:42:47 -07:00
esphome[bot] 1add726892 Bump bundled esphome-device-builder to 1.11.0 (#18403) 2026-08-15 13:36:52 -07:00
Samuel Sieb 5a000cf5e4 [rotary_encoder] account for min and max value when resetting (#18197) 2026-08-15 11:16:04 -07:00
Joppy Furr 6ed676fe32 [lvgl] Restore long_press_repeat_time functionality (#18393) 2026-08-14 23:14:53 -07:00
esphome[bot] 7cceddb8a3 Bump bundled esphome-device-builder to 1.10.0 (#18389) 2026-08-15 01:48:27 +00:00
J. Nick Koston 039b897e7b [ethernet] Defer clk_mode removal to 2026.11.0 (#18380) 2026-08-14 18:45:36 -07:00
J. Nick Koston b178f74e5d [core] Save the validated config cache on the first upload or logs run (#18367) 2026-08-14 18:45:23 -07:00
Clyde Stubbs e5224e22ae [ci] Compare merge-branch base ref against the default branch (#18385) 2026-08-15 06:18:22 +10:00
Clyde Stubbs be66e8b99c [ci] Disable CodSpeed benchmarks job outside esphome/esphome (#18372) 2026-08-15 04:33:47 +10:00
Jesse Hills 4b91c8aff4 Merge branch 'beta' into dev 2026-08-14 23:15:57 +12:00
Jesse Hills 9d7997f55a Merge pull request #18368 from esphome/bump-2026.8.0b3
2026.8.0b3
2026-08-14 22:49:40 +12:00
Jesse Hills 4db47de556 Bump version to 2026.8.0b3 2026-08-14 17:36:56 +12:00
Karl Beecken c8de632764 [core] fix PYTHONPATH leak (#18360) 2026-08-14 17:36:54 +12:00
J. Nick Koston b794b7b1d1 [core] Restore cv.parse_esphome_version as a deprecated helper (#18366) 2026-08-14 17:36:54 +12:00
J. Nick Koston add18d4e35 [core] Partially revert "Hash entity keys from the raw name to fix collisions" (#18361) 2026-08-14 17:36:54 +12:00
J. Nick Koston 02c1810c3a [core] Load component aliases from a generated registry (#18335) 2026-08-14 17:36:54 +12:00
J. Nick Koston 4f3153375a [ota] Retry uploads that fail from network errors (#18332) 2026-08-14 17:36:54 +12:00
J. Nick Koston 1c3a67b5e8 [wifi] Fix ESP8266 crash in cnx_node_search when lwIP transmits after disconnect (#18333) 2026-08-14 17:36:54 +12:00
J. Nick Koston 236ff33a09 [esp32_ble] Silence spurious warnings for local key GAP events (#18359) 2026-08-14 17:36:54 +12:00
J. Nick Koston 7c07fb48c5 [esp32_ble_tracker] Fix missed BLE advertisements with WiFi on ESP-IDF 5.5.5 (#18356) 2026-08-14 17:36:54 +12:00
J. Nick Koston d72bab79d7 [web_server_base] Stop deleting the web server on captive portal teardown (#18324) 2026-08-14 17:36:54 +12:00
Karl Beecken 617e2ec1e0 [core] fix PYTHONPATH leak (#18360) 2026-08-14 17:23:34 +12:00
J. Nick Koston 1afac0312d [core] Restore cv.parse_esphome_version as a deprecated helper (#18366) 2026-08-14 17:22:22 +12:00
J. Nick Koston b05465145f [core] Add preference key stability integration test (#18364) 2026-08-13 21:14:30 -05:00
dependabot[bot] 990fc402fd Bump bleak from 2.1.1 to 3.0.2 (#16246)
Signed-off-by: dependabot[bot] <support@github.com>
2026-08-13 20:24:02 -05:00
J. Nick Koston dd51624fbb [core] Partially revert "Hash entity keys from the raw name to fix collisions" (#18361) 2026-08-13 20:06:48 -05:00
dependabot[bot] 45a056e337 Bump platformdirs from 4.11.1 to 4.11.2 (#18363)
Signed-off-by: dependabot[bot] <support@github.com>
2026-08-13 18:54:51 -05:00
dependabot[bot] 37782f7206 Bump prek from 0.4.12 to 0.4.13 (#18362)
Signed-off-by: dependabot[bot] <support@github.com>
2026-08-13 18:54:37 -05:00
J. Nick Koston 945c2458b3 [core] Load component aliases from a generated registry (#18335) 2026-08-13 13:31:19 -05:00
J. Nick Koston 7420d23867 [ota] Retry uploads that fail from network errors (#18332) 2026-08-13 13:31:06 -05:00
J. Nick Koston 191686c5b3 [wifi] Fix ESP8266 crash in cnx_node_search when lwIP transmits after disconnect (#18333) 2026-08-13 13:30:53 -05:00
J. Nick Koston 137351fa8d [esp32_ble] Silence spurious warnings for local key GAP events (#18359) 2026-08-13 13:27:01 -05:00
J. Nick Koston db5173697a [esp32_ble_tracker] Fix missed BLE advertisements with WiFi on ESP-IDF 5.5.5 (#18356) 2026-08-13 13:22:48 -05:00
c7940382a9 [const] move CONF_LABEL to components/const (#18354)
Co-authored-by: Oliver Kleinecke <kleinecke.oliver@googlemail.com>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-08-13 14:18:22 -04:00
esphome[bot] 87045ab9c0 Bump aioesphomeapi from 45.10.1 to 45.10.2 (#18357) 2026-08-13 11:14:14 -05:00
Oliver KleineckeandOliver Kleinecke f1c4086778 [const] Move CONF_SLOT to components/const (#18350)
Co-authored-by: Oliver Kleinecke <p1ngb4ck@users.noreply.github.com>
2026-08-13 23:36:11 +12:00
dependabot[bot]andJonathan Swoboda f337d0acff Bump pylint from 4.0.6 to 4.0.7 (#18320)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-08-13 02:13:29 +00:00
J. Nick Koston 9cc05b30d4 [web_server_base] Stop deleting the web server on captive portal teardown (#18324) 2026-08-12 21:09:01 -05:00
Jesse Hills e0b68c4d6d Merge branch 'beta' into dev 2026-08-13 14:06:46 +12:00
Jesse Hills 9c3407cfdd Merge pull request #18334 from esphome/bump-2026.8.0b2
2026.8.0b2
2026-08-13 14:06:28 +12:00
Jesse Hills d3e27054f6 Bump version to 2026.8.0b2 2026-08-13 13:30:47 +12:00
J. Nick Koston bd58b5c8b3 [core] Retry framework downloads on transient network errors (#18330) 2026-08-13 13:30:46 +12:00
J. Nick Koston c1a326f32e [core] Don't block logs startup on MQTT IP discovery when addresses are known (#18313) 2026-08-13 13:30:46 +12:00
J. Nick Koston a14ea0e8fa [ld2420] Fix out-of-bounds read when device reports unknown command error (#18322) 2026-08-13 13:30:46 +12:00
esphome[bot] 48d6368ff9 Bump bundled esphome-device-builder to 1.9.6 (#18328) 2026-08-13 13:30:46 +12:00
Kevin Ahrendt 83cff59fdd [sendspin] Bump sendspin-cpp to v0.7.2 (#18316) 2026-08-13 13:30:46 +12:00
esphome[bot] 89489b1f0d Bump aioesphomeapi from 45.10.0 to 45.10.1 (#18318) 2026-08-13 13:30:46 +12:00
J. Nick Koston 7569a7b5ce [usb_uart] Fix uint32_t format specifier warning in pl2303 (#18310) 2026-08-13 13:30:45 +12:00
6373 changed files with 276854 additions and 71465 deletions

No files matched your search

+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.
+123
View File
@@ -0,0 +1,123 @@
---
name: hardware-test
description: Test an ESPHome change on real boards over USB. Use this whenever a task asks to test, verify, reproduce, bench, A/B or soak a pull request, a branch or a crash on hardware, or mentions flashing, serial logs, esphome upload, a captive portal or fallback AP test, or results to post on a PR. Covers finding the serial ports, building from the right checkout, flashing each chip family (including the LibreTiny kits that stock tools cannot flash), bounded log capture, proven test flows and how to report results.
allowed-tools: Read, Write, Edit, Bash, Glob, Grep
---
# Hardware testing
The goal of a hardware test is evidence a reviewer can trust: which build ran on which board, what was
driven, what the logs show, and the same flow on a `dev` build when a quirk could be pre-existing.
`$SKILL` below is this directory (`.agents/skills/hardware-test`).
## Ground rules
1. **Serial ports cannot be shared.** One bounded capture per port, stopped before any flash on that
port, never an open-ended `esphome logs`. Stop a capture with `pkill -f "[s]erial_capture.py <port> "`,
naming the port so a capture on another board survives; the bracket keeps the pattern from matching
the shell that runs it. Never run `esphome logs` in a
`while true` loop: the loop respawns it and holds the port.
2. **Credentials stay local.** Put a `secrets.yaml` next to the scratch configs and reference it with
`!secret`. Never paste a config with credentials into a PR, never commit scratch configs or logs.
3. **Build from the tree under test.** `python -m esphome` puts the current directory first on
`sys.path`, so run it with the checkout or worktree as the working directory and pass the yaml by
its absolute path. A venv `esphome` entry point may import an installed snapshot instead of the
checkout. Confirm with `python -c 'import esphome; print(esphome.__file__)'`, and for an A/B grep
the build's `src/` for the change before trusting any number.
4. **PR build and baseline build get different `name:` values.** Builds share
`.esphome/build/<name>`, so the same name means `esphome upload` can flash the wrong binary. Saved
Wi-Fi credentials are keyed by the name too, so a test that must start unprovisioned gets a fresh
name every run.
5. **Flash and RAM deltas are measured against the PR's merge base** (`git merge-base origin/dev HEAD`
checked out in a throwaway worktree), with the baseline commit named in the table. When a delta
looks odd, diff symbol sizes (`nm -S -C --size-sort` from the matching toolchain) before quoting it.
6. **Prove a quirk is pre-existing before blaming the PR:** run the identical flow on a `dev` build in
the same session and say so in the results.
7. **Pipe safely.** Set `PYTHONUNBUFFERED=1` on any `esphome run` or `esphome logs` whose output is
piped, or lines land minutes late. Bound every long command with `timeout` (coreutils) or
`perl -e 'alarm shift; exec @ARGV' <seconds> <command>` on macOS without it.
## Workflow
1. **Find the boards:** `python $SKILL/scripts/ports.py` (any Python with pyserial, for example the
ESPHome venv). Port names follow the USB hub port and change on every replug, so never reuse a name
from an earlier session. CH340 adapters (D1 mini, BW15 kit, BK7238, CB3S) all look the same; tell
them apart with a 10 s capture using `serial_capture.py --reset` (ESP8266 prints its ROM banner at
74880 baud, LibreTiny boards print `[LT]` and ESPHome lines, BK7238 prints its bootloader version).
`ports.py --wait 2e8a` blocks until a Pico re-enumerates.
2. **Make a scratch directory** outside the checkout, with `secrets.yaml` and the templates you need.
One yaml per board per variant, file name equal to `name:`.
3. **Compile** in the right tree:
`(cd <worktree> && PYTHONUNBUFFERED=1 timeout 1500 python -m esphome compile <abs yaml> > <name>-compile.log 2>&1)`.
Keep the RAM and flash lines at the end. Two ESP-IDF or two LibreTiny builds at the same time race
over tool caches; run them one at a time.
4. **Flash** per chip family (table below). Stop the capture on that port first.
5. **Capture** with `serial_capture.py PORT SECONDS OUT` in the background, then poll the file with
`grep` in an `until ... sleep 2` loop with a cap. USB CDC boards (Pico, nRF52, native USB ESP32)
drop the port on reboot; the script reopens it until its deadline.
6. **Drive** the test: a second board running a client config, `curl`, an aioesphomeapi script that
presses a template button and subscribes to logs, MQTT publishes, or `interval:` and `on_boot:`
automations in the config itself.
7. **Analyze** with `grep -a -c` counts over the capture and keep an `-events.txt` extract of the lines
that matter. Decode crashes with the platform's `addr2line` (see `reference/boards.md`).
8. **Report** on the PR as a table: build SHA, board and framework, what was driven, counts, result,
plus the `dev` control row and the exact flow in a `<details>` block. No credentials, no port names,
no IP addresses.
## Flashing by chip family
| Board | Flash | Log |
|---|---|---|
| ESP8266 D1 mini (CH340) | `python -m esphome upload <yaml> --device <port>` | `serial_capture.py` at 115200; `esphome logs` on some CH340s emits garbage after the RTS reset. A failed esptool write can wedge the adapter until the board is replugged. `esptool erase_flash` before a test that needs an unprovisioned device: the preference sector survives a reflash |
| ESP32 devkit (CP2102 or CH340) | `esphome upload --device <port>`, ESP-IDF or Arduino | `serial_capture.py`. Default Wi-Fi light sleep delays API driven actions by seconds; judge timing from device logs. `power_save_mode: none` for OTA overlap tests |
| ESP32-S3, C3, C6, H2 native USB | `esphome upload --device /dev/cu.usbmodem*` | `logger: hardware_uart: USB_SERIAL_JTAG`. The USB console can wedge when the app takes over the console (0 bytes, esptool `No serial data received`); replug, or watch through `esphome logs --device <name>.local` |
| Pico W / Pico 2 W | `esphome upload --device <port>` (1200 baud touch); first flash ever via BOOTSEL and `--device /Volumes/RPI-RP2` or `RP2350` | port re-enumerates after upload: `ports.py --wait 2e8a` then capture |
| BW15 kit (RTL8720CF, rtl87xx) | `~/.platformio/penv/.libretiny/bin/python $SKILL/scripts/flash_rtl87xx_kit.py flash write -d <port> <build>/.pioenvs/<name>/*.uf2`. Plain `esphome upload` and plain ltchiptool time out because opening the port asserts DTR/RTS and holds the chip in reset | `serial_capture.py --released`, never `esphome logs` |
| BK7238 / CB3S (bk72xx, RTS on CEN) | `~/.platformio/penv/.libretiny/bin/python $SKILL/scripts/flash_bk72xx.py flash write -d <port> <uf2>` (about 75 s) | logs are on UART2 by default; `logger: hardware_uart: UART1` puts them on the flashing adapter. Opening the port resets the chip, so keep one capture open for the whole test |
| LN882H | ltchiptool forces 1 Mbaud for writes; weak adapters need `LN882H_YM_BAUDRATE` patched to 115200 in the penv's `ln882htool.py`; short BOOT to GND while the tool connects | OTA after the first flash |
| nRF52840 Dongle (PCA10059) | Nordic `nrfutil nrf5sdk-tools dfu usb-serial` on the bootloader port, or `dfu: true` in the config so the 1200 baud touch pin-resets into the bootloader; adafruit-nrfutil is refused | app has two CDC ports, ESPHome log on the second |
| Adafruit-style nRF52 (UF2 bootloader) | `UF2BOOT` volume or adafruit-nrfutil; `dfu: true` works through GPREGRET | single CDC port |
| Any reachable device | `esphome upload <yaml> --device <ip or name.local>`; `--file <bin>` for a prebuilt image | `esphome logs --device <ip>` bounded with `timeout` |
ESP-IDF OTA image: `.esphome/build/<name>/build/firmware.ota.bin`. LibreTiny OTA port is 8892.
## Scripts in `$SKILL/scripts`
- `ports.py`: list ports with VID:PID, product, serial and a board guess; `--wait VID[:PID]`,
`--product NAME`, `--serial SN` (one specific board, whatever path it comes back under) and
`--serial-of PORT` (the serial number behind a path).
- `serial_capture.py PORT SECONDS OUT [--baud] [--released] [--reset] [--append]`: bounded timestamped
capture, ANSI stripped, `LOG_SECRET` text written as `<secret>`, reopens a vanished port, exits at the
deadline. OUT is truncated unless `--append` is given, so counts over it cover one run.
- `flash_rtl87xx_kit.py`: ltchiptool wrapper for the BW15 kit whose CH340 drives CEN (RTS) and the boot
strap (DTR). `flash read` needs the family `realtek-ambz2`; `flash write` does not.
- `flash_bk72xx.py`: ltchiptool wrapper for BK72xx boards whose RTS drives CEN.
- `wifisave_hammer.sh KIND DUT_PORT DUT_YAML DUT_TREE CLIENT_PORT CLIENT_YAML CLIENT_TREE [SETTLE]`:
the fallback AP plus captive portal hammer end to end (compile, flash, capture, counts) with the
templates below. KIND is `esp`, `pico`, `bw15` or `bk`. It ends with `RESULT PASS` (exit 0),
`RESULT FAIL` (exit 3: a crash, a reboot after the AP came up, or the device never joined the network),
`RESULT INVALID`
(exit 2: a precondition was missed, the stimulus was insufficient, or a capture did not last the run)
or exits 1 when a build, flash or port step failed.
## Templates in `$SKILL/templates`
- `dut-fallback-ap.yaml` (package): a device that never finds its network, falls back to its AP and
serves the captive portal. `example-dut.yaml` is the per-run file with the platform block.
- `client-wifisave.yaml` (package): joins that AP, posts N bogus `/wifisave` requests, then the real
credentials from `!secret`. `example-client.yaml` is the per-run file. `bogus_saves: "1"` gives a
plain provisioning check.
## When something looks wrong
- A capture stopped early or a line is missing: check the timestamps before trusting it.
- The client never joins the AP: another board may still run an AP with the same SSID, or the device
booted with saved credentials because the name was reused. One AP device at a time on the bench.
- Builds come out byte-identical between branches: the wrong tree was imported (rule 3).
- Multicast from the host never arrives on some access points: test it device to device. Broadcast
is lossy under Wi-Fi power save, unicast is not. Throttle UDP floods to a fixed packet rate.
- `api:` reboots a board after 15 min without a client: `reboot_timeout: 0s` on `api:` and `mqtt:` for
soak tests.
`reference/boards.md` has per-chip facts and crash decoding; `reference/recipes.md` has the proven
flows. Add new findings there.
@@ -0,0 +1,138 @@
# Board facts
Ports are not listed on purpose: they follow the USB hub port and change on every replug. Run
`scripts/ports.py`.
## ESP8266 (D1 mini, NodeMCU, CH340 or CP2102)
- Opening the port usually resets the board through DTR, but not always; `serial_capture.py --reset`
pulses RTS to capture from boot. 74880 baud shows the ROM boot messages (`rst cause`, `boot mode`).
- `esphome logs` on some CH340s emits thousands of duplicated garbage lines right after the RTS reset;
a plain pyserial capture at 115200 is reliable.
- A failed esptool write can wedge the adapter (`termios` error 22, or an open that hangs) until replug.
- The preference sector survives a reflash: a provisioned API key or saved Wi-Fi credentials stay
active. `esptool erase_flash` gives a clean device.
- `Panic core_esp8266_main.cpp:191 __yield` with `ctx: sys` means a yield from lwIP context (a web
handler, an lwIP callback). Decode stacks with
`xtensa-lx106-elf-addr2line -e <build>/.pioenvs/<name>/firmware.elf -f -C -i <addr...>` from the
toolchain under `~/.platformio/packages/toolchain-xtensa/bin/` (PlatformIO builds) or the ESPHome
cache directory (native toolchain builds).
- Constant data lives in RAM unless marked `PROGMEM`; with 80 KB of RAM a few hundred bytes are worth a
row in the PR.
- AsyncMqttClient drops inbound topics over 128 characters; topics of 255 or more wrap a counter and
stall the link until the broker drops TCP.
## ESP32 classic devkit
- Takes ESP-IDF and Arduino. Default Wi-Fi light sleep delays API driven actions by up to 6 s; judge
timings from device logs, not from the host. `power_save_mode: none` for OTA overlap tests (an IDF web
upload of 739 KB took 107 s with power save and 45 s without).
- Receives only a few percent of IPv6 multicast on some access points with power save on; a raw lwIP
socket loses the same, so that is the AP or driver, not a PR.
- Decode with `xtensa-esp32-elf-addr2line` from the IDF tools directory against
`<build>/.pioenvs/<name>/firmware.elf` (PlatformIO) or `<build>/build/<name>.elf` (IDF native).
- Clean reboot between runs with the capture stopped: `esptool --after hard_reset chip_id`.
- Trigger a forced crash only after the app is marked valid (45 s after boot is plenty), never on an
image still pending OTA rollback.
- Signed OTA testing: build with `esp32.framework.advanced.signed_ota_verification.verification_keys`
under a different name, sign with `python -m espsecure sign_data --version 2 --keyfile
tests/components/esp32/dummy_signing_key.pem`, upload with `esphome upload --file`. The verifier
refuses unsigned images afterwards, so restore with a signed plain build.
## ESP32-S3, C3, C6, H2 with native USB
- `/dev/cu.usbmodem*`, esptool works, `logger: hardware_uart: USB_SERIAL_JTAG`.
- The USB console can die at the moment the app takes over the console during a reboot (log stops after
`Disabling RNG early entropy source`, then 0 bytes, esptool reports `No serial data received`). Only a
USB replug fixes it. Fallback: `esphome logs --device <name>.local` (API log stream, which drops lines
while Wi-Fi is off).
- The ESP32-P4 before silicon revision 3 is bricked by rev3-only firmware (boot loop at bootloader entry).
## RP2040 / RP2350 (Pico W, Pico 2 W)
- The CDC port re-enumerates after every reboot and after `esphome upload` (1200 baud touch). Re-detect
with `ports.py --wait 2e8a` before capturing; `serial_capture.py` reopens on its own if the port
vanishes mid-capture.
- First flash ever: BOOTSEL, then `esphome upload --device /Volumes/RPI-RP2` (or `RP2350`).
- arduino-pico creates the lwIP netif in `WiFi.begin()`, so any IGMP join during component setup fails;
joins must wait for `network::is_connected()`.
- Parallel Pico builds race on package extraction after a PlatformIO package wipe; one at a time.
- An RTS pulse from pyserial does not reset the board; reboot with `App.safe_reboot()` or the touch.
## BW15 kit (RTL8720CF, LibreTiny rtl87xx)
- The kit's CH340 wires RTS to CEN and DTR to PA00 (boot strap). A plain pyserial open asserts both and
holds the chip in reset, which is why `esphome upload`, plain `ltchiptool` and `esphome logs` all fail
("Timeout while linking", or silence). `scripts/flash_rtl87xx_kit.py` sets DTR high and RTS released
on every open, pulses RTS once so the ROM answers `ping`, and after the command drops DTR and pulses
RTS to boot the app. Run it with the PlatformIO LibreTiny penv Python.
- `flash write -d <port> <uf2>` takes no family argument; `flash read -d <port> -s 0 -l <len>
realtek-ambz2 <out>` does.
- Capture with `serial_capture.py --released`. The ESPHome log is on the flashing UART already.
- The macOS CH340 driver only pushes DTR/RTS to the wire once a session has driven them, so the first
plain open after a replug streams logs and the second is silent; always open with the lines released.
- Driver quirks seen on `dev`: `WiFi.begin` can refuse the real network on the first try after bogus
credentials (it connects after two cooldowns); right after connecting while the AP is still up the
driver can bus-fault in `rtw_is_adapter_up` (`R0 = 0xdeadbeef`) and reboot; the AP restarts on every
STA retry, so stations cannot stay joined; `WiFi.begin` fails synchronously for an absent network and
lands in a cooldown at once; connecting blocks the loop 2.5 to 4.1 s.
- Decode faults with `arm-none-eabi-addr2line -e <build>/.pioenvs/<name>/raw_firmware.elf -f -C -i <PC>`
from `~/.platformio/packages/toolchain-gccarmnoneeabi/bin/` (`raw_firmware.elf`, not `firmware.elf`).
Fault dumps print PC, LR and R0 to R3.
- The AmebaZ2 SDK sets `LWIP_TCPIP_CORE_LOCKING 0`.
## BK7238, WB3S, CB3S (BK7231N) (LibreTiny bk72xx, CH340, RTS on CEN)
- Stock ltchiptool closes and reopens the port after its reset, and pyserial raises both lines on open,
holding CEN low through the BootROM window. `scripts/flash_bk72xx.py` opens with both released and
pulses the reset right before the link checks. A write takes about 75 s. Only a CEN or power reset
reaches the downloader; no software reboot does.
- ESPHome logs on UART2 by default; `logger: hardware_uart: UART1` puts them on the flashing adapter.
- Opening the port resets the chip (bootloader banner on every open, even with DTR/RTS low), so keep one
capture open for the whole test and never reopen mid-phase.
- Callbacks run on the `async_tcp` task and log lines can lose a byte. Wi-Fi connect takes tens of ms;
radio enable 160 to 278 ms, disable about 200 ms.
## BW16 kit (RTL8720DN, AmebaD)
- The kit's own USB-C (CH340) is on LOG_UART and flashes fine. Its two-transistor circuit resets while
RTS alone is asserted and straps BURN while DTR alone is; both asserted does nothing. ROM entry is
esptool's classic sequence (`dtr:false,rts:true` 100 ms, `dtr:true,rts:false` 400 ms, both false); the
first pulse after a replug can be lost, retry up to three times.
- Any reopen resets the chip, 460800 is flaky, 115200 is solid: one open per session, in-place baud
changes only. The ROM and the loader NAK continuously while idle, so ACK scans must drain first.
- The AmebaD core has no mDNS responder (`mdns: disabled: true`).
## LN882H
- ltchiptool forces 1 Mbaud for writes; adapters that cannot hold it fail with "Timeout while linking"
or die mid-write. Patch `LN882H_YM_BAUDRATE` in the penv's `ltchiptool/soc/ln882h/util/ln882htool.py`
to 115200 (lost on reprovision). Weak 3.3 V from the adapter: feed the module's 5 V pad instead. Short
BOOT to GND while the tool connects. OTA afterwards.
- `USE_WIFI_AP_EXCLUSIVE` (one radio mode at a time) exists only on LN882x; compile-check changes there.
## nRF52840
- Nordic PCA10059 dongle: the factory Open DFU bootloader enumerates as `1915:521F` on its own port
name; the app as `2FE3:0100` with two CDC ports, ESPHome log on the second. Only Nordic
`nrfutil nrf5sdk-tools` works (`pkg generate --hw-version 52 --sd-req 0x00 --application-version 1
--application zephyr.hex app.zip`, then `dfu usb-serial -pkg app.zip -p <port>`); ESPHome downloads
it on the first build. GPREGRET does not enter the bootloader; a pin reset through P0.19 does, which
`dfu: true` wires up on this board. A pulsing red LED means bootloader; the side RESET button
re-enters it.
- Makerdiary MDK USB dongle: UF2 bootloader without SoftDevice, app at 0x1000, `UF2BOOT` volume,
adafruit-nrfutil, GPREGRET 0x57 works. RGB LED active low (red P0.23, green P0.22, blue P0.24),
button P0.18.
## ESP Thread Border Router board v1.2 (S3 host + H2 RCP)
- S3 host on USB JTAG serial, H2 RCP on its own USB. Spinel UART1 from the host: `rx_pin: GPIO17`,
`tx_pin: GPIO18`, RCP reset `GPIO7` active low, 460800. The config needs `network: enable_ipv6: true`.
- The shipped H2 firmware fails `CheckRadioCapabilities` with IDF 5.5; build `examples/openthread/ot_rcp`
for esp32h2 with ESPHome's own IDF (`IDF_PATH` at the cached framework, its penv and tools,
`IDF_PYTHON_CHECK_CONSTRAINTS=no`) and flash with esptool at 0x0, 0x8000 and 0x10000.
- A crash-looping S3 pulses the H2 reset every boot, so the H2 cannot be flashed and the S3 will not
enter its bootloader over USB. Hold S3 BOOT, tap S3 RST, flash the H2, release, then replug both.
- `dns-sd -L <name> _meshcop._udp local.` shows the `omr` prefix; it disappears when routing is dead.
Keep one capture per chip; never reset the H2 over USB mid-test unless that is the test (the host
logs `Unexpected RCP reset: RESET_OTHER` and recovers).
@@ -0,0 +1,117 @@
# Proven test flows
Each flow names the pull requests it was used on, so their result tables show what to post.
## Captive portal `/wifisave` hammer (#20802, #20784)
Reproduces a `/wifisave` arriving during the STA cooldown, and proves a fix on every board.
Device under test: `templates/example-dut.yaml` (bogus network, `ap:` with `ap_timeout: 15s`,
`captive_portal:`). Client: `templates/example-client.yaml` on an ESP32 or a D1 mini; it joins the
device's AP, posts 15 bogus saves 3 s apart, then the real credentials. With the portal up the cooldown
is 30 s, so nearly every save lands inside one. Driver: `scripts/wifisave_hammer.sh`. Settle 40 s on
ESP boards, 90 s on the BW15 (its first attempt on the real network fails, see boards.md).
Counts that matter: `Requested WiFi Settings Change` (saves received), `Exiting cooldown early`, crash
markers, boots (`Log initialized`), and the client's `bogus save status 200` and `real save status 200`.
A healthy run: every save answered 200, one cooldown exit per save (two on the BW15, whose retry enters
the adapter restart), no panic, and the real credentials connect the device within about 10 s.
Fault injection for AP start failures: wrap `this->ap_setup_ = this->wifi_start_ap_(this->ap_)` in
`setup_ap_config_()` with an `HWTEST_AP_FAIL_COUNT` build flag
(`esphome: platformio_options: build_flags: -DHWTEST_AP_FAIL_COUNT=2`), raise the portal's "Captive
portal started" from LOGV to LOGI, and after provisioning curl the device's STA address: connection
refused proves the portal's server is gone.
## `reboot_timeout` with a fallback AP (#9958)
Only one board runs the AP at a time, or stations join the wrong one. AP probe: bogus network,
`ap: {ssid, password, ap_timeout: 5s}`, `reboot_timeout: 60s`, `logger: level: VERBOSE` (ESP32 and
ESP8266 log `AP client connected MAC=` at verbose), `on_boot: logger.log: BOOT`. Station probes:
`reboot_timeout: 0s`, `interval: 150s` running `wifi.disable`, `delay: 20s`, `wifi.enable` (60 s cycles
are too short to cover the due moment). Pass pattern: "Can't connect; rebooting" exactly 60 s after boot
with no station joined, no reboot for minutes while one is, then a reboot within a second of the last
disconnect. The BW15 restarts its AP on every STA retry and cannot be tested this way.
## MQTT (#20433, #17935, #20762, #20767, #20768)
A local mosquitto with `allow_anonymous true`, `log_type all` and `log_dest file`: the broker log gives
each subscription's QoS, the device address (`New client connected from <ip> as <name>-<mac>`) and exact
subscribe counts. Kill and restart mosquitto to exercise `on_disconnect` (reason 0) and resubscription.
Keep `reboot_timeout: 0s` on `api:` and `mqtt:`. The ESP-IDF outbox swings free heap about 2 kB right
after bulk subscribe and unsubscribe; read the heap seconds later over several rounds before calling a
leak. Spawned subscriptions need about 2.5 s to reach the broker before the next publish.
`esphome logs --device MQTT` only uses the MQTT transport when the config has no `api:`. Driver pattern: a
paho script publishes and polls the serial capture file.
## UDP, multicast, sockets (#20556 to #20559, #20585, #20590)
A listener on port 9 needs no privileges on macOS. Test multicast device to device; host to device
multicast never arrives on some access points, and `239.255.x.x` groups are unreliable even to a stock
listener. Broadcast to boards is lossy under power save, unicast is not. Flood at a fixed packet rate
(the host hits `ENOBUFS` unthrottled); 1500 packets per second survived on every board. A forced Wi-Fi
reconnect on the Pico keeps IGMP membership. Set a long loop interval from `wifi.on_connect`
(`App.set_loop_interval(20000)`), not `on_boot`, or Wi-Fi connect crawls. The `logger.log` action
rejects `%.*s`; use a lambda.
## OTA overlap and lock (#20588), OTA timeouts (#19041), deflate (#19037)
All overlap orders (a web upload during a native OTA and the reverse) must be refused; the lock release
after an interrupted web upload waits about 14 s (ESP8266) or 7 s (ESP-IDF) for disconnect detection. A
refused web client on `web_server_idf` never gets the busy reply; the connection drops at reboot.
`pkill -f "a\|b"` does not alternate; use two pkills.
## DHCP lease cache (#17767)
Cycle 20 s awake, `deep_sleep.enter 30s`, wake, 20 s, `App.safe_reboot()`, logging `millis()` at the
`wifi.connected` condition. An RTS hardware reset must fall back to DHCP; a software reboot and a deep
sleep wake must apply the cache. `esphome run --no-logs` silently skipped the upload on one board; use
`upload` then `logs`.
## HDMI-CEC two-board wire (#9189)
Two boards on one wire: promiscuous mode, a 0x8F request answered with 0x90 0x00, a `destination:`
filter on the reply handler, template buttons for each target plus lambdas calling
`send(source, dest, span)` as another initiator. Healthy numbers: device-side turnaround 15 to 22 ms,
round trip 144 to 156 ms. Press buttons over the API; judge timings from device logs (ESP32 light sleep
lags the press).
## LD2450 polygon zones (#20154)
To make a target "leave" without anyone moving, set the radar's Zone-1 numbers over the target and
switch the zone type to `Filter` (the count drops to 0 within 0.5 s); set it back to `Disabled` after.
Writing those numbers blocks the ld2450 loop about 50 ms.
## Scheduler and loop wake A/B (#20356)
An external component whose template button arms an SDK `os_timer` (50 ms, one shot) on the ESP8266;
the callback runs in SYS context, records `millis()` and calls `set_timeout` or `defer`; the timeout
logs the elapsed time and loop count delta. Drive with an aioesphomeapi script (`subscribe_logs` plus
`button_command`). Build the same yaml twice with different names, one per tree.
## Noise handshake and OTA encryption on the ESP8266 (#19062, #19233)
Host side timing is useless on a weak link; use an on-device bench with the minimum of five runs over
two boots. Provision a key with `noise_encryption_set_key` through aioesphomeapi and upload with
`espota2.run_ota(host, 8266, None, bin, noise_psk=key)` when the key is not in the yaml.
## ESP-NOW on the ESP8266 (#14407)
The NONOS SDK only delivers broadcast ESP-NOW frames while the station is associated or a soft-AP is
up; a hidden, open, client-less soft-AP (60 s beacon, DHCP stopped) makes broadcasts arrive. In AP-only
mode the device transmits from the soft-AP MAC.
## Wi-Fi Easy Connect (DPP) enrollee (#17934)
Only an Android phone configures: Settings, network details, Add device, Share Wi-Fi (the Wi-Fi list's
QR scanner makes the phone an enrollee). Render the URI with the `qrcode` module and open the PNG.
`esp32: framework: log_level: DEBUG` plus `sdkconfig_options: CONFIG_WPA_DEBUG_PRINT: y` prints the
supplicant state machine.
## Thread border router (#18104)
See boards.md for the ESP Thread BR board. Check that the H2 answers Spinel before blaming a PR.
## nRF52840 dongle boards (#19009, #20814)
Test config: the two LEDs as inverted GPIO outputs, the button as an inverted pull-up binary sensor, a
10 s alive interval. To retest the nrfutil download path, move the cached `sdk-nrf/nrfutil` aside first.
@@ -0,0 +1,51 @@
"""ltchiptool for boards whose RTS drives CEN: reset and link on one open port.
Stock ltchiptool closes and reopens the port after its DTR/RTS reset, and
pyserial raises both lines on open, holding CEN low through the BootROM's
listen window. Open with both lines released and pulse the reset right
before the LinkChecks instead.
"""
import sys
import bk7231tools.serial.main as bk_main
from ltchiptool.__main__ import cli
from ltchiptool.soc.bk72xx.flash import BK72XXFlash
from serial import Serial
class ReleasedSerial(Serial):
def __init__(self, port=None, **kwargs):
super().__init__(None, **kwargs)
self.dtr = False
self.rts = False
self.port = port
self.open()
bk_main.Serial = ReleasedSerial
_connect = BK72XXFlash.flash_connect
def flash_hw_reset(self) -> None:
self._reset_pending = True
def flash_connect(self) -> None:
pending = getattr(self, "_reset_pending", False)
if self.bk and self.conn.linked and not pending:
return
self.flash_build_protocol()
if pending:
self._reset_pending = False
self.bk.hw_reset()
self.conn.linked = False # the chip rebooted, so link again below
_connect(self)
BK72XXFlash.flash_hw_reset = flash_hw_reset
BK72XXFlash.flash_connect = flash_connect
sys.argv = ["ltchiptool", *sys.argv[1:]]
cli()
@@ -0,0 +1,87 @@
"""ltchiptool for the BW15 kit whose CH340 drives CEN (RTS) and PA00 (DTR).
A plain open asserts both lines and holds the chip in reset. Here every open keeps DTR high and RTS
released; the first open pulses RTS so the ROM comes up; after the command DTR low plus an RTS pulse
boots the app. The exit status is ltchiptool's; 2 means the flash succeeded but the app was not
booted (boot pulse failed, or no -d PORT to send it to), so the board sits in the ROM until reset.
Usage: python flash_rtl87xx_kit.py flash write -d <port> firmware.uf2
"""
import sys
import time
import serial
_first = [True]
_orig_open = serial.Serial.open
def _open(self):
self.dtr = True # PA00 high: ROM download mode on the next reset
self.rts = False # CEN released
_orig_open(self)
if _first[0]:
_first[0] = False
self.rts = True
time.sleep(0.1)
self.rts = False
time.sleep(0.3) # reset into the ROM
self.reset_input_buffer()
serial.Serial.open = _open
from ltchiptool.__main__ import cli_entrypoint # noqa: E402
def port_argument(argv: list[str]) -> str | None:
for i, a in enumerate(argv):
if a in ("-d", "--device") and i + 1 < len(argv):
return argv[i + 1]
if a.startswith(("-d=", "--device=")):
return a.split("=", 1)[1]
if a.startswith("-d") and len(a) > 2:
return a[2:]
return None
def boot_app(port: str) -> None:
s = serial.Serial(port=None)
s.port = port
s.dtr = False
s.rts = False
_orig_open(s)
s.rts = True
time.sleep(0.1)
s.rts = False
s.close()
def main() -> int:
rc = 1
try:
result = cli_entrypoint.main(args=sys.argv[1:], standalone_mode=False)
rc = int(result) if isinstance(result, int) else 0
finally:
port = port_argument(sys.argv)
if not port:
print(
"[flash_rtl87xx_kit] no -d PORT given, the application was not booted",
file=sys.stderr,
)
if rc == 0:
rc = 2
else:
try:
boot_app(port)
print("[flash_rtl87xx_kit] application booted")
except (serial.SerialException, OSError) as err:
print(f"[flash_rtl87xx_kit] boot pulse failed: {err}", file=sys.stderr)
if rc == 0:
rc = 2 # flashed, but the board is still in the ROM: press reset
return rc
if __name__ == "__main__":
sys.exit(main())
@@ -0,0 +1,123 @@
"""List the boards on USB, or wait for one to enumerate.
ports.py table: port, vid:pid, product, serial number, guess
ports.py --wait 2e8a print the first port with VID 0x2e8a once it appears (default 60 s)
ports.py --wait 1a86:7523 --timeout 30
ports.py --product "USB Serial" first port whose USB product string matches (case-insensitive)
ports.py --wait 2e8a --serial SN the port of one specific board, whatever name it comes back under
ports.py --serial-of PORT print the USB serial number behind a port name
Run with a Python that has pyserial (any ESPHome venv). Port names follow the hub port on macOS, so
re-run this after every replug instead of trusting a name from an earlier session.
"""
import argparse
import sys
import time
from serial.tools import list_ports
GUESS = [
(
(0x1A86, 0x7523),
"CH340: D1 mini, BW15 kit, BK7238/CB3S or LN882H adapter; identify by the boot banner",
),
((0x1A86, 0x55D3), "CH343: ESP32-P4 devkit console"),
((0x1A86, 0x55D4), "CH343: ESP32 devkit"),
((0x1A86, None), "WCH CH34x USB serial bridge"),
((0x10C4, 0xEA60), "CP2102: classic ESP32 devkit (esp32dev)"),
(
(0x303A, 0x1001),
"Espressif USB JTAG serial: S3/C3/C6/H2 native USB (logger hardware_uart USB_SERIAL_JTAG)",
),
(
(0x2E8A, None),
"Raspberry Pi Pico W / Pico 2 W CDC; the port vanishes and comes back on every reboot",
),
((0x1915, 0x521F), "nRF52840 Dongle (PCA10059) in the Open DFU bootloader"),
(
(0x2FE3, 0x0100),
"nRF52840 Dongle running a Zephyr app; two CDC ports, ESPHome log on the second",
),
((0x1915, 0xCAFE), "Makerdiary nRF52840 MDK dongle running a Zephyr app"),
((0x239A, None), "Adafruit style nRF52 board (UF2 bootloader family)"),
]
def guess(vid: int, pid: int) -> str:
for (gv, gp), text in GUESS:
if vid == gv and (gp is None or pid == gp):
return text
return ""
def parse_id(text: str) -> tuple[int, int | None]:
vid, _, pid = text.partition(":")
return int(vid, 16), (int(pid, 16) if pid else None)
def matches(
port, vid: int | None, pid: int | None, product: str | None, serial: str | None
) -> bool:
if vid is not None and (port.vid != vid or (pid is not None and port.pid != pid)):
return False
if product and (port.product or "").lower() != product.lower():
return False
return not serial or (port.serial_number or "") == serial
def main() -> int:
ap = argparse.ArgumentParser(
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
)
ap.add_argument(
"--wait",
metavar="VID[:PID]",
help="block until a matching port appears, print it",
)
ap.add_argument("--product", help="USB product string to match")
ap.add_argument("--serial", help="USB serial number to match")
ap.add_argument(
"--serial-of", metavar="PORT", help="print the USB serial number of PORT"
)
ap.add_argument("--timeout", type=float, default=60)
args = ap.parse_args()
if args.serial_of:
for p in list_ports.comports():
if p.device == args.serial_of and p.serial_number:
print(p.serial_number)
return 0
print(f"no USB serial number for {args.serial_of}", file=sys.stderr)
return 1
vid = pid = None
if args.wait:
vid, pid = parse_id(args.wait)
if args.wait or args.product or args.serial:
deadline = time.time() + args.timeout
while True:
for p in sorted(list_ports.comports(), key=lambda p: p.device):
if p.vid and matches(p, vid, pid, args.product, args.serial):
print(p.device)
return 0
if time.time() >= deadline:
print("no matching port", file=sys.stderr)
return 1
time.sleep(1)
rows = [p for p in list_ports.comports() if p.vid]
if not rows:
print("no USB serial ports")
return 1
for p in sorted(rows, key=lambda p: p.device):
print(
f"{p.device:32} {p.vid:04x}:{p.pid:04x} {p.product or '?':34} "
f"{p.serial_number or '':18} {guess(p.vid, p.pid)}"
)
return 0
if __name__ == "__main__":
sys.exit(main())
@@ -0,0 +1,157 @@
"""Bounded, timestamped serial capture that always exits and closes the port.
serial_capture.py PORT SECONDS OUTFILE [--baud 115200] [--released] [--reset] [--append]
default open with DTR/RTS as pyserial leaves them (asserted); fine for ESP devkits and the Pico.
--released drive DTR and RTS low before opening: the BW15 and BK7238 CH340 kits strap the boot pin
(DTR) and reset (RTS), so a plain open holds the chip in reset.
--reset pulse reset after opening (DTR low, RTS high 100 ms, RTS low) to capture a fresh boot.
--baud 115200 by default; 74880 shows the ESP8266 ROM boot messages.
--append keep what OUTFILE already holds; by default it is truncated so counts cover this run only.
Exit status 1 when nothing was received (wrong port name, or a port another capture still holds).
Text ESPHome marks as secret (LOG_SECRET, the Wi-Fi password) is written as <secret>.
Lines are written as [HH:MM:SS]text with ANSI colour stripped and flushed one by one, so a shell
loop can grep the file while the capture runs. If the port is missing or disappears (USB CDC boards
re-enumerate on reboot) the script retries until the deadline, and follows the board by its USB serial
number when it comes back under another device path.
Never run an open-ended serial session: every capture has a deadline, and a flash on the same port
needs the capture stopped first (pkill -f "[s]erial_capture.py" does not match the killing shell).
"""
import argparse
import contextlib
import pathlib
import re
import sys
import time
import serial
from serial.tools import list_ports
ANSI = re.compile(rb"\x1b\[[0-9;]*[A-Za-z]")
# ESPHome's LOG_SECRET span (a Wi-Fi password); a span cut off before its end marker is secret to the end
SECRET = re.compile(rb"\x1b\[8m.*?(?:\x1b\[28m|$)", re.DOTALL)
PORT_ERRORS = (serial.SerialException, OSError)
def identify(port: str) -> tuple[str | None, int]:
"""USB serial number of the port and its position among that device's ports (a dongle has two)."""
ports = sorted(list_ports.comports(), key=lambda p: p.device)
serial_number = next((p.serial_number for p in ports if p.device == port), None)
siblings = [
p.device for p in ports if serial_number and p.serial_number == serial_number
]
return serial_number, siblings.index(port) if port in siblings else 0
def current_path(port: str, serial_number: str | None, index: int) -> str:
"""The port's current device path: a USB CDC board can come back under a new name after a reboot."""
if serial_number:
ports = sorted(list_ports.comports(), key=lambda p: p.device)
siblings = [p.device for p in ports if p.serial_number == serial_number]
if index < len(siblings):
return siblings[index]
return port
def open_port(args: argparse.Namespace, path: str) -> serial.Serial:
s = serial.Serial(port=None, baudrate=args.baud, timeout=0.2)
s.port = path
if args.released:
s.dtr = False
s.rts = False
s.open()
if args.reset:
try:
s.dtr = False
s.rts = True
time.sleep(0.1)
s.rts = False
s.reset_input_buffer()
except PORT_ERRORS:
s.close()
raise
return s
def write_line(out, tag: bytes, data: bytes) -> None:
stamp = time.strftime("[%H:%M:%S]").encode()
out.write(stamp + tag + ANSI.sub(b"", SECRET.sub(b"<secret>", data)) + b"\n")
out.flush()
def note(out, text: str) -> None:
write_line(out, b"[capture] ", text.encode())
def capture(args: argparse.Namespace, out) -> int:
"""Returns the number of bytes received."""
deadline = time.time() + args.seconds
buf = b""
s = None
received = 0
serial_number, index = None, 0
while time.time() < deadline:
if s is None:
path = current_path(args.port, serial_number, index)
try:
s = open_port(args, path)
args.reset = False # only reset on the first open
if serial_number is None:
serial_number, index = identify(path)
if path != args.port:
note(out, f"reopened as {path}")
except PORT_ERRORS as err:
note(out, f"open failed: {err}")
time.sleep(1)
continue
try:
data = s.read(4096)
except PORT_ERRORS as err:
if buf: # keep the cut-off line apart from the next boot's first line
write_line(out, b"[partial]", buf)
buf = b""
note(out, f"port lost: {err}")
with contextlib.suppress(*PORT_ERRORS):
s.close()
s = None
time.sleep(1)
continue
if not data:
continue
received += len(data)
buf += data
while b"\n" in buf:
line, buf = buf.split(b"\n", 1)
write_line(out, b"", line.rstrip(b"\r"))
if buf:
write_line(out, b"[partial]", buf)
if s is not None:
s.close()
return received
def main() -> int:
ap = argparse.ArgumentParser(
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
)
ap.add_argument("port")
ap.add_argument("seconds", type=float)
ap.add_argument("outfile")
ap.add_argument("--baud", type=int, default=115200)
ap.add_argument("--released", action="store_true")
ap.add_argument("--reset", action="store_true")
ap.add_argument("--append", action="store_true")
args = ap.parse_args()
with pathlib.Path(args.outfile).open("ab" if args.append else "wb") as out:
received = capture(args, out)
if received == 0:
print(f"[capture] nothing received from {args.port}", file=sys.stderr)
return 1
return 0
if __name__ == "__main__":
sys.exit(main())
+133
View File
@@ -0,0 +1,133 @@
#!/bin/bash
# wifisave_hammer.sh: the device under test (DUT) falls back to its AP; a client board joins it and posts
# /wifisave repeatedly, then the real credentials. Compiles, flashes, captures both serial logs and
# prints counts. Configs come from templates/example-dut.yaml and templates/example-client.yaml.
#
# Usage: wifisave_hammer.sh DUT_KIND DUT_PORT DUT_YAML DUT_TREE CLIENT_PORT CLIENT_YAML CLIENT_TREE [SETTLE_S]
# DUT_KIND esp (esphome upload), pico (upload, then re-find the CDC port by USB serial number),
# bw15 (flash_rtl87xx_kit.py) or bk (flash_bk72xx.py)
# *_YAML absolute path; the file name without .yaml must equal the config's `name`
# *_TREE checkout or worktree whose esphome code must build that yaml (cwd for python -m esphome)
# SETTLE_S seconds to keep capturing after the client reports the real save (default 40; BW15 wants 90)
# Env: PY (python with esphome; default DUT_TREE/venv/bin/python, else python3 on PATH; an explicit PY must exist), LTPY
# (LibreTiny penv python with ltchiptool; default ~/.platformio/penv/.libretiny/bin/python)
# Output next to each yaml: NAME-compile.log, NAME-upload.log, NAME-serial.log, NAME-capture.err, and DUT NAME-events.txt
# (addresses redacted so the extract can go on a PR; the serial logs themselves stay local).
# Exit status: 0 RESULT PASS (no crash marker or reboot after the AP came up, the device joined the network,
# and real stimulus: saves answered and a cooldown exit); 3 RESULT FAIL (the counts show
# a failure); 2 RESULT INVALID (a precondition or the stimulus was missed, or a capture did not last the run);
# 1 when a build, flash or port step fails (nothing is reported).
set -u
SKILL=$(cd "$(dirname "$0")/.." && pwd)
KIND=$1; DPORT=$2; DYAML=$3; DTREE=$4; CPORT=$5; CYAML=$6; CTREE=$7; SETTLE=${8:-40}
if [ -n "${PY:-}" ]; then [ -x "$PY" ] || { echo "[hammer] FAILED: PY=$PY is not executable"; exit 1; }
else PY=$DTREE/venv/bin/python; [ -x "$PY" ] || PY=$(command -v python3) || { echo "[hammer] FAILED: no python found, set PY"; exit 1; }; fi
LTPY=${LTPY:-$HOME/.platformio/penv/.libretiny/bin/python}
DDIR=$(dirname "$DYAML"); DNAME=$(basename "$DYAML" .yaml)
CDIR=$(dirname "$CYAML"); CNAME=$(basename "$CYAML" .yaml)
DLOG=$DDIR/$DNAME-serial.log; CLOG=$CDIR/$CNAME-serial.log
INVALID=""
AP_WAIT=120; UPLOAD_WAIT=300; SAVE_WAIT=240
# Crash signatures (extended regex), not bare words: "aborting connection attempt" is a normal Wi-Fi retry line
CRASH_RE='Panic |abort\(\)|Fatal exception|Exception \(|Guru Meditation|HardFault|Bus Fault|Backtrace:|Soft WDT reset|wdt reset|Brownout|rst cause:[24]'
# Boot banners and port losses are counted apart: a Pico reboot shows as a port loss (and maybe a banner too),
# while on a UART board a port loss comes from the adapter or the hub, not the chip
BANNER_RE='Log initialized'; LOSS_RE='\[capture\] port lost'
DUT_CAPTURE=$((AP_WAIT + UPLOAD_WAIT + SAVE_WAIT + SETTLE + 60)); CLIENT_CAPTURE=$((SAVE_WAIT + SETTLE + 60))
bound() { local t=$1; shift; if command -v timeout >/dev/null; then timeout "$t" "$@"; else perl -e 'alarm shift; exec @ARGV' "$t" "$@"; fi; }
count() { LC_ALL=C grep -a -c "$1" "$2"; }
wait_for() { # wait_for FILE PATTERN SECONDS -> prints elapsed, status 1 on timeout
local n=0; until LC_ALL=C grep -a -q "$2" "$1"; do sleep 2; n=$((n+2)); [ $n -ge "$3" ] && { echo "$n"; return 1; }; done; echo "$n"; }
# Only this run's captures: another capture on the host (a soak on a third board) must survive
stop_tails() { pkill -f "[s]erial_capture.py $DPORT " 2>/dev/null; pkill -f "[s]erial_capture.py $CPORT " 2>/dev/null; sleep 1; }
fail() { echo "[hammer] FAILED: $1"; stop_tails; exit 1; }
port_never_opened() { # an open failure and no line that is not a capture note: the port, not the firmware
LC_ALL=C grep -a -q '\[capture\] open failed' "$1" && ! LC_ALL=C grep -a -q -v '^\[[0-9:]*\]\[capture\]' "$1"; }
compile() { # compile YAML TREE
(cd "$2" && PYTHONUNBUFFERED=1 bound 1500 "$PY" -m esphome compile "$1" > "${1%.yaml}-compile.log" 2>&1); local rc=$?
echo "[hammer] compile $(basename "$1"): rc=$rc"; return $rc; }
esp_upload() { # esp_upload YAML PORT TREE
(cd "$3" && bound $UPLOAD_WAIT "$PY" -m esphome upload "$1" --device "$2" > "${1%.yaml}-upload.log" 2>&1)
local ok; ok=$(count 'Successfully uploaded' "${1%.yaml}-upload.log"); echo "[hammer] upload $(basename "$1"): ok=$ok"; [ "$ok" -ge 1 ]; }
stop_tails
compile "$DYAML" "$DTREE" || fail "DUT build failed, see $DDIR/$DNAME-compile.log"
compile "$CYAML" "$CTREE" || fail "client build failed, see $CDIR/$CNAME-compile.log"
TAILOPT=""
case $KIND in
esp) esp_upload "$DYAML" "$DPORT" "$DTREE" || fail "DUT upload failed" ;;
pico)
SN=$("$PY" "$SKILL/scripts/ports.py" --serial-of "$DPORT") || fail "no USB serial number for $DPORT"
esp_upload "$DYAML" "$DPORT" "$DTREE" || fail "DUT upload failed"
# The Pico drops its CDC port on reboot and may already be back: take whatever path this board has now;
# the capture follows the serial number if the path changes again
DPORT=$("$PY" "$SKILL/scripts/ports.py" --wait 2e8a --serial "$SN" --timeout 60) || fail "pico did not re-enumerate"
echo "[hammer] pico port after upload: $DPORT"; sleep 2 ;;
bw15|bk)
UF2=$(ls "$DDIR/.esphome/build/$DNAME/.pioenvs/$DNAME/"*.uf2 2>/dev/null | head -1)
[ -n "$UF2" ] || fail "no uf2 under $DDIR/.esphome/build/$DNAME"
echo "[hammer] uf2: ${UF2##*/}"
W=$SKILL/scripts/flash_rtl87xx_kit.py; [ "$KIND" = bk ] && W=$SKILL/scripts/flash_bk72xx.py
(cd "$DDIR" && bound 400 "$LTPY" "$W" flash write -d "$DPORT" "$UF2" > "$DDIR/$DNAME-upload.log" 2>&1); rc=$?
echo "[hammer] $KIND flash: rc=$rc"; [ $rc -eq 0 ] || fail "DUT flash failed, see $DDIR/$DNAME-upload.log"
TAILOPT="--released" ;;
*) echo "unknown DUT_KIND $KIND"; exit 1 ;;
esac
: > "$DLOG"; ("$PY" "$SKILL/scripts/serial_capture.py" "$DPORT" $DUT_CAPTURE "$DLOG" $TAILOPT 2> "${DLOG%-serial.log}-capture.err" &)
if ! n=$(wait_for "$DLOG" "Captive portal\|Starting fallback AP" $AP_WAIT); then
[ -s "$DLOG" ] || fail "the DUT capture produced nothing (the capture itself failed), see ${DLOG%-serial.log}-capture.err"
port_never_opened "$DLOG" && fail "could not open $DPORT (held by another process, or the wrong port), see $DLOG"
fail "DUT never raised its AP within ${n}s, see $DLOG"
fi
echo "[hammer] DUT fallback AP after ${n}s"; sleep 2
# reboots are counted from here; the first banner may predate the capture
BANNER_BASE=$(count "$BANNER_RE" "$DLOG"); LOSS_BASE=$(count "$LOSS_RE" "$DLOG")
esp_upload "$CYAML" "$CPORT" "$CTREE" || fail "client upload failed"
: > "$CLOG"; ("$PY" "$SKILL/scripts/serial_capture.py" "$CPORT" $CLIENT_CAPTURE "$CLOG" 2> "${CLOG%-serial.log}-capture.err" &)
if n=$(wait_for "$CLOG" "real save status\|posting the real" $SAVE_WAIT); then
echo "[hammer] client posted the real credentials after ${n}s; settling ${SETTLE}s"; sleep "$SETTLE"
else
[ -s "$CLOG" ] || fail "the client capture produced nothing (the capture itself failed), see ${CLOG%-serial.log}-capture.err"
port_never_opened "$CLOG" && fail "could not open $CPORT (held by another process, or the wrong port), see $CLOG"
INVALID="client never reached the real save within ${n}s"; echo "[hammer] WARNING: $INVALID"
fi
# A verdict needs complete evidence: both captures must have lasted the run and logged no error of their own
for spec in "$DPORT:${DLOG%-serial.log}-capture.err" "$CPORT:${CLOG%-serial.log}-capture.err"; do
port=${spec%%:*}; err=${spec#*:}
pgrep -f "[s]erial_capture.py $port " >/dev/null || INVALID="${INVALID:+$INVALID; }the capture on $port ended before the run did, see $err"
[ -s "$err" ] && INVALID="${INVALID:+$INVALID; }the capture on $port reported an error, see $err"
done
CRASHES=$(LC_ALL=C grep -a -c -E "$CRASH_RE" "$DLOG")
BANNERS=$(( $(count "$BANNER_RE" "$DLOG") - BANNER_BASE )); LOSSES=$(( $(count "$LOSS_RE" "$DLOG") - LOSS_BASE ))
if [ "$KIND" = pico ]; then
REBOOTS=$BANNERS; [ "$LOSSES" -gt "$REBOOTS" ] && REBOOTS=$LOSSES # one reboot can show as both, so take the larger
else
REBOOTS=$BANNERS; [ "$LOSSES" -gt 0 ] && INVALID="${INVALID:+$INVALID; }the DUT port dropped $LOSSES time(s) mid-run (adapter or hub, not the chip)"
fi
STA=$(LC_ALL=C grep -a 'IP Address:' "$DLOG" | grep -v -c '192\.168\.4\.'); REAL=$(LC_ALL=C grep -a -o 'real save status [-0-9]*' "$CLOG" | tail -1 | awk '{print $4}')
SAVES=$(count 'Requested WiFi Settings Change' "$DLOG"); EXITS=$(count 'cooldown early' "$DLOG"); ANSWERED=$(count 'bogus save status 200' "$CLOG")
echo "[hammer] $DNAME: saves requested $SAVES, cooldown exits $EXITS, crash markers $CRASHES, after the AP came up: boot banners $BANNERS, port losses $LOSSES, reboots $REBOOTS; STA addresses $STA"
FAILED=$(count 'bogus save status -1' "$CLOG")
echo "[hammer] $CNAME: bogus saves answered $ANSWERED, failed $FAILED, real save status ${REAL:-none}"
# Only whole messages, matched where the message starts, so a credential line or an odd SSID never gets in
LC_ALL=C grep -a -E "^\[[0-9:]+\](\[[A-Z]\]\[[a-z_]+:[0-9]+\]: *(Exiting cooldown early|Starting fallback AP|Captive portal|Requested WiFi Settings Change|Retry phase|Connected\$|IP Address: )|\[capture\] port lost|($CRASH_RE))" "$DLOG" | LC_ALL=C tr -cd '\11\12\15\40-\176' | sed -E 's/(IP Address: )[^ ]+/\1<redacted>/' | cut -c1-110 | head -80 > "$DDIR/$DNAME-events.txt"
echo "[hammer] events: $DDIR/$DNAME-events.txt"
stop_tails
[ -z "$INVALID" ] || { echo "[hammer] RESULT INVALID: $INVALID"; exit 2; }
# Provisioning is proven by the device joining the network; the client's answer to the real save can time
# out because the device leaves the AP before replying, so its status is information only
if [ "$CRASHES" -gt 0 ] || [ "$REBOOTS" -gt 0 ] || [ "$STA" -lt 1 ]; then
echo "[hammer] RESULT FAIL: crash markers $CRASHES, reboots $REBOOTS, STA addresses $STA, real save status ${REAL:-none}"; exit 3
fi
# A pass needs the stimulus the flow is about: at least half the configured bogus saves answered and one cooldown exit
CONFIGURED=$(LC_ALL=C grep -a -E '^[[:space:]]*bogus_saves:' "$CYAML" | head -1 | tr -dc '0-9'); CONFIGURED=${CONFIGURED:-15}
if [ "$EXITS" -lt 1 ] || [ $((ANSWERED * 2)) -lt "$CONFIGURED" ]; then
echo "[hammer] RESULT INVALID: insufficient stimulus (bogus saves answered $ANSWERED of $CONFIGURED, cooldown exits $EXITS)"; exit 2
fi
[ "$FAILED" -eq 0 ] || echo "[hammer] WARNING: $FAILED of $CONFIGURED bogus saves got no answer"
echo "[hammer] RESULT PASS"; exit 0
@@ -0,0 +1,77 @@
# Package for the client board: it joins the device under test's AP (priority 10), posts
# `bogus_saves` bogus /wifisave requests `save_interval` apart, then posts the real credentials once.
# Include from a per-run yaml that holds the platform block and the substitutions (example-client.yaml);
# real_ssid and real_psk come from the scratch directory's secrets.yaml and must be URL safe.
# Set bogus_saves to 1 for a plain provisioning check. Log lines the driver script counts:
# "bogus save N", "bogus save status 200", "posting the real credentials", "real save status 200"
# The hammer is a single-mode script, so a reconnect to the AP (the device under test rebooted) does not
# start a second series on top of the first.
substitutions:
ap_password: hwtestpass
bogus_saves: "15"
save_interval: 3s
bogus_ssid: hwtest-no-such-network
bogus_psk: nosuchpassword
esphome:
name: ${name}
logger:
level: DEBUG
http_request:
id: http
verify_ssl: false
timeout: 10s
wifi:
id: wifi_id
networks:
- ssid: ${ap_ssid}
password: ${ap_password}
priority: 10
- ssid: ${real_ssid}
password: ${real_psk}
on_connect:
- lambda: |-
char b[40];
ESP_LOGI("test", "connected, ip %s", id(wifi_id).get_ip_addresses()[0].str_to(b));
- if:
condition:
lambda: |-
char b[40];
return strncmp(id(wifi_id).get_ip_addresses()[0].str_to(b), "192.168.4.", 10) == 0;
then:
- script.execute: hammer
script:
- id: hammer
mode: single
then:
- delay: 3s
- logger.log: "hammering /wifisave with bogus credentials"
- repeat:
count: ${bogus_saves}
then:
- lambda: ESP_LOGI("test", "bogus save %u", (unsigned) iteration + 1);
- http_request.get:
url: "http://192.168.4.1/wifisave?ssid=${bogus_ssid}&psk=${bogus_psk}"
capture_response: true
on_response:
- lambda: ESP_LOGI("test", "bogus save status %d", response->status_code);
on_error:
- lambda: ESP_LOGW("test", "bogus save status -1");
- delay: ${save_interval}
- logger.log: "posting the real credentials"
- http_request.get:
url: "http://192.168.4.1/wifisave?ssid=${real_ssid}&psk=${real_psk}"
capture_response: true
on_response:
- lambda: ESP_LOGI("test", "real save status %d", response->status_code);
on_error:
- lambda: ESP_LOGW("test", "real save status -1");
interval:
- interval: 5s
then:
- lambda: ESP_LOGI("test", "alive %u s", (unsigned) (millis() / 1000));
@@ -0,0 +1,31 @@
# Package for a device under test that never finds its network, falls back to its AP and serves the
# captive portal. Include it from a per-run yaml that holds the platform block and the substitutions
# (see example-dut.yaml). Bump `name` on every run: saved credentials are keyed by it, and a reused
# name boots straight onto the real network and never raises the AP.
substitutions:
ap_password: hwtestpass
ap_timeout: 15s
bogus_ssid: hwtest-no-such-network
bogus_psk: nosuchpassword
esphome:
name: ${name}
logger:
level: DEBUG
wifi:
networks:
- ssid: ${bogus_ssid}
password: ${bogus_psk}
ap:
ssid: ${ap_ssid}
password: ${ap_password}
ap_timeout: ${ap_timeout}
captive_portal:
interval:
- interval: 5s
then:
- lambda: ESP_LOGI("test", "alive %u s", (unsigned) (millis() / 1000));
@@ -0,0 +1,18 @@
# Client board for the /wifisave hammer. Copy into a scratch directory next to a secrets.yaml;
# ap_ssid must match the device under test, real_ssid and real_psk must be URL safe.
substitutions:
name: hwtest-client-1
ap_ssid: hwtest-ap
real_ssid: !secret wifi_ssid
real_psk: !secret wifi_password
bogus_saves: "15"
esp32:
board: esp32dev
framework:
type: esp-idf
# esp8266: {board: d1_mini}
packages:
hwtest: !include client-wifisave.yaml
@@ -0,0 +1,16 @@
# Device under test for the /wifisave hammer. Copy into a scratch directory, keep the file name equal
# to `name`, pick one platform block, bump `name` every run.
substitutions:
name: hwtest-dut-1
ap_ssid: hwtest-ap
esp8266:
board: d1_mini
# esp32: {board: esp32dev, framework: {type: esp-idf}}
# rtl87xx: {board: bw15}
# bk72xx: {board: generic-bk7238} # plus logger: {hardware_uart: UART1}, BK72xx logs on UART2 by default
# rp2040: {board: rpipico2w}
packages:
hwtest: !include dut-fallback-ap.yaml
File renamed without changes.
+16
View File
@@ -0,0 +1,16 @@
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "d=\"${CLAUDE_PROJECT_DIR:-.}\"; [ -x \"$d/venv/bin/python\" ] || { mkdir -p \"$d/.temp\"; env -u VIRTUAL_ENV \"$d/script/setup\" >\"$d/.temp/setup.log\" 2>&1 || echo '{\"systemMessage\":\"script/setup failed; see .temp/setup.log\"}'; }",
"statusMessage": "Setting up dev environment (script/setup)...",
"timeout": 900
}
]
}
]
}
}
+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 }}
@@ -0,0 +1,50 @@
name: Cache clang-tidy idedata
description: >
Cache the clang-tidy idedata and the headers it references under .temp
(headers only, about 30MB per env). Run after restore-python and cache-esp-idf.
inputs:
environment:
description: 'clang-tidy environment (e.g. esp32-idf-tidy).'
required: true
runs:
using: composite
steps:
- name: Compute cache key
id: key
shell: bash
run: |
. venv/bin/activate
[ -n "${{ inputs.environment }}" ] || { echo "::error::cache-clang-tidy-idedata: 'environment' input is empty"; exit 1; }
hash=$(python -c 'import sys; sys.path.insert(0, "script"); from clang_tidy_hash import idedata_cache_hash; print(idedata_cache_hash("${{ inputs.environment }}"))')
pyver=$(python -c 'import platform; print(platform.python_version())')
# Generating idedata is what installs ESP-IDF; never skip it over a missing
# install. This also skips the save, so a dev run that installs ESP-IDF
# warms the idedata cache on the next run.
if [ -d ~/.esphome-idf/frameworks ]; then
echo "skip=false" >> "$GITHUB_OUTPUT"
else
echo "ESP-IDF install missing, not using the clang-tidy idedata cache"
echo "skip=true" >> "$GITHUB_OUTPUT"
fi
echo "key=${{ runner.os }}-tidy-idedata-${{ inputs.environment }}-$hash-py$pyver" >> "$GITHUB_OUTPUT"
{
echo "path<<EOF"
printf '%s\n' '.temp/idedata-*.json' '.temp/idedata-*.hash'
printf '.temp/**/*.%s\n' h hpp hh hxx inc inl ipp tpp
echo "EOF"
} >> "$GITHUB_OUTPUT"
# Mirror cache-esp-idf: write on dev, restore-only on PRs. The post-step
# save only runs when the job succeeded, so a failed generation is never saved.
# Extend the extension list if a component ships extensionless headers.
- name: Cache clang-tidy idedata (write on dev)
if: (github.ref == 'refs/heads/dev' || contains(github.event.pull_request.labels.*.name, 'ci-cache-write')) && steps.key.outputs.skip != 'true'
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ${{ steps.key.outputs.path }}
key: ${{ steps.key.outputs.key }}
- name: Cache clang-tidy idedata (restore-only off dev)
if: github.ref != 'refs/heads/dev' && !contains(github.event.pull_request.labels.*.name, 'ci-cache-write') && steps.key.outputs.skip != 'true'
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ${{ steps.key.outputs.path }}
key: ${{ steps.key.outputs.key }}
+21 -5
View File
@@ -26,6 +26,9 @@ runs:
# The native-IDF version is pinned in code, not in any file that feeds the
# other cache keys, so resolve it explicitly. Keying on it means the cache
# invalidates on a version bump (actions/cache never overwrites a key).
# Also key on the Python version: the cached IDF venv links to the
# runner's toolcache interpreter and is reinstalled every run after a
# runner image bump.
id: version
shell: bash
run: |
@@ -36,19 +39,32 @@ runs:
version=$(python -c 'from esphome.components.esp32 import ESP_IDF_FRAMEWORK_VERSION_LOOKUP as L; print(L["recommended"])')
fi
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "python-version=$(python -c 'import platform; print(platform.python_version())')" >> "$GITHUB_OUTPUT"
# Mirror the adjacent PlatformIO cache: only dev-branch runs write the
# shared cache (so it lives in the default-branch scope readable by all
# PRs), and PRs are restore-only -- they never push multi-GB artifacts into
# their own scope / the repo quota (e.g. on a version-bump PR).
# their own scope / the repo quota (e.g. on a version-bump PR). The
# ci-cache-write label lets a PR write into its own scope to test the hit path;
# that costs about 1GB of the repo cache quota per run, so remove it when done.
# -slim: bump when prune-esp-idf changes what it removes; a key is never overwritten.
- name: Cache ESP-IDF install (write on dev)
if: github.ref == 'refs/heads/dev' && inputs.restore-only != 'true'
if: (github.ref == 'refs/heads/dev' || contains(github.event.pull_request.labels.*.name, 'ci-cache-write')) && inputs.restore-only != 'true'
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.esphome-idf
key: ${{ runner.os }}-esphome-idf-${{ steps.version.outputs.version }}
key: ${{ runner.os }}-esphome-idf-${{ steps.version.outputs.version }}-py${{ steps.version.outputs.python-version }}-slim
- name: Cache ESP-IDF install (restore-only off dev)
if: github.ref != 'refs/heads/dev' || inputs.restore-only == 'true'
if: github.ref != 'refs/heads/dev' && !contains(github.event.pull_request.labels.*.name, 'ci-cache-write') || inputs.restore-only == 'true'
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.esphome-idf
key: ${{ runner.os }}-esphome-idf-${{ steps.version.outputs.version }}
key: ${{ runner.os }}-esphome-idf-${{ steps.version.outputs.version }}-py${{ steps.version.outputs.python-version }}-slim
# Install explicitly so the prune below sees the toolchains on a cache miss
# too, instead of the install happening inside the first build step.
- name: Install ESP-IDF
shell: bash
run: |
. venv/bin/activate
python -c 'from esphome.espidf.framework import check_esp_idf_install; check_esp_idf_install("${{ steps.version.outputs.version }}")'
- name: Prune ESP-IDF install
uses: ./.github/actions/prune-esp-idf
+34
View File
@@ -0,0 +1,34 @@
name: Prune ESP-IDF install
description: >
Remove the picolibc sysroots (1.1GB of the 3.9GB install) from the native
ESP-IDF toolchains; IDF 5.x links newlib. Skipped when an IDF 6 install is
present, which links picolibc (see esp32/__init__.py).
runs:
using: composite
steps:
- name: Prune picolibc
shell: bash
run: |
shopt -s nullglob
prefix="${ESPHOME_ESP_IDF_PREFIX:-$HOME/.esphome-idf}"
prefix="${prefix/#\~/$HOME}"
for fw in "$prefix"/frameworks/*/; do
case "$(basename "$fw")" in
[6-9].*) echo "IDF $(basename "$fw") installed, keeping picolibc"; exit 0 ;;
esac
done
n=0
for dir in "$prefix"/tools/*-esp-elf/*/*-esp-elf/picolibc; do
echo "Removing $dir ($(du -sh "$dir" | cut -f1))"
rm -rf "$dir"
n=$((n + 1))
done
# The marker rides along in the cache entry so a restored slim tree stays quiet.
if [ "$n" -gt 0 ]; then
touch "$prefix/.picolibc-pruned"
elif [ -d "$prefix/tools" ] && [ ! -f "$prefix/.picolibc-pruned" ]; then
echo "::warning::no picolibc sysroots matched under $prefix/tools"
fi
if [ -d "$prefix" ]; then
du -sh "$prefix"
fi
+3 -3
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@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
with:
enable-cache: true
# Pull request saves land in per-PR scopes nothing else can
@@ -49,7 +49,7 @@ runs:
python -m venv venv
source venv/bin/activate
python --version
uv pip install -r requirements.txt -r requirements_test.txt
uv pip install -r requirements.txt -r requirements_dev.txt -r requirements_test.txt
uv pip install -e .
- name: Create Python virtual environment
if: steps.cache-venv.outputs.cache-hit != 'true' && runner.os == 'Windows'
@@ -58,5 +58,5 @@ runs:
python -m venv venv
source ./venv/Scripts/activate
python --version
uv pip install -r requirements.txt -r requirements_test.txt
uv pip install -r requirements.txt -r requirements_dev.txt -r requirements_test.txt
uv pip install -e .
+2 -1
View File
@@ -70,6 +70,7 @@ async function isStackedPr(github, context) {
async function detectMergeBranch(github, context) {
const labels = new Set();
const baseRef = context.payload.pull_request.base.ref;
const defaultBranch = context.payload.repository.default_branch;
if (baseRef === 'release') {
labels.add('merging-to-release');
@@ -78,7 +79,7 @@ async function detectMergeBranch(github, context) {
} else if (await isStackedPr(github, context)) {
// GitHub manages the merge order for a stack, so these are not blocked.
labels.add('stacked-pr');
} else if (baseRef !== 'dev') {
} else if (baseRef !== defaultBranch) {
// A chain built by hand: it must not merge until its base branch does.
labels.add('chained-pr');
}
+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
+26 -10
View File
@@ -42,8 +42,8 @@ function generateReviewMessages(finalLabels, originalLabelCount, deprecatedInfo,
let message = `${TOO_BIG_MARKER}\n### 📦 Pull Request Size\n\n`;
message +=
`Hey @${prAuthor}, thanks for the contribution! Just a heads up, ` +
`this PR is on the large side `;
`Hey @${prAuthor}, thanks for the contribution. This PR is on the ` +
`large side `;
if (tooManyLabels && tooManyChanges) {
message +=
@@ -57,16 +57,31 @@ function generateReviewMessages(finalLabels, originalLabelCount, deprecatedInfo,
}
message += `, which makes it harder for maintainers to review.\n\n`;
let condition;
if (tooManyLabels && tooManyChanges) {
condition =
`is at most ${TOO_BIG_THRESHOLD} lines (excluding tests) and touches ` +
`at most ${MAX_LABELS} components/areas`;
} else if (tooManyLabels) {
condition = `touches at most ${MAX_LABELS} components/areas`;
} else {
condition = `is at most ${TOO_BIG_THRESHOLD} lines (excluding tests)`;
}
message +=
`Smaller, focused PRs tend to be reviewed much faster since they ` +
`fit into the short gaps between other maintainer work; large ones ` +
`often have to wait for a rare long uninterrupted block of time. ` +
`If you can break this up into smaller pieces that can be reviewed ` +
`independently, it will almost certainly land faster overall.\n\n`;
`**This PR will not be reviewed until it ${condition}.** We know ` +
`that is not what you want to hear after this much work, and we want ` +
`to be upfront so you do not wait for a review that is not coming: ` +
`please split the change. The label is removed automatically once ` +
`the PR ${condition}.\n\n`;
message +=
`Before putting more time in, it's also worth popping into ` +
`\`#devs\` on [Discord](https://esphome.io/chat) so we can help ` +
`you scope things and flag anything already in flight.\n\n`;
`There is no exception you can request. Rarely, a maintainer agrees in ` +
`\`#devs\` on [Discord](https://esphome.io/chat) to review a change that ` +
`genuinely cannot be split as one piece and adds the \`mega-pr\` label, ` +
`but that is rare enough that you should plan on splitting rather than ` +
`wait for it. Smaller, focused PRs are picked up much faster because ` +
`they fit into the short gaps between other maintainer work. For a new ` +
`component, the first PR brings the hub and a single platform (for ` +
`example \`sensor\`); each further platform is its own PR on top of it.\n\n`;
message +=
`For more details (including how to split the work up), see: ` +
`https://developers.esphome.io/contributing/submitting-your-work/` +
@@ -214,6 +229,7 @@ async function handleMaintainerAccessComment(github, context, maintainerAccess)
}
module.exports = {
generateReviewMessages,
handleReviews,
handleMaintainerAccessComment
};
@@ -43,14 +43,14 @@ const WITHOUT_SCHEMA = 'CODEOWNERS = ["@esphome/core"]';
// Builds a fresh context for detectMergeBranch tests instead of mutating the
// shared CONTEXT fixture above (which other describe blocks rely on).
function makeMergeContext(baseRef, { stack } = {}) {
function makeMergeContext(baseRef, { stack, defaultBranch = 'dev' } = {}) {
const pull_request = { number: 1, base: { ref: baseRef } };
if (stack !== undefined) {
pull_request.stack = stack;
}
return {
repo: { owner: 'esphome', repo: 'esphome' },
payload: { pull_request }
payload: { pull_request, repository: { default_branch: defaultBranch } }
};
}
@@ -136,6 +136,21 @@ describe('detectMergeBranch', () => {
assert.deepEqual(Array.from(labels).sort(), ['chained-pr']);
assert.equal(state.calls, 1);
});
it('base ref matches default branch adds no labels', async () => {
const { github } = makeStackGithub({ stack: null });
const context = makeMergeContext('other', { defaultBranch: 'other' });
const labels = await detectMergeBranch(github, context);
assert.deepEqual(Array.from(labels).sort(), []);
});
it('base ref dev when the default branch is main adds chained-pr', async () => {
const { github } = makeStackGithub({ stack: null });
const context = makeMergeContext('dev', { defaultBranch: 'main' });
const labels = await detectMergeBranch(github, context);
assert.deepEqual(Array.from(labels).sort(), ['chained-pr']);
});
});
// ---------------------------------------------------------------------------
@@ -0,0 +1,38 @@
const { describe, it } = require('node:test');
const assert = require('node:assert/strict');
const { generateReviewMessages } = require('../reviews');
const MAX_LABELS = 15;
const TOO_BIG_THRESHOLD = 1000;
function tooBigMessage(labelCount, additions) {
const files = [{ filename: 'esphome/components/a/a.cpp', additions, deletions: 0 }];
const messages = generateReviewMessages(['too-big'], labelCount, null, files, additions, 0, 'someone', MAX_LABELS, TOO_BIG_THRESHOLD);
assert.equal(messages.length, 1);
return messages[0];
}
describe('too-big review message', () => {
it('names the line cap when the PR is too long', () => {
const message = tooBigMessage(3, 1500);
assert.match(message, /will not be reviewed until it is at most 1000 lines \(excluding tests\)\./);
assert.match(message, /removed automatically once the PR is at most 1000 lines/);
assert.doesNotMatch(message, /components\/areas\./);
});
it('names the area cap when the PR touches too many areas but is short', () => {
const message = tooBigMessage(20, 200);
assert.match(message, /will not be reviewed until it touches at most 15 components\/areas\./);
assert.doesNotMatch(message, /until it is at most 1000 lines/);
});
it('names both caps when both are exceeded', () => {
const message = tooBigMessage(20, 1500);
assert.match(message, /is at most 1000 lines \(excluding tests\) and touches at most 15 components\/areas\./);
});
it('tells new-component authors to bring one platform first', () => {
assert.match(tooBigMessage(3, 1500), /hub and a single platform/);
assert.match(tooBigMessage(3, 1500), /mega-pr/);
});
});
+1
View File
@@ -0,0 +1 @@
../.agents/skills
+1 -1
View File
@@ -28,7 +28,7 @@ jobs:
- name: Generate a token
id: generate-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
uses: actions/create-github-app-token@64ab729a6294000f5aa4a4bc0b73d1f32bc45510 # v3.3.0
with:
client-id: ${{ vars.ESPHOME_GITHUB_APP_CLIENT_ID }}
private-key: ${{ secrets.ESPHOME_GITHUB_APP_PRIVATE_KEY }}
+27 -5
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@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
with:
enable-cache: true
# Pull-request-only workflow: a save could never be shared and
@@ -41,10 +41,32 @@ jobs:
version: "0.11.15"
- name: Install apt dependencies
# PR-only workflow, so nothing on dev could seed a shared apt cache
# entry; the cached apt action would save one copy per PR. Plain apt
# with every call bounded: the apt.conf.d timeouts make a dead
# mirror fail over in seconds, and timeout runs under sudo so it can
# kill apt-get itself. Install without update first: image lists are
# fresh, and the index refresh is what a congested mirror makes slow.
timeout-minutes: 15
run: |
sudo apt update
sudo apt-cache show protobuf-compiler
sudo apt install -y protobuf-compiler
sudo tee /etc/apt/apt.conf.d/99ci-acquire-timeouts >/dev/null <<'EOF'
Acquire::Retries "1";
Acquire::http::Timeout "15";
Acquire::https::Timeout "15";
EOF
# Common path: the image's package lists are fresh enough.
if sudo DEBIAN_FRONTEND=noninteractive timeout -k 15 90 \
apt-get install -y protobuf-compiler; then
protoc --version
exit 0
fi
# Rescue path: refresh the lists once with a generous bound; the
# apt config already fails a stalled mirror over quickly.
sudo DEBIAN_FRONTEND=noninteractive timeout -k 10 30 \
dpkg --configure -a || true
sudo timeout -k 15 300 apt-get update
sudo DEBIAN_FRONTEND=noninteractive timeout -k 15 300 \
apt-get install -y protobuf-compiler
protoc --version
- name: Install python dependencies
run: uv pip install --system aioesphomeapi -c requirements.txt -r requirements_dev.txt
@@ -75,7 +97,7 @@ jobs:
run: git diff
- if: failure()
name: Archive artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: generated-proto-files
path: |
+20 -15
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@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
uses: docker/setup-buildx-action@594f3bf4285d9ea8dc53c9a0c9c4092420091003 # v4.4.0
- name: Determine tag and whether to push
id: tag
@@ -119,16 +118,22 @@ jobs:
# pushed image) keeps it working for fork PRs, which never push to ghcr.io.
- name: Export image for compile-test
if: matrix.os == 'ubuntu-24.04' && matrix.build_type == 'docker'
run: docker save "ghcr.io/esphome/esphome-amd64:${{ steps.tag.outputs.tag }}" | gzip > compile-test-image.tar.gz
# zstd over gzip: docker save is on the critical path for every
# compile-test job, and zstd -T0 is multithreaded (export 50s -> 9s).
# docker load auto-detects the format; its time is layer extraction,
# not decompression, so it is unchanged. shell: bash adds pipefail so
# a failed docker save cannot upload a truncated artifact.
shell: bash
run: docker save "ghcr.io/esphome/esphome-amd64:${{ steps.tag.outputs.tag }}" | zstd -T0 -3 > compile-test-image.tar.zst
- name: Upload compile-test image artifact
if: matrix.os == 'ubuntu-24.04' && matrix.build_type == 'docker'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
# The tar is already gzipped, so upload it as-is. archive: false skips
# the redundant zip and makes the file name the artifact name (the
# `name` input is ignored in that mode).
path: compile-test-image.tar.gz
# The tar is already compressed, so upload it as-is. archive: false
# skips the redundant zip and makes the file name the artifact name
# (the `name` input is ignored in that mode).
path: compile-test-image.tar.zst
retention-days: 1
archive: false
@@ -153,7 +158,7 @@ jobs:
with:
python-version: "3.12"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.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
@@ -182,8 +187,6 @@ jobs:
contents: read # actions/checkout to load the test configs
strategy:
fail-fast: false
# Modest cap so this smoke test leaves room on the shared runner pool.
max-parallel: 8
matrix:
# One entry per distinct toolchain. ESP32 variants (c3/c6/s2/s3/p4)
# share a toolchain bundle, so esp32 is exercised on the base variant
@@ -193,6 +196,7 @@ jobs:
# the default.
id:
- esp8266-arduino
- esp8266-arduino-native
- esp32-arduino-platformio
- esp32-arduino-esp-idf
- esp32-idf-platformio
@@ -206,14 +210,15 @@ jobs:
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Download image artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: compile-test-image.tar.gz
name: compile-test-image.tar.zst
- name: Load image
run: docker load --input compile-test-image.tar.gz
run: docker load --input compile-test-image.tar.zst
- name: Compile ${{ matrix.id }}
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"
+469 -99
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@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
with:
enable-cache: true
# Pull request saves land in per-PR scopes nothing else can
@@ -68,6 +68,22 @@ jobs:
uv pip install -r requirements.txt -r requirements_dev.txt -r requirements_test.txt
uv pip install -e .
seed-apt-cache:
name: Seed apt package cache
runs-on: ubuntu-24.04
# PR-branch cache saves are invisible to other PRs, so dev/beta/release
# pushes seed the one shared entry PR jobs restore. The key is derived
# only from the package list and version; keep both identical in every
# step that restores it. In ci-status needs so a broken seed fails dev.
if: github.event_name == 'push'
timeout-minutes: 10
steps:
- name: Install apt packages (cached)
uses: awalsh128/cache-apt-pkgs-action@553a35bb8ebd9fcabcb1c9451aa4c98e1b4ca8a9 # v1.6.3
with:
packages: libsdl2-dev libmbedtls-dev ccache
version: 1.2
determine-jobs:
name: Determine which jobs to run
runs-on: ubuntu-24.04
@@ -76,6 +92,7 @@ jobs:
outputs:
core-ci: ${{ steps.determine.outputs.core-ci }}
integration-tests: ${{ steps.determine.outputs.integration-tests }}
integration-run-all: ${{ steps.determine.outputs.integration-run-all }}
integration-test-buckets: ${{ steps.determine.outputs.integration-test-buckets }}
clang-tidy: ${{ steps.determine.outputs.clang-tidy }}
clang-tidy-mode: ${{ steps.determine.outputs.clang-tidy-mode }}
@@ -85,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 }}
@@ -96,6 +115,12 @@ jobs:
component-test-batches: ${{ steps.determine.outputs.component-test-batches }}
validate-only-components: ${{ steps.determine.outputs.validate-only-components }}
benchmarks: ${{ steps.determine.outputs.benchmarks }}
# "true" when this run is a pull request into one of the release
# branches. Those pull requests are batches of changes already tested on
# their original dev pull requests, so several jobs below trade coverage
# for turnaround time on them. Matched exactly, not by prefix, so an
# ordinary branch named e.g. "release-notes" is not caught by it.
release-pr: ${{ github.base_ref == 'beta' || github.base_ref == 'release' }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
@@ -130,6 +155,9 @@ jobs:
# Extract individual fields
echo "core-ci=$(echo "$output" | jq -r '.core_ci')" >> $GITHUB_OUTPUT
echo "integration-tests=$(echo "$output" | jq -r '.integration_tests')" >> $GITHUB_OUTPUT
# A missing key must fail here, not silently disable the junit upload
run_all=$(echo "$output" | jq -r 'if has("integration_run_all") then .integration_run_all else error("integration_run_all missing") end')
echo "integration-run-all=${run_all}" >> $GITHUB_OUTPUT
echo "integration-test-buckets=$(echo "$output" | jq -c '.integration_test_buckets')" >> $GITHUB_OUTPUT
echo "clang-tidy=$(echo "$output" | jq -r '.clang_tidy')" >> $GITHUB_OUTPUT
echo "clang-tidy-mode=$(echo "$output" | jq -r '.clang_tidy_mode')" >> $GITHUB_OUTPUT
@@ -139,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
@@ -157,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
@@ -179,6 +236,7 @@ jobs:
. venv/bin/activate
script/ci-custom.py
script/build_codeowners.py --check
script/build_alias_registry.py --check
script/build_language_schema.py --check
script/generate-esp32-boards.py --check
script/generate-rp2-boards.py --check
@@ -213,15 +271,24 @@ jobs:
runs-on: ubuntu-latest
needs:
- determine-jobs
if: github.event_name == 'pull_request' && !startsWith(github.base_ref, 'beta') && !startsWith(github.base_ref, 'release') && needs.determine-jobs.outputs.core-ci == 'true'
if: github.event_name == 'pull_request' && needs.determine-jobs.outputs.release-pr == 'false' && needs.determine-jobs.outputs.core-ci == 'true'
steps:
- name: Check out code from GitHub
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Read prek version from requirements_test.txt
id: prek
# requirements_test.txt is the only place the version is pinned, so a
# Dependabot bump there is picked up here without a second edit.
run: |
if ! version=$(sed -nE 's/^prek==([^[:space:]#]+).*/\1/p' requirements_test.txt) || [ -z "$version" ]; then
echo "::error::No prek== pin found in requirements_test.txt."
exit 1
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:
# Keep in sync with requirements_test.txt.
prek-version: "0.4.11"
prek-version: ${{ steps.prek.outputs.version }}
# This job only runs on pull requests, so nothing ever populates
# the cache on dev. Every run would miss and then write a per-pull
# request copy, which is what the old seed-cache job existed to
@@ -232,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
@@ -285,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
@@ -314,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
@@ -322,41 +389,48 @@ jobs:
integration-tests:
name: Run integration tests (${{ matrix.bucket.name }})
runs-on: ubuntu-latest
# Must match seed-apt-cache's image: the apt cache key has no OS in it.
runs-on: ubuntu-24.04
needs:
- common
- determine-jobs
if: needs.determine-jobs.outputs.integration-tests == 'true'
# A hung test is failed by pytest's --timeout below; this bounds the rest
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
bucket: ${{ fromJson(needs.determine-jobs.outputs.integration-test-buckets) }}
env:
# 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
- name: Install ccache
# Speeds up the host compiles: tests in a bucket compile overlapping
# component sets, so later tests reuse earlier tests' objects.
run: |
sudo apt-get update -qq
sudo apt-get install -y --no-install-recommends ccache
- name: Restore ccache (restore-only)
# esphome stores the PlatformIO ccache under the machine-global cache
# dir (see _ccache_env() in esphome/platformio/toolchain.py). The
# bucket-name prefix prefers a same-bucket seed; the bare prefix falls
# back to any seed when the bucket layout differs from dev.
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
- name: Install apt packages (cached)
# ccache speeds up the host compiles. A cache hit never touches apt
# (mirror outages cannot hang the job); the timeout bounds the cold
# path. Packages and version must match seed-apt-cache exactly.
# libsdl2-dev is needed by the headless display tests, which capture
# screenshots.
timeout-minutes: 10
uses: awalsh128/cache-apt-pkgs-action@553a35bb8ebd9fcabcb1c9451aa4c98e1b4ca8a9 # v1.6.3
with:
path: ~/.cache/esphome/platformio-ccache
key: integration-ccache-${{ matrix.bucket.name }}-${{ github.sha }}
restore-keys: |
integration-ccache-${{ matrix.bucket.name }}-
integration-ccache-
packages: libsdl2-dev libmbedtls-dev ccache
version: 1.2
- name: Set up Python 3.13
id: python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.13"
- 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_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
@@ -366,7 +440,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@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
with:
enable-cache: true
# Pull request saves land in per-PR scopes nothing else can
@@ -394,20 +468,47 @@ jobs:
run: |
. venv/bin/activate
mapfile -t test_files < <(echo "$BUCKET_TESTS" | jq -r '.[]')
if [ "${#test_files[@]}" -eq 0 ]; then
echo "::error::Empty integration test bucket; pytest would collect the whole tree"
exit 1
fi
echo "Bucket ${{ matrix.bucket.name }}: running ${#test_files[@]} integration tests"
pytest -vv --no-cov --tb=native --durations=30 -n auto "${test_files[@]}"
# A hung test fails after 30 min: the 15 min shared build lock wait plus a cold compile
pytest -vv --no-cov --tb=native --durations=30 -n auto --dist worksteal --timeout=1800 \
--junitxml=junit-integration.xml "${test_files[@]}"
- name: Upload test artifacts
# Tests that compare rendered output write the image they actually got here, so a
# failure can be looked at without reproducing the whole build locally.
if: failure()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: integration-test-artifacts-${{ matrix.bucket.name }}
path: test_artifacts/
if-no-files-found: ignore
retention-days: 7
- name: Upload junit timings
# Consumed by sync-integration-durations.yml through
# script/update_integration_test_durations.py; only full matrix dev
# runs produce usable data.
if: github.ref == 'refs/heads/dev' && needs.determine-jobs.outputs.integration-run-all == 'true'
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: junit-integration-${{ strategy.job-index }}
path: junit-integration.xml
if-no-files-found: error
# 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 ccache
# Pull request saves land in per-PR scopes nothing else can reuse;
# dev pushes seed the shared copy instead.
if: github.event_name != 'pull_request'
# 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.library-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cache/esphome/platformio-ccache
key: integration-ccache-${{ matrix.bucket.name }}-${{ github.sha }}
path: ${{ env.INTEGRATION_LIBRARY_CACHE_PATH }}
key: ${{ steps.library-cache.outputs.cache-primary-key }}
import-time:
name: Check import esphome.__main__ time
@@ -430,7 +531,7 @@ jobs:
script/check_import_time.py --check --har importtime.har
- name: Upload waterfall HAR
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: import-time-waterfall
path: importtime.har
@@ -440,12 +541,28 @@ jobs:
benchmarks:
name: Run CodSpeed benchmarks
runs-on: ubuntu-24.04
timeout-minutes: 30
needs:
- common
- determine-jobs
if: >-
(github.event_name == 'push' && github.ref_name == 'dev') ||
(github.event_name == 'pull_request' && needs.determine-jobs.outputs.benchmarks == 'true')
github.repository == 'esphome/esphome' && (
(github.event_name == 'push' && github.ref_name == 'dev') ||
(
github.event_name == 'pull_request' &&
needs.determine-jobs.outputs.release-pr == 'false' &&
needs.determine-jobs.outputs.benchmarks == 'true'
)
)
# CodSpeed benchmarks require a CodSpeed account linked to the repository to run
# (https://codspeed.io) -- disabled on forks that aren't esphome/esphome itself.
#
# Pull requests into beta and release are skipped as well. CodSpeed compares a
# pull request against the newest commit of its base branch that has a benchmark
# run of its own, and only dev is benchmarked. A release pull request therefore
# falls back to dev's latest run, so every speed-up merged into dev since the
# release branched is reported as a regression in the release. The changes there
# have already been benchmarked on their original dev pull requests.
steps:
- name: Check out code from GitHub
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
@@ -459,14 +576,60 @@ jobs:
- name: Build benchmarks
id: build
run: |
# pipefail: without it a failed build is masked by the grep/cut
# pipeline below, leaving BINARY empty and silently dropping every
# C++ benchmark from the run while the job still reports success.
set -o pipefail
. venv/bin/activate
export BENCHMARK_LIB_CONFIG=$(python script/setup_codspeed_lib.py)
# --build-only prints BUILD_BINARY=<path> to stdout
BINARY=$(script/cpp_benchmark.py --all --build-only | grep '^BUILD_BINARY=' | tail -1 | cut -d= -f2-)
BENCHMARK_LIB_CONFIG=$(python script/setup_codspeed_lib.py)
export BENCHMARK_LIB_CONFIG
# --build-only prints BUILD_BINARY=<path> to stdout; the grep is
# non-fatal so a missing marker reaches the check below instead of
# tripping errexit at this assignment
BINARY=$(script/cpp_benchmark.py --all --build-only | { grep '^BUILD_BINARY=' || true; } | tail -1 | cut -d= -f2-)
if [ -z "$BINARY" ]; then
echo "::error::Benchmark build did not report a binary path"
exit 1
fi
echo "binary=$BINARY" >> $GITHUB_OUTPUT
- name: Bound apt fetches and pre-install libc6-dbg
# The CodSpeed runner installs valgrind + libc6-dbg via its own
# unbounded apt-get update; per-invocation apt options cannot reach
# it. The apt.conf.d timeouts below bound every later apt call in
# this job, the runner's included. Pre-installing libc6-dbg lets the
# runner skip apt once its valgrind cache is restored (it checks
# ``dpkg -s libc6-dbg``, so the cache action's unregistered restores
# would not count). Install without update first: image lists are
# fresh, and the index refresh is what a congested mirror makes
# slow. Best effort; the job timeout is the last backstop.
timeout-minutes: 15
continue-on-error: true
run: |
sudo tee /etc/apt/apt.conf.d/99ci-acquire-timeouts >/dev/null <<'EOF'
Acquire::Retries "1";
Acquire::http::Timeout "15";
Acquire::https::Timeout "15";
EOF
if dpkg -s libc6-dbg >/dev/null 2>&1; then
echo "libc6-dbg already installed"
exit 0
fi
# Common path: the image's package lists are fresh enough.
if sudo DEBIAN_FRONTEND=noninteractive timeout -k 15 90 \
apt-get install -y libc6-dbg; then
exit 0
fi
# Rescue path: refresh the lists once with a generous bound; the
# apt config already fails a stalled mirror over quickly.
sudo DEBIAN_FRONTEND=noninteractive timeout -k 10 30 \
dpkg --configure -a || true
sudo timeout -k 15 300 apt-get update
sudo DEBIAN_FRONTEND=noninteractive timeout -k 15 300 \
apt-get install -y libc6-dbg
- name: Run CodSpeed benchmarks
uses: CodSpeedHQ/action@4296e51e7041e24dadb86d1d6e8b9320d223dbe8 # v5.0.3
uses: CodSpeedHQ/action@f842b9a3ffe213dbb81768fd7f7eeb4dfcf1c369 # v5.4.2
with:
run: |
. venv/bin/activate
@@ -520,7 +683,7 @@ jobs:
include:
- id: clang-tidy
name: Run script/clang-tidy for ESP8266
options: --environment esp8266-arduino-tidy --grep USE_ESP8266
options: --environment esp8266-arduino-tidy --grep USE_ESP8266 --grep USE_SOCKET_IMPL_LWIP_TCP --grep USE_SHD_FIRMWARE_DATA
pio_cache_key: tidyesp8266
- id: clang-tidy
name: Run script/clang-tidy for ESP32 Arduino
@@ -533,12 +696,16 @@ jobs:
ignore_errors: false
- id: clang-tidy
name: Run script/clang-tidy for RP2
options: --environment rp2-tidy --grep USE_RP2
options: --environment rp2-tidy --grep USE_RP2 --grep USE_BLE_SCAN_RESPONSE_MERGER
pio_cache_key: tidyrp2
- id: clang-tidy
name: Run script/clang-tidy for host
options: --environment host-tidy --grep USE_HOST
pio_cache_key: tidyhost
- id: clang-tidy
name: Run script/clang-tidy for LibreTiny
environments: bk72xx-tidy ln882h-tidy rtl87xxb-tidy rtl87xxc-tidy
options: --grep USE_LIBRETINY --grep USE_BK72XX --grep USE_RTL87XX --grep USE_LN882X
options: --grep USE_LIBRETINY --grep USE_BK72XX --grep USE_RTL87XX --grep USE_LN882X --grep USE_LN882H_BLE --grep USE_SOCKET_IMPL_LWIP_SOCKETS
pio_cache_key: tidylibretiny
steps:
@@ -547,26 +714,41 @@ jobs:
with:
# Need history for HEAD~1 to work for checking changed files
fetch-depth: 2
- name: Install apt packages (cached)
if: matrix.pio_cache_key == 'tidyhost'
# libsdl2-dev is needed by the sdl component on the host, libmbedtls-dev
# by dsmr and dlms_meter. Packages and version must match seed-apt-cache
# exactly.
timeout-minutes: 10
uses: awalsh128/cache-apt-pkgs-action@553a35bb8ebd9fcabcb1c9451aa4c98e1b4ca8a9 # v1.6.3
with:
packages: libsdl2-dev libmbedtls-dev ccache
version: 1.2
- name: Restore Python
id: restore-python
uses: ./.github/actions/restore-python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
# Key on the exact Python version as well: LibreTiny creates a venv under
# ~/.platformio/penv whose interpreter is a symlink into the runner's
# hosted toolcache, so a cache saved on an older runner image breaks once
# a new image ships a newer patch release and drops the old interpreter.
- name: Cache platformio
if: github.ref == 'refs/heads/dev' && matrix.pio_cache_key
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.platformio
key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
key: platformio-${{ matrix.pio_cache_key }}-${{ steps.restore-python.outputs.python-version }}-${{ hashFiles('platformio.ini') }}
- name: Cache platformio
if: github.ref != 'refs/heads/dev' && matrix.pio_cache_key
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.platformio
key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
key: platformio-${{ matrix.pio_cache_key }}-${{ steps.restore-python.outputs.python-version }}-${{ hashFiles('platformio.ini') }}
- name: Cache ESP-IDF install
if: matrix.cache_idf
@@ -574,6 +756,12 @@ jobs:
with:
framework: arduino
- name: Cache clang-tidy idedata
if: matrix.cache_idf
uses: ./.github/actions/cache-clang-tidy-idedata
with:
environment: esp32-arduino-tidy
- name: Cache nRF Connect SDK install
if: matrix.cache_sdk_nrf
uses: ./.github/actions/cache-sdk-nrf
@@ -623,6 +811,10 @@ jobs:
# Also cache libdeps, store them in a ~/.platformio subfolder
PLATFORMIO_LIBDEPS_DIR: ~/.platformio/libdeps
- name: Prune ESP-IDF install before cache save
if: matrix.cache_idf && (github.ref == 'refs/heads/dev' || contains(github.event.pull_request.labels.*.name, 'ci-cache-write'))
uses: ./.github/actions/prune-esp-idf
- name: Suggested changes
run: script/ci-suggest-changes ${{ matrix.ignore_errors && '|| true' || '' }}
# yamllint disable-line rule:line-length
@@ -655,6 +847,11 @@ jobs:
- name: Cache ESP-IDF install
uses: ./.github/actions/cache-esp-idf
- name: Cache clang-tidy idedata
uses: ./.github/actions/cache-clang-tidy-idedata
with:
environment: esp32-idf-tidy
- name: Register problem matchers
run: |
echo "::add-matcher::.github/workflows/matchers/gcc.json"
@@ -689,6 +886,10 @@ jobs:
# Also cache libdeps, store them in a ~/.platformio subfolder
PLATFORMIO_LIBDEPS_DIR: ~/.platformio/libdeps
- name: Prune ESP-IDF install before cache save
if: (github.ref == 'refs/heads/dev' || contains(github.event.pull_request.labels.*.name, 'ci-cache-write'))
uses: ./.github/actions/prune-esp-idf
- name: Suggested changes
run: script/ci-suggest-changes
if: always()
@@ -709,14 +910,20 @@ jobs:
matrix:
include:
- id: clang-tidy
name: Run script/clang-tidy for ESP32 IDF 1/3
options: --environment esp32-idf-tidy --split-num 3 --split-at 1
name: Run script/clang-tidy for ESP32 IDF 1/5
options: --environment esp32-idf-tidy --split-num 5 --split-at 1
- id: clang-tidy
name: Run script/clang-tidy for ESP32 IDF 2/3
options: --environment esp32-idf-tidy --split-num 3 --split-at 2
name: Run script/clang-tidy for ESP32 IDF 2/5
options: --environment esp32-idf-tidy --split-num 5 --split-at 2
- id: clang-tidy
name: Run script/clang-tidy for ESP32 IDF 3/3
options: --environment esp32-idf-tidy --split-num 3 --split-at 3
name: Run script/clang-tidy for ESP32 IDF 3/5
options: --environment esp32-idf-tidy --split-num 5 --split-at 3
- id: clang-tidy
name: Run script/clang-tidy for ESP32 IDF 4/5
options: --environment esp32-idf-tidy --split-num 5 --split-at 4
- id: clang-tidy
name: Run script/clang-tidy for ESP32 IDF 5/5
options: --environment esp32-idf-tidy --split-num 5 --split-at 5
steps:
- name: Check out code from GitHub
@@ -734,6 +941,11 @@ jobs:
- name: Cache ESP-IDF install
uses: ./.github/actions/cache-esp-idf
- name: Cache clang-tidy idedata
uses: ./.github/actions/cache-clang-tidy-idedata
with:
environment: esp32-idf-tidy
- name: Register problem matchers
run: |
echo "::add-matcher::.github/workflows/matchers/gcc.json"
@@ -768,6 +980,10 @@ jobs:
# Also cache libdeps, store them in a ~/.platformio subfolder
PLATFORMIO_LIBDEPS_DIR: ~/.platformio/libdeps
- name: Prune ESP-IDF install before cache save
if: (github.ref == 'refs/heads/dev' || contains(github.event.pull_request.labels.*.name, 'ci-cache-write'))
uses: ./.github/actions/prune-esp-idf
- name: Suggested changes
run: script/ci-suggest-changes
if: always()
@@ -790,17 +1006,20 @@ jobs:
- id: clang-tidy
name: Run script/clang-tidy for ESP32 S3
# yamllint disable-line rule:line-length
options: --environment esp32s3-idf-tidy --grep SOC_TEMP_SENSOR_SUPPORTED --grep USE_ESP32_VARIANT_ESP32S3 --grep USE_LOGGER_USB_CDC
options: --environment esp32s3-idf-tidy --grep SOC_TEMP_SENSOR_SUPPORTED --grep USE_ESP32_VARIANT_ESP32S3 --grep USE_LOGGER_USB_CDC --grep SOC_I2S_SUPPORTS_TDM --grep USE_OPENTHREAD
tidy_environment: esp32s3-idf-tidy
- id: clang-tidy
name: Run script/clang-tidy for ESP32 P4
# P4 has no native Wi-Fi/BLE; those run over the hosted co-processor,
# so their code paths differ -- lint them under the P4 build too.
# yamllint disable-line rule:line-length
options: --environment esp32p4-idf-tidy --grep USE_ESP32_VARIANT_ESP32P4 --grep USE_ESP32_HOSTED --grep USE_WIFI --grep USE_BLE
options: --environment esp32p4-idf-tidy --grep USE_ESP32_VARIANT_ESP32P4 --grep CONFIG_IDF_TARGET_ESP32P4 --grep USE_ESP32_HOSTED --grep USE_WIFI --grep USE_BLE
tidy_environment: esp32p4-idf-tidy
- id: clang-tidy
name: Run script/clang-tidy for ESP32 C6
# yamllint disable-line rule:line-length
options: --environment esp32c6-idf-tidy --grep SOC_LP_I2C_SUPPORTED --grep USE_ESP32_VARIANT_ESP32C6 --grep USE_OPENTHREAD --grep USE_ZIGBEE
tidy_environment: esp32c6-idf-tidy
steps:
- name: Check out code from GitHub
@@ -818,6 +1037,11 @@ jobs:
- name: Cache ESP-IDF install
uses: ./.github/actions/cache-esp-idf
- name: Cache clang-tidy idedata
uses: ./.github/actions/cache-clang-tidy-idedata
with:
environment: ${{ matrix.tidy_environment }}
- name: Register problem matchers
run: |
echo "::add-matcher::.github/workflows/matchers/gcc.json"
@@ -851,6 +1075,10 @@ jobs:
script/clang-tidy --fix --changed ${{ matrix.options }}
fi
- name: Prune ESP-IDF install before cache save
if: (github.ref == 'refs/heads/dev' || contains(github.event.pull_request.labels.*.name, 'ci-cache-write'))
uses: ./.github/actions/prune-esp-idf
- name: Suggested changes
run: script/ci-suggest-changes
if: always()
@@ -871,7 +1099,6 @@ jobs:
ESPHOME_SDK_NRF_PREFIX: ~/.esphome-sdk-nrf
strategy:
fail-fast: false
max-parallel: ${{ (startsWith(github.base_ref, 'beta') || startsWith(github.base_ref, 'release')) && 32 || 16 }}
matrix:
batch: ${{ fromJson(needs.determine-jobs.outputs.component-test-batches) }}
steps:
@@ -883,12 +1110,17 @@ jobs:
- name: List components
run: echo ${{ matrix.batch.components }}
- name: Install apt packages
# Not cached: this job is pull-request-only, so a cache save could
# never be shared and would only consume quota.
run: |
sudo apt-get update -qq
sudo apt-get install -y --no-install-recommends libsdl2-dev ccache
- name: Install apt packages (cached)
# A cache hit (seeded on dev by seed-apt-cache) never touches apt,
# so mirror outages cannot hang this PR-only job; the timeout bounds
# the cold path. Packages and version must match seed-apt-cache
# exactly. The action has no --no-install-recommends; same package
# set this job used before #17463.
timeout-minutes: 10
uses: awalsh128/cache-apt-pkgs-action@553a35bb8ebd9fcabcb1c9451aa4c98e1b4ca8a9 # v1.6.3
with:
packages: libsdl2-dev libmbedtls-dev ccache
version: 1.2
- name: Check out code from GitHub
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
@@ -913,6 +1145,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
@@ -953,7 +1190,7 @@ jobs:
# - This catches pin conflicts and other issues in directly changed code
# - Grouped tests use --testing-mode to allow config merging (disables some checks)
# - Dependencies are safe to group since they weren't modified in this PR
if [[ "${{ github.base_ref }}" == beta* ]] || [[ "${{ github.base_ref }}" == release* ]]; then
if [[ "${{ needs.determine-jobs.outputs.release-pr }}" == "true" ]]; then
directly_changed_csv=""
echo "Testing components: $components_csv"
echo "Target branch: ${{ github.base_ref }} - grouping all components"
@@ -1011,9 +1248,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
@@ -1053,7 +1309,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..."
@@ -1062,6 +1318,125 @@ 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
test-esp8266-native-windows:
name: Compile esp8266 with the native toolchain on Windows (ccache ${{ matrix.ccache }})
runs-on: windows-latest
needs:
- common
- determine-jobs
if: github.event_name == 'pull_request' && needs.determine-jobs.outputs.esp8266-native == 'true'
# A hung compiler must fail the job, not hold the runner for six hours.
timeout-minutes: 40
strategy:
fail-fast: false
matrix:
# Both ways the build runs on Windows: ESPHome Desktop bundles
# ccache and puts it on PATH, a pip install has none.
ccache: ["1", "0"]
env:
ESPHOME_CCACHE_ENABLE: ${{ matrix.ccache }}
# The test script prints characters the cp1252 console cannot encode
PYTHONUTF8: "1"
# The exact static build ESPHome Desktop ships
CCACHE_URL: https://github.com/ccache/ccache/releases/download/v4.14/ccache-4.14-windows-x86_64.zip
CCACHE_SHA256: 2568347a697e103ca1b073981c704ad76fb2507d066c38dba038dd73399d968f
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: Install ccache
if: matrix.ccache == '1'
shell: pwsh
run: |
$dir = Join-Path $env:RUNNER_TEMP "ccache"
New-Item -ItemType Directory -Force $dir | Out-Null
$zip = Join-Path $dir "ccache.zip"
Invoke-WebRequest -Uri $env:CCACHE_URL -OutFile $zip
$hash = (Get-FileHash $zip -Algorithm SHA256).Hash.ToLower()
if ($hash -ne $env:CCACHE_SHA256) {
Write-Host "::error::ccache archive sha256 $hash does not match $env:CCACHE_SHA256"
exit 1
}
Expand-Archive -Path $zip -DestinationPath $dir
$bin = Get-ChildItem -Path $dir -Recurse -Filter ccache.exe | Select-Object -First 1
Add-Content -Path $env:GITHUB_PATH -Value $bin.DirectoryName
& $bin.FullName --version
- name: Compile
shell: pwsh
# One component test: web_server brings wifi, api and the async web
# server libraries, the shape of a typical ESP8266 device
run: |
. .\venv\Scripts\Activate.ps1
$arguments = "script/test_build_components.py", "-e", "compile", "-t", "esp8266-ard", "-c", "web_server", "--base-only", "--toolchain", "arduino", "--fail-on-no-tests"
$p = Start-Process -FilePath python -ArgumentList $arguments -NoNewWindow -PassThru
# Reading the handle keeps ExitCode available after the process exits
$null = $p.Handle
if (-not $p.WaitForExit(25 * 60 * 1000)) {
Write-Host "::error::The compile did not finish in 25 minutes; processes still running:"
Get-CimInstance Win32_Process |
Where-Object { $_.CommandLine -match 'xtensa|ccache|ninja|esphome' } |
Select-Object ProcessId, ParentProcessId, CreationDate, CommandLine |
Format-List | Out-String -Width 4096 | Write-Host
Get-Process |
Where-Object { $_.Name -match 'cc1|ccache|ninja|xtensa|python' } |
Select-Object Id, Name, CPU, WorkingSet64 |
Format-Table | Out-String | Write-Host
taskkill /PID $p.Id /T /F
exit 1
}
exit $p.ExitCode
- name: Show ccache statistics
# Also after a failed or killed compile: the counters say whether
# ccache was ever reached
if: always() && matrix.ccache == '1'
shell: pwsh
# The build keeps its store under the esphome tools cache
run: |
$env:CCACHE_DIR = Join-Path $env:LOCALAPPDATA "esphome\Cache\arduino8266\ccache"
ccache -s
device-builder:
name: Test downstream esphome/device-builder
runs-on: ubuntu-24.04
@@ -1090,7 +1465,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@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
with:
enable-cache: true
# Pull request saves land in per-PR scopes nothing else can
@@ -1149,31 +1524,27 @@ jobs:
echo "::warning::ci_memory_impact_extract.py not found on target branch, skipping memory impact analysis"
fi
# Check if test files exist on the target branch for the requested
# components and platform. When a PR adds new test files for a platform,
# the target branch won't have them yet, so skip instead of failing.
# This check must be done here (not in determine-jobs.py) because
# determine-jobs runs on the PR branch and cannot see what the target
# branch has.
# The build below uses --base-only, so skip unless the target branch has
# test.<platform>.yaml for every requested component (test-*.yaml
# variants alone compile nothing, and a partial set would compare a
# smaller target build against the PR build). Checked here, not in
# determine-jobs.py, because determine-jobs runs on the PR branch and
# cannot see the target branch.
- name: Check for test files on target branch
id: check-tests
if: steps.check-script.outputs.skip != 'true'
run: |
components='${{ toJSON(fromJSON(needs.determine-jobs.outputs.memory_impact).components) }}'
platform="${{ fromJSON(needs.determine-jobs.outputs.memory_impact).platform }}"
found=false
missing=""
for component in $(echo "$components" | jq -r '.[]'); do
# Check for test files matching the platform (test.platform.yaml or test-*.platform.yaml)
for f in tests/components/${component}/test*.${platform}.yaml; do
if [ -f "$f" ]; then
found=true
break 2
fi
done
if [ ! -f "tests/components/${component}/test.${platform}.yaml" ]; then
missing="${missing} ${component}"
fi
done
if [ "$found" = false ]; then
if [ -n "$missing" ]; then
echo "skip=true" >> $GITHUB_OUTPUT
echo "::warning::No test files found on target branch for platform ${platform}, skipping memory impact analysis"
echo "::warning::No test.${platform}.yaml on target branch for${missing}, skipping memory impact analysis"
else
echo "skip=false" >> $GITHUB_OUTPUT
fi
@@ -1231,12 +1602,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'
@@ -1296,7 +1664,7 @@ jobs:
fi
- name: Upload memory analysis JSON
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: memory-analysis-target
path: memory-analysis-target.json
@@ -1321,11 +1689,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: |
@@ -1361,7 +1727,7 @@ jobs:
--platform "$platform"
- name: Upload memory analysis JSON
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: memory-analysis-pr
path: memory-analysis-pr.json
@@ -1391,13 +1757,13 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }}
cache-key: ${{ needs.common.outputs.cache-key }}
- name: Download target analysis JSON
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: memory-analysis-target
path: ./memory-analysis
continue-on-error: true
- name: Download PR analysis JSON
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: memory-analysis-pr
path: ./memory-analysis
@@ -1423,6 +1789,8 @@ jobs:
# this check.
needs:
- common
- seed-apt-cache
- seed-esp8266-native-cache
- determine-jobs
- ci-custom
- pylint
@@ -1438,6 +1806,8 @@ jobs:
- clang-tidy-esp32-variants
- test-build-components-split
- test-esp32-platformio
- test-esp8266-native
- test-esp8266-native-windows
- device-builder
- memory-impact-target-branch
- memory-impact-pr-branch
@@ -35,7 +35,7 @@ jobs:
- name: Generate a token
id: generate-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
uses: actions/create-github-app-token@64ab729a6294000f5aa4a4bc0b73d1f32bc45510 # v3.3.0
with:
client-id: ${{ vars.ESPHOME_GITHUB_APP_CLIENT_ID }}
private-key: ${{ secrets.ESPHOME_GITHUB_APP_PRIVATE_KEY }}
+2 -2
View File
@@ -56,7 +56,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6
uses: github/codeql-action/init@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3
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@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6
uses: github/codeql-action/analyze@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3
with:
category: "/language:${{matrix.language}}"
+15 -9
View File
@@ -15,7 +15,7 @@ jobs:
steps:
- name: Generate a token
id: generate-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
uses: actions/create-github-app-token@64ab729a6294000f5aa4a4bc0b73d1f32bc45510 # v3.3.0
with:
client-id: ${{ vars.ESPHOME_GITHUB_APP_CLIENT_ID }}
private-key: ${{ secrets.ESPHOME_GITHUB_APP_PRIVATE_KEY }}
@@ -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@9f6577fd37b5cf773ab1b9be929714a0dcd15661 # 2026.7.0
uses: esphome/workflows/.github/workflows/lock.yml@cc3e76de337dc59bc1cba8da58d963cd23b873f1 # 2026.9.0
+5 -5
View File
@@ -123,7 +123,7 @@ jobs:
python-version: "3.12"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
uses: docker/setup-buildx-action@594f3bf4285d9ea8dc53c9a0c9c4092420091003 # v4.4.0
- name: Log in to docker hub
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
@@ -162,7 +162,7 @@ jobs:
# version: ${{ needs.init.outputs.tag }}
- name: Upload digests
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: digests-${{ matrix.platform.arch }}
path: /tmp/digests
@@ -195,14 +195,14 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Download digests
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
pattern: digests-*
path: /tmp/digests
merge-multiple: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
uses: docker/setup-buildx-action@594f3bf4285d9ea8dc53c9a0c9c4092420091003 # v4.4.0
- name: Log in to docker hub
if: matrix.registry == 'dockerhub'
@@ -245,7 +245,7 @@ jobs:
steps:
- name: Generate a token
id: generate-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
uses: actions/create-github-app-token@64ab729a6294000f5aa4a4bc0b73d1f32bc45510 # v3.3.0
with:
client-id: ${{ vars.ESPHOME_GITHUB_APP_CLIENT_ID }}
private-key: ${{ secrets.ESPHOME_GITHUB_APP_PRIVATE_KEY }}
+16 -2
View File
@@ -16,16 +16,18 @@ 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@61fd37a044cad4e9aa4303027b2a61b6a34da855 # main
uses: esphome/workflows/.github/workflows/stale.yml@4fbce13026c393d6eb6b247e394dcd9ef1af76c2 # main
secrets:
ESPHOME_GITHUB_APP_PRIVATE_KEY: ${{ secrets.ESPHOME_GITHUB_APP_PRIVATE_KEY }}
with:
# Live only on dev: a workflow_dispatch from any other branch is a dry run
dry-run: ${{ github.ref != 'refs/heads/dev' }}
days-before-stale: 90
days-before-stale-conflicting: 30
days-before-close: 7
stale-label: stale
exempt-label: not-stale
conflict-label: has-conflicts
ignored-users: esphbot,codecov-commenter
stale-pr-message: >
There hasn't been any activity on this pull request recently. This
@@ -33,9 +35,21 @@ jobs:
and will be closed if no further activity occurs within 7 days.
If you are the author of this PR, please leave a comment if you want
to keep it open. Also, please rebase your PR onto the latest dev
to keep it open. Also, please merge the latest dev branch into your
branch to ensure that it's up to date with the latest changes.
Thank you for your contribution!
conflict-pr-message: >
This pull request has merge conflicts. Please merge the latest dev
branch into your branch to resolve them.
stale-conflicting-pr-message: >
The merge conflicts on this pull request have not been resolved for
30 days, so it has been marked as stale and will be closed if they
are not resolved within 7 days.
Please merge the latest dev branch into your branch. Once the pull
request is mergeable again the stale label is removed.
Thank you for your contribution!
stale-issue-message: >
There hasn't been any activity on this issue recently. Due to the
@@ -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
@@ -0,0 +1,96 @@
# Keeps pre-commit hook revs in sync with the requirements files.
#
# Dependabot only bumps the pins in requirements*.txt. Some of those tools
# are pinned again as hook revs in .pre-commit-config.yaml. This workflow
# runs script/sync_dependency_versions.py against the pull request branch
# and pushes a commit with the revs updated.
name: Sync dependency versions
on:
# pull_request_target rather than pull_request so the App secret is
# available on Dependabot pull requests (pull_request runs opened by
# Dependabot only see Dependabot secrets). The job below only touches
# branches in this repository and only ever executes the script from the
# 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
- .pre-commit-config.yaml
- script/sync_dependency_versions.py
# The push to the pull request branch uses the App token minted below, so
# the workflow's GITHUB_TOKEN does not need any scopes.
permissions: {}
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
sync:
name: Sync pinned versions
runs-on: ubuntu-latest
# Same-repository branches only: a push to a fork is not possible with
# this token, and it keeps untrusted heads out of a privileged job.
if: >-
github.repository == 'esphome/esphome'
&& github.event.pull_request.head.repo.full_name == github.repository
steps:
- name: Generate a token
id: generate-token
uses: actions/create-github-app-token@64ab729a6294000f5aa4a4bc0b73d1f32bc45510 # v3.3.0
with:
client-id: ${{ vars.ESPHOME_GITHUB_APP_CLIENT_ID }}
private-key: ${{ secrets.ESPHOME_GITHUB_APP_PRIVATE_KEY }}
# A push made with the workflow's own GITHUB_TOKEN would not start
# CI on the new commit; a push with the App token does.
permission-contents: write # git push of the sync commit to the pull request branch
- name: Check out base branch
# Provides the script that runs below. Deliberately the base branch
# so the pull request cannot change what executes here.
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.pull_request.base.sha }}
persist-credentials: false
- name: Check out pull request branch
# No allow-unsafe-pr-checkout here on purpose: checkout v7 only
# refuses heads that live in a different repository, and the job
# condition above already limits runs to same-repository branches.
# Leaving it off keeps that refusal as a backstop for fork heads.
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.pull_request.head.ref }}
path: pull-request
token: ${{ steps.generate-token.outputs.token }}
- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.12"
- name: Install yamlrocks
# The script edits YAML through yamlrocks. Take the pin from the
# base branch requirements so this workflow has no copy of its own.
run: pip install "$(grep -E '^yamlrocks==' requirements_test.txt | cut -d'#' -f1)"
- name: Sync pinned versions
run: python script/sync_dependency_versions.py --root pull-request
- name: Push changes
working-directory: pull-request
run: |
if git diff --quiet; then
echo "All pinned versions already match the requirements files."
exit 0
fi
git config user.name "esphome[bot]"
git config user.email "115708604+esphome[bot]@users.noreply.github.com"
git commit -am "Sync pinned tool versions with requirements files"
git push
+2 -2
View File
@@ -19,7 +19,7 @@ jobs:
steps:
- name: Generate a token
id: generate-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
uses: actions/create-github-app-token@64ab729a6294000f5aa4a4bc0b73d1f32bc45510 # v3.3.0
with:
client-id: ${{ vars.ESPHOME_GITHUB_APP_CLIENT_ID }}
private-key: ${{ secrets.ESPHOME_GITHUB_APP_PRIVATE_KEY }}
@@ -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@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
uses: astral-sh/setup-uv@1c37ad07a6a961277cf70c0d37d6f313000f5884 # v10.3.0
with:
enable-cache: true
# Pin uv version so the action does not have to fetch the
@@ -0,0 +1,98 @@
---
name: Refresh integration test durations
on:
workflow_dispatch:
schedule:
- cron: "45 5 * * 1"
# Repo writes (branch push, PR open) happen via the App token minted below,
# so the workflow's GITHUB_TOKEN does not need any write scopes.
permissions:
contents: read
actions: read # gh api / gh run download for the CI junit artifacts
jobs:
sync:
name: Refresh integration test durations
runs-on: ubuntu-latest
if: github.repository == 'esphome/esphome'
steps:
- name: Generate a token
id: generate-token
uses: actions/create-github-app-token@64ab729a6294000f5aa4a4bc0b73d1f32bc45510 # v3.3.0
with:
client-id: ${{ vars.ESPHOME_GITHUB_APP_CLIENT_ID }}
private-key: ${{ secrets.ESPHOME_GITHUB_APP_PRIVATE_KEY }}
permission-contents: write # push the sync branch
permission-pull-requests: write # open or refresh the sync PR
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.13"
- name: Refresh from the newest usable dev run
env:
GH_TOKEN: ${{ github.token }}
run: |
# Only full matrix dev runs upload junit-integration-* artifacts
# (see the integration-tests job); the merge script re-checks
# coverage regardless.
# Newest-first candidates via their bucket-0 artifact. Fork PRs run
# their own ci.yml, so name and branch are spoofable; require
# same-repo. Assignment failures trip set -e and fail loudly.
candidates=$(
gh api "repos/${GITHUB_REPOSITORY}/actions/artifacts?name=junit-integration-0&per_page=100" \
--jq '.artifacts[] | select(.expired | not) | .workflow_run
| select(.head_branch == "dev" and .head_repository_id != null
and .head_repository_id == .repository_id)
| .id'
)
# Green runs first, then the rest newest first; a run missing a
# bucket fails the coverage check and the next one is tried
green=""
rest=""
for id in ${candidates}; do
conclusion=$(gh api "repos/${GITHUB_REPOSITORY}/actions/runs/${id}" --jq '.conclusion // ""')
if [ "${conclusion}" = "success" ]; then
green="${green} ${id}"
elif [ -n "${conclusion}" ]; then
rest="${rest} ${id}"
fi
done
# helpers.py imports colorama; the script needs nothing else
pip install colorama
for id in ${green} ${rest}; do
rm -rf /tmp/junit
if ! gh run download "${id}" --repo "${GITHUB_REPOSITORY}" -p "junit-integration-*" -D /tmp/junit; then
echo "::warning::Could not download artifacts for run ${id}; trying the next"
continue
fi
status=0
python script/update_integration_test_durations.py /tmp/junit || status=$?
if [ "${status}" -eq 0 ]; then
echo "Refreshed from run ${id}"
exit 0
fi
# Only EXIT_LOW_COVERAGE (3) from the script advances to the next run
[ "${status}" -eq 3 ] || exit 1
echo "::warning::Run ${id} covers too few test files; trying the next"
done
echo "::error::No dev CI run with usable junit artifacts in range; the feed is starved"
exit 1
- name: Commit changes
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
with:
commit-message: "[ci] Refresh integration test durations"
committer: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
author: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
branch: sync/integration-durations
delete-branch: true
title: "[ci] Refresh integration test durations"
body-path: .github/PULL_REQUEST_TEMPLATE.md
token: ${{ steps.generate-token.outputs.token }}
+2
View File
@@ -137,6 +137,8 @@ config/
!tests/component_tests/**/config/
tests/build/
tests/.esphome/
# Output kept by failing tests for inspection; uploaded by CI
test_artifacts/
/.temp-clang-tidy.cpp
/.temp/
.pio/
+4 -5
View File
@@ -1,7 +1,6 @@
---
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
ci:
autoupdate_commit_msg: 'pre-commit: autoupdate'
autoupdate_schedule: off # Disabled until ruff versions are synced between deps and pre-commit
@@ -11,7 +10,7 @@ ci:
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.16.0
rev: v0.17.0
hooks:
# Run the linter.
- id: ruff
@@ -19,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:
@@ -37,12 +36,12 @@ repos:
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/asottile/pyupgrade
rev: v3.21.2
rev: v3.22.0
hooks:
- id: pyupgrade
args: [--py312-plus]
- repo: https://github.com/adrienverge/yamllint.git
rev: v1.37.1
rev: v1.38.0
hooks:
- id: yamllint
exclude: ^(\.clang-format|\.clang-tidy)$
+297 -6
View File
@@ -44,6 +44,16 @@ This document provides essential context for AI models interacting with this pro
## 4. Coding Conventions & Style Guide
**Read the developer documentation before writing a component.** https://developers.esphome.io covers the
component lifecycle, the main loop, and the reasoning behind the rules below in far more depth than this
file does, and it is the authority when they disagree. The most useful starting points:
* https://developers.esphome.io/architecture/components/ - component lifecycle, `setup()`, `loop()`,
setup priorities, and how a component is registered.
* https://developers.esphome.io/architecture/components/advanced/ - choosing between `loop()`,
`set_interval`, `set_timeout` and `defer`; waking the loop from another thread; the RAM cost of each.
* https://developers.esphome.io/contributing/code/ - contribution rules, public API and breaking changes.
* **Formatting:**
* **Python:** Uses `ruff` and `flake8` for linting and formatting. Configuration is in `pyproject.toml`.
* **C++:** Uses `clang-format` for formatting. Configuration is in `.clang-format`.
@@ -57,6 +67,7 @@ This document provides essential context for AI models interacting with this pro
- Function-local constants: `lower_snake_case`
- Protected/private fields: `lower_snake_case_with_trailing_underscore_`
- Favor descriptive names over abbreviations
- Device components are named brand plus model (`siemens_ira211`), without a protocol-family prefix
- Enumerator names: prefix every value of an `enum class` with the enum name converted to
`UPPER_SNAKE_CASE` (e.g. `UARTFlushResult::UART_FLUSH_RESULT_SUCCESS`). Never use bare
names like `SUCCESS`, `FAILURE`, `OK`, or `FAIL`: platform SDK headers define macros with
@@ -133,6 +144,9 @@ This document provides essential context for AI models interacting with this pro
* **C++ Preprocessor Directives:**
* **Avoid `#define` for constants:** Using `#define` for constants is discouraged and should be replaced with `const` variables or enums.
* **A constant with a compile-time initializer is `static constexpr`,** not `static const`, at namespace scope
and inside functions; `static const` is only for a value computed at run time. A function-local
`static constexpr` table is a static constant to clang-tidy, so it is `UPPER_SNAKE_CASE` too.
* **Use `#define` only for:**
- Conditional compilation (`#ifdef`, `#ifndef`)
- Compile-time sizes calculated during Python code generation (e.g., configuring `std::array` or `StaticVector` dimensions via `cg.add_define()`)
@@ -142,6 +156,95 @@ This document provides essential context for AI models interacting with this pro
* **Indentation:** Use spaces (two per indentation level), not tabs
* **Type aliases:** Prefer `using type_t = int;` over `typedef int type_t;`
* **Line length:** Wrap lines at no more than 120 characters
* **Timing in `loop()`:** Never call `millis()` in a `loop()` body. The current tick's timestamp is
already cached - use `App.get_loop_component_start_time()` (from `esphome/core/application.h`).
Only reach for `millis()` when you genuinely need sub-tick resolution inside a long operation.
* **The main loop runs every 16 ms.** A rate-limit gate shorter than that does nothing: the check
passes on essentially every pass of the loop, so it costs a comparison and buys nothing. Pick an
interval comfortably coarser than 16 ms, or drop the gate entirely and accept running every loop.
```cpp
// Bad - a 10ms gate against a 16ms loop never holds anything back
static constexpr uint32_t POLL_INTERVAL_MS = 10;
const uint32_t now = millis();
if (now - this->last_poll_ < POLL_INTERVAL_MS)
return;
this->last_poll_ = now;
```
```cpp
// Good - an interval that actually rate limits, off the cached timestamp
static constexpr uint32_t POLL_INTERVAL_MS = 100;
const uint32_t now = App.get_loop_component_start_time();
if (now - this->last_poll_ < POLL_INTERVAL_MS)
return;
this->last_poll_ = now;
```
Pick the primitive by cadence: under 250 ms use a gated `loop()`; 500 ms and above use
`set_interval`. Full reasoning, including why `set_interval` costs more below 500 ms:
https://developers.esphome.io/architecture/components/advanced/#quick-rule-of-thumb
* **Repeating work:** use `set_interval`, never a `set_timeout` that re-arms itself; every re-arm
rebuilds the callback and re-queues a scheduler item, and the period drifts.
* **Scheduler ids:** name a timer only when it must be cancelled or replaced, and use a
`static constexpr uint32_t` id, never a string. Ids are per component instance and cannot clash with other
components, so number them from 0 and keep all of a component's ids together in one place.
```cpp
static constexpr uint32_t READ_TIMEOUT_ID = 0;
this->set_timeout(READ_TIMEOUT_ID, 50, [this]() { this->read_(); });
```
* **Don't override a default with the same value:** if a base class method already returns what you
want, do not override it. `Component::get_setup_priority()` returns `setup_priority::DATA`, so a
component that wants `DATA` should simply leave it alone.
```cpp
// Bad - this is exactly what the base class already does
float get_setup_priority() const override { return setup_priority::DATA; }
```
* **Constant data lives in RAM on ESP8266** unless marked `PROGMEM`: `const` tables, class-scope
constant arrays, and in-class `std::array` initializers (a `.rodata` template plus a copy). Mark the
table `PROGMEM` and read it with `progmem_memcpy()` or `progmem_read_byte()` (`esphome/core/hal.h`);
for a table of structs copy the whole entry out and return the copy, never a pointer into flash.
On other platforms they are a plain `memcpy` and dereference, so no `#ifdef` is needed. Declare log tags with
`ESPHOME_LOG_TAG(TAG, "name")`, pass status text as `LOG_STR`, e.g.
`this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL))`, and give an action whose payload is a byte array
a `TEMPLATABLE_BYTES` field so a static code stays in flash.
* **Fixed sets of names** (status words, model names): a `PROGMEM_STRING_TABLE` (`esphome/core/progmem.h`),
logged with `get_log_str` and `LOG_STR_ARG`, copied out with `ESPHOME_strncpy_P` before `publish_state`.
A `dump_config` helper that picks a name returns `const LogString *` from `LOG_STR(...)`.
* **CRCs and bit tricks** come from `esphome/core/helpers.h` (`crc8`, `crc16`, `reverse_bits`, `encode_uint16`);
pick the helper's polynomial and bit-order arguments instead of writing a CRC loop.
* **An unset float is `NAN`**, not `optional<float>`; the optional costs 4 to 8 bytes of padding per member.
* **`PollingComponent` first runs `update()`** after a random share of the interval; call `this->update()` at the
end of `setup()` when the entities need a state at boot.
* **A multi-step command sequence** leaves the step pending when the answer is malformed, so the existing
resend and give-up logic handles it; never advance on an answer that was only logged.
* **Entities that each watch a register field:** one class taking register, shift, mask and match from a Python
tuple table, in typed `StaticVector`s sized by `cg.slot_counter`; not a template per bit, not a virtual
listener base (a second vptr per entity).
* **A global feature define** says the code is compiled in, not that this instance uses it: keep the
per-instance check (`if (this->aux_device_ == ...)`) inside the `#ifdef`.
* **Model-specific code** sits behind a define the Python side emits only for configs that use that model
(`cg.add_define("USE_SELEC_METER_EM4M")`), so the other models' builds leave it out; the runtime model check
stays inside the `#ifdef`.
* **Failures after `setup()`:** `mark_failed()` is for `setup()` only. A runtime read or write failure
sets `status_set_warning()` and the next success clears it; writes skip work when `is_failed()`.
* **`dump_config()` already prints the failure reason:** for a failed component, core prints
`<component> is marked FAILED: <reason>` right after the component's `dump_config()`, using the reason
passed to `mark_failed()`. So `dump_config()` never checks `is_failed()` to log a failure, and no
`error_code_` member exists only to pick that message. Pass the reason where setup fails instead:
`this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL))` for a failed bus transfer, a short specific reason
otherwise (`LOG_STR("Wrong chip ID")`). Core also logs it at that moment, so no `ESP_LOGE` repeats it
before `mark_failed()`; keep a separate line only when it prints a value read at run time, such as the
chip ID that did not match. Only the first reason is kept, so do not call `status_set_error()` before
`mark_failed()` and do not mark the component failed again after a helper already did.
* **Fixed platform facts compile to nothing:** gate on `#ifdef USE_ESP8266` and friends, not on a
runtime check of something the build already knows.
* **Logging string literals:** wrap literals passed as `%s` arguments in `LOG_STR_LITERAL()` so they
can be stored in flash rather than RAM.
```cpp
// Bad
ESP_LOGV(TAG, "Key %u %s", key, pressed ? "pressed" : "released");
// Good
ESP_LOGV(TAG, "Key %u %s", key, pressed ? LOG_STR_LITERAL("pressed") : LOG_STR_LITERAL("released"));
```
* **Constructor parameters vs setters:** Component properties that are both **required** and **invariant**
(never change after construction) should be constructor parameters rather than set via setter methods.
This makes the dependency explicit and prevents use of the object in an incompletely-initialized state.
@@ -179,8 +282,13 @@ This document provides essential context for AI models interacting with this pro
```
* **Component Metadata:**
- `DOMAIN`: Required in every component's `__init__.py`, set to the component's own name as a plain
string (e.g. `DOMAIN = "my_component"`). Other code refers to the component through it, e.g.
`from esphome.components.my_component import DOMAIN`. Keep it in alphabetical order with the other
metadata constants. CI (`lint_component_domain`) fails without it.
- `DEPENDENCIES`: List of required components
- `AUTO_LOAD`: Components to automatically load
- `AUTO_LOAD`: Components to automatically load. Not a way to put entities under the component's
own key; see "Entities of a hub" below.
- `CONFLICTS_WITH`: Incompatible components
- `CODEOWNERS`: GitHub usernames responsible for maintenance
- `MULTI_CONF`: Whether multiple instances are allowed
@@ -192,6 +300,7 @@ This document provides essential context for AI models interacting with this pro
import esphome.config_validation as cv
from esphome.const import CONF_KEY, CONF_ID
DOMAIN = "my_component"
CONF_PARAM = "param" # A constant that does not yet exist in esphome/const.py
my_component_ns = cg.esphome_ns.namespace("my_component")
@@ -271,6 +380,55 @@ This document provides essential context for AI models interacting with this pro
var = await switch.new_switch(config)
```
- **Entities of a hub:** a new component's entities go in platform files (`sensor.py`,
`binary_sensor.py`, ...) that take the hub's id, never under the hub's own key with their platforms
added to `AUTO_LOAD` to make that work. Auto loading a platform because the C++ uses its type is
fine. See https://developers.esphome.io/architecture/components/#exposing-multiple-entities.
```yaml
# Bad - entities under the hub's key
my_hub:
connected:
name: Connected
# Good - the hub, then one platform entry that holds its entities
my_hub:
id: hub1
binary_sensor:
- platform: my_hub
my_hub_id: hub1 # optional with a single hub
connected:
name: Connected
```
- **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
# my_hub/sensor.py
async def to_code(config):
hub = await cg.get_variable(config[CONF_MY_HUB_ID])
sensors = sensor.sub_sensors(config)
await sensors(CONF_TEMPERATURE, hub.set_temperature_sensor)
await sensors(CONF_HUMIDITY, hub.set_humidity_sensor)
# my_hub/button.py
async def to_code(config):
hub = await cg.get_variable(config[CONF_MY_HUB_ID])
buttons = button.sub_buttons(config, parent=hub)
await buttons(CONF_RESTART, hub.set_restart_button)
```
- **Pins and referenced components:** set them with `await cg.add_pin(var.set_cs_pin, config[CONF_CS_PIN])`
and `await cg.add_variable(var.set_bus, config[CONF_BUS_ID])`; both skip a `None` value, so pass
`config.get(KEY)` for an optional key, and return what they created. Use `cg.register_parented` for `set_parent`.
* **Automations (Triggers, Actions, Conditions):**
Automations have three building blocks: **Triggers** (fire when something happens), **Actions** (do something), and **Conditions** (check if something is true).
@@ -314,6 +472,25 @@ This document provides essential context for AI models interacting with this pro
)
```
When the callback's parameters differ from the automation's arguments, or the trigger should only
fire for some values, pass `params`, `forward` and `when`; the helper then generates a capture-less
lambda (stored inline, nothing allocated). Name the parent with `automation.parent_ref(var)` and build
`forward` from `MockObj` calls, not f-strings of C++:
```python
parent = automation.parent_ref(var)
index = cg.RawExpression("index")
await automation.build_callback_automation(
var,
"add_on_state_callback",
[(cg.StringRef, "x"), (cg.size_t, "i")],
conf,
params=[(cg.size_t, "index")],
forward=[cg.StringRef(parent.option_at(index)), index],
)
```
`build_parent_callback_automation`, `build_trigger_callback`, `build_callback_automations` and
`build_trigger_automations` cover the other shapes; see their docstrings in `esphome/automation.py`.
**C++ -- no trigger class needed.** The callback registration method must be templatized to accept both `std::function` and lightweight forwarder structs (which avoid heap allocation):
```cpp
class MyComponent : public Component {
@@ -380,7 +557,31 @@ This document provides essential context for AI models interacting with this pro
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
@@ -392,11 +593,29 @@ This document provides essential context for AI models interacting with this pro
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`.
An intentionally empty body (an unused `register_dumper` builder, for example) gets a one-line docstring, not `pass`.
* **Configuration Validation:**
* **One schema per model:** when a component's options depend on a `model` key, use `cv.typed_schema({...},
key=CONF_MODEL, default_type=...)` with each model's own keys and defaults, not one schema plus a validator
that rejects keys and fills defaults afterwards.
* **Reuse existing validators:** Before writing a custom validator, check for an existing one in `config_validation.py` and compose it in `cv.All(...)` rather than duplicating logic across components. For example, rename a config key with `cv.rename_key(CONF_OLD, CONF_NEW, removed_in="2026.6.0")`, and reject mutually-exclusive keys with `cv.has_at_most_one_key(...)` / `cv.has_exactly_one_key(...)`. See how `api` composes `cv.has_exactly_one_key` + `cv.rename_key`.
* **Common Validators:** `cv.int_`, `cv.float_`, `cv.string`, `cv.boolean`, `cv.int_range(min=0, max=100)`, `cv.positive_int`, `cv.percentage`.
* **Complex Validation:** `cv.All(cv.string, cv.Length(min=1, max=50))`, `cv.Any(cv.int_, cv.string)`.
@@ -430,6 +649,18 @@ This document provides essential context for AI models interacting with this pro
* **Testing:**
* **Python:** Run unit tests with `pytest`.
* **C++:** Use `clang-tidy` for static analysis.
* **C++ unit tests:** gtests in `tests/components/<component>/*.cpp`, run with
`script/cpp_unit_test.py <component>`; see `tests/components/README.md`. Set the loop clock in a test
with `LoopBlockingGuard{nullptr, nullptr, now_ms}`; the runner resets it before every test.
* **Integration tests:** `tests/integration/` runs real firmware on the host. Serial components prove
timeouts and recovery with the `uart_mock` fixture (`tests/integration/test_uart_mock_*.py`). Its
`responses` match on content every time they see it; to answer a repeated request differently, use an
`on_tx` lambda with a static counter and `inject_to_rx_buffer` (see `uart_mock_ld2412_requery_after_giveup.yaml`).
* **Keep build-test variants few:** one `test.<platform>.yaml` per platform the code actually differs
on. A variant per board for platform-identical code only adds CI time.
* **A test that needs a shared component in a particular state** (`esp32_ble` security settings, an option
that PSRAM rejects) cannot be grouped: the merged block wins and the whole batch fails. Add the component to
`ISOLATED_COMPONENTS` in `script/analyze_component_buses.py`, or move the option to a `test-<variant>` file.
* **Component Tests:** YAML-based compilation tests are located in `tests/`. The structure is as follows:
```
tests/
@@ -476,6 +707,7 @@ This document provides essential context for AI models interacting with this pro
name: My Sensor
```
Components that define buses directly are flagged as "NEEDS MIGRATION" and cannot be grouped, increasing CI build time.
`!extend` and `!remove` in a `test.*.yaml` also make the component ungroupable; add a standalone entity with its own id instead.
* **Testing All Components Together:** To verify that all components can be tested together without ID conflicts or configuration issues, use:
```bash
@@ -502,6 +734,7 @@ This document provides essential context for AI models interacting with this pro
4. **Lint:** Run `prek` to ensure code is compliant.
5. **Commit:** Commit your changes. There is no strict format for commit messages.
6. **Pull Request:** Submit a PR against the `dev` branch. The Pull Request title must start with a `[tag]` prefix. For component work, use the component name (e.g., `[display] Fix bug`, `[abc123] Add new component`); for changes to shared/core code that isn't tied to a single component, use `[core]` (e.g., `[core] Add validator`). Update documentation, examples, and add `CODEOWNERS` entries as needed. Pull requests should always be made using the `.github/PULL_REQUEST_TEMPLATE.md` template - fill out all sections completely without removing any parts of the template.
7. **Comments:** When commenting on GitHub PRs or issues, don't tag contributors, especially bots. Avoid referring to list items (e.g. from reviews) with the form #nn - this will be interpreted by GitHub as a reference to issue or PR nn. Keep comments short and exclude irrelevant details, backstories, restatement of previous comments and anything that is already obvious to the reader.
* **Documentation Contributions:**
* Documentation is hosted in the separate `esphome/esphome.io` repository.
@@ -562,6 +795,36 @@ This document provides essential context for AI models interacting with this pro
Use `cg.add_define("MAX_SERVICES", count)` to set the size from Python configuration.
Like `std::array` but with vector-like API (`push_back()`, `size()`) and no STL reallocation code.
**Listener and child-entity registration lists are the most common case, and the most commonly
missed.** A `register_*()` method called once per child at code generation time has a count that
is known at compile time, so it should never be a `std::vector`. Use `cg.slot_counter()`: it
returns a function that each consumer calls once per slot it will occupy, and after every
`to_code` has run it emits the define with the final count. When nothing registers, no define is
emitted and the storage plus its registration method compile out entirely.
```python
# hub component's __init__.py
_request_listener_slot = cg.slot_counter("MY_COMPONENT_LISTENER_COUNT")
async def register_listener(hub: MockObj, var: MockObj) -> None:
_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);
#endif
protected:
#ifdef MY_COMPONENT_LISTENER_COUNT
StaticVector<MyComponentListener *, MY_COMPONENT_LISTENER_COUNT> listeners_;
#endif
```
Request slots from `to_code`, not from a job that runs after `CoroPriority.FINAL` - a late
request raises rather than silently undercounting.
3. **Runtime-known sizes:** Use `FixedVector` from `esphome/core/helpers.h` when the size is only known at runtime initialization.
```cpp
// Bad - generates STL realloc code (_M_realloc_insert)
@@ -599,9 +862,27 @@ This document provides essential context for AI models interacting with this pro
```
Linear search on small datasets (1-16 elements) is often faster than hashing/tree overhead, but this depends on lookup frequency and access patterns. For frequent lookups in hot code paths, the O(1) vs O(n) complexity difference may still matter even for small datasets. `std::vector` with simple structs is usually fine—it's the heavy containers (`map`, `set`, `unordered_map`) that should be avoided for small datasets unless profiling shows otherwise.
5. **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.
5. **Strings set once from configuration:** Use `StringRef` (`esphome/core/string_ref.h`) rather than
`std::string`. Code generation passes a string literal that lives in flash for the life of the
program, so storing a `std::string` copies it onto the heap for nothing. `StringRef` is a
non-owning pointer plus length; it does not copy, and it must only ever refer to storage that
outlives it (a string literal, or a buffer owned elsewhere).
```cpp
// Bad - heap copy of a literal that is already in flash
void set_keys(std::string keys) { this->keys_ = std::move(keys); }
std::string keys_;
```
```cpp
// Good - no allocation
void set_keys(const char *keys) { this->keys_ = StringRef(keys); }
StringRef keys_;
```
6. **Detection:** Look for these patterns in compiler output:
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. **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)
@@ -745,7 +1026,7 @@ This document provides essential context for AI models interacting with this pro
cv.rename_key(
CONF_OLD_KEY, CONF_NEW_KEY, removed_in="2026.6.0", component="my_component"
),
cv.Schema({ ... }),
cv.Schema({...}),
)
```
For other deprecations, warn manually during validation:
@@ -763,3 +1044,13 @@ The project uses English for non-code content. When drafting documentation, code
PR descriptions, and similar text, avoid technical jargon. Instead, express concepts in plain English,
using standard technical terms only when required. Ensure the text is readily comprehensible to a wide
audience, including non-native English speakers.
## 10. Code Comments
Code comments on individual lines should be used only where necessary to flag issues that may not be obvious
on a simple reading of the code. Keep them short (e.g. 1 or 2 lines).
Function and method comment blocks may include more detail as required to make
calling contracts clear and document parameter usage, but should still be kept concise.
Avoid redundancy and repetition; comments should never simply restate what the code already says.
+107 -5
View File
@@ -30,6 +30,9 @@ esphome/components/airthings_ble/* @jeromelaban
esphome/components/airthings_wave_base/* @jeromelaban @ncareau
esphome/components/airthings_wave_mini/* @ncareau
esphome/components/airthings_wave_plus/* @jeromelaban @precurse
esphome/components/airton/* @Procsiab
esphome/components/airton/select/* @Procsiab
esphome/components/airton/switch/* @Procsiab
esphome/components/alarm_control_panel/* @grahambrown11 @hwstar
esphome/components/alpha3/* @jan-hofmeier
esphome/components/am2315c/* @swoboda1337
@@ -39,12 +42,17 @@ esphome/components/am43/sensor/* @buxtronix
esphome/components/analog_threshold/* @ianchi
esphome/components/animation/* @syndlex
esphome/components/anova/* @buxtronix
esphome/components/apc1/* @ademuri
esphome/components/apc_proteous/* @clydebarrow
esphome/components/apds9306/* @aodrenah
esphome/components/apds9930/* @iyesin
esphome/components/api/* @esphome/core
esphome/components/aqi/* @freekode @jasstrong @ximex
esphome/components/aranet/* @patrickod
esphome/components/as5600/* @ammmze
esphome/components/as5600/sensor/* @ammmze
esphome/components/as7341/* @mrgnr
esphome/components/as734x/* @latonita @mrgnr
esphome/components/async_tcp/* @esphome/core
esphome/components/at581x/* @X-Ryl669
esphome/components/atc_mithermometer/* @ahpohl
@@ -65,6 +73,7 @@ esphome/components/bedjet/climate/* @jhansche
esphome/components/bedjet/fan/* @jhansche
esphome/components/bedjet/sensor/* @javawizard @jhansche
esphome/components/beken_spi_led_strip/* @Mat931
esphome/components/bh1745/* @latonita
esphome/components/bh1750/* @OttoWinter
esphome/components/bh1900nux/* @B48D81EFCC
esphome/components/binary_sensor/* @esphome/core
@@ -100,6 +109,10 @@ esphome/components/bmp581_i2c/* @danielkent-net @kahrendt
esphome/components/bmp581_spi/* @danielkent-net @kahrendt
esphome/components/bp1658cj/* @Cossid
esphome/components/bp5758d/* @Cossid
esphome/components/bq25186/* @Rapsssito
esphome/components/bq27220/* @rggammon
esphome/components/bq76972/* @limpkin
esphome/components/bridge/* @kbx81
esphome/components/bthome_mithermometer/* @nagyrobi
esphome/components/button/* @esphome/core
esphome/components/bytebuffer/* @clydebarrow
@@ -111,6 +124,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 +137,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
@@ -131,6 +147,7 @@ esphome/components/cst816/* @clydebarrow
esphome/components/cst9220/* @clydebarrow
esphome/components/ct_clamp/* @jesserockz
esphome/components/current_based/* @djwmarcx
esphome/components/d01/* @ch604
esphome/components/dac7678/* @NickB1
esphome/components/daikin_arc/* @MagicBear
esphome/components/daikin_brc/* @hagak
@@ -143,15 +160,22 @@ esphome/components/delonghi/* @grob6000
esphome/components/dew_point/* @CFlix
esphome/components/dfplayer/* @glmnet
esphome/components/dfrobot_sen0395/* @niklasweber
esphome/components/dfrobot_sen0465_base/* @tzabbi
esphome/components/dfrobot_sen0465_i2c/* @tzabbi
esphome/components/dfrobot_sen0465_uart/* @tzabbi
esphome/components/dfrobot_sen0609/* @remcom
esphome/components/dht/* @OttoWinter
esphome/components/display_menu_base/* @numo68
esphome/components/dlms_meter/* @latonita @PolarGoose @SimonFischer04 @Tomer27cz
esphome/components/dps310/* @kbx81
esphome/components/ds1307/* @badbadc0ffee
esphome/components/ds1603l/* @JakeLC15
esphome/components/ds2484/* @mrk-its
esphome/components/ds248x/* @tomwellnitz
esphome/components/ds3231/* @linkedupbits
esphome/components/dsmr/* @glmnet @PolarGoose
esphome/components/duty_time/* @dudanov
esphome/components/ecocomfort2/* @gledian
esphome/components/ee895/* @Stock-M
esphome/components/ektf2232/touchscreen/* @jesserockz
esphome/components/emc2101/* @ellull
@@ -168,6 +192,7 @@ esphome/components/es7243e/* @kbx81
esphome/components/es8156/* @kbx81
esphome/components/es8311/* @kahrendt @kroimon
esphome/components/es8388/* @P4uLT
esphome/components/es9038q2m_katana/* @GioMez
esphome/components/esp32/* @esphome/core
esphome/components/esp32_ble/* @bdraco @jesserockz @Rapsssito
esphome/components/esp32_ble_client/* @bdraco @jesserockz
@@ -177,27 +202,34 @@ 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_power_management/* @rwrozelle
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/esphome/ota/* @esphome/core
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
esphome/components/ezsp_proxy_tap/* @kbx81 @puddly
esphome/components/factory_reset/* @anatoly-savchenkov
esphome/components/fastled_base/* @OttoWinter
esphome/components/feedback/* @ianchi
esphome/components/file/* @esphome/core
esphome/components/filter_lifetime/* @tuct
esphome/components/fingerprint_grow/* @alexborro @loongyh @OnFreund
esphome/components/font/* @clydebarrow @esphome/core
esphome/components/friedrich/* @rwrozelle
esphome/components/fs3000/* @kahrendt
esphome/components/ft5x06/* @clydebarrow
esphome/components/ft63x6/* @gpambrozio
esphome/components/fusb302b/* @remcom
esphome/components/gcja5/* @gcormier
esphome/components/gdk101/* @Szewcson
esphome/components/gl_r01_i2c/* @pkejval
@@ -219,9 +251,14 @@ esphome/components/gt911/* @clydebarrow @jesserockz
esphome/components/haier/* @paveldn
esphome/components/haier/binary_sensor/* @paveldn
esphome/components/haier/button/* @paveldn
esphome/components/haier/select/* @paveldn
esphome/components/haier/sensor/* @paveldn
esphome/components/haier/switch/* @paveldn
esphome/components/haier/text_sensor/* @paveldn
esphome/components/haier_tundra/* @fauxpark
esphome/components/haier_tundra/button/* @fauxpark
esphome/components/haier_tundra/switch/* @fauxpark
esphome/components/haier_water_heater/* @TheLexus
esphome/components/havells_solar/* @sourabhjaiswal
esphome/components/hbridge/fan/* @WeekendWarrior
esphome/components/hbridge/light/* @DotNetDann
@@ -230,6 +267,7 @@ esphome/components/hc8/* @omartijn
esphome/components/hdc2010/* @optimusprimespace @ssieb
esphome/components/hdc2080/* @G-Pereira @jesserockz
esphome/components/hdc302x/* @joshuasing
esphome/components/hdmi_cec/* @JosVanEijndhoven
esphome/components/he60r/* @clydebarrow
esphome/components/heatpumpir/* @rob-deutsch
esphome/components/hitachi_ac424/* @sourabhjaiswal
@@ -240,8 +278,12 @@ 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/light/* @egormanga
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
@@ -253,6 +295,7 @@ esphome/components/http_request/ota/* @oarcher
esphome/components/http_request/update/* @jesserockz
esphome/components/htu31d/* @betterengineering
esphome/components/hub75/* @stuartparmenter
esphome/components/husb238/* @latonita
esphome/components/hydreon_rgxx/* @functionpointer
esphome/components/hyt271/* @Philippe12
esphome/components/i2c/* @esphome/core
@@ -260,9 +303,13 @@ esphome/components/i2c_device/* @gabest11
esphome/components/i2s_audio/* @jesserockz
esphome/components/i2s_audio/microphone/* @jesserockz
esphome/components/i2s_audio/speaker/* @jesserockz @kahrendt
esphome/components/i2s_clockless_led_strip/* @j9brown
esphome/components/iaqcore/* @yozik04
esphome/components/icm20689/* @lboue
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
@@ -275,6 +322,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
@@ -286,9 +334,11 @@ esphome/components/kuntze/* @ssieb
esphome/components/lc709203f/* @ilikecake
esphome/components/lcd_menu/* @numo68
esphome/components/ld2410/* @regevbr @sebcaps
esphome/components/ld2410s/* @NovakIrs
esphome/components/ld2412/* @Rihan9
esphome/components/ld2420/* @descipher
esphome/components/ld2450/* @hareeshmu
esphome/components/ld2460/* @jesusvallejo
esphome/components/ld24xx/* @kbx81
esphome/components/ld6002b/* @hepter
esphome/components/ledc/* @OttoWinter
@@ -297,6 +347,10 @@ esphome/components/libretiny_pwm/* @kuba2k2
esphome/components/light/* @esphome/core
esphome/components/lightwaverf/* @max246
esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
esphome/components/lis2dw12/* @latonita
esphome/components/lis2dw12_i2c/* @latonita
esphome/components/lis2dw12_spi/* @latonita
esphome/components/lis3dh/* @zebble
esphome/components/lm75b/* @beormund
esphome/components/ln882h_ble/* @Bl00d-B0b
esphome/components/ln882h_ble_tracker/* @Bl00d-B0b
@@ -304,6 +358,7 @@ esphome/components/ln882x/* @lamauny
esphome/components/lock/* @esphome/core
esphome/components/logger/* @esphome/core
esphome/components/logger/select/* @clydebarrow
esphome/components/lp5562/* @DennisGaida
esphome/components/lps22/* @nagisa
esphome/components/lsm6ds/* @clydebarrow
esphome/components/ltr390/* @latonita @sjtrny
@@ -314,7 +369,9 @@ esphome/components/m5stack_8angle/* @rnauber
esphome/components/mapping/* @clydebarrow
esphome/components/matrix_keypad/* @ssieb
esphome/components/max17043/* @blacknell
esphome/components/max17048/* @B48D81EFCC
esphome/components/max31865/* @DAVe3283
esphome/components/max31888/* @JiriPrchal
esphome/components/max44009/* @berfenger
esphome/components/max6956/* @looping40
esphome/components/max7219digit/* @rspaargaren
@@ -333,7 +390,6 @@ esphome/components/mcp4461/* @p1ngb4ck
esphome/components/mcp4728/* @berfenger
esphome/components/mcp47a1/* @jesserockz
esphome/components/mcp9600/* @mreditor97
esphome/components/mcp9808/* @k7hpn
esphome/components/md5/* @esphome/core
esphome/components/mdns/* @esphome/core
esphome/components/media_player/* @jesserockz
@@ -342,14 +398,18 @@ esphome/components/micro_wake_word/* @jesserockz @kahrendt
esphome/components/micronova/* @edenhaus @jorre05
esphome/components/microphone/* @jesserockz @kahrendt
esphome/components/mics_4514/* @jesserockz
esphome/components/mics_5524/* @nliaudat
esphome/components/midea/* @dudanov
esphome/components/midea_ir/* @dudanov
esphome/components/midea_portasplit/* @Fexiven
esphome/components/mipi_dsi/* @clydebarrow
esphome/components/mipi_rgb/* @clydebarrow
esphome/components/mipi_spi/* @clydebarrow
esphome/components/mistral_ir/* @lerhum
esphome/components/mitsubishi/* @RubyBailey
esphome/components/mitsubishi_cn105/* @crnjan
esphome/components/mixer/speaker/* @kahrendt
esphome/components/mk2pvrouter/* @FredM67
esphome/components/mlx90393/* @functionpointer
esphome/components/mlx90614/* @jesserockz
esphome/components/mmc5603/* @benhoff
@@ -372,6 +432,9 @@ esphome/components/mpl3115a2/* @kbickar
esphome/components/mpu6886/* @fabaff
esphome/components/ms8607/* @e28eta
esphome/components/msa3xx/* @latonita
esphome/components/mt6701/* @slimcdk
esphome/components/mt6701_i2c/* @slimcdk
esphome/components/mt6701_spi/* @slimcdk
esphome/components/nau7802/* @cujomalainey
esphome/components/network/* @esphome/core
esphome/components/nextion/* @edwardtfn @senexcrenshaw
@@ -381,8 +444,11 @@ esphome/components/nextion/switch/* @senexcrenshaw
esphome/components/nextion/text_sensor/* @senexcrenshaw
esphome/components/nfc/* @jesserockz @kbx81
esphome/components/noblex/* @AGalfra
esphome/components/noise/* @esphome/core
esphome/components/npi19/* @bakerkj
esphome/components/nrf52/* @tomaszduda23
esphome/components/ns2009/* @tchilov
esphome/components/ns2009/touchscreen/* @tchilov
esphome/components/number/* @esphome/core
esphome/components/one_wire/* @ssieb
esphome/components/online_image/* @clydebarrow @guillempages
@@ -393,10 +459,12 @@ esphome/components/ota/* @esphome/core
esphome/components/output/* @esphome/core
esphome/components/packet_transport/* @clydebarrow
esphome/components/pca6416a/* @Mat931
esphome/components/pca9505/* @ajfriesen @ssieb
esphome/components/pca9554/* @bdraco @clydebarrow @hwstar
esphome/components/pcf85063/* @brogon
esphome/components/pcf8563/* @KoenBreeman
esphome/components/pcm5122/* @remcom
esphome/components/pd_pioneer_ir/* @martinsah
esphome/components/pi4ioe5v6408/* @jesserockz
esphome/components/pid/* @OttoWinter
esphome/components/pipsolar/* @andreashergert1984
@@ -414,6 +482,8 @@ 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_management/* @rwrozelle
esphome/components/power_supply/* @esphome/core
esphome/components/preferences/* @esphome/core
esphome/components/provisioning/* @esphome/core
@@ -421,6 +491,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
@@ -433,6 +504,8 @@ esphome/components/rc522/* @glmnet
esphome/components/rc522_i2c/* @glmnet
esphome/components/rc522_spi/* @glmnet
esphome/components/rd03d/* @jasstrong
esphome/components/remko_ar715/* @naKruul
esphome/components/renogy_inverter_ble/* @emilioaray-dev
esphome/components/resampler/speaker/* @kahrendt
esphome/components/restart/* @esphome/core
esphome/components/rf_bridge/* @jesserockz
@@ -446,11 +519,15 @@ esphome/components/rp2040_pwm/* @jesserockz
esphome/components/rp2_ble_tracker/* @bdraco
esphome/components/rpi_dpi_rgb/* @clydebarrow
esphome/components/rtl87xx/* @kuba2k2
esphome/components/rtsp_server/* @max246
esphome/components/rtttl/* @glmnet @ximex
esphome/components/runtime_image/* @clydebarrow @guillempages @kahrendt
esphome/components/runtime_stats/* @bdraco
esphome/components/rx8025t/* @remcom
esphome/components/rx8111/* @techdregs
esphome/components/rx8130/* @beormund
esphome/components/safe_mode/* @jsuanet @kbx81 @paulmonigatti
esphome/components/samsung/* @jorofi
esphome/components/scd4x/* @martgras @sjtrny
esphome/components/script/* @esphome/core
esphome/components/sdl/* @bdm310 @clydebarrow
@@ -470,18 +547,23 @@ 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
esphome/components/serial_proxy/* @kbx81
esphome/components/sfa30/* @ghsensdev
esphome/components/sfa40/* @NoQuarrel
esphome/components/sgp40/* @SenexCrenshaw
esphome/components/sgp4x/* @martgras @SenexCrenshaw
esphome/components/sh1122_base/* @pl4nkton
esphome/components/sh1122_spi/* @pl4nkton
esphome/components/sha256/* @esphome/core
esphome/components/shelly_dimmer/* @edge90 @rnauber
esphome/components/sht3xd/* @mrtoy-me
esphome/components/sht4x/* @sjtrny
esphome/components/shutdown/* @esphome/core @jsuanet
esphome/components/siemens_ira211/* @fonix232
esphome/components/sigma_delta_output/* @Cat-Ion
esphome/components/sim800l/* @glmnet
esphome/components/sm10bit_base/* @Cossid
@@ -491,12 +573,14 @@ esphome/components/sm2335/* @Cossid
esphome/components/sml/* @alengwenus
esphome/components/smt100/* @piechade
esphome/components/sn74hc165/* @jesserockz
esphome/components/snapshot/* @clydebarrow
esphome/components/socket/* @esphome/core
esphome/components/sonoff_d1/* @anatoly-savchenkov
esphome/components/sound_level/* @kahrendt
esphome/components/spa06_base/* @danielkent-net
esphome/components/spa06_i2c/* @danielkent-net
esphome/components/spa06_spi/* @danielkent-net
esphome/components/spd2010/* @grischard
esphome/components/speaker/* @jesserockz @kahrendt
esphome/components/speaker/media_player/* @kahrendt @synesthesiam
esphome/components/speaker_source/* @kahrendt
@@ -526,6 +610,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
@@ -536,29 +621,38 @@ 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/tca8418/* @zebble
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/teleinfo/* @0hax @FredM67
esphome/components/tem3200/* @bakerkj
esphome/components/template/alarm_control_panel/* @grahambrown11 @hwstar
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
esphome/components/tinyusb/* @kbx81
esphome/components/tinyusb_keyboard/* @frederikbolding
esphome/components/tlc5947/* @rnauber
esphome/components/tlc5971/* @IJIJI
esphome/components/tm1621/* @Philippe12
esphome/components/tm1637/* @glmnet
esphome/components/tm1638/* @skykingjwc
esphome/components/tm1651/* @mrtoy-me
esphome/components/tmc2209/* @remcom
esphome/components/tmc22xx/* @remcom
esphome/components/tmp102/* @timsavage
esphome/components/tmp1075/* @sybrenstuvel
esphome/components/tmp117/* @Azimath
@@ -575,15 +669,19 @@ esphome/components/tuya/select/* @bearpawmaxim
esphome/components/tuya/sensor/* @jesserockz
esphome/components/tuya/switch/* @jesserockz
esphome/components/tuya/text_sensor/* @dentra
esphome/components/tuya/water_heater/* @iago-veiga
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
@@ -594,10 +692,11 @@ esphome/components/vbus/* @ssieb
esphome/components/veml3235/* @kbx81
esphome/components/veml7700/* @latonita
esphome/components/version/* @esphome/core
esphome/components/vevor_dtx/* @Bofr5555
esphome/components/voice_assistant/* @jesserockz @kahrendt
esphome/components/wake_on_lan/* @clydebarrow @willwill2will54
esphome/components/watchdog/* @oarcher
esphome/components/water_heater/* @dhoeben
esphome/components/water_heater/* @esphome/core
esphome/components/waveshare_epaper/* @clydebarrow
esphome/components/waveshare_io_ch32v003/* @latonita
esphome/components/web_server/ota/* @esphome/core
@@ -606,7 +705,7 @@ esphome/components/web_server_idf/* @dentra
esphome/components/weikai/* @DrCoolZic
esphome/components/weikai_i2c/* @DrCoolZic
esphome/components/weikai_spi/* @DrCoolZic
esphome/components/whirlpool/* @glmnet
esphome/components/whirlpool/* @arpena @glmnet
esphome/components/whynter/* @aeonsablaze
esphome/components/wiegand/* @ssieb
esphome/components/wireguard/* @droscy @lhoracek @thomas0bernard
@@ -623,9 +722,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
@@ -634,6 +735,7 @@ esphome/components/xl9535/* @mreditor97
esphome/components/xpt2046/touchscreen/* @nielsnl68 @numo68
esphome/components/xxtea/* @clydebarrow
esphome/components/zephyr/* @tomaszduda23
esphome/components/zephyr_coredump/* @tomaszduda23
esphome/components/zephyr_mcumgr/ota/* @tomaszduda23
esphome/components/zephyr_pwm/* @wiomoc
esphome/components/zhlt01/* @cfeenstra1024
+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.9.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
+52 -3
View File
@@ -23,7 +23,8 @@ For this repository there are two trusted inputs by design:
1. **The configuration.** Anyone who can supply or edit a YAML config is trusted
(see below).
2. **Authenticated peers of a running device** — clients holding the device's
API encryption key / password, OTA password, or web server credentials.
API/OTA encryption key, API password, OTA password, or web server
credentials.
The security boundary is therefore **unauthenticated network traffic vs. those
trusted inputs.** A bug that lets an unauthenticated attacker cross it is a
@@ -76,8 +77,8 @@ These *are* security bugs in this repo, and we want to hear about them privately
captive portal, etc.) **without** valid credentials.
- Authentication or encryption bypass on the device — reaching API calls, OTA
updates, or the web server without the configured key/password.
- Flaws that weaken the device's API encryption (Noise), OTA, or web server auth
below their documented guarantees.
- Flaws that weaken the device's API or OTA encryption (Noise), OTA auth, or
web server auth below their documented guarantees.
## The web server is an open HTTP API by design
@@ -121,6 +122,54 @@ and any memory-safety or protocol bug in the server reachable without credential
This section documents the current design and scope; it is not a judgment that the
design is optimal or that it will not change.
## OTA update encryption
The `esphome` OTA platform optionally encrypts updates with the same Noise
`NNpsk0` pattern the native API uses; one key protects the device. A device
whose `api:` block has an encryption key, static in the YAML or provisioned at
runtime, compiles in the transport and offers it on every OTA connection once
it holds a key, so an uploader presenting that key gets the guarantees below
even without an `ota: encryption:` block; only that block makes the device
require encryption. The guarantees are: the firmware image is confidential in
transit, the uploader is authenticated by the pre-shared key, and the plaintext
negotiation preceding the handshake is bound into the handshake prologue, so
stripping or tampering with it fails the first MAC. With `ota: encryption:`
configured both ends fail closed with no override: the device refuses
plaintext uploads, and the CLI refuses to send plaintext when a key is
configured. Without that block the CLI tries a static api key when the device
offers and, until 2027.3.0, falls back to plaintext with a warning when the
offer is missing or the handshake fails; a runtime provisioned key never
reaches the CLI, so those uploads stay plaintext.
Defeating any of that without the key is in scope: a device that requires
encryption accepting a plaintext or downgraded upload, getting past the MAC,
or recovering image contents from captured traffic.
The following are **not** vulnerabilities, by design:
- Plaintext OTA on a device with no `ota: encryption:` block, including one
that offers encryption because it has an api key. That is the documented
default, authenticated (if at all) by the OTA password. An uploader that
takes the offer skips the password; the key authenticates it. With a
runtime provisioned key and no `provisioning:` window, whoever provisions
the key gains that upload path too; validation warns about the pair.
- The CLI plaintext fallback until 2027.3.0: without `ota: encryption:` an
active attacker who strips the offer or breaks the handshake can make a
keyed CLI upload plaintext, with the pre-existing plaintext exposure. A
device that requires encryption still refuses that upload.
- The enablement window: firmware built with a static api key already offers
encryption, so turning on `ota: encryption:` is itself an encrypted upload.
Older firmware needs one last plaintext upload of an offering build, with
the pre-existing plaintext exposure.
- The web OTA `/update` endpoint alongside encryption. With the `web_server`
or `prometheus` component the shared listener is always up, so the endpoint
stays reachable and validation warns about that combination;
`captive_portal:` alone brings the listener up only for the fallback AP
window, which is the intended recovery path, so that is not warned about.
- CLI retry behavior on transport or MAC failures; every attempt renegotiates
a fresh handshake with fresh ephemerals, so retrying does not weaken
authentication.
## Explicitly out of scope
- Local attackers who already have shell access on the host that runs `esphome`.
+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.9.6
RUN uv pip install --no-cache-dir esphome-device-builder==1.24.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)
+5 -1
View File
@@ -1,5 +1,6 @@
#!/usr/bin/env python3
import argparse
import os
import re
CHANNEL_DEV = "dev"
@@ -64,7 +65,10 @@ def main():
suffix = f"-{args.suffix}" if args.suffix else ""
image_name = f"esphome/esphome{suffix}"
repository = (
(os.environ.get("GITHUB_REPOSITORY") or "esphome/esphome").strip().lower()
)
image_name = f"{repository}{suffix}"
print(f"channel={channel}")
@@ -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:
+407 -244
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,
@@ -26,14 +27,15 @@ from esphome.const import (
CONF_DEASSERT_RTS_DTR,
CONF_DISABLED,
CONF_DISCOVER_IP,
CONF_ENCRYPTION,
CONF_ESPHOME,
CONF_KEY,
CONF_LEVEL,
CONF_LOG,
CONF_LOG_TOPIC,
CONF_LOGGER,
CONF_MDNS,
CONF_MQTT,
CONF_NAME,
CONF_NAME_ADD_MAC_SUFFIX,
CONF_OTA,
CONF_PASSWORD,
@@ -46,19 +48,24 @@ from esphome.const import (
CONF_WEB_SERVER,
CONF_WIFI,
ENV_NOGITIGNORE,
KEY_CORE,
KEY_ESP32,
KEY_TARGET_PLATFORM,
KEY_VARIANT,
PLATFORM_HOST,
SECRETS_FILES,
ErrorFormat,
Toolchain,
)
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,
@@ -66,6 +73,7 @@ from esphome.util import (
get_serial_ports,
is_picotool_usb_permission_error,
list_yaml_files,
read_secret_line,
run_external_command,
run_external_process,
safe_print,
@@ -82,7 +90,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
@@ -147,6 +154,7 @@ class ArgsProtocol(Protocol):
file: str | None
no_logs: bool
only_generate: bool
skip_bootloader: bool
show_secrets: bool
dashboard: bool
configuration: str
@@ -475,12 +483,9 @@ def has_ota() -> bool:
def has_native_ota() -> bool:
"""Check if native API OTA upload is available (``platform: esphome``)."""
if CONF_OTA not in CORE.config:
return False
return any(
ota_item.get(CONF_PLATFORM) == CONF_ESPHOME
for ota_item in CORE.config[CONF_OTA]
)
from esphome.espota2 import esphome_ota_items
return bool(esphome_ota_items(CORE.config))
def has_web_server_ota() -> bool:
@@ -762,9 +767,11 @@ def _wrap_to_code(name, comp, yaml_util):
async def wrapped(conf):
cg.add(cg.LineComment(f"{name}:"))
if comp.config_schema is not None:
conf_str = yaml_util.dump(conf)
# sort_keys: voluptuous fills defaults in set order, so an
# unsorted dump would churn main.cpp and relink every run
conf_str = yaml_util.dump(conf, sort_keys=True)
conf_str = conf_str.replace("//", "")
# remove tailing \ to avoid multi-line comment warning
# remove trailing \ to avoid multi-line comment warning
conf_str = conf_str.replace("\\\n", "\n")
cg.add(cg.LineComment(indent(conf_str)))
await coro(conf)
@@ -813,7 +820,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()
@@ -822,6 +831,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:
@@ -831,7 +848,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()
@@ -852,10 +869,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()
toolchain.get_idedata()
from esphome.build_helpers.idedata import warn_if_idedata_missing
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
@@ -958,12 +985,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
@@ -1202,14 +1233,28 @@ def upload_program(
config: ConfigType, args: ArgsProtocol, devices: list[str]
) -> tuple[int, str | None]:
host = devices[0]
port_type = get_port_type(host)
ota_key = getattr(args, "ota_key", None)
if ota_key and port_type in (PortType.SERIAL, PortType.BOOTSEL):
_LOGGER.warning(
"The presented OTA key only applies to network uploads; ignored for %s",
host,
)
platform_upload = platform_hooks.get_platform_hook(
CORE.target_platform, "upload_program"
)
if platform_upload is not None and platform_upload(config, args, host):
if ota_key and port_type not in (PortType.SERIAL, PortType.BOOTSEL):
# A platform's own network path (nrf52 mcumgr) has no key handshake
_LOGGER.warning(
"The presented OTA key only applies to the %s OTA platform; "
"ignored for %s",
CONF_ESPHOME,
host,
)
return 0, host
port_type = get_port_type(host)
# MQTT and MQTTIP are also OTA paths; MQTTIP gets resolved to a real IP later by
# _resolve_network_devices(). Only SERIAL and BOOTSEL are non-OTA upload paths.
is_partition_table = getattr(args, "partition_table", False)
@@ -1252,6 +1297,8 @@ def upload_program(
network_devices = _resolve_network_devices(devices, config, args)
if chosen_platform == CONF_WEB_SERVER:
if ota_key:
raise EsphomeError(OTA_KEY_PLATFORM_ERROR)
if is_partition_table or is_bootloader:
raise EsphomeError(
f"{option_string} is only supported with the esphome OTA platform; "
@@ -1265,6 +1312,17 @@ def upload_program(
return _upload_via_native_api(config, network_devices, args)
def _ota_upload_platforms(config: ConfigType) -> dict[str, None]:
"""The network OTA platforms the config provides, one entry each, in
config order; the web_server final-validate hook merges duplicates."""
return {
ota_platform: None
for ota_item in config.get(CONF_OTA, [])
if (ota_platform := ota_item.get(CONF_PLATFORM))
in (CONF_ESPHOME, CONF_WEB_SERVER)
}
def _choose_ota_platform(config: ConfigType, requested: str | None) -> str:
"""Pick the OTA platform to use, optionally honoring ``--ota-platform``.
@@ -1272,22 +1330,12 @@ 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
# membership checks see one entry per platform even if the user has
# multiple ``ota:`` items of the same platform; the web_server OTA
# 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
available = _ota_upload_platforms(config)
if not available:
raise EsphomeError(
f"Cannot upload Over the Air as the {CONF_OTA} configuration is not "
@@ -1308,19 +1356,53 @@ def _choose_ota_platform(config: ConfigType, requested: str | None) -> str:
return CONF_WEB_SERVER
def _esphome_ota_conf(config: ConfigType) -> ConfigType | None:
"""The validated esphome OTA platform block, if configured."""
from esphome.espota2 import esphome_ota_items
return next(iter(esphome_ota_items(config)), None)
def _upload_via_native_api(
config: ConfigType, network_devices: list[str], args: ArgsProtocol
) -> tuple[int, str | None]:
ota_conf: ConfigType = {}
for ota_item in config.get(CONF_OTA, []):
if ota_item.get(CONF_PLATFORM) == CONF_ESPHOME:
ota_conf = ota_item
break
ota_conf = _esphome_ota_conf(config) or {}
from esphome import espota2
from esphome.components.noise import static_encryption_key
remote_port = int(ota_conf[CONF_PORT])
password = ota_conf.get(CONF_PASSWORD)
# Fail closed: an encryption block whose key did not resolve must never
# fall back to a plaintext upload
noise_psk = None
old_noise_psk = None
plaintext_fallback = False
allow_plaintext_upload = False
if ota_key := getattr(args, "ota_key", None):
# The user presents a key the device still runs instead of the
# configured one (a key change in progress); an explicit key never
# downgrades to plaintext
noise_psk = ota_key
elif (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; "
"set the key under 'ota: encryption:' or 'api: encryption:'"
)
# Ensure the key is a string, as required by the underlying OTA implementation.
# It arrives here as a SensitiveStr which aioesphomeapi rejects.
noise_psk = str(noise_psk)
if old_key := encryption_conf.get(espota2.CONF_OLD_KEY):
old_noise_psk = str(old_key)
elif api_key := static_encryption_key(config.get(CONF_API) or {}):
# Remove before 2027.3.0: the api key is tried, falling back to plaintext
noise_psk = str(api_key)
plaintext_fallback = True
def check_partition_access(option_string: str) -> None:
if not ota_conf.get("allow_partition_access"):
@@ -1341,6 +1423,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:
@@ -1351,15 +1439,45 @@ def _upload_via_native_api(
if ota_type == espota2.OTA_TYPE_UPDATE_BOOTLOADER:
_validate_bootloader_binary(binary)
return espota2.run_ota(network_devices, remote_port, password, binary, ota_type)
return espota2.run_ota(
network_devices,
remote_port,
password,
binary,
ota_type,
noise_psk,
plaintext_fallback=plaintext_fallback,
allow_plaintext_upload=allow_plaintext_upload,
old_noise_psk=old_noise_psk,
)
def _old_key_notice(config: ConfigType) -> None:
"""After `run`, whose image was just built from this config; a plain
`upload` may send a stale image that still carries the previous key."""
from esphome import espota2
for item in espota2.esphome_ota_items(config):
if (item.get(CONF_ENCRYPTION) or {}).get(espota2.CONF_OLD_KEY):
_LOGGER.warning(espota2.OLD_KEY_REMOVE_NOTICE)
def _upload_via_web_server(
config: ConfigType, network_devices: list[str], binary: Path
) -> tuple[int, str | None]:
from esphome import web_server_ota
from esphome.espota2 import esphome_ota_items
from esphome.web_server_helpers import get_web_server_connection
if any(
ota_item.get(CONF_ENCRYPTION) is not None
for ota_item in esphome_ota_items(config)
):
_LOGGER.warning(
"This config has OTA encryption, but the web_server OTA path sends "
"the image over plaintext HTTP; use the esphome OTA platform to "
"keep it confidential"
)
remote_port, username, password = get_web_server_connection(config)
return web_server_ota.run_ota(
network_devices, remote_port, username, password, binary
@@ -1655,21 +1773,66 @@ def command_compile(args: ArgsProtocol, config: ConfigType) -> int | None:
if exit_code != 0:
return exit_code
if CORE.is_host:
if CORE.using_toolchain_esp_idf:
from esphome.espidf import toolchain
program_path = str(toolchain.get_elf_path())
else:
from esphome.platformio.toolchain import get_idedata
program_path = str(get_idedata(config).firmware_elf_path)
_LOGGER.info("Successfully compiled program to path '%s'", program_path)
_LOGGER.info("Successfully compiled program to path '%s'", CORE.firmware_bin)
else:
_LOGGER.info("Successfully compiled program.")
return 0
ENV_OTA_KEY = "ESPHOME_OTA_KEY"
OTA_KEY_PLATFORM_ERROR = (
f"--prompt-ota-key and {ENV_OTA_KEY} only apply to the {CONF_ESPHOME} OTA "
"platform; to flash by serial instead, name the port with --device"
)
def _read_ota_key(args: ArgsProtocol, config: ConfigType) -> None:
"""Take the key to present to the device for this upload, from the
prompt or from ESPHOME_OTA_KEY, before any compile so the prompt is
not buried in build output."""
prompt = getattr(args, "prompt_ota_key", False)
env_key = getattr(args, "env_ota_key", None)
if not prompt and env_key is None:
return
if CORE.data.get(KEY_CORE, {}).get(KEY_TARGET_PLATFORM) == PLATFORM_HOST:
_LOGGER.warning("A host program runs locally; the presented OTA key is ignored")
return
# The HTTP path has no key handshake; decide before prompting or
# compiling. A serial target is a serial flash: the key is not even
# asked for, and --ota-platform means nothing to it. Every entry must
# be serial: a missing port is skipped and the next entry may be a host
devices = getattr(args, "device", None) or []
serial_target = bool(devices) and all(
device == "SERIAL"
or get_port_type(device) in (PortType.SERIAL, PortType.BOOTSEL)
for device in devices
)
chosen = (
_choose_ota_platform(config, getattr(args, "ota_platform", None))
if not serial_target and _ota_upload_platforms(config)
else None
)
if chosen == CONF_WEB_SERVER:
raise EsphomeError(OTA_KEY_PLATFORM_ERROR)
from esphome.components.noise import validate_encryption_key
from esphome.config_validation import Invalid
if serial_target:
_LOGGER.warning(
"The presented OTA key only applies to network uploads; ignored for %s",
", ".join(devices),
)
return
key = read_secret_line("OTA encryption key: ") if prompt else env_key
try:
validate_encryption_key(key)
except Invalid as err:
raise EsphomeError(f"Invalid OTA encryption key: {err}") from err
args.ota_key = key
def command_upload(args: ArgsProtocol, config: ConfigType) -> int | None:
_read_ota_key(args, config)
# Get devices, resolving special identifiers like OTA
devices = choose_upload_log_host(
default=args.device,
@@ -1705,6 +1868,19 @@ 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
_read_ota_key(args, config)
exit_code = write_cpp(config)
if exit_code != 0:
return exit_code
@@ -1713,14 +1889,7 @@ def command_run(args: ArgsProtocol, config: ConfigType) -> int | None:
return exit_code
_LOGGER.info("Successfully compiled program.")
if CORE.is_host:
if CORE.using_toolchain_esp_idf:
from esphome.espidf import toolchain
program_path = str(toolchain.get_elf_path())
else:
from esphome.platformio.toolchain import get_idedata
program_path = str(get_idedata(config).firmware_elf_path)
program_path = str(CORE.firmware_bin)
_LOGGER.info("Running program from path '%s'", program_path)
return run_external_process(program_path)
@@ -1737,6 +1906,7 @@ def command_run(args: ArgsProtocol, config: ConfigType) -> int | None:
exit_code, successful_device = upload_program(config, args, devices)
if exit_code == 0:
_LOGGER.info("Successfully uploaded program.")
_old_key_notice(config)
else:
_LOGGER.warning("Failed to upload to %s", devices)
return exit_code
@@ -1873,7 +2043,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)}")
@@ -1912,12 +2085,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",
@@ -1956,6 +2129,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:
@@ -1967,13 +2156,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
@@ -2027,155 +2233,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 = {
@@ -2210,6 +2270,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.
@@ -2233,6 +2302,25 @@ def _add_states_args(parser: argparse.ArgumentParser) -> None:
)
def _error_format(value: str) -> ErrorFormat:
try:
return ErrorFormat(value.lower())
except ValueError:
choices = ", ".join(ErrorFormat)
raise argparse.ArgumentTypeError(
f"invalid choice: '{value}' (choose from {choices})"
) from None
ARGUMENT_HELP_PROMPT_OTA_KEY = (
"Ask for the OTA encryption key to present to the device instead of using "
"the one in the configuration, for a device that still runs a previous key. "
"The key is read without echo, or as one line from stdin when that is not "
"a terminal; it is never taken from the command line. A calling program "
f"can hand the key over in the {ENV_OTA_KEY} environment variable instead."
)
def parse_args(argv):
options_parser = argparse.ArgumentParser(add_help=False)
options_parser.add_argument(
@@ -2290,7 +2378,19 @@ 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}."
),
)
options_parser.add_argument(
"--error-format",
type=_error_format,
metavar="{" + ",".join(f.value for f in ErrorFormat) + "}",
help=(
"How configuration errors are shown. 'yaml' shows them inside the "
"failing YAML; 'line' prints one 'file:line:column: error: message' "
"line per error. Default: $ESPHOME_ERROR_FORMAT, else 'yaml'."
),
)
@@ -2350,6 +2450,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",
@@ -2388,6 +2489,11 @@ def parse_args(argv):
help="Upload as partition table (OTA).",
action="store_true",
)
parser_upload.add_argument(
"--prompt-ota-key",
action="store_true",
help=ARGUMENT_HELP_PROMPT_OTA_KEY,
)
parser_upload.add_argument(
"--bootloader",
help="Upload as bootloader (OTA).",
@@ -2413,7 +2519,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)
@@ -2446,6 +2552,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)
@@ -2454,7 +2561,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",
@@ -2467,6 +2574,11 @@ def parse_args(argv):
"(HTTP Basic auth) when that is the only configured platform."
),
)
parser_run.add_argument(
"--prompt-ota-key",
action="store_true",
help=ARGUMENT_HELP_PROMPT_OTA_KEY,
)
parser_clean = subparsers.add_parser(
"clean-mqtt",
@@ -2595,9 +2707,18 @@ def parse_args(argv):
args, unknown_args = parser.parse_known_args(arguments)
if unknown_args:
_LOGGER.warning("Ignored unrecognized arguments: %s", unknown_args)
return args
else:
args = parser.parse_args(arguments)
return parser.parse_args(arguments)
# Read here rather than as the option default, so a bad value is reported
# against the environment variable instead of --error-format
if args.error_format is None:
env_value = os.getenv("ESPHOME_ERROR_FORMAT", ErrorFormat.YAML.value)
try:
args.error_format = _error_format(env_value)
except argparse.ArgumentTypeError as err:
parser.error(f"ESPHOME_ERROR_FORMAT: {err}")
return args
def _warn_if_source_tree_mismatch() -> None:
@@ -2643,12 +2764,34 @@ 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
args = parse_args(argv)
CORE.dashboard = args.dashboard
CORE.testing_mode = args.testing_mode
CORE.error_format = args.error_format
# Create address cache from command-line arguments
CORE.address_cache = AddressCache.from_cli_args(
@@ -2661,7 +2804,25 @@ def run_esphome(argv):
args.log_level = "CRITICAL"
setup_log(log_level=args.log_level)
# Taken out of the environment before any command runs, so no build
# tool inherits a key a calling program handed over
args.env_ota_key = os.environ.pop(ENV_OTA_KEY, None)
if args.command in ("upload", "run"):
# One key is one device's; several configs run in child processes
# that would never see it
if (args.env_ota_key is not None or args.prompt_ota_key) and len(
args.configuration
) > 1:
raise EsphomeError(
f"--prompt-ota-key and {ENV_OTA_KEY} take one configuration; "
f"{len(args.configuration)} were given"
)
elif args.env_ota_key is not None:
_LOGGER.warning(
"%s is set but the %s command does not use it", ENV_OTA_KEY, args.command
)
_warn_if_source_tree_mismatch()
_warn_if_intel_macos()
if args.command in PRE_CONFIG_ACTIONS:
try:
@@ -2705,6 +2866,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
@@ -2719,10 +2881,14 @@ def run_esphome(argv):
# Skipped when -s overrides are passed, since the cache was written
# against the previous substitution set.
config: ConfigType | None = None
cache_eligible = (
cache_write_eligible = (
args.command in ("upload", "logs") and not command_line_substitutions
)
if cache_eligible:
# An explicit --toolchain must re-run the per-platform validators, so
# gate only the cache read; the refresh below saves the result unless
# the sidecar records a different toolchain.
cache_read_eligible = cache_write_eligible and args.toolchain is None
if cache_read_eligible:
from esphome.compiled_config import load_compiled_config
config = load_compiled_config(conf_path)
@@ -2732,7 +2898,8 @@ def run_esphome(argv):
conf_path.name,
)
if config is None:
cache_missed = config is None
if cache_missed:
from esphome.config import read_config
config = read_config(
@@ -2741,26 +2908,22 @@ def run_esphome(argv):
# Snapshot only needed by `esphome config --no-defaults`.
snapshot_user_config=getattr(args, "no_defaults", False),
)
# Refresh the cache so the next upload/logs hits the fast path
# instead of re-running read_config. Skip when the storage
# sidecar is absent (no compile has run): the cache would
# never be loaded back, so writing secrets to disk is wasted.
if cache_eligible and config is not None:
from esphome.compiled_config import save_compiled_config
from esphome.storage_json import ext_storage_path
if ext_storage_path(conf_path.name).exists():
save_compiled_config(config)
if config is None:
return 2
if config is None:
return 2
CORE.config = config
# Fallback for platforms whose validators didn't set the toolchain
# (only the esp32 component reads esp32.framework.toolchain). All
# other platforms only support PlatformIO today.
# The cache fast path skips validation, and legacy sidecars lack the
# toolchain field. Must run before the cache refresh below.
if CORE.toolchain is None:
CORE.toolchain = Toolchain.PLATFORMIO
# Refresh the cache so the next upload/logs hits the fast path
# instead of re-running read_config.
if cache_write_eligible and cache_missed:
from esphome.compiled_config import save_compiled_config_and_sidecar
save_compiled_config_and_sidecar(config)
if args.command not in POST_CONFIG_ACTIONS:
safe_print(f"Unknown command {args.command}")
return 1
+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",
+1 -3
View File
@@ -23,9 +23,7 @@ from esphome.util import safe_print
if TYPE_CHECKING:
from collections.abc import Callable
from aioesphomeapi.api_pb2 import (
SubscribeLogsResponse, # pylint: disable=no-name-in-module
)
from aioesphomeapi.api_pb2 import SubscribeLogsResponse # pylint: disable=no-name-in-module
_LOGGER = logging.getLogger(__name__)
File renamed without changes.
+549
View File
@@ -0,0 +1,549 @@
"""Arduino-core backend for the shared PlatformIO library converter.
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. 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
manifest ``extraScript``; manifest ``-I`` flags join the global include path;
``precompiled``/``ldflags`` properties are refused by name.
"""
from __future__ import annotations
from dataclasses import dataclass, field
import logging
from pathlib import Path
import re
from esphome.core import CORE, EsphomeError, Library
from esphome.helpers import walk_files
from esphome.platformio.extra_script import apply_extra_script
from esphome.platformio.library import (
DEFAULT_BUILD_INCLUDE_DIR,
DEFAULT_BUILD_SRC_FILTER,
ESPHOME_DATA_KEY,
ESPHOME_DATA_LINK_FLAGS_KEY,
LIBRARY_HEADER_SUFFIXES,
SRC_FILE_EXTENSIONS,
ConvertedLibrary,
IncompatiblePlatform,
InvalidLibrary,
LibraryBackend,
_url_or_none,
check_library_data,
collect_filtered_files,
convert_libraries,
ensure_list,
is_lib_ignored,
lex_build_flags,
lib_ignore_set,
normalize_dependencies,
parse_library_json,
parse_library_properties,
warn_properties_depends,
)
_LOGGER = logging.getLogger(__name__)
@dataclass
class ArduinoLibrary:
"""One resolved library, ready for the ninja generator."""
name: str
sources: list[Path] = field(default_factory=list)
include_dirs: list[Path] = field(default_factory=list)
# Extra compile flags private to this library's own sources
flags: list[str] = field(default_factory=list)
# PlatformIO's build.libArchive / Arduino's dot_a_linkage: when False the
# objects go to the linker directly (symbols nothing references survive)
lib_archive: bool = True
# Link inputs the library contributes (-L dirs / -l libs, e.g. from
# precompiled vendor blobs) and -Wl, options for the firmware link
link_dirs: list[Path] = field(default_factory=list)
link_libs: list[str] = field(default_factory=list)
link_flags: list[str] = field(default_factory=list)
# Source-like suffixes the case-sensitive suffix map rejects
_UNMAPPED_SOURCE_SUFFIXES = frozenset(
{s.lower() for s in SRC_FILE_EXTENSIONS} | {".ino"}
)
# Filename-plain names: an allowlist excludes separators, drive colons,
# and dot-only names by shape
_SAFE_LIBRARY_NAME_RE = re.compile(r"[A-Za-z0-9_][A-Za-z0-9_. +-]*\Z")
def _is_safe_library_name(name: object) -> bool:
"""Whether a name may be joined under the framework's libraries dir."""
return isinstance(name, str) and _SAFE_LIBRARY_NAME_RE.fullmatch(name) is not None
def _manifest_build(name: str, data: object) -> dict:
"""The manifest's ``build`` section; malformed manifests fail by name."""
build = data.get("build", {}) if isinstance(data, dict) else None
if not isinstance(build, dict):
raise EsphomeError(f"Library {name} has a malformed manifest")
return build
def _resolve_src_dir(name: str, read_path: Path, build: dict) -> str:
"""Resolve PIO's source dir: manifest srcDir, else src/Src, else the root."""
if "srcDir" not in build:
return next((d for d in ("src", "Src") if (read_path / d).is_dir()), ".")
# A declared srcDir (falsy included) that does not resolve is a manifest error
src_dir = build["srcDir"]
if not (isinstance(src_dir, str) and src_dir and (read_path / src_dir).is_dir()):
raise EsphomeError(
f"Library {name} declares srcDir {src_dir!r} which does not exist"
)
return src_dir
def _reject_unsupported_link_fields(name: str, data: dict) -> None:
# PIO honors these; ignoring them would fail at link with no stated
# cause. Property values are strings, so "false" is not a declaration.
precompiled = data.get("precompiled")
if precompiled and str(precompiled).strip().lower() != "false":
raise EsphomeError(
f"Library {name} declares precompiled, which this backend does not support"
)
if data.get("ldflags"):
raise EsphomeError(
f"Library {name} declares ldflags, which this backend does not support"
)
def _resolve_lib_archive(name: str, data: dict, build: dict) -> bool:
"""build.libArchive, else dot_a_linkage (an Arduino IDE property PIO
ignores; a deliberate extra), else archive."""
# Strict parse: bool("false") is True
def _parse(key: str, raw: object) -> bool:
if isinstance(raw, bool):
return raw
value = str(raw).strip().lower()
if value in ("true", "false"):
return value == "true"
raise EsphomeError(f"Library {name} has a malformed {key} value {raw!r}")
if "libArchive" in build:
return _parse("libArchive", build["libArchive"])
if "dot_a_linkage" in data:
return _parse("dot_a_linkage", data["dot_a_linkage"])
return True
def _classify_build_flags(
name: str, read_path: Path, lib: ArduinoLibrary, flag_tokens: list[str]
) -> list[str]:
"""Route the lexed build.flags into the library's flag lists.
Returns the ``-I`` arguments for the include-dir resolution.
"""
include_flags: list[str] = []
for tok in flag_tokens:
if tok.startswith("-I"):
include_flags.append(tok[2:])
elif tok.startswith("-L"):
link_dir = (read_path / tok[2:]).resolve()
if not link_dir.is_dir():
# Kept (the linker ignores missing -L dirs); the warning
# names the culprit before a bare "cannot find -lfoo"
_LOGGER.warning(
"Library %s declares library dir %s which does not exist",
name,
tok[2:],
)
lib.link_dirs.append(link_dir)
elif tok.startswith("-l"):
lib.link_libs.append(tok[2:])
elif tok.startswith("-Wl,"):
lib.link_flags.append(tok)
else:
lib.flags.append(tok)
return include_flags
def _resolve_include_dirs(
name: str,
read_path: Path,
lib: ArduinoLibrary,
build: dict,
src_dir: str,
include_flags: list[str],
) -> None:
include_dir = build.get("includeDir", DEFAULT_BUILD_INCLUDE_DIR)
if not isinstance(include_dir, str):
raise EsphomeError(f"Library {name} has a malformed includeDir")
for d, explicit in [
(include_dir, "includeDir" in build),
(src_dir, False), # _resolve_src_dir already validated it
*((flag, True) for flag in include_flags),
]:
if (path := (read_path / d)).is_dir():
lib.include_dirs.append(path.resolve())
elif explicit:
# Warn-and-drop (unlike srcDir): a missing include dir is
# harmless until a header is needed, and the compile names it
_LOGGER.warning(
"Library %s declares include dir %s which does not exist", name, d
)
def _collect_lib_sources(
name: str,
read_path: Path,
lib: ArduinoLibrary,
src_dir: str,
src_filter: list[str],
) -> None:
sources: list[Path] = []
dropped: list[str] = []
saw_header = False
for f in collect_filtered_files(read_path / src_dir, src_filter):
path = Path(f)
suffix = path.suffix
if suffix in SRC_FILE_EXTENSIONS:
# resolve() per file: srcFilter patterns may escape src_dir
sources.append(path.resolve())
elif suffix.lower() in _UNMAPPED_SOURCE_SUFFIXES:
# A source-like suffix the case-sensitive map rejects (.CPP,
# .ino) is a dropped compilation unit; headers fall through
dropped.append(path.name)
elif suffix.lower() in LIBRARY_HEADER_SUFFIXES:
saw_header = True
lib.sources = sorted(sources)
if dropped:
_LOGGER.warning(
"Library %s: %d file(s) with unmapped source suffixes are not compiled: %s",
name,
len(dropped),
", ".join(sorted(dropped)),
)
if not lib.sources and not saw_header:
# Matched headers mean header-only; a filter matching nothing is
# a manifest/tree problem (a truly empty tree raises elsewhere)
_LOGGER.warning("Library %s: no source files matched", name)
def _library_info(name: str, read_path: Path, data: dict) -> ArduinoLibrary:
"""Resolve one library's sources, include dirs, and flags (PIO semantics)."""
build = _manifest_build(name, data)
_reject_unsupported_link_fields(name, data)
src_dir = _resolve_src_dir(name, read_path, build)
src_filter = ensure_list(build.get("srcFilter", DEFAULT_BUILD_SRC_FILTER))
if not all(isinstance(entry, str) for entry in src_filter):
raise EsphomeError(f"Library {name} has a malformed srcFilter")
lib = ArduinoLibrary(name=name, lib_archive=_resolve_lib_archive(name, data, build))
# PlatformIO shell-lexes each build.flags entry
include_flags = _classify_build_flags(
name, read_path, lib, lex_build_flags(build.get("flags", []), f"library {name}")
)
_resolve_include_dirs(name, read_path, lib, build, src_dir, include_flags)
_collect_lib_sources(name, read_path, lib, src_dir, src_filter)
return lib
def _bundled_library(framework_path: Path, name: str) -> ArduinoLibrary:
"""A library bundled with the Arduino core, read from the framework tree.
``library.json`` wins over ``library.properties`` when both exist, as in
PlatformIO's LibBuilderFactory; only the JSON manifest can carry a
``build`` section (srcDir, srcFilter, flags).
"""
lib_dir = framework_path / "libraries" / name
manifest_json = lib_dir / "library.json"
if manifest_json.is_file():
try:
data = parse_library_json(manifest_json)
except ValueError as err: # JSONDecodeError
raise EsphomeError(
f"Bundled library {name} has a corrupt library.json ({err}); "
"the framework install may be incomplete (run 'esphome clean-all')"
) from err
elif (manifest := lib_dir / "library.properties").is_file():
data = parse_library_properties(manifest)
else:
# Debug, not warning: the legacy manifest-less layout is legal and
# the 3.1.2 core ships one such library (FSTools), so a warning
# would be unactionable noise on every build using it
_LOGGER.debug("Bundled library %s has no manifest; using defaults", name)
data = {}
if isinstance(data, dict):
# Bundled manifest deps are never walked; make the skip visible
if data.get("dependencies"):
_LOGGER.warning(
"Bundled library %s declares dependencies, which are not "
"resolved automatically; add them with add_library() if needed",
name,
)
warn_properties_depends(name, data)
build = data.get("build")
if isinstance(build, dict) and build.get("extraScript"):
# Scripts only run on the converted path; building without
# the script's flags would miscompile
raise EsphomeError(
f"Bundled library {name} declares an extraScript, which is "
"not run for bundled libraries"
)
lib = _library_info(name, lib_dir, data)
_assert_tree_has_code(
name,
lib_dir,
"the framework install may be incomplete (run 'esphome clean-all')",
)
return lib
def _assert_tree_has_code(name: str, root: Path, hint: str) -> None:
"""An empty or half-extracted tree can never link; fail by name (a
warning would scroll away and resurface as undefined symbols)."""
if not any(
Path(p).suffix in SRC_FILE_EXTENSIONS
or Path(p).suffix.lower() in LIBRARY_HEADER_SUFFIXES
for p in walk_files(root)
):
raise EsphomeError(f"Library {name} has no sources or headers; {hint}")
def _external_short_name(name: str) -> str:
"""The short library name of a requested spec.
"owner/Name" and plain names take the last path segment; "Name=<url>"
takes the declared name. Git tails (".git", "#ref") are stripped like
the walk's URL normalization; the comparand is a manifest dependency
name, never a spec.
"""
head, sep, tail = name.partition("=")
if sep and "://" in tail:
return head
short = name.rsplit("/", maxsplit=1)[-1]
return short.partition("#")[0].removesuffix(".git")
def _check_unfulfilled_provides(
provided_requests: set[str], satisfied: set[str], still_requested: set[str]
) -> None:
"""Fail by name when a walk-skipped dependency was never added.
An unfulfilled provides() promise only surfaces as undefined symbols
at link. The walk records across re-resolutions, so a name no final
manifest still requests is stale state, never a failure.
"""
if missing := sorted((provided_requests & still_requested) - satisfied):
raise EsphomeError(
"provides() skipped these dependencies but nothing added them: "
f"{', '.join(missing)}; the build is missing libraries"
)
def resolve_libraries(
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. ``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
bundled-first grouping is incidental).
"""
bundled: list[ArduinoLibrary] = []
external: list[Library] = []
# PlatformIO's lib_ignore covers framework-bundled libraries too; the
# shared converter only filters the registry/git ones.
lib_ignore = lib_ignore_set()
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()
)
def _provided(name: object) -> bool:
return _is_safe_library_name(name) and name in bundled_dir_names
for library in CORE.platformio_libraries.values():
if is_lib_ignored(library.name, lib_ignore):
continue
# Bundled only for a bare name with a matching framework dir; pinned
# or unmatched names resolve from the registry, as under PlatformIO.
if not library.repository and not library.version and _provided(library.name):
# Bundled manifest deps are not walked; _bundled_library warns
bundled.append(_bundled_library(framework_path, library.name))
else:
external.append(library)
converted: list[ArduinoLibrary] = []
bundled_names = {lib.name for lib in bundled}
converted_manifest_names: set[str] = set()
# Bundled candidates skipped on purpose (platform filter); the
# provides() reconciliation must count them as satisfied
knowingly_skipped: set[str] = set()
# Dependency names of the manifests actually emitted; a walk recording
# for a since-re-resolved manifest must not fail the reconciliation
final_dep_names: set[str] = set()
# Ordered set of bundled dependency names to add once conversion is done
pending_bundled: dict[str, None] = {}
# Deps matching a separately-requested external are already in the build
# (a duplicate archive means duplicate-symbol link errors)
external_short_names = {
_external_short_name(lib.name) for lib in external if lib.name
}
def _add_bundled_dependencies(component: ConvertedLibrary) -> None:
# A version-less bare name ("Hash") is a core-bundled library the
# shared converter cannot resolve from the registry
for dep in normalize_dependencies(
component.data.get("dependencies"), component.name
):
# normalize_dependencies guarantees a non-empty str name
name = dep["name"]
final_dep_names.add(name)
if "/" in name:
owner, _, pkg = name.partition("/")
if _is_safe_library_name(owner) and _is_safe_library_name(pkg):
# Owner-qualified; the converter resolves it from the registry
continue
if not _is_safe_library_name(name):
# The name becomes a path component; never join a traversal
_LOGGER.warning(
"Ignoring malformed dependency entry %r of library %s",
dep,
component.name,
)
continue
if name in external_short_names:
if _provided(name):
# A bundled copy is suppressed; a coincidental name
# collision would surface as link errors
_LOGGER.warning(
"Dependency %s of %s is assumed satisfied by a "
"requested external library; the bundled copy is "
"not added",
name,
component.name,
)
else:
_LOGGER.debug(
"Dependency %s of %s assumed satisfied by a requested "
"external library",
name,
component.name,
)
continue
if name in bundled_names or is_lib_ignored(name, lib_ignore):
continue
if _url_or_none(dep.get("version")) is not None:
# A URL names one specific source; never add the bundled copy
continue
if dep.get("owner") or not _provided(name):
# Only owner-less framework-tree names take the bundled
# copy (PIO's process_dependencies); the walk reports drops
continue
try:
# framework=None: the walk already warned for non-platform
# causes; debug keeps one fault from warning twice (pinned
# by test_nonplatform_rejection_warns_once_through_real_converter)
check_library_data(dep, pio_platform, None)
except IncompatiblePlatform as err:
# A knowing skip (platform filter), not a broken promise
knowingly_skipped.add(name)
_LOGGER.debug("Skip bundled candidate %s: %s", name, err)
continue
except InvalidLibrary as err:
# Malformed manifest data never counts as satisfied; the
# walk owns the warning (see the warns-once test above)
_LOGGER.debug("Skip malformed bundled candidate %s: %s", name, err)
continue
# Deferred: a later manifest name may satisfy this
pending_bundled.setdefault(name)
def _emit(component: ConvertedLibrary) -> None:
apply_extra_script(
component, board_mcu=lambda: board_mcu, pio_platform=pio_platform
)
_assert_tree_has_code(
component.get_require_name(),
component.source_dir,
"the download may be incomplete (run 'esphome clean-all')",
)
if isinstance(manifest_name := component.data.get("name"), str):
converted_manifest_names.add(manifest_name)
lib = _library_info(
component.get_require_name(), component.source_dir, component.data
)
# Extra-script LINKFLAGS travel outside build.flags; dropping
# them would link wrong with no stated cause
lib.link_flags.extend(
component.data.get(ESPHOME_DATA_KEY, {}).get(
ESPHOME_DATA_LINK_FLAGS_KEY, []
)
)
converted.append(lib)
_add_bundled_dependencies(component)
backend = LibraryBackend(
platform=pio_platform,
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)
for name in pending_bundled:
if name in converted_manifest_names:
# The converted library is this one; the bundled copy would
# double the archive. Warn like the external_short_names twin.
_LOGGER.warning(
"Dependency %s is assumed satisfied by a converted library's "
"manifest name; the bundled copy is not added",
name,
)
continue
bundled_names.add(name)
bundled.append(_bundled_library(framework_path, name))
_check_unfulfilled_provides(
backend.provided_requests,
bundled_names
| converted_manifest_names
| external_short_names
| knowingly_skipped,
final_dep_names,
)
return bundled + converted
+9
View File
@@ -0,0 +1,9 @@
"""Native (PlatformIO-free) build support for the ESP8266 Arduino core.
This package downloads the Arduino ESP8266 core and the xtensa-lx106
toolchain, generates a ninja build for them plus the ESPHome sources, and
drives the build directly — the ESP8266 equivalent of ``esphome.espidf``.
Deliberately importable without the esp8266 component to avoid circular
imports; the component wires these modules in via lazy imports.
"""
+207
View File
@@ -0,0 +1,207 @@
"""Download and install the Arduino ESP8266 core, toolchain, and ninja.
Artifacts land in a machine-global cache (shared across projects, like the
ESP-IDF install in ``esphome.espidf.framework``):
<cache>/arduino8266/frameworks/<tag>/ the Arduino core
<cache>/arduino8266/toolchains/<tag>/ xtensa-lx106-elf gcc 10.3
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
import os
from pathlib import Path
from typing import NamedTuple
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 (
Download,
PackageSpec,
Resolver,
get_systype,
install_packages,
prefetch_packages,
)
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.3",
"3af533a85163804e164500a5b254dc23b922080e25ecb7dc37b3dd8ac84651db",
37185650,
),
}
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 = "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", "")
)
ESPHOME_ARDUINO8266_TOOLCHAIN_MIRRORS = str_to_lst_of_str(
os.environ.get("ESPHOME_ARDUINO8266_TOOLCHAIN_MIRRORS", "")
)
def get_arduino8266_tools_path() -> Path:
# Machine-global so all projects share one install; see
# espidf.framework.get_idf_tools_path for the location rationale.
return tools_cache_path(*ARDUINO8266_TOOLS_CACHE)
def framework_release(version: Version) -> FrameworkRelease:
if (release := FRAMEWORK_RELEASES.get(version)) is None:
raise EsphomeError(
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'"
)
return release
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."""
framework: Path
toolchain: Path
ninja: Path
def check_and_install(framework_version: Version) -> InstalledPaths:
"""Ensure framework, toolchain, and ninja are installed; return their paths."""
release = framework_release(framework_version)
# Probe the cheap local dependency before ~110 MB of downloads
ninja_path = find_ninja()
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,
release.tag,
framework_path,
ESPHOME_ARDUINO8266_FRAMEWORK_MIRRORS,
("cores/esp8266", "tools/sdk", "libraries"),
),
PackageSpec(
TOOLCHAIN_PACKAGE,
TOOLCHAIN_VERSION,
toolchain_path,
ESPHOME_ARDUINO8266_TOOLCHAIN_MIRRORS,
# xtensa-lx106-elf pins the target: every gcc package has a bin/
("bin", "xtensa-lx106-elf"),
),
)
# 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
)
def toolchain_tool(toolchain_path: Path, name: str) -> Path:
"""Path to one toolchain tool (gcc, g++, ar, size, addr2line, ...).
The single owner of the ``bin/xtensa-lx106-elf-<name>`` layout and the
Windows suffix, so a toolchain package bump touches one spot.
"""
suffix = ".exe" if os.name == "nt" else ""
return toolchain_path / "bin" / f"xtensa-lx106-elf-{name}{suffix}"
def get_build_env(toolchain_path: Path, ccache: str | None) -> dict[str, str]:
env = os.environ.copy()
# Drop empty entries: a trailing separator from an absent PATH would
# make the shell search the current directory for tools
parts = [
str(toolchain_path / "bin"),
*filter(None, env.get("PATH", "").split(os.pathsep)),
]
env["PATH"] = os.pathsep.join(parts)
env.update(ccache_env(ccache, ARDUINO8266_TOOLS_CACHE))
return env
+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,
)
+838 -123
View File
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+108
View File
@@ -0,0 +1,108 @@
"""Tiny cross-platform build steps invoked from the generated ninja file.
Plain script (not ``python -m``): it runs from ninja with whatever Python
started esphome and must not depend on the package being importable.
Subcommands:
ar <ar-binary> <archive> <rspfile> remove stale archive, then ``ar rcs``
copy <src> <dst> copy a file
The ar rspfile carries one object path per line (the generating rule must
use ``$in_newline``, never ``$in``).
"""
from pathlib import Path
import shutil
import subprocess
import sys
def _read_rspfile(rspfile: str) -> list[str]:
r"""The object paths listed in ``rspfile``, unquoted.
GNU ar treats backslashes in response files as escapes (corrupts
Windows paths), so the caller expands the list into argv; strip the
simple surrounding quote ninja adds to special paths, then undo
ninja's POSIX escape for an embedded quote ('a'\\''b.o' -> a'b.o).
"""
return [
line[1:-1].replace("'\\''", "'")
if len(line) >= 2 and line[0] == line[-1] and line[0] in "'\""
else line
for line in Path(rspfile).read_text(encoding="utf-8").splitlines()
if line
]
def _run_ar(ar: str, archive: str, rspfile: str) -> int:
# Remove first: ``ar rcs`` replaces members but never drops ones whose
# source was removed from the build, which would leak stale objects.
Path(archive).unlink(missing_ok=True)
objects = _read_rspfile(rspfile)
if not objects:
# An empty archive would "succeed" here and fail far away at link
print(f"ar: no objects listed in {rspfile} for {archive}", file=sys.stderr)
return 1
# Batch by argv length: expanding the rspfile gives back the Windows
# 32767-char command-line limit it existed to avoid. "rcs" creates,
# "qs" appends; the s keeps the symbol index explicit on every ar.
op = "rcs"
ok = False
try:
while objects:
batch = [objects.pop(0)]
batch_len = len(batch[0])
while objects and batch_len + len(objects[0]) < 25000:
batch_len += len(objects[0]) + 1
batch.append(objects.pop(0))
rc = subprocess.run(
[ar, op, archive, *batch], check=False, close_fds=False
).returncode
if rc != 0:
return rc
op = "qs"
ok = True
return 0
finally:
if not ok:
# Any failure (bad exit, missing ar binary, interrupt) must not
# leave a truncated archive behind
Path(archive).unlink(missing_ok=True)
def _run_copy(src: str, dst: str) -> int:
try:
shutil.copyfile(src, dst)
except OSError as err:
# Never leave a partially written output (e.g. a firmware image);
# SameFileError means dst IS src, where unlinking destroys the input
if not isinstance(err, shutil.SameFileError):
Path(dst).unlink(missing_ok=True)
print(f"copy: {src} -> {dst} failed: {err}", file=sys.stderr)
return 1
return 0
# mode -> (handler, expected operand count); surplus argv means a
# mis-specified ninja rule and must error, not silently drop operands
_MODES = {"ar": (_run_ar, 3), "copy": (_run_copy, 2)}
def main() -> int:
mode = sys.argv[1] if len(sys.argv) > 1 else ""
if entry := _MODES.get(mode):
handler, argc = entry
args = sys.argv[2:]
if len(args) != argc:
print(
f"build_tool {mode}: expected {argc} arguments, got {len(args)}",
file=sys.stderr,
)
return 1
return handler(*args)
print(f"unknown build_tool mode: {mode}", file=sys.stderr)
return 1
if __name__ == "__main__": # pragma: no cover
sys.exit(main())
+374 -40
View File
@@ -1,17 +1,29 @@
"""ESP-IDF direct build generator for ESPHome."""
import json
import logging
from pathlib import Path
import re
import textwrap
from esphome.components.esp32 import get_esp32_variant, idf_version
from esphome.build_helpers import pch
from esphome.components.esp32 import (
get_esp32_variant,
get_excluded_builtin_components,
get_managed_component_require_names,
idf_version,
)
import esphome.config_validation as cv
from esphome.core import CORE
from esphome.espidf import variant_to_idf_target
from esphome.framework_helpers import (
get_project_compile_flags,
get_project_cxx_compile_flags,
get_project_link_flags,
)
from esphome.helpers import mkdir_p, write_file_if_changed
from esphome.helpers import get_bool_env, mkdir_p, write_file_if_changed
_LOGGER = logging.getLogger(__name__)
# Replaces the IDF default C++ standard (-std=gnu++2b appended to
# CXX_COMPILE_OPTIONS by project.cmake's __build_init) with the one set via
@@ -24,13 +36,91 @@ 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:
"""Get list of built-in ESP-IDF components from project_description.json.
"""List the built-in ESP-IDF components from ``project_description.json``.
Excludes ``src``, IDF-managed components (``managed_components/``), and
converted PIO libs (``pio_components/``). Returns ``None`` if the build
dir or ``project_description.json`` isn't ready yet.
Only components below its ``idf_path/components`` count, which leaves out
``src``, IDF-managed components, converted PIO libs and project local
ones such as the Arduino ``component_stubs``. Returns ``None`` if the
build dir or ``project_description.json`` isn't ready yet.
"""
if CORE.build_path is None:
return None
@@ -41,45 +131,157 @@ def get_available_components() -> list[str] | None:
try:
with project_desc.open(encoding="utf-8") as f:
data = json.load(f)
component_info = data.get("build_component_info", {})
result = []
for name, info in component_info.items():
# Exclude our own src component
if name == "src":
continue
# Exclude IDF-managed and converted-PIO components (external).
comp_dir = info.get("dir", "")
if "managed_components" in comp_dir or "pio_components" in comp_dir:
continue
result.append(name)
return result
except (json.JSONDecodeError, OSError):
root = (Path(data["idf_path"]) / "components").resolve()
result = [
name
for name, info in data.get("build_component_info", {}).items()
if (comp_dir := info.get("dir"))
and Path(comp_dir).resolve().is_relative_to(root)
]
except (json.JSONDecodeError, KeyError, OSError) as err:
_LOGGER.debug("Could not read %s: %s", project_desc, err)
return None
if not result:
_LOGGER.warning("No ESP-IDF components found under %s", root)
return result
def has_discovered_components() -> bool:
"""Check if we have discovered components from a previous configure."""
return get_available_components() is not None
"""Check if a previous configure discovered any built-in components."""
return bool(get_available_components())
def get_project_cmakelists(minimal: bool = False) -> str:
def _cmake_quote(value: str) -> str:
"""Quote a cmake arg value for a set() line. add_cmake_arg rejects
whitespace, quotes, and '$', so only backslashes need escaping."""
escaped = value.replace("\\", "\\\\")
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 _cmake_args_for_write(minimal: bool) -> dict[str, str]:
"""The cg.add_cmake_arg() values for one project write.
The discovery (minimal) write lets every built-in component register so
the list it finds, cached per target, serves any exclusion set; the full
write drops the excluded names from that list and passes all of them to
IDF. Names outside the framework's components directory (managed
components) stay excluded on the discovery write too, since the list
only holds built-in ones. A checkout supplied through IDF_PATH caches
nothing, so its discovery keeps every exclusion.
"""
from esphome.espidf.toolchain import _esphome_manages_idf, _get_idf_path
args = dict(CORE.cmake_args)
if (
not minimal
or not _esphome_manages_idf()
or not (excluded := args.get("EXCLUDE_COMPONENTS"))
):
return args
root = _get_idf_path() / "components"
outside = [name for name in excluded.split(";") if not (root / name).is_dir()]
if outside:
args["EXCLUDE_COMPONENTS"] = ";".join(outside)
else:
del args["EXCLUDE_COMPONENTS"]
return args
def get_project_cmakelists(
minimal: bool = False, builtin_components: list[str] | None = None
) -> str:
"""Generate the top-level CMakeLists.txt for ESP-IDF project.
When ``minimal`` is true, omit ``ESPHOME_PROJECT_BUILTIN_COMPONENTS``
since ``project_description.json`` may be stale on the first write.
``builtin_components`` supplies the discovered list (from the cache)
instead of reading it from ``project_description.json``.
"""
# Get IDF target from ESP32 variant (e.g., ESP32S3 -> esp32s3)
variant = get_esp32_variant()
idf_target = variant.lower().replace("-", "")
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
@@ -109,6 +311,30 @@ def get_project_cmakelists(minimal: bool = False) -> str:
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.
cmake_args = "\n".join(
f"set({name} {_cmake_quote(value)})"
for name, value in sorted(_cmake_args_for_write(minimal).items())
)
# Per-project list exposed as a CMake variable so converted PIO libs
# can reference ${ESPHOME_PROJECT_MANAGED_COMPONENTS} without baking
# project-specific names into their cached CMakeLists.
@@ -119,8 +345,6 @@ def get_project_cmakelists(minimal: bool = False) -> str:
# runs as a separate CMake script invocation that doesn't load the
# project's top-level CMakeLists; without this, ${ESPHOME_PROJECT_
# MANAGED_COMPONENTS} in a converted-lib REQUIRES expands to empty).
from esphome.components.esp32 import get_managed_component_require_names
managed_components_property = "\n".join(
f"idf_build_set_property(ESPHOME_PROJECT_MANAGED_COMPONENTS {name} APPEND)"
for name in get_managed_component_require_names()
@@ -131,12 +355,24 @@ def get_project_cmakelists(minimal: bool = False) -> str:
# component's REQUIRES including real IDF components). Referenced by
# src/CMakeLists and by each converted PIO lib's CMakeLists. Skipped
# on minimal writes because project_description.json may be stale.
# Excluded components are dropped here as well: the discovery list
# (cached per target) still holds them, and requiring an excluded
# component pulls it back into the build (IDF requirement expansion
# overrides EXCLUDE_COMPONENTS).
# Derived from the EXCLUDE_COMPONENTS cmake arg emitted above so the
# two can never disagree within one generated file.
builtin_components_property = (
""
if minimal
else "\n".join(
f"idf_build_set_property(ESPHOME_PROJECT_BUILTIN_COMPONENTS {name} APPEND)"
for name in sorted(get_available_components() or [])
for name in sorted(
set(
builtin_components
if builtin_components is not None
else get_available_components() or []
).difference(CORE.cmake_args.get("EXCLUDE_COMPONENTS", "").split(";"))
)
)
)
@@ -163,8 +399,13 @@ set(CMAKE_NINJA_FORCE_RESPONSE_FILE 1)
set(IDF_TARGET {idf_target})
set(EXTRA_COMPONENT_DIRS ${{CMAKE_SOURCE_DIR}}/src)
{cmake_args}
include($ENV{{IDF_PATH}}/tools/cmake/project.cmake)
{IDF_BOOTLOADER_OVERRIDE}
{ldgen_override}
{cpp_standard_options}
{cxx_compile_options}
@@ -177,12 +418,31 @@ 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.
# IDF's size target depends on the map, so ninja has to know the link
# writes it for size to build in the same run as all. IDF declares the
# map itself since espressif/esp-idf#19201 (and in cmakev2), and ninja
# rejects two rules for one output, so declare it here only when IDF has
# not. add_custom_command(OUTPUT) marks its outputs GENERATED.
get_source_file_property(esphome_map_declared
${{CMAKE_BINARY_DIR}}/${{CMAKE_PROJECT_NAME}}.map GENERATED)
if(esphome_map_declared)
set(esphome_map_byproducts)
else()
set(esphome_map_byproducts BYPRODUCTS ${{CMAKE_BINARY_DIR}}/${{CMAKE_PROJECT_NAME}}.map)
endif()
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
${{esphome_map_byproducts}}
WORKING_DIRECTORY ${{CMAKE_BINARY_DIR}}
VERBATIM
)
@@ -245,10 +505,75 @@ 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 write_project(minimal: bool = False) -> None:
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:
"""Write ESP-IDF project files."""
mkdir_p(CORE.build_path)
mkdir_p(CORE.relative_src_path())
@@ -256,7 +581,7 @@ def write_project(minimal: bool = False) -> None:
# Write top-level CMakeLists.txt
write_file_if_changed(
CORE.relative_build_path("CMakeLists.txt"),
get_project_cmakelists(minimal=minimal),
get_project_cmakelists(minimal=minimal, builtin_components=builtin_components),
)
# Write component CMakeLists.txt in src/
@@ -264,3 +589,12 @@ def write_project(minimal: bool = False) -> None:
CORE.relative_src_path("CMakeLists.txt"),
get_component_cmakelists(),
)
# Snapshot the exclusion set so has_outdated_files() can trigger a
# reconfigure when it changes. The ESPHOME_PROJECT_BUILTIN_COMPONENTS
# property drops excluded names, so re-including one (e.g. a config
# gains mqtt) needs a reconfigure to rebuild it from the discovery list.
write_file_if_changed(
CORE.relative_build_path("exclude_components.esphomeinternal"),
";".join(get_excluded_builtin_components()),
)
+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(ccache),
*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))
+17
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,19 @@ 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.
if CORE.cmake_args:
CORE.add_platformio_option(
"board_build.cmake_extra_args",
" ".join(
f"-D{name}={value}" for name, value in sorted(CORE.cmake_args.items())
),
replace=True,
)
content = "[platformio]\n"
content += f"description = ESPHome {__version__}\n"
@@ -95,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"
+1
View File
@@ -0,0 +1 @@
"""Build helpers shared by the native (non-PlatformIO) toolchains."""
+131
View File
@@ -0,0 +1,131 @@
"""Shared ccache policy for build backends: env-knob parsing, binary
resolution, and default ``CCACHE_*`` values."""
from __future__ import annotations
import logging
import os
from pathlib import Path
from esphome.framework_helpers import strip_win_long_path_prefix, tool_version_runs
from esphome.helpers import FALSY_ENV_STRINGS, TRUTHY_ENV_STRINGS
_LOGGER = logging.getLogger(__name__)
def _ccache_runs(ccache: str) -> bool:
"""Return True when the ``ccache`` found on PATH actually runs."""
return tool_version_runs(
ccache,
"Ignoring ccache at %s because it failed to run; compiling without ccache",
)
def parse_enable_env(name: str) -> bool | None:
"""Strictly parse an on/off environment knob; None when unset or invalid.
``bool(str)`` truthiness would flip ``no``/``off`` to enabled, so only
1/true/yes/on and 0/false/no/off count; anything else warns and reads
as unset so the caller's default policy applies.
"""
raw = os.environ.get(name)
if raw is None:
return None
lowered = raw.strip().lower()
if not lowered:
# ENV KNOB= (Docker/CI) has always read as a disable
return False
if lowered in TRUTHY_ENV_STRINGS:
return True
if lowered in FALSY_ENV_STRINGS:
return False
_LOGGER.warning("Ignoring unrecognized %s=%r; use 1 or 0", name, raw)
return None
def resolve_ccache_path() -> str | None:
"""The ccache binary to wrap compiles with, or None when disabled.
An explicit ``ESPHOME_CCACHE_ENABLE=1`` skips the runnability probe; the
Windows extended-length prefix is stripped before probing (#18399).
"""
import shutil
explicit = parse_enable_env("ESPHOME_CCACHE_ENABLE")
if explicit is False:
return None
ccache = shutil.which("ccache")
if ccache is None:
if explicit:
_LOGGER.warning(
"ESPHOME_CCACHE_ENABLE is set but no ccache binary is on PATH; "
"compiling without ccache"
)
return None
ccache = strip_win_long_path_prefix(ccache)
if not explicit and not _ccache_runs(ccache):
return 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).
Values the user already set in the environment are respected. Depend
mode is on: both native backends emit depfiles (-MMD / CMake), which
keeps cache-miss overhead low.
"""
from esphome.core import CORE
# An unset build_path means the env was built before preload; fail loudly
# rather than silently drop CCACHE_BASEDIR.
if CORE.build_path is None:
raise ValueError(
"CORE.build_path must be set before constructing the build environment"
)
defaults = {
# 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()).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
+406
View File
@@ -0,0 +1,406 @@
"""Derive idedata from a native (non-PlatformIO) build's ``compile_commands.json``.
PlatformIO exposes a curated ``pio run -t idedata`` JSON; the native
toolchains have no such command, but each build produces a
``compile_commands.json`` (CMAKE_EXPORT_COMPILE_COMMANDS for ESP-IDF, ninja's
compdb tool otherwise). This module turns that file into the same fields
consumers (IDE integration, clang-tidy) expect:
{cc_path, cxx_path, cxx_flags, defines, includes: {build, toolchain}}
"""
from __future__ import annotations
from collections.abc import Callable
import json
import logging
import os
from pathlib import Path
import shlex
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 = (
EsphomeError,
LookupError,
OSError,
RuntimeError,
ValueError,
)
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")
# Suffixes of input/output files that appear bare on the command line (and so
# must not be mistaken for compiler flags).
_INPUT_FILE_SUFFIXES = (*_CXX_SUFFIXES, ".c", ".o", ".S", ".s")
# Path marker identifying an ESPHome source translation unit.
_ESPHOME_SRC_MARKER = "/src/esphome/"
def _is_esphome_src(file: str) -> bool:
"""Whether ``file`` is an ESPHome C++ translation unit; normalized to
``/`` first since Windows compile DBs use backslashes."""
return _ESPHOME_SRC_MARKER in file.replace("\\", "/") and file.endswith(
_CXX_SUFFIXES
)
def _split_command(command: str) -> list[str]:
r"""Tokenize a compile_commands.json / response-file command string.
On Windows, tokenize per Windows ``argv`` rules via ``CommandLineToArgvW``.
ESP-IDF's compile_commands.json there mixes two backslash conventions in one
string: literal path separators in the compiler path (``C:\Users\...g++.exe``,
no quote follows) and shell quote-escaping in -D defines (``-DVER=\"1.2.3\"``).
Only the real Windows parser — where a backslash escapes solely a following
quote — handles both, and it is the exact tokenizer the compiler is launched
with. ``shlex`` cannot: POSIX mode eats the path separators, and disabling
its escape mangles the defines.
"""
if os.name != "nt":
return shlex.split(command)
import ctypes
from ctypes import wintypes
# CommandLineToArgvW("") returns the current process name, not []; guard it
# so an empty response file tokenizes the same as it would via shlex.
if not command.strip():
return []
CommandLineToArgvW = ctypes.windll.shell32.CommandLineToArgvW
CommandLineToArgvW.argtypes = [wintypes.LPCWSTR, ctypes.POINTER(ctypes.c_int)]
CommandLineToArgvW.restype = ctypes.POINTER(wintypes.LPWSTR)
argc = ctypes.c_int()
argv = CommandLineToArgvW(command, ctypes.byref(argc))
if not argv: # pragma: no cover
raise ctypes.WinError()
try:
return [argv[i] for i in range(argc.value)]
finally:
ctypes.windll.kernel32.LocalFree(argv)
def _expand_response_files(tokens: list[str], directory: Path) -> list[str]:
"""Inline any ``@response-file`` arguments (paths relative to ``directory``).
GCC response files embed flags that must be expanded so GCC-only flags
inside them (e.g. ``-mlongcalls``) can be filtered downstream; left as
``@file`` clang would read them and choke.
"""
out: list[str] = []
for tok in tokens:
if tok.startswith("@"):
rf = Path(tok[1:])
if not rf.is_absolute():
rf = directory / rf
try:
out.extend(
_expand_response_files(
_split_command(rf.read_text(encoding="utf-8")), directory
)
)
continue
except OSError as err:
# Keep the literal token if the file can't be read, but log it
# so the (otherwise opaque) downstream clang failure is traceable.
_LOGGER.warning("Could not read response file %s: %s", rf, err)
out.append(tok)
return out
def _pick_entry(entries: list[dict]) -> dict:
"""Pick a representative ESPHome C++ TU; all share the same component
flags/defines."""
for entry in entries:
if _is_esphome_src(entry["file"]):
return entry
for entry in entries:
if entry["file"].endswith(_CXX_SUFFIXES):
return entry
raise ValueError("no C++ translation unit found in compile_commands.json")
# Compiler launchers that may prefix a compile command; a closed launcher
# denylist beats enumerating compiler names, an open set.
_LAUNCHER_STEMS = frozenset({"ccache", "sccache", "distcc", "icecc", "buildcache"})
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]]:
"""Parse one compile_commands entry -> (cxx_path, defines, includes, cxx_flags)."""
directory = Path(entry["directory"])
tokens = _expand_response_files(_split_command(entry["command"]), directory)
def _include(raw: str) -> str:
# Resolve against the entry's ``directory`` so cached idedata works
# from any cwd; emit forward slashes to match the JSON's own entries
raw = raw.strip()
if raw and not Path(raw).is_absolute():
raw = os.path.normpath(directory / raw)
return raw.replace("\\", "/")
# A launcher-wrapped command ("ccache g++ ...") names the compiler second
if launcher is not None and tokens[:1] == [launcher]:
tokens = tokens[1:]
if not tokens:
# An empty command, or one that was only the launcher; fail by name
raise ValueError(f"empty compile command for {entry.get('file')}")
if _is_launcher(tokens[0]) and len(tokens) > 1 and not tokens[1].startswith("-"):
# Stale DB built with a launcher this run no longer configures; the
# real compiler is the next token
_LOGGER.warning("Stripping unconfigured launcher %s", tokens[0])
tokens = tokens[1:]
# token0 is the compiler path; the rest of the command already uses forward
# slashes on Windows, so normalize it too for a consistent idedata file.
cxx_path = tokens[0].replace("\\", "/")
# Enforced here so no caller can record ccache as the compiler
reject_launcher_compiler(cxx_path)
defines: list[str] = []
includes: list[str] = []
cxx_flags: list[str] = []
it = iter(tokens[1:])
for tok in it:
if tok in ("-c", "-o"):
next(it, None) # drop the flag and its argument (input/output)
elif tok.startswith("-D"):
# ``.strip()`` handles tokens like ``-D CONFIGURED=1`` (a single
# quoted arg with a space after -D) that some flags arrive as.
defines.append(tok[2:].strip() if len(tok) > 2 else next(it, "").strip())
elif tok.startswith("-I"):
includes.append(_include(tok[2:] if len(tok) > 2 else next(it, "")))
elif tok == "-isystem":
includes.append(_include(next(it, "")))
elif tok.startswith("-isystem"):
includes.append(_include(tok[len("-isystem") :]))
elif tok in ("-MT", "-MF", "-MQ"):
next(it, None) # dependency-file flag + its argument
elif tok.startswith(("-MD", "-MMD", "-MP", "-MM")):
pass # dependency-generation flags, no argument
elif tok.endswith(_INPUT_FILE_SUFFIXES):
pass # input/output files
else:
cxx_flags.append(tok)
return cxx_path, defines, includes, cxx_flags
def get_toolchain_includes(cxx_path: str) -> list[str]:
"""Query the compiler for its builtin ``#include <...>`` search dirs."""
result = subprocess.run(
[cxx_path, "-E", "-x", "c++", "-", "-v"],
input="",
text=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.PIPE,
check=False,
close_fds=False,
)
includes: list[str] = []
capture = False
for line in result.stderr.splitlines():
if "#include <...> search starts here:" in line:
capture = True
continue
if "End of search list." in line:
break
if capture:
includes.append(line.strip())
if result.returncode != 0 or not includes:
raise RuntimeError(
f"Could not query builtin include dirs from {cxx_path} "
f"(return code {result.returncode}); stderr:\n{result.stderr.strip()}"
)
return includes
def _cc_path_from_cxx(cxx_path: str) -> str:
"""Derive the C compiler path from the C++ compiler path.
compile_commands.json only names the C++ compiler, but consumers reach the
rest of the toolchain (objdump, readelf, addr2line) by rewriting the tail of
``cc_path``, so they need the ``gcc``-suffixed name.
"""
stem, suffix = (
(cxx_path[: -len(".exe")], ".exe")
if cxx_path.endswith(".exe")
else (cxx_path, "")
)
# Rewrite the program name only when it is g++ itself, or a toolchain
# prefixed one such as xtensa-esp32-elf-g++ -> xtensa-esp32-elf-gcc.
# Requiring a separator before the "g++" keeps names that merely end in
# those three characters intact: "clang++" must not become "clangcc".
head = stem[: -len("g++")]
if stem.endswith("g++") and (not head or head.endswith(("-", "/", "\\"))):
stem = f"{head}gcc"
return f"{stem}{suffix}"
def _cache_usable(cached: object) -> bool:
"""Check a cached idedata dict against the guarantees of the write path.
Caches written by older versions predate the launcher rejection and the
include-union shape; serving one would bypass both. The dict check also
keeps "in" from substring-matching a bare JSON string.
"""
if not isinstance(cached, dict) or "cc_path" not in cached:
return False
cxx_path = cached.get("cxx_path")
if not isinstance(cxx_path, str) or _is_launcher(cxx_path):
return False
includes = cached.get("includes")
return isinstance(includes, dict) and isinstance(includes.get("build"), list)
def load_or_build_idedata(
compile_commands: Path,
elf_path: Path,
cache: Path,
launcher: str | None = None,
) -> dict | None:
"""Return idedata for a compile_commands.json build, cached on mtime.
Shared by the native ESP-IDF and ESP8266 Arduino toolchains. Returns None
when the compile DB doesn't exist yet (nothing was built). ``launcher``
is the compiler-launcher path (ccache) the build was generated with, if
any; commands in the compile DB are prefixed with it.
"""
if not compile_commands.is_file():
_LOGGER.debug("No %s yet; skipping idedata generation", compile_commands)
return None
if cache.is_file() and cache.stat().st_mtime >= compile_commands.stat().st_mtime:
try:
cached = json.loads(cache.read_text(encoding="utf-8"))
except (ValueError, OSError) as err:
# A recurring cause (interrupted write, disk full) would otherwise
# look like unexplained slow builds
_LOGGER.warning("Discarding unreadable idedata cache %s: %s", cache, err)
else:
if _cache_usable(cached):
# Re-stamp so a relocated build dir cannot serve a stale ELF path
cached["prog_path"] = str(elf_path)
return cached
_LOGGER.debug("Regenerating idedata: cache %s fails validation", cache)
data = idedata_from_build(compile_commands, launcher)
data["prog_path"] = str(elf_path)
cache.parent.mkdir(parents=True, exist_ok=True)
# Atomic so a crash mid-write cannot leave a truncated cache
write_file(cache, json.dumps(data, indent=2) + "\n")
return data
def reject_launcher_compiler(cxx_path: str) -> None:
"""Reject a compile DB naming a launcher (ccache) as the compiler; it
must never be probed, cached, or consumed."""
if _is_launcher(cxx_path):
raise EsphomeError(
f"compile_commands.json names the launcher {cxx_path} as the "
"compiler; the compile database is unusable"
)
def idedata_from_build(compile_commands: Path, launcher: str | None = None) -> dict:
"""Parse compile_commands.json into the idedata fields consumers expect.
A single compile entry only carries the include set its own translation
unit was built with (per-component under ESP-IDF), but consumers
(clang-tidy) analyze ESPHome headers that transitively pull in other
components. So take cxx_path / cxx_flags / defines from a representative
ESPHome TU, but union the include dirs across all ESPHome TUs to get a
project-wide superset (as PlatformIO's idedata provides).
"""
entries = json.loads(Path(compile_commands).read_text(encoding="utf-8"))
if not isinstance(entries, list) or not all(isinstance(e, dict) for e in entries):
# A TypeError here would escape IDEDATA_BEST_EFFORT_ERRORS
raise EsphomeError(f"{compile_commands} is not a compile-command list")
representative = _pick_entry(entries)
cxx_path, defines, rep_includes, cxx_flags = parse_entry(representative, launcher)
# Seed with the representative's includes so it is not parsed twice
has_esphome_tu = _is_esphome_src(representative["file"])
build_includes: dict[str, None] = dict.fromkeys(
rep_includes if has_esphome_tu else ()
)
def _shape(entry: dict) -> str:
# directory + command minus TU-specific paths: same shape means the
# same include set, so tokenize once per shape. Response-file
# commands never dedupe (the .rsp contents differ per object)
command = entry["command"]
directory = entry.get("directory", "")
if "@" in command:
return f"unique:{directory}|{entry.get('output') or command}"
stripped = command.replace(entry.get("file", ""), "").replace(
entry.get("output", ""), ""
)
return f"{directory}|{stripped}"
seen_shapes = {_shape(representative)}
for entry in entries:
if entry is representative or not _is_esphome_src(entry["file"]):
continue
has_esphome_tu = True
if (shape := _shape(entry)) in seen_shapes:
_LOGGER.debug("Include union: %s shares a command shape", entry["file"])
continue
seen_shapes.add(shape)
for inc in parse_entry(entry, launcher)[2]:
build_includes.setdefault(inc, None)
if not has_esphome_tu:
# An arbitrary fallback TU breaks clang-tidy/IDE consumers, and a
# warning would be cached into permanence; call sites downgrade this
raise EsphomeError(
f"No ESPHome translation unit found in {compile_commands}; "
"refusing to cache unusable idedata"
)
return {
"cc_path": _cc_path_from_cxx(cxx_path),
"cxx_path": cxx_path,
"cxx_flags": cxx_flags,
"defines": defines,
"includes": {
"build": list(build_includes),
"toolchain": get_toolchain_includes(cxx_path),
},
}
+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,
)
+182
View File
@@ -0,0 +1,182 @@
"""Platform-neutral helpers for ninja-driven native builds."""
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__)
def _ninja_runs(binary: str) -> bool:
"""Whether the ninja found on PATH actually runs (see tool_version_runs)."""
return tool_version_runs(
binary,
"Ignoring ninja at %s because it failed to run; "
"falling back to the bundled wheel",
)
# 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 = absolute_tool(binary)
if _ninja_runs(binary):
return Path(binary)
import_error: ImportError | None = None
try:
import ninja
except ImportError as err:
import_error = err
wheel_binary = None
else:
wheel_binary = Path(ninja.BIN_DIR) / (
"ninja.exe" if os.name == "nt" else "ninja"
)
if wheel_binary is None or not wheel_binary.is_file():
raise EsphomeError(
"ninja not found on PATH or in the ninja package; reinstall the "
"esphome Python environment"
) from import_error
return wheel_binary
def escape(value: Path | str) -> str:
"""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:
"""Quote with the CreateProcess argv rule (as ``subprocess.list2cmdline``):
backslash runs double only before a quote. Windows-only; ``$`` must
already be doubled for ninja.
"""
quoted = re.sub(r'(\\*)"', lambda m: m.group(1) * 2 + '\\"', tok)
quoted = re.sub(r"(\\+)\Z", lambda m: m.group(1) * 2, quoted)
return f'"{quoted}"'
# Force-quote any token containing a character outside the shlex.quote-style
# safe set: ninja hands POSIX commands to /bin/sh -c, so bare (, ;, <, *, `
# and friends would be re-parsed as shell syntax.
_NEEDS_QUOTE = re.compile(r"[^\w@%+=:,./-]")
def shell_token(tok: str, force: bool = False) -> str:
"""Re-quote a lexed token for the platform shell; ``force`` always quotes.
Single quotes on POSIX (/bin/sh), the argv rule on Windows
(CreateProcess). ``$`` is doubled first because ninja expands it before
the command reaches the shell.
"""
tok = tok.replace("$", "$$") # ninja would expand a bare $ to nothing
if not (force or not tok or _NEEDS_QUOTE.search(tok)):
return tok
# An empty token must become '' / "" or it vanishes from the argv
if os.name == "nt":
return quote_arg(tok)
# shlex.quote's rule; inlined because the $-doubled token must not be
# re-examined for safe characters
return "'" + tok.replace("'", "'\"'\"'") + "'"
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)
+288
View File
@@ -0,0 +1,288 @@
"""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(ccache: str | None) -> list[str]:
"""The compile rules; ``$own_includes`` is empty unless an edge sets it.
``ccache`` is the launcher ``tool_lines`` was given; the same test here
keeps the variable and the rules in step. It is spliced in rather than
left as an empty variable: a leading space on the command line makes
CreateProcess fail on Windows.
"""
launcher = "$ccache " if ccache else ""
return [
"rule c",
f" command = {launcher}$cc -MMD -MF $out.d $own_includes $cflags $flags -c $in -o $out",
" depfile = $out.d",
" deps = gcc",
" description = CC $out",
"rule cxx",
f" command = {launcher}$cxx -MMD -MF $out.d $own_includes $cxxflags $flags -c $in -o $out",
" depfile = $out.d",
" deps = gcc",
" description = CXX $out",
"rule aspp",
f" command = {launcher}$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",
f" command = {launcher}$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)
+24
View File
@@ -0,0 +1,24 @@
"""The PlatformIO-format size bar shared by the native toolchains."""
from __future__ import annotations
def format_bar(used: int, total: int) -> str:
"""Match PlatformIO's ``_format_availale_bytes`` (sic, pioupload.py) exactly."""
pct_raw = used / total if total else 0
blocks = 10
filled = min(int(round(blocks * pct_raw)), blocks)
progress = "=" * filled
return (
f"[{progress:<{blocks}}] {pct_raw: 6.1%} "
f"(used {used:d} bytes from {total:d} bytes)"
)
def print_size_line(label: str, used: int, total: int) -> None:
"""One PlatformIO-format summary line (``RAM``/``Flash``).
The label padding is part of the format: ``script/ci_memory_impact_extract.py``
matches these lines verbatim.
"""
print(f"{label + ':':<7}{format_bar(used, total)}")
+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
+46
View File
@@ -0,0 +1,46 @@
"""Machine-global tools cache location shared by the native backends."""
from __future__ import annotations
from pathlib import Path
def tools_cache_path(env_var: str, subdir: str) -> Path:
"""A backend's machine-global tools directory, with an env override.
A blank/whitespace override is treated as unset: ``Path("")`` resolves
to the CWD, which ``clean-all`` would then delete.
"""
import platformdirs
from esphome.helpers import get_str_env
if prefix := get_str_env(env_var, "").strip():
# 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
return (
Path(platformdirs.user_cache_dir("esphome", appauthor=False)) / subdir
).resolve()
# (env override, cache subdir) per native backend. writer.clean_all wipes
# every entry via tools_cache_path, so listing a cache here is the single
# step that registers it for removal; the backends' own path getters use
# the same named pairs so the two cannot drift.
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")
# 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,
)
+20 -12
View File
@@ -102,25 +102,21 @@ _KNOWN_FILE_EXTENSIONS = frozenset(
)
# Matches !secret references in YAML text. An optional surrounding
# quote pair around the key is allowed and ignored: YAML treats
# ``!secret 'foo'`` and ``!secret foo`` as the same key. This is
# intentionally a simple regex scan rather than a YAML parse — it may
# match inside comments or multi-line strings, which is the conservative
# direction (include more secrets rather than fewer).
_SECRET_RE = re.compile(r"""!secret\s+['"]?([^\s'"]+)""")
def _find_used_secret_keys(yaml_files: list[Path]) -> set[str]:
"""Scan YAML files for ``!secret <key>`` references."""
keys: set[str] = set()
for fpath in yaml_files:
try:
text = fpath.read_text(encoding="utf-8")
except (OSError, UnicodeDecodeError):
except (OSError, UnicodeDecodeError) as err:
_LOGGER.warning(
"Could not scan %s for !secret references (%s); the bundled "
"secret set may be incomplete",
fpath,
err,
)
continue
for match in _SECRET_RE.finditer(text):
keys.add(match.group(1))
keys |= yaml_util.find_secret_references(text)
return keys
@@ -422,6 +418,18 @@ class ConfigBundleCreator:
must ship every candidate so the remote build can pick any one.
"""
discovered = yaml_util.discover_user_yaml_files(self._config_path)
if discovered.load_errors:
_LOGGER.warning(
"Bundle may be incomplete; could not load all configuration files: %s",
"; ".join(discovered.load_errors),
)
if discovered.unresolved:
_LOGGER.warning(
"Bundle may be incomplete; %d !include path(s) use "
"substitutions and cannot be captured: %s",
len(discovered.unresolved),
", ".join(discovered.unresolved),
)
self._secrets_paths.update(discovered.secrets)
# A !secret inside a file this re-parse does not reach (for example
# a git-fetched package the builder re-fetches) still resolves
+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
+11 -1
View File
@@ -25,32 +25,42 @@ from esphome.cpp_generator import ( # noqa: F401
add,
add_build_flag,
add_build_unflag,
add_cmake_arg,
add_cxx_build_flag,
add_define,
add_global,
add_library,
add_platformio_option,
extern_progmem_array,
get_variable,
get_variable_with_full_id,
is_static_pointer,
is_template,
new_Pvariable,
new_variable,
process_lambda,
progmem_array,
progmem_string,
safe_exp,
set_cpp_standard,
shared_progmem_array,
shared_progmem_string,
statement,
static_const_array,
static_function,
templatable,
variable,
with_local_variable,
)
from esphome.cpp_helpers import ( # noqa: F401
add_pin,
add_variable,
build_registry_entry,
build_registry_list,
extract_registry_entry_config,
get_slot_count,
gpio_pin_expression,
new_bus_component,
past_safe_mode,
register_component,
register_parented,
@@ -63,7 +73,6 @@ from esphome.cpp_types import ( # noqa: F401
Application,
Component,
ComponentPtr,
Controller,
EntityBase,
EntityCategory,
ESPTime,
@@ -77,6 +86,7 @@ from esphome.cpp_types import ( # noqa: F401
StringRef,
arduino_json_ns,
bool_,
char,
const_char_ptr,
double,
esphome_ns,
+84 -8
View File
@@ -18,9 +18,9 @@ from pathlib import Path
from typing import Any
from esphome.const import __version__ as ESPHOME_VERSION
from esphome.core import CORE, Lambda
from esphome.core import CORE, EsphomeError, Lambda
from esphome.helpers import write_file
from esphome.storage_json import StorageJSON, ext_storage_path
from esphome.storage_json import StorageJSON, ext_storage_path, storage_path
from esphome.types import ConfigType
_LOGGER = logging.getLogger(__name__)
@@ -65,7 +65,86 @@ def save_compiled_config(config: ConfigType) -> None:
# non-basic dict key), so every upload/logs pays the slow path.
_LOGGER.warning("Cannot cache the validated config: %s", err)
except Exception as err: # noqa: BLE001 # pylint: disable=broad-except
_LOGGER.debug("Skipping compiled config cache write: %s", err)
# Likely persistent (permissions, full disk): every upload/logs
# pays the slow path until it clears, so surface it.
_LOGGER.warning("Skipping compiled config cache write: %s", err)
def save_compiled_config_and_sidecar(config: ConfigType) -> None:
"""Refresh the cache from the upload/logs fallback (CORE.config must be set).
The cache is only written when a complete sidecar is on disk:
load_compiled_config can't use it otherwise, and it holds resolved
secrets.
"""
if _refresh_sidecar():
save_compiled_config(config)
def _refresh_sidecar() -> bool:
"""Ensure a complete sidecar is on disk; True when one is.
Writes one (without claiming a build) when missing or wizard-only.
Failures are non-fatal; the next upload/logs pays the slow path again.
"""
try:
path = storage_path()
try:
old = StorageJSON.load_strict(path)
except Exception as err: # noqa: BLE001 # pylint: disable=broad-except
# Present but unreadable: it may hold a real build's metadata,
# and a fresh rewrite would also stop the next compile from
# cleaning a possibly incoherent build tree.
_LOGGER.warning(
"Not caching: storage sidecar %s is unreadable (%s)", path, err
)
return False
if old is not None and old.can_apply_to_core():
if (
old.toolchain is not None
and CORE.toolchain is not None
and old.toolchain != CORE.toolchain.value
):
# Platforms normalize toolchain-sensitive keys differently;
# never cache a config validated under a different toolchain
# than the compile's
_LOGGER.debug(
"Not caching: config validated with toolchain %r but the "
"last compile used %r",
CORE.toolchain.value,
old.toolchain,
)
return False
# Compile-written; nothing to refresh.
return True
if CORE.build_path is not None and CORE.build_path.exists():
# An unvalidated build tree: its absent or mismatched sidecar
# is what makes the next compile wipe it, so don't vouch for
# a build this run never saw.
_LOGGER.warning(
"Not caching: build tree %s has no matching sidecar; "
"'esphome compile' will settle it",
CORE.build_path,
)
return False
new = StorageJSON.from_esphome_core(CORE, old, claim_build=False)
if not new.can_apply_to_core():
_LOGGER.warning("Not caching: rebuilt storage sidecar is still incomplete")
return False
new.save(path)
return True
except (OSError, EsphomeError) as err:
# write_file wraps OSError into EsphomeError. Persistent
# (unwritable storage dir), so surface that every upload/logs
# pays the slow path.
_LOGGER.warning("Could not refresh the storage sidecar: %s", err)
except Exception: # noqa: BLE001 # pylint: disable=broad-except
# A structural bug; keep the traceback so it isn't mistaken
# for the I/O failure above.
_LOGGER.warning(
"Unexpected error refreshing the storage sidecar", exc_info=True
)
return False
def load_compiled_config(conf_path: Path) -> ConfigType | None:
@@ -98,11 +177,8 @@ def load_compiled_config(conf_path: Path) -> ConfigType | None:
return None
storage = StorageJSON.load(ext_storage_path(conf_path.name))
if storage is None:
return None
# apply_to_core assumes a real compile wrote the sidecar; wizard-only
# sidecars leave both of these unset and can't drive upload/logs.
if not storage.core_platform and not storage.target_platform:
if storage is None or not storage.can_apply_to_core():
_LOGGER.debug("Ignoring compiled config cache: sidecar missing or incomplete")
return None
storage.apply_to_core()
return config
+11
View File
@@ -0,0 +1,11 @@
"""Component alias registry.
Generated by script/build_alias_registry.py - do not edit manually.
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
View File
@@ -1 +1,2 @@
CODEOWNERS = ["@MrSuicideParrot"]
DOMAIN = "a01nyub"
+4 -6
View File
@@ -6,18 +6,16 @@
namespace esphome::a01nyub {
static const char *const TAG = "a01nyub.sensor";
ESPHOME_LOG_TAG(TAG, "a01nyub.sensor");
void A01nyubComponent::loop() {
uint8_t data;
while (this->available() > 0) {
this->read_byte(&data);
this->read_available([this](uint8_t data) {
if (this->buffer_.empty() && (data != 0xff))
continue;
return;
buffer_.push_back(data);
if (this->buffer_.size() == 4)
this->check_buffer_();
}
});
}
void A01nyubComponent::check_buffer_() {
+2 -1
View File
@@ -6,6 +6,7 @@ from esphome.const import (
STATE_CLASS_MEASUREMENT,
UNIT_METER,
)
from esphome.types import ConfigType
CODEOWNERS = ["@MrSuicideParrot"]
DEPENDENCIES = ["uart"]
@@ -35,7 +36,7 @@ FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema(
)
async def to_code(config):
async def to_code(config: ConfigType) -> None:
var = await sensor.new_sensor(config)
await cg.register_component(var, config)
await uart.register_uart_device(var, config)
+1
View File
@@ -1 +1,2 @@
CODEOWNERS = ["@TH-Braemer"]
DOMAIN = "a02yyuw"
+4 -6
View File
@@ -6,18 +6,16 @@
namespace esphome::a02yyuw {
static const char *const TAG = "a02yyuw.sensor";
ESPHOME_LOG_TAG(TAG, "a02yyuw.sensor");
void A02yyuwComponent::loop() {
uint8_t data;
while (this->available() > 0) {
this->read_byte(&data);
this->read_available([this](uint8_t data) {
if (this->buffer_.empty() && (data != 0xff))
continue;
return;
buffer_.push_back(data);
if (this->buffer_.size() == 4)
this->check_buffer_();
}
});
}
void A02yyuwComponent::check_buffer_() {
+2 -1
View File
@@ -6,6 +6,7 @@ from esphome.const import (
STATE_CLASS_MEASUREMENT,
UNIT_MILLIMETER,
)
from esphome.types import ConfigType
CODEOWNERS = ["@TH-Braemer"]
DEPENDENCIES = ["uart"]
@@ -35,7 +36,7 @@ FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema(
)
async def to_code(config):
async def to_code(config: ConfigType) -> None:
var = await sensor.new_sensor(config)
await cg.register_component(var, config)
await uart.register_uart_device(var, config)
+1
View File
@@ -0,0 +1 @@
DOMAIN = "a4988"
+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) {
+5 -9
View File
@@ -3,6 +3,7 @@ import esphome.codegen as cg
from esphome.components import stepper
import esphome.config_validation as cv
from esphome.const import CONF_DIR_PIN, CONF_ID, CONF_SLEEP_PIN, CONF_STEP_PIN
from esphome.types import ConfigType
a4988_ns = cg.esphome_ns.namespace("a4988")
A4988 = a4988_ns.class_("A4988", stepper.Stepper, cg.Component)
@@ -17,16 +18,11 @@ CONFIG_SCHEMA = stepper.STEPPER_SCHEMA.extend(
).extend(cv.COMPONENT_SCHEMA)
async def to_code(config):
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await stepper.register_stepper(var, config)
step_pin = await cg.gpio_pin_expression(config[CONF_STEP_PIN])
cg.add(var.set_step_pin(step_pin))
dir_pin = await cg.gpio_pin_expression(config[CONF_DIR_PIN])
cg.add(var.set_dir_pin(dir_pin))
if sleep_pin_config := config.get(CONF_SLEEP_PIN):
sleep_pin = await cg.gpio_pin_expression(sleep_pin_config)
cg.add(var.set_sleep_pin(sleep_pin))
await cg.add_pin(var.set_step_pin, config[CONF_STEP_PIN])
await cg.add_pin(var.set_dir_pin, config[CONF_DIR_PIN])
await cg.add_pin(var.set_sleep_pin, config.get(CONF_SLEEP_PIN))
@@ -1 +1,2 @@
CODEOWNERS = ["@DAVe3283"]
DOMAIN = "absolute_humidity"
@@ -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) {
Loaded 100 of 6373 files, more files were not shown because too many files have changed in this diff. Show more