esphome 2025.9.3__py3-none-any.whl → 2025.10.0b2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (351) hide show
  1. esphome/__main__.py +94 -31
  2. esphome/address_cache.py +142 -0
  3. esphome/automation.py +130 -32
  4. esphome/build_gen/platformio.py +1 -3
  5. esphome/codegen.py +1 -0
  6. esphome/components/animation/animation.cpp +2 -2
  7. esphome/components/api/__init__.py +166 -3
  8. esphome/components/api/api_connection.cpp +84 -41
  9. esphome/components/api/api_connection.h +22 -16
  10. esphome/components/api/api_frame_helper.cpp +33 -19
  11. esphome/components/api/api_frame_helper.h +19 -4
  12. esphome/components/api/api_frame_helper_noise.cpp +41 -53
  13. esphome/components/api/api_frame_helper_noise.h +1 -1
  14. esphome/components/api/api_frame_helper_plaintext.cpp +22 -31
  15. esphome/components/api/api_frame_helper_plaintext.h +1 -1
  16. esphome/components/api/api_pb2.cpp +189 -15
  17. esphome/components/api/api_pb2.h +132 -20
  18. esphome/components/api/api_pb2_dump.cpp +97 -9
  19. esphome/components/api/api_pb2_service.cpp +118 -160
  20. esphome/components/api/api_pb2_service.h +31 -3
  21. esphome/components/api/api_server.cpp +68 -10
  22. esphome/components/api/api_server.h +32 -4
  23. esphome/components/api/custom_api_device.h +8 -8
  24. esphome/components/api/homeassistant_service.h +123 -6
  25. esphome/components/api/proto.h +6 -2
  26. esphome/components/api/user_services.h +2 -2
  27. esphome/components/as7341/sensor.py +1 -1
  28. esphome/components/audio/__init__.py +1 -1
  29. esphome/components/audio/audio.cpp +1 -1
  30. esphome/components/audio/audio_decoder.cpp +9 -9
  31. esphome/components/bl0906/bl0906.cpp +2 -2
  32. esphome/components/bl0942/bl0942.cpp +2 -2
  33. esphome/components/ble_client/__init__.py +1 -1
  34. esphome/components/bluetooth_proxy/__init__.py +4 -30
  35. esphome/components/bluetooth_proxy/bluetooth_connection.cpp +11 -4
  36. esphome/components/bluetooth_proxy/bluetooth_connection.h +2 -2
  37. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +2 -2
  38. esphome/components/camera_encoder/__init__.py +2 -4
  39. esphome/components/camera_encoder/esp32_camera_jpeg_encoder.cpp +4 -2
  40. esphome/components/camera_encoder/esp32_camera_jpeg_encoder.h +3 -1
  41. esphome/components/canbus/canbus.cpp +7 -5
  42. esphome/components/canbus/canbus.h +7 -7
  43. esphome/components/captive_portal/__init__.py +18 -1
  44. esphome/components/captive_portal/captive_portal.cpp +40 -46
  45. esphome/components/captive_portal/captive_portal.h +20 -22
  46. esphome/components/captive_portal/dns_server_esp32_idf.cpp +205 -0
  47. esphome/components/captive_portal/dns_server_esp32_idf.h +27 -0
  48. esphome/components/ccs811/ccs811.cpp +1 -1
  49. esphome/components/climate/climate.cpp +10 -7
  50. esphome/components/cm1106/cm1106.cpp +1 -1
  51. esphome/components/copy/lock/copy_lock.cpp +1 -1
  52. esphome/components/cover/cover.cpp +1 -0
  53. esphome/components/daikin_arc/daikin_arc.cpp +19 -12
  54. esphome/components/dashboard_import/dashboard_import.cpp +1 -1
  55. esphome/components/dashboard_import/dashboard_import.h +1 -1
  56. esphome/components/deep_sleep/__init__.py +9 -2
  57. esphome/components/deep_sleep/deep_sleep_component.h +11 -9
  58. esphome/components/deep_sleep/deep_sleep_esp32.cpp +51 -27
  59. esphome/components/ektf2232/touchscreen/__init__.py +8 -5
  60. esphome/components/ektf2232/touchscreen/ektf2232.cpp +4 -4
  61. esphome/components/ektf2232/touchscreen/ektf2232.h +2 -2
  62. esphome/components/epaper_spi/__init__.py +1 -0
  63. esphome/components/epaper_spi/display.py +80 -0
  64. esphome/components/epaper_spi/epaper_spi.cpp +227 -0
  65. esphome/components/epaper_spi/epaper_spi.h +93 -0
  66. esphome/components/epaper_spi/epaper_spi_model_7p3in_spectra_e6.cpp +42 -0
  67. esphome/components/epaper_spi/epaper_spi_model_7p3in_spectra_e6.h +45 -0
  68. esphome/components/epaper_spi/epaper_spi_spectra_e6.cpp +135 -0
  69. esphome/components/epaper_spi/epaper_spi_spectra_e6.h +23 -0
  70. esphome/components/es7210/es7210.cpp +3 -3
  71. esphome/components/esp32/__init__.py +256 -340
  72. esphome/components/esp32/boards.py +81 -0
  73. esphome/components/esp32/preferences.cpp +23 -17
  74. esphome/components/esp32_ble/__init__.py +167 -44
  75. esphome/components/esp32_ble/ble.cpp +47 -3
  76. esphome/components/esp32_ble/ble.h +18 -0
  77. esphome/components/esp32_ble/ble_advertising.cpp +7 -3
  78. esphome/components/esp32_ble/ble_advertising.h +4 -0
  79. esphome/components/esp32_ble/ble_uuid.cpp +16 -42
  80. esphome/components/esp32_ble_beacon/__init__.py +3 -4
  81. esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp +0 -4
  82. esphome/components/esp32_ble_client/ble_client_base.cpp +14 -12
  83. esphome/components/esp32_ble_server/__init__.py +28 -14
  84. esphome/components/esp32_ble_server/ble_characteristic.cpp +67 -57
  85. esphome/components/esp32_ble_server/ble_characteristic.h +27 -16
  86. esphome/components/esp32_ble_server/ble_descriptor.cpp +4 -3
  87. esphome/components/esp32_ble_server/ble_descriptor.h +13 -9
  88. esphome/components/esp32_ble_server/ble_server.cpp +59 -24
  89. esphome/components/esp32_ble_server/ble_server.h +38 -20
  90. esphome/components/esp32_ble_server/ble_server_automations.cpp +49 -33
  91. esphome/components/esp32_ble_server/ble_server_automations.h +39 -24
  92. esphome/components/esp32_ble_tracker/__init__.py +25 -80
  93. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +2 -8
  94. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +0 -3
  95. esphome/components/esp32_camera/__init__.py +1 -3
  96. esphome/components/esp32_can/esp32_can.cpp +22 -4
  97. esphome/components/esp32_can/esp32_can.h +3 -0
  98. esphome/components/esp32_hosted/__init__.py +2 -1
  99. esphome/components/esp32_improv/esp32_improv_component.cpp +135 -65
  100. esphome/components/esp32_improv/esp32_improv_component.h +7 -1
  101. esphome/components/esp32_rmt_led_strip/led_strip.cpp +1 -1
  102. esphome/components/esp8266/__init__.py +3 -3
  103. esphome/components/esphome/ota/__init__.py +21 -2
  104. esphome/components/esphome/ota/ota_esphome.cpp +456 -146
  105. esphome/components/esphome/ota/ota_esphome.h +49 -2
  106. esphome/components/ethernet/__init__.py +39 -22
  107. esphome/components/ethernet/ethernet_component.cpp +28 -5
  108. esphome/components/ethernet/ethernet_component.h +5 -1
  109. esphome/components/external_components/__init__.py +8 -6
  110. esphome/components/fingerprint_grow/fingerprint_grow.cpp +1 -1
  111. esphome/components/fingerprint_grow/fingerprint_grow.h +2 -1
  112. esphome/components/font/__init__.py +5 -5
  113. esphome/components/graph/graph.cpp +1 -1
  114. esphome/components/graphical_display_menu/graphical_display_menu.cpp +3 -2
  115. esphome/components/haier/hon_climate.cpp +2 -2
  116. esphome/components/haier/hon_climate.h +1 -1
  117. esphome/components/hdc1080/hdc1080.cpp +42 -34
  118. esphome/components/hdc1080/hdc1080.h +1 -3
  119. esphome/components/homeassistant/number/homeassistant_number.cpp +2 -2
  120. esphome/components/homeassistant/switch/homeassistant_switch.cpp +2 -2
  121. esphome/components/http_request/__init__.py +3 -3
  122. esphome/components/htu21d/htu21d.cpp +13 -18
  123. esphome/components/htu21d/htu21d.h +1 -1
  124. esphome/components/i2s_audio/__init__.py +1 -2
  125. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +1 -1
  126. esphome/components/ili9xxx/ili9xxx_display.cpp +2 -2
  127. esphome/components/improv_serial/improv_serial_component.cpp +12 -15
  128. esphome/components/improv_serial/improv_serial_component.h +6 -8
  129. esphome/components/json/json_util.cpp +42 -44
  130. esphome/components/json/json_util.h +57 -0
  131. esphome/components/kamstrup_kmp/kamstrup_kmp.cpp +2 -2
  132. esphome/components/key_collector/key_collector.h +4 -4
  133. esphome/components/libretiny/__init__.py +6 -6
  134. esphome/components/libretiny/preferences.cpp +23 -16
  135. esphome/components/light/light_call.cpp +98 -120
  136. esphome/components/light/light_call.h +17 -7
  137. esphome/components/lm75b/__init__.py +0 -0
  138. esphome/components/lm75b/lm75b.cpp +39 -0
  139. esphome/components/lm75b/lm75b.h +19 -0
  140. esphome/components/lm75b/sensor.py +34 -0
  141. esphome/components/lock/lock.h +12 -6
  142. esphome/components/logger/__init__.py +15 -27
  143. esphome/components/logger/logger.cpp +10 -20
  144. esphome/components/logger/logger.h +105 -62
  145. esphome/components/logger/logger_esp32.cpp +0 -48
  146. esphome/components/logger/logger_zephyr.cpp +2 -3
  147. esphome/components/logger/select/logger_level_select.cpp +6 -7
  148. esphome/components/logger/select/logger_level_select.h +7 -0
  149. esphome/components/ltr501/ltr501.cpp +7 -6
  150. esphome/components/ltr_als_ps/ltr_als_ps.cpp +7 -6
  151. esphome/components/matrix_keypad/matrix_keypad.h +4 -4
  152. esphome/components/max7219digit/max7219digit.cpp +1 -1
  153. esphome/components/mcp23xxx_base/mcp23xxx_base.h +3 -3
  154. esphome/components/mcp2515/mcp2515.cpp +31 -3
  155. esphome/components/mcp2515/mcp2515_defs.h +3 -1
  156. esphome/components/md5/md5.cpp +0 -26
  157. esphome/components/md5/md5.h +10 -20
  158. esphome/components/mdns/__init__.py +93 -19
  159. esphome/components/mdns/mdns_component.cpp +57 -94
  160. esphome/components/mdns/mdns_component.h +35 -11
  161. esphome/components/mdns/mdns_esp32.cpp +7 -13
  162. esphome/components/mdns/mdns_esp8266.cpp +7 -7
  163. esphome/components/mdns/mdns_libretiny.cpp +3 -4
  164. esphome/components/mdns/mdns_rp2040.cpp +3 -4
  165. esphome/components/mipi/__init__.py +1 -5
  166. esphome/components/mipi_spi/display.py +24 -8
  167. esphome/components/mipi_spi/mipi_spi.h +3 -3
  168. esphome/components/mixer/speaker/mixer_speaker.cpp +3 -3
  169. esphome/components/mmc5603/mmc5603.cpp +3 -3
  170. esphome/components/modbus/modbus.cpp +27 -13
  171. esphome/components/modbus/modbus.h +5 -3
  172. esphome/components/modbus/modbus_definitions.h +86 -0
  173. esphome/components/modbus_controller/__init__.py +29 -1
  174. esphome/components/modbus_controller/const.py +4 -0
  175. esphome/components/modbus_controller/modbus_controller.cpp +38 -13
  176. esphome/components/modbus_controller/modbus_controller.h +18 -29
  177. esphome/components/mpr121/mpr121.cpp +41 -42
  178. esphome/components/mpr121/mpr121.h +0 -1
  179. esphome/components/nau7802/nau7802.cpp +2 -2
  180. esphome/components/network/__init__.py +7 -3
  181. esphome/components/nextion/display.py +4 -4
  182. esphome/components/nextion/nextion.cpp +8 -8
  183. esphome/components/number/__init__.py +2 -0
  184. esphome/components/number/number_call.cpp +23 -12
  185. esphome/components/number/number_call.h +5 -0
  186. esphome/components/online_image/bmp_image.cpp +2 -1
  187. esphome/components/online_image/jpeg_image.cpp +4 -2
  188. esphome/components/opentherm/opentherm.cpp +5 -5
  189. esphome/components/opentherm/opentherm.h +3 -3
  190. esphome/components/openthread/openthread.cpp +11 -10
  191. esphome/components/openthread/openthread.h +0 -1
  192. esphome/components/ota/ota_backend.h +1 -0
  193. esphome/components/packages/__init__.py +10 -8
  194. esphome/components/packet_transport/packet_transport.cpp +2 -0
  195. esphome/components/pid/pid_controller.cpp +1 -1
  196. esphome/components/prometheus/prometheus_handler.cpp +239 -239
  197. esphome/components/psram/__init__.py +30 -28
  198. esphome/components/qmc5883l/qmc5883l.cpp +15 -0
  199. esphome/components/qmc5883l/qmc5883l.h +3 -0
  200. esphome/components/qmc5883l/sensor.py +31 -12
  201. esphome/components/remote_base/gobox_protocol.cpp +3 -3
  202. esphome/components/remote_receiver/__init__.py +14 -2
  203. esphome/components/remote_receiver/{remote_receiver_esp8266.cpp → remote_receiver.cpp} +2 -2
  204. esphome/components/remote_receiver/remote_receiver.h +4 -0
  205. esphome/components/remote_receiver/remote_receiver_esp32.cpp +18 -1
  206. esphome/components/remote_transmitter/__init__.py +2 -2
  207. esphome/components/remote_transmitter/remote_transmitter.cpp +103 -0
  208. esphome/components/rp2040/__init__.py +11 -11
  209. esphome/components/rtttl/rtttl.cpp +2 -2
  210. esphome/components/scd30/sensor.py +1 -1
  211. esphome/components/script/__init__.py +1 -1
  212. esphome/components/script/script.h +7 -7
  213. esphome/components/select/select.cpp +5 -4
  214. esphome/components/select/select_call.cpp +1 -1
  215. esphome/components/sensirion_common/i2c_sensirion.cpp +2 -1
  216. esphome/components/sensor/__init__.py +2 -0
  217. esphome/components/sha256/__init__.py +22 -0
  218. esphome/components/sha256/sha256.cpp +116 -0
  219. esphome/components/sha256/sha256.h +60 -0
  220. esphome/components/socket/lwip_raw_tcp_impl.cpp +34 -6
  221. esphome/components/sonoff_d1/sonoff_d1.cpp +1 -1
  222. esphome/components/spi/__init__.py +0 -3
  223. esphome/components/split_buffer/__init__.py +5 -0
  224. esphome/components/split_buffer/split_buffer.cpp +133 -0
  225. esphome/components/split_buffer/split_buffer.h +40 -0
  226. esphome/components/sps30/sps30.cpp +14 -10
  227. esphome/components/sps30/sps30.h +2 -0
  228. esphome/components/st7567_i2c/st7567_i2c.cpp +3 -1
  229. esphome/components/st7789v/st7789v.cpp +3 -2
  230. esphome/components/statsd/statsd.cpp +1 -1
  231. esphome/components/substitutions/__init__.py +3 -1
  232. esphome/components/substitutions/jinja.py +13 -3
  233. esphome/components/sx126x/__init__.py +16 -0
  234. esphome/components/sx126x/sx126x.cpp +15 -1
  235. esphome/components/sx126x/sx126x.h +9 -1
  236. esphome/components/sx126x/sx126x_reg.h +2 -0
  237. esphome/components/text_sensor/text_sensor.cpp +16 -0
  238. esphome/components/text_sensor/text_sensor.h +3 -10
  239. esphome/components/tormatic/tormatic_cover.cpp +1 -1
  240. esphome/components/tuya/select/tuya_select.cpp +1 -1
  241. esphome/components/tuya/tuya.cpp +29 -4
  242. esphome/components/uart/__init__.py +37 -27
  243. esphome/components/uart/uart.h +6 -0
  244. esphome/components/uart/uart_component.cpp +8 -0
  245. esphome/components/uart/uart_component.h +28 -0
  246. esphome/components/uart/uart_component_esp_idf.cpp +64 -10
  247. esphome/components/uart/uart_component_esp_idf.h +5 -2
  248. esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp +1 -1
  249. esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.cpp +1 -1
  250. esphome/components/uponor_smatrix/uponor_smatrix.cpp +3 -3
  251. esphome/components/usb_host/__init__.py +12 -2
  252. esphome/components/usb_host/usb_host.h +89 -14
  253. esphome/components/usb_host/usb_host_client.cpp +157 -22
  254. esphome/components/usb_host/usb_host_component.cpp +1 -1
  255. esphome/components/usb_uart/__init__.py +0 -1
  256. esphome/components/usb_uart/ch34x.cpp +4 -4
  257. esphome/components/usb_uart/cp210x.cpp +3 -3
  258. esphome/components/usb_uart/usb_uart.cpp +88 -32
  259. esphome/components/usb_uart/usb_uart.h +30 -6
  260. esphome/components/valve/valve.cpp +1 -0
  261. esphome/components/veml7700/veml7700.cpp +7 -6
  262. esphome/components/version/version_text_sensor.cpp +2 -1
  263. esphome/components/voice_assistant/voice_assistant.cpp +3 -2
  264. esphome/components/waveshare_epaper/waveshare_epaper.cpp +4 -4
  265. esphome/components/web_server/list_entities.cpp +3 -4
  266. esphome/components/web_server/list_entities.h +8 -10
  267. esphome/components/web_server/ota/__init__.py +1 -1
  268. esphome/components/web_server/ota/ota_web_server.cpp +9 -3
  269. esphome/components/web_server/web_server.cpp +509 -404
  270. esphome/components/web_server/web_server.h +5 -6
  271. esphome/components/web_server/web_server_v1.cpp +21 -19
  272. esphome/components/web_server_base/__init__.py +5 -2
  273. esphome/components/web_server_base/web_server_base.h +27 -7
  274. esphome/components/web_server_idf/__init__.py +1 -1
  275. esphome/components/web_server_idf/multipart.cpp +2 -2
  276. esphome/components/web_server_idf/multipart.h +2 -2
  277. esphome/components/web_server_idf/utils.cpp +2 -2
  278. esphome/components/web_server_idf/utils.h +2 -2
  279. esphome/components/web_server_idf/web_server_idf.cpp +118 -26
  280. esphome/components/web_server_idf/web_server_idf.h +12 -10
  281. esphome/components/wifi/__init__.py +13 -11
  282. esphome/components/wifi/wifi_component.cpp +74 -56
  283. esphome/components/wifi/wifi_component.h +4 -4
  284. esphome/components/wifi/wifi_component_esp8266.cpp +1 -1
  285. esphome/components/wifi/wifi_component_esp_idf.cpp +24 -4
  286. esphome/components/wireguard/__init__.py +1 -1
  287. esphome/components/wts01/__init__.py +0 -0
  288. esphome/components/wts01/sensor.py +41 -0
  289. esphome/components/wts01/wts01.cpp +91 -0
  290. esphome/components/wts01/wts01.h +27 -0
  291. esphome/components/zephyr/__init__.py +5 -5
  292. esphome/components/zwave_proxy/__init__.py +43 -0
  293. esphome/components/zwave_proxy/zwave_proxy.cpp +346 -0
  294. esphome/components/zwave_proxy/zwave_proxy.h +93 -0
  295. esphome/config.py +79 -24
  296. esphome/config_validation.py +13 -15
  297. esphome/const.py +9 -2
  298. esphome/core/__init__.py +33 -22
  299. esphome/core/component.cpp +28 -18
  300. esphome/core/component_iterator.h +2 -1
  301. esphome/core/config.py +15 -15
  302. esphome/core/defines.h +21 -0
  303. esphome/core/entity_helpers.py +9 -6
  304. esphome/core/hash_base.h +56 -0
  305. esphome/core/helpers.cpp +19 -3
  306. esphome/core/helpers.h +26 -0
  307. esphome/core/scheduler.cpp +5 -21
  308. esphome/core/scheduler.h +19 -8
  309. esphome/core/string_ref.h +1 -1
  310. esphome/core/time.cpp +5 -5
  311. esphome/cpp_generator.py +4 -29
  312. esphome/dashboard/const.py +21 -4
  313. esphome/dashboard/core.py +10 -8
  314. esphome/dashboard/dns.py +15 -0
  315. esphome/dashboard/entries.py +15 -21
  316. esphome/dashboard/models.py +76 -0
  317. esphome/dashboard/settings.py +7 -7
  318. esphome/dashboard/status/mdns.py +46 -2
  319. esphome/dashboard/web_server.py +367 -93
  320. esphome/espota2.py +112 -32
  321. esphome/external_files.py +6 -7
  322. esphome/git.py +8 -0
  323. esphome/helpers.py +124 -77
  324. esphome/loader.py +8 -9
  325. esphome/pins.py +2 -2
  326. esphome/platformio_api.py +56 -18
  327. esphome/storage_json.py +26 -21
  328. esphome/types.py +30 -2
  329. esphome/util.py +32 -16
  330. esphome/vscode.py +8 -8
  331. esphome/wizard.py +10 -10
  332. esphome/writer.py +50 -15
  333. esphome/yaml_util.py +37 -31
  334. esphome/zeroconf.py +12 -3
  335. {esphome-2025.9.3.dist-info → esphome-2025.10.0b2.dist-info}/METADATA +12 -12
  336. {esphome-2025.9.3.dist-info → esphome-2025.10.0b2.dist-info}/RECORD +340 -320
  337. esphome/components/event_emitter/__init__.py +0 -5
  338. esphome/components/event_emitter/event_emitter.cpp +0 -14
  339. esphome/components/event_emitter/event_emitter.h +0 -63
  340. esphome/components/remote_receiver/remote_receiver_libretiny.cpp +0 -125
  341. esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +0 -107
  342. esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp +0 -110
  343. esphome/components/uart/uart_component_esp32_arduino.cpp +0 -214
  344. esphome/components/uart/uart_component_esp32_arduino.h +0 -60
  345. esphome/components/wifi/wifi_component_esp32_arduino.cpp +0 -860
  346. esphome/core/string_ref.cpp +0 -12
  347. esphome/dashboard/util/file.py +0 -63
  348. {esphome-2025.9.3.dist-info → esphome-2025.10.0b2.dist-info}/WHEEL +0 -0
  349. {esphome-2025.9.3.dist-info → esphome-2025.10.0b2.dist-info}/entry_points.txt +0 -0
  350. {esphome-2025.9.3.dist-info → esphome-2025.10.0b2.dist-info}/licenses/LICENSE +0 -0
  351. {esphome-2025.9.3.dist-info → esphome-2025.10.0b2.dist-info}/top_level.txt +0 -0
esphome/writer.py CHANGED
@@ -266,7 +266,7 @@ def generate_version_h():
266
266
 
267
267
  def write_cpp(code_s):
268
268
  path = CORE.relative_src_path("main.cpp")
269
- if os.path.isfile(path):
269
+ if path.is_file():
270
270
  text = read_file(path)
271
271
  code_format = find_begin_end(
272
272
  text, CPP_AUTO_GENERATE_BEGIN, CPP_AUTO_GENERATE_END
@@ -292,43 +292,79 @@ def write_cpp(code_s):
292
292
 
293
293
  def clean_cmake_cache():
294
294
  pioenvs = CORE.relative_pioenvs_path()
295
- if os.path.isdir(pioenvs):
296
- pioenvs_cmake_path = CORE.relative_pioenvs_path(CORE.name, "CMakeCache.txt")
297
- if os.path.isfile(pioenvs_cmake_path):
295
+ if pioenvs.is_dir():
296
+ pioenvs_cmake_path = pioenvs / CORE.name / "CMakeCache.txt"
297
+ if pioenvs_cmake_path.is_file():
298
298
  _LOGGER.info("Deleting %s", pioenvs_cmake_path)
299
- os.remove(pioenvs_cmake_path)
299
+ pioenvs_cmake_path.unlink()
300
300
 
301
301
 
302
302
  def clean_build():
303
303
  import shutil
304
304
 
305
+ # Allow skipping cache cleaning for integration tests
306
+ if os.environ.get("ESPHOME_SKIP_CLEAN_BUILD"):
307
+ _LOGGER.warning("Skipping build cleaning (ESPHOME_SKIP_CLEAN_BUILD set)")
308
+ return
309
+
305
310
  pioenvs = CORE.relative_pioenvs_path()
306
- if os.path.isdir(pioenvs):
311
+ if pioenvs.is_dir():
307
312
  _LOGGER.info("Deleting %s", pioenvs)
308
313
  shutil.rmtree(pioenvs)
309
314
  piolibdeps = CORE.relative_piolibdeps_path()
310
- if os.path.isdir(piolibdeps):
315
+ if piolibdeps.is_dir():
311
316
  _LOGGER.info("Deleting %s", piolibdeps)
312
317
  shutil.rmtree(piolibdeps)
313
318
  dependencies_lock = CORE.relative_build_path("dependencies.lock")
314
- if os.path.isfile(dependencies_lock):
319
+ if dependencies_lock.is_file():
315
320
  _LOGGER.info("Deleting %s", dependencies_lock)
316
- os.remove(dependencies_lock)
321
+ dependencies_lock.unlink()
317
322
 
318
323
  # Clean PlatformIO cache to resolve CMake compiler detection issues
319
324
  # This helps when toolchain paths change or get corrupted
320
325
  try:
321
- from platformio.project.helpers import get_project_cache_dir
326
+ from platformio.project.config import ProjectConfig
322
327
  except ImportError:
323
328
  # PlatformIO is not available, skip cache cleaning
324
329
  pass
325
330
  else:
326
- cache_dir = get_project_cache_dir()
327
- if cache_dir and cache_dir.strip() and os.path.isdir(cache_dir):
331
+ config = ProjectConfig.get_instance()
332
+ cache_dir = Path(config.get("platformio", "cache_dir"))
333
+ if cache_dir.is_dir():
328
334
  _LOGGER.info("Deleting PlatformIO cache %s", cache_dir)
329
335
  shutil.rmtree(cache_dir)
330
336
 
331
337
 
338
+ def clean_all(configuration: list[str]):
339
+ import shutil
340
+
341
+ # Clean entire build dir
342
+ for dir in configuration:
343
+ build_dir = Path(dir) / ".esphome"
344
+ if build_dir.is_dir():
345
+ _LOGGER.info("Cleaning %s", build_dir)
346
+ # Don't remove storage as it will cause the dashboard to regenerate all configs
347
+ for item in build_dir.iterdir():
348
+ if item.is_file():
349
+ item.unlink()
350
+ elif item.name != "storage" and item.is_dir():
351
+ shutil.rmtree(item)
352
+
353
+ # Clean PlatformIO project files
354
+ try:
355
+ from platformio.project.config import ProjectConfig
356
+ except ImportError:
357
+ # PlatformIO is not available, skip cleaning
358
+ pass
359
+ else:
360
+ config = ProjectConfig.get_instance()
361
+ for pio_dir in ["cache_dir", "packages_dir", "platforms_dir", "core_dir"]:
362
+ path = Path(config.get("platformio", pio_dir))
363
+ if path.is_dir():
364
+ _LOGGER.info("Deleting PlatformIO %s %s", pio_dir, path)
365
+ shutil.rmtree(path)
366
+
367
+
332
368
  GITIGNORE_CONTENT = """# Gitignore settings for ESPHome
333
369
  # This is an example and may include too much for your use-case.
334
370
  # You can modify this file to suit your needs.
@@ -339,6 +375,5 @@ GITIGNORE_CONTENT = """# Gitignore settings for ESPHome
339
375
 
340
376
  def write_gitignore():
341
377
  path = CORE.relative_config_path(".gitignore")
342
- if not os.path.isfile(path):
343
- with open(file=path, mode="w", encoding="utf-8") as f:
344
- f.write(GITIGNORE_CONTENT)
378
+ if not path.is_file():
379
+ path.write_text(GITIGNORE_CONTENT, encoding="utf-8")
esphome/yaml_util.py CHANGED
@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from collections.abc import Callable
4
- import fnmatch
5
4
  import functools
6
5
  import inspect
7
6
  from io import BytesIO, TextIOBase, TextIOWrapper
@@ -9,6 +8,7 @@ from ipaddress import _BaseAddress, _BaseNetwork
9
8
  import logging
10
9
  import math
11
10
  import os
11
+ from pathlib import Path
12
12
  from typing import Any
13
13
  import uuid
14
14
 
@@ -69,7 +69,7 @@ class ESPHomeDataBase:
69
69
  self._content_offset = database.content_offset
70
70
 
71
71
 
72
- class ESPForceValue:
72
+ class ESPLiteralValue:
73
73
  pass
74
74
 
75
75
 
@@ -109,7 +109,9 @@ def _add_data_ref(fn):
109
109
  class ESPHomeLoaderMixin:
110
110
  """Loader class that keeps track of line numbers."""
111
111
 
112
- def __init__(self, name: str, yaml_loader: Callable[[str], dict[str, Any]]) -> None:
112
+ def __init__(
113
+ self, name: Path, yaml_loader: Callable[[Path], dict[str, Any]]
114
+ ) -> None:
113
115
  """Initialize the loader."""
114
116
  self.name = name
115
117
  self.yaml_loader = yaml_loader
@@ -254,12 +256,8 @@ class ESPHomeLoaderMixin:
254
256
  f"Environment variable '{node.value}' not defined", node.start_mark
255
257
  )
256
258
 
257
- @property
258
- def _directory(self) -> str:
259
- return os.path.dirname(self.name)
260
-
261
- def _rel_path(self, *args: str) -> str:
262
- return os.path.join(self._directory, *args)
259
+ def _rel_path(self, *args: str) -> Path:
260
+ return self.name.parent / Path(*args)
263
261
 
264
262
  @_add_data_ref
265
263
  def construct_secret(self, node: yaml.Node) -> str:
@@ -269,8 +267,8 @@ class ESPHomeLoaderMixin:
269
267
  if self.name == CORE.config_path:
270
268
  raise e
271
269
  try:
272
- main_config_dir = os.path.dirname(CORE.config_path)
273
- main_secret_yml = os.path.join(main_config_dir, SECRET_YAML)
270
+ main_config_dir = CORE.config_path.parent
271
+ main_secret_yml = main_config_dir / SECRET_YAML
274
272
  secrets = self.yaml_loader(main_secret_yml)
275
273
  except EsphomeError as er:
276
274
  raise EsphomeError(f"{e}\n{er}") from er
@@ -329,7 +327,7 @@ class ESPHomeLoaderMixin:
329
327
  files = filter_yaml_files(_find_files(self._rel_path(node.value), "*.yaml"))
330
328
  mapping = OrderedDict()
331
329
  for fname in files:
332
- filename = os.path.splitext(os.path.basename(fname))[0]
330
+ filename = fname.stem
333
331
  mapping[filename] = self.yaml_loader(fname)
334
332
  return mapping
335
333
 
@@ -350,9 +348,15 @@ class ESPHomeLoaderMixin:
350
348
  return Lambda(str(node.value))
351
349
 
352
350
  @_add_data_ref
353
- def construct_force(self, node: yaml.Node) -> ESPForceValue:
354
- obj = self.construct_scalar(node)
355
- return add_class_to_obj(obj, ESPForceValue)
351
+ def construct_literal(self, node: yaml.Node) -> ESPLiteralValue:
352
+ obj = None
353
+ if isinstance(node, yaml.ScalarNode):
354
+ obj = self.construct_scalar(node)
355
+ elif isinstance(node, yaml.SequenceNode):
356
+ obj = self.construct_sequence(node)
357
+ elif isinstance(node, yaml.MappingNode):
358
+ obj = self.construct_mapping(node)
359
+ return add_class_to_obj(obj, ESPLiteralValue)
356
360
 
357
361
  @_add_data_ref
358
362
  def construct_extend(self, node: yaml.Node) -> Extend:
@@ -369,8 +373,8 @@ class ESPHomeLoader(ESPHomeLoaderMixin, FastestAvailableSafeLoader):
369
373
  def __init__(
370
374
  self,
371
375
  stream: TextIOBase | BytesIO,
372
- name: str,
373
- yaml_loader: Callable[[str], dict[str, Any]],
376
+ name: Path,
377
+ yaml_loader: Callable[[Path], dict[str, Any]],
374
378
  ) -> None:
375
379
  FastestAvailableSafeLoader.__init__(self, stream)
376
380
  ESPHomeLoaderMixin.__init__(self, name, yaml_loader)
@@ -382,8 +386,8 @@ class ESPHomePurePythonLoader(ESPHomeLoaderMixin, PurePythonLoader):
382
386
  def __init__(
383
387
  self,
384
388
  stream: TextIOBase | BytesIO,
385
- name: str,
386
- yaml_loader: Callable[[str], dict[str, Any]],
389
+ name: Path,
390
+ yaml_loader: Callable[[Path], dict[str, Any]],
387
391
  ) -> None:
388
392
  PurePythonLoader.__init__(self, stream)
389
393
  ESPHomeLoaderMixin.__init__(self, name, yaml_loader)
@@ -409,29 +413,29 @@ for _loader in (ESPHomeLoader, ESPHomePurePythonLoader):
409
413
  "!include_dir_merge_named", _loader.construct_include_dir_merge_named
410
414
  )
411
415
  _loader.add_constructor("!lambda", _loader.construct_lambda)
412
- _loader.add_constructor("!force", _loader.construct_force)
416
+ _loader.add_constructor("!literal", _loader.construct_literal)
413
417
  _loader.add_constructor("!extend", _loader.construct_extend)
414
418
  _loader.add_constructor("!remove", _loader.construct_remove)
415
419
 
416
420
 
417
- def load_yaml(fname: str, clear_secrets: bool = True) -> Any:
421
+ def load_yaml(fname: Path, clear_secrets: bool = True) -> Any:
418
422
  if clear_secrets:
419
423
  _SECRET_VALUES.clear()
420
424
  _SECRET_CACHE.clear()
421
425
  return _load_yaml_internal(fname)
422
426
 
423
427
 
424
- def _load_yaml_internal(fname: str) -> Any:
428
+ def _load_yaml_internal(fname: Path) -> Any:
425
429
  """Load a YAML file."""
426
430
  try:
427
- with open(fname, encoding="utf-8") as f_handle:
431
+ with fname.open(encoding="utf-8") as f_handle:
428
432
  return parse_yaml(fname, f_handle)
429
433
  except (UnicodeDecodeError, OSError) as err:
430
434
  raise EsphomeError(f"Error reading file {fname}: {err}") from err
431
435
 
432
436
 
433
437
  def parse_yaml(
434
- file_name: str, file_handle: TextIOWrapper, yaml_loader=_load_yaml_internal
438
+ file_name: Path, file_handle: TextIOWrapper, yaml_loader=_load_yaml_internal
435
439
  ) -> Any:
436
440
  """Parse a YAML file."""
437
441
  try:
@@ -483,9 +487,9 @@ def substitute_vars(config, vars):
483
487
 
484
488
  def _load_yaml_internal_with_type(
485
489
  loader_type: type[ESPHomeLoader] | type[ESPHomePurePythonLoader],
486
- fname: str,
490
+ fname: Path,
487
491
  content: TextIOWrapper,
488
- yaml_loader: Any,
492
+ yaml_loader: Callable[[Path], dict[str, Any]],
489
493
  ) -> Any:
490
494
  """Load a YAML file."""
491
495
  loader = loader_type(content, fname, yaml_loader)
@@ -512,13 +516,14 @@ def _is_file_valid(name: str) -> bool:
512
516
  return not name.startswith(".")
513
517
 
514
518
 
515
- def _find_files(directory, pattern):
519
+ def _find_files(directory: Path, pattern):
516
520
  """Recursively load files in a directory."""
517
- for root, dirs, files in os.walk(directory, topdown=True):
521
+ for root, dirs, files in os.walk(directory):
518
522
  dirs[:] = [d for d in dirs if _is_file_valid(d)]
519
- for basename in files:
520
- if _is_file_valid(basename) and fnmatch.fnmatch(basename, pattern):
521
- filename = os.path.join(root, basename)
523
+ for f in files:
524
+ filename = Path(f)
525
+ if _is_file_valid(f) and filename.match(pattern):
526
+ filename = Path(root) / filename
522
527
  yield filename
523
528
 
524
529
 
@@ -627,3 +632,4 @@ ESPHomeDumper.add_multi_representer(TimePeriod, ESPHomeDumper.represent_stringif
627
632
  ESPHomeDumper.add_multi_representer(Lambda, ESPHomeDumper.represent_lambda)
628
633
  ESPHomeDumper.add_multi_representer(core.ID, ESPHomeDumper.represent_id)
629
634
  ESPHomeDumper.add_multi_representer(uuid.UUID, ESPHomeDumper.represent_stringify)
635
+ ESPHomeDumper.add_multi_representer(Path, ESPHomeDumper.represent_stringify)
esphome/zeroconf.py CHANGED
@@ -68,8 +68,11 @@ class DashboardBrowser(AsyncServiceBrowser):
68
68
 
69
69
 
70
70
  class DashboardImportDiscovery:
71
- def __init__(self) -> None:
71
+ def __init__(
72
+ self, on_update: Callable[[str, DiscoveredImport | None], None] | None = None
73
+ ) -> None:
72
74
  self.import_state: dict[str, DiscoveredImport] = {}
75
+ self.on_update = on_update
73
76
 
74
77
  def browser_callback(
75
78
  self,
@@ -85,7 +88,9 @@ class DashboardImportDiscovery:
85
88
  state_change,
86
89
  )
87
90
  if state_change == ServiceStateChange.Removed:
88
- self.import_state.pop(name, None)
91
+ removed = self.import_state.pop(name, None)
92
+ if removed and self.on_update:
93
+ self.on_update(name, None)
89
94
  return
90
95
 
91
96
  if state_change == ServiceStateChange.Updated and name not in self.import_state:
@@ -139,7 +144,7 @@ class DashboardImportDiscovery:
139
144
  if friendly_name is not None:
140
145
  friendly_name = friendly_name.decode()
141
146
 
142
- self.import_state[name] = DiscoveredImport(
147
+ discovered = DiscoveredImport(
143
148
  friendly_name=friendly_name,
144
149
  device_name=node_name,
145
150
  package_import_url=import_url,
@@ -147,6 +152,10 @@ class DashboardImportDiscovery:
147
152
  project_version=project_version,
148
153
  network=network,
149
154
  )
155
+ is_new = name not in self.import_state
156
+ self.import_state[name] = discovered
157
+ if is_new and self.on_update:
158
+ self.on_update(name, discovered)
150
159
 
151
160
  def update_device_mdns(self, node_name: str, version: str):
152
161
  storage_path = ext_storage_path(node_name + ".yaml")
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: esphome
3
- Version: 2025.9.3
3
+ Version: 2025.10.0b2
4
4
  Summary: ESPHome is a system to configure your microcontrollers by simple yet powerful configuration files and control them remotely through Home Automation systems.
5
5
  Author-email: The ESPHome Authors <esphome@openhomefoundation.org>
6
- License: MIT
6
+ License-Expression: MIT
7
7
  Project-URL: Documentation, https://esphome.io
8
8
  Project-URL: Source Code, https://github.com/esphome/esphome
9
9
  Project-URL: Bug Tracker, https://github.com/esphome/esphome/issues
@@ -16,7 +16,6 @@ Platform: any
16
16
  Classifier: Environment :: Console
17
17
  Classifier: Intended Audience :: Developers
18
18
  Classifier: Intended Audience :: End Users/Desktop
19
- Classifier: License :: OSI Approved :: MIT License
20
19
  Classifier: Programming Language :: C++
21
20
  Classifier: Programming Language :: Python :: 3
22
21
  Classifier: Topic :: Home Automation
@@ -25,7 +24,7 @@ Description-Content-Type: text/markdown
25
24
  License-File: LICENSE
26
25
  Requires-Dist: cryptography==45.0.1
27
26
  Requires-Dist: voluptuous==0.15.2
28
- Requires-Dist: PyYAML==6.0.2
27
+ Requires-Dist: PyYAML==6.0.3
29
28
  Requires-Dist: paho-mqtt==1.6.1
30
29
  Requires-Dist: colorama==0.4.6
31
30
  Requires-Dist: icmplib==3.0.4
@@ -34,13 +33,14 @@ Requires-Dist: tzlocal==5.3.1
34
33
  Requires-Dist: tzdata>=2021.1
35
34
  Requires-Dist: pyserial==3.5
36
35
  Requires-Dist: platformio==6.1.18
37
- Requires-Dist: esptool==5.0.2
36
+ Requires-Dist: esptool==5.1.0
38
37
  Requires-Dist: click==8.1.7
39
- Requires-Dist: esphome-dashboard==20250904.0
40
- Requires-Dist: aioesphomeapi==40.2.1
41
- Requires-Dist: zeroconf==0.147.2
38
+ Requires-Dist: esphome-dashboard==20251009.0
39
+ Requires-Dist: aioesphomeapi==41.14.0
40
+ Requires-Dist: zeroconf==0.148.0
42
41
  Requires-Dist: puremagic==1.30
43
42
  Requires-Dist: ruamel.yaml==0.18.15
43
+ Requires-Dist: ruamel.yaml.clib==0.2.12
44
44
  Requires-Dist: esphome-glyphsets==0.2.0
45
45
  Requires-Dist: pillow==10.4.0
46
46
  Requires-Dist: cairosvg==2.8.2
@@ -54,15 +54,15 @@ Requires-Dist: clang-format==13.0.1; extra == "dev"
54
54
  Requires-Dist: clang-tidy==18.1.8; extra == "dev"
55
55
  Requires-Dist: yamllint==1.37.1; extra == "dev"
56
56
  Provides-Extra: test
57
- Requires-Dist: pylint==3.3.8; extra == "test"
57
+ Requires-Dist: pylint==3.3.9; extra == "test"
58
58
  Requires-Dist: flake8==7.3.0; extra == "test"
59
- Requires-Dist: ruff==0.12.12; extra == "test"
59
+ Requires-Dist: ruff==0.14.0; extra == "test"
60
60
  Requires-Dist: pyupgrade==3.20.0; extra == "test"
61
61
  Requires-Dist: pre-commit; extra == "test"
62
62
  Requires-Dist: pytest==8.4.2; extra == "test"
63
63
  Requires-Dist: pytest-cov==7.0.0; extra == "test"
64
- Requires-Dist: pytest-mock==3.15.0; extra == "test"
65
- Requires-Dist: pytest-asyncio==1.1.0; extra == "test"
64
+ Requires-Dist: pytest-mock==3.15.1; extra == "test"
65
+ Requires-Dist: pytest-asyncio==1.2.0; extra == "test"
66
66
  Requires-Dist: pytest-xdist==3.8.0; extra == "test"
67
67
  Requires-Dist: asyncmock==0.4.2; extra == "test"
68
68
  Requires-Dist: hypothesis==6.92.1; extra == "test"