esphome 2025.8.4__py3-none-any.whl → 2025.9.0__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 (369) hide show
  1. esphome/__main__.py +177 -105
  2. esphome/components/absolute_humidity/absolute_humidity.cpp +3 -5
  3. esphome/components/adc/__init__.py +1 -26
  4. esphome/components/adc/adc_sensor_esp32.cpp +29 -6
  5. esphome/components/adc/sensor.py +20 -0
  6. esphome/components/ade7880/ade7880.cpp +1 -1
  7. esphome/components/ags10/ags10.cpp +3 -18
  8. esphome/components/ags10/ags10.h +2 -12
  9. esphome/components/aht10/aht10.cpp +3 -3
  10. esphome/components/airthings_ble/__init__.py +2 -2
  11. esphome/components/alarm_control_panel/__init__.py +2 -2
  12. esphome/components/am2315c/am2315c.cpp +1 -17
  13. esphome/components/am2315c/am2315c.h +2 -3
  14. esphome/components/api/__init__.py +2 -2
  15. esphome/components/api/api_connection.cpp +38 -34
  16. esphome/components/api/api_connection.h +20 -40
  17. esphome/components/api/api_frame_helper.cpp +25 -25
  18. esphome/components/api/api_frame_helper.h +3 -3
  19. esphome/components/api/api_frame_helper_noise.cpp +75 -40
  20. esphome/components/api/api_frame_helper_noise.h +3 -7
  21. esphome/components/api/api_frame_helper_plaintext.cpp +17 -4
  22. esphome/components/api/api_frame_helper_plaintext.h +1 -4
  23. esphome/components/api/api_pb2.cpp +12 -2
  24. esphome/components/api/api_pb2.h +144 -143
  25. esphome/components/api/api_pb2_dump.cpp +6 -1
  26. esphome/components/api/api_pb2_service.cpp +0 -14
  27. esphome/components/api/api_pb2_service.h +1 -3
  28. esphome/components/api/client.py +5 -3
  29. esphome/components/api/proto.cpp +33 -37
  30. esphome/components/async_tcp/__init__.py +2 -2
  31. esphome/components/atm90e26/sensor.py +2 -0
  32. esphome/components/atm90e32/sensor.py +4 -2
  33. esphome/components/audio_adc/__init__.py +2 -2
  34. esphome/components/audio_dac/__init__.py +2 -2
  35. esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp +1 -1
  36. esphome/components/bedjet/bedjet_hub.cpp +1 -1
  37. esphome/components/binary_sensor/__init__.py +2 -2
  38. esphome/components/binary_sensor/binary_sensor.cpp +13 -0
  39. esphome/components/binary_sensor/binary_sensor.h +4 -7
  40. esphome/components/bl0940/__init__.py +6 -1
  41. esphome/components/bl0940/bl0940.cpp +178 -41
  42. esphome/components/bl0940/bl0940.h +121 -76
  43. esphome/components/bl0940/button/__init__.py +27 -0
  44. esphome/components/bl0940/button/calibration_reset_button.cpp +20 -0
  45. esphome/components/bl0940/button/calibration_reset_button.h +19 -0
  46. esphome/components/bl0940/number/__init__.py +94 -0
  47. esphome/components/bl0940/number/calibration_number.cpp +29 -0
  48. esphome/components/bl0940/number/calibration_number.h +26 -0
  49. esphome/components/bl0940/sensor.py +151 -2
  50. esphome/components/bl0942/bl0942.cpp +1 -1
  51. esphome/components/ble_client/output/__init__.py +4 -4
  52. esphome/components/bluetooth_proxy/__init__.py +1 -1
  53. esphome/components/bluetooth_proxy/bluetooth_connection.h +1 -1
  54. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +15 -7
  55. esphome/components/bluetooth_proxy/bluetooth_proxy.h +6 -3
  56. esphome/components/button/__init__.py +2 -2
  57. esphome/components/button/button.cpp +13 -0
  58. esphome/components/button/button.h +4 -7
  59. esphome/components/camera/buffer.h +18 -0
  60. esphome/components/camera/buffer_impl.cpp +20 -0
  61. esphome/components/camera/buffer_impl.h +26 -0
  62. esphome/components/camera/camera.h +43 -0
  63. esphome/components/camera/encoder.h +69 -0
  64. esphome/components/camera_encoder/__init__.py +62 -0
  65. esphome/components/camera_encoder/encoder_buffer_impl.cpp +23 -0
  66. esphome/components/camera_encoder/encoder_buffer_impl.h +25 -0
  67. esphome/components/camera_encoder/esp32_camera_jpeg_encoder.cpp +82 -0
  68. esphome/components/camera_encoder/esp32_camera_jpeg_encoder.h +39 -0
  69. esphome/components/captive_portal/__init__.py +2 -2
  70. esphome/components/captive_portal/captive_index.h +77 -97
  71. esphome/components/captive_portal/captive_portal.cpp +35 -12
  72. esphome/components/captive_portal/captive_portal.h +3 -3
  73. esphome/components/ccs811/ccs811.cpp +3 -3
  74. esphome/components/climate/__init__.py +2 -2
  75. esphome/components/climate/climate.cpp +1 -1
  76. esphome/components/cover/__init__.py +5 -5
  77. esphome/components/cover/cover.cpp +1 -1
  78. esphome/components/cover/cover.h +2 -2
  79. esphome/components/dallas_temp/dallas_temp.cpp +2 -2
  80. esphome/components/datetime/__init__.py +2 -2
  81. esphome/components/datetime/date_entity.h +2 -2
  82. esphome/components/datetime/datetime_entity.h +2 -2
  83. esphome/components/datetime/time_entity.h +2 -2
  84. esphome/components/debug/debug_esp32.cpp +1 -1
  85. esphome/components/display/__init__.py +4 -4
  86. esphome/components/duty_time/duty_time_sensor.cpp +1 -1
  87. esphome/components/esp32/__init__.py +0 -5
  88. esphome/components/esp32/gpio.cpp +27 -23
  89. esphome/components/esp32/gpio.h +26 -11
  90. esphome/components/esp32/preferences.cpp +8 -4
  91. esphome/components/esp32_ble/__init__.py +7 -2
  92. esphome/components/esp32_ble/ble_uuid.cpp +30 -9
  93. esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp +4 -3
  94. esphome/components/esp32_ble_client/ble_client_base.cpp +7 -3
  95. esphome/components/esp32_ble_client/ble_client_base.h +8 -5
  96. esphome/components/esp32_ble_tracker/__init__.py +2 -2
  97. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +11 -47
  98. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +2 -14
  99. esphome/components/esp8266/__init__.py +2 -2
  100. esphome/components/esp8266/core.cpp +2 -2
  101. esphome/components/esp8266/gpio.py +4 -4
  102. esphome/components/esp8266/preferences.cpp +30 -28
  103. esphome/components/esphome/ota/__init__.py +2 -2
  104. esphome/components/esphome/ota/ota_esphome.cpp +21 -19
  105. esphome/components/esphome/ota/ota_esphome.h +6 -5
  106. esphome/components/ethernet/__init__.py +18 -2
  107. esphome/components/ethernet/ethernet_component.cpp +53 -3
  108. esphome/components/ethernet/ethernet_component.h +4 -0
  109. esphome/components/event/__init__.py +2 -2
  110. esphome/components/event/event.h +4 -4
  111. esphome/components/factory_reset/button/factory_reset_button.cpp +18 -1
  112. esphome/components/factory_reset/button/factory_reset_button.h +6 -1
  113. esphome/components/factory_reset/switch/factory_reset_switch.cpp +18 -1
  114. esphome/components/factory_reset/switch/factory_reset_switch.h +5 -1
  115. esphome/components/fan/__init__.py +2 -2
  116. esphome/components/fan/fan.cpp +2 -1
  117. esphome/components/gdk101/gdk101.cpp +4 -4
  118. esphome/components/globals/__init__.py +2 -2
  119. esphome/components/gpio/binary_sensor/gpio_binary_sensor.cpp +19 -18
  120. esphome/components/gpio_expander/cached_gpio.h +36 -16
  121. esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.cpp +5 -5
  122. esphome/components/gt911/touchscreen/gt911_touchscreen.cpp +1 -1
  123. esphome/components/haier/haier_base.cpp +1 -1
  124. esphome/components/haier/hon_climate.cpp +1 -1
  125. esphome/components/hlw8012/hlw8012.cpp +5 -5
  126. esphome/components/honeywellabp2_i2c/honeywellabp2.cpp +4 -4
  127. esphome/components/host/preferences.h +3 -2
  128. esphome/components/hte501/hte501.cpp +3 -21
  129. esphome/components/hte501/hte501.h +2 -3
  130. esphome/components/http_request/ota/__init__.py +2 -2
  131. esphome/components/i2c/__init__.py +2 -2
  132. esphome/components/i2c/i2c.cpp +13 -9
  133. esphome/components/i2c/i2c_bus.h +36 -6
  134. esphome/components/i2s_audio/__init__.py +8 -2
  135. esphome/components/i2s_audio/media_player/__init__.py +1 -1
  136. esphome/components/i2s_audio/microphone/__init__.py +1 -1
  137. esphome/components/i2s_audio/speaker/__init__.py +1 -1
  138. esphome/components/ina2xx_base/__init__.py +4 -2
  139. esphome/components/inkplate/__init__.py +1 -0
  140. esphome/components/inkplate/const.py +105 -0
  141. esphome/components/inkplate/display.py +238 -0
  142. esphome/components/{inkplate6 → inkplate}/inkplate.cpp +156 -74
  143. esphome/components/{inkplate6 → inkplate}/inkplate.h +28 -68
  144. esphome/components/inkplate6/__init__.py +0 -1
  145. esphome/components/inkplate6/display.py +2 -211
  146. esphome/components/integration/integration_sensor.cpp +1 -1
  147. esphome/components/json/__init__.py +2 -2
  148. esphome/components/lc709203f/lc709203f.cpp +4 -17
  149. esphome/components/lc709203f/lc709203f.h +2 -3
  150. esphome/components/ld2420/text_sensor/{text_sensor.cpp → ld2420_text_sensor.cpp} +1 -1
  151. esphome/components/ld2450/ld2450.cpp +1 -1
  152. esphome/components/libretiny/preferences.cpp +13 -5
  153. esphome/components/light/__init__.py +2 -2
  154. esphome/components/light/addressable_light_effect.h +7 -0
  155. esphome/components/light/base_light_effects.h +8 -0
  156. esphome/components/light/light_call.cpp +22 -20
  157. esphome/components/light/light_effect.cpp +36 -0
  158. esphome/components/light/light_effect.h +14 -0
  159. esphome/components/light/light_json_schema.cpp +9 -1
  160. esphome/components/light/light_state.cpp +2 -2
  161. esphome/components/light/light_state.h +38 -0
  162. esphome/components/lock/__init__.py +2 -2
  163. esphome/components/lock/lock.h +2 -2
  164. esphome/components/logger/__init__.py +2 -2
  165. esphome/components/logger/logger.cpp +25 -4
  166. esphome/components/logger/logger.h +1 -1
  167. esphome/components/logger/logger_esp32.cpp +16 -8
  168. esphome/components/logger/logger_esp8266.cpp +11 -3
  169. esphome/components/logger/logger_libretiny.cpp +13 -3
  170. esphome/components/logger/logger_rp2040.cpp +14 -3
  171. esphome/components/logger/logger_zephyr.cpp +15 -4
  172. esphome/components/lvgl/defines.py +1 -0
  173. esphome/components/lvgl/hello_world.py +96 -33
  174. esphome/components/lvgl/number/lvgl_number.h +1 -1
  175. esphome/components/lvgl/select/lvgl_select.h +1 -1
  176. esphome/components/lvgl/widgets/__init__.py +0 -1
  177. esphome/components/lvgl/widgets/spinbox.py +20 -11
  178. esphome/components/m5stack_8angle/binary_sensor/m5stack_8angle_binary_sensor.cpp +1 -1
  179. esphome/components/m5stack_8angle/sensor/m5stack_8angle_sensor.cpp +1 -1
  180. esphome/components/mapping/__init__.py +13 -5
  181. esphome/components/mapping/mapping.h +69 -0
  182. esphome/components/max17043/max17043.cpp +2 -2
  183. esphome/components/mcp23016/__init__.py +1 -0
  184. esphome/components/mcp23016/mcp23016.cpp +20 -5
  185. esphome/components/mcp23016/mcp23016.h +10 -4
  186. esphome/components/mcp23x08_base/mcp23x08_base.cpp +1 -1
  187. esphome/components/mcp23x17_base/mcp23x17_base.cpp +2 -2
  188. esphome/components/md5/md5.cpp +3 -2
  189. esphome/components/mdns/__init__.py +2 -2
  190. esphome/components/mdns/mdns_component.cpp +145 -54
  191. esphome/components/media_player/__init__.py +2 -2
  192. esphome/components/micro_wake_word/__init__.py +2 -2
  193. esphome/components/microphone/__init__.py +2 -2
  194. esphome/components/mipi/__init__.py +77 -33
  195. esphome/components/mipi_rgb/__init__.py +2 -0
  196. esphome/components/mipi_rgb/display.py +321 -0
  197. esphome/components/mipi_rgb/mipi_rgb.cpp +388 -0
  198. esphome/components/mipi_rgb/mipi_rgb.h +127 -0
  199. esphome/components/mipi_rgb/models/guition.py +24 -0
  200. esphome/components/mipi_rgb/models/lilygo.py +228 -0
  201. esphome/components/mipi_rgb/models/rpi.py +9 -0
  202. esphome/components/mipi_rgb/models/st7701s.py +214 -0
  203. esphome/components/mipi_rgb/models/waveshare.py +64 -0
  204. esphome/components/mipi_spi/models/jc.py +229 -0
  205. esphome/components/mlx90614/mlx90614.cpp +1 -16
  206. esphome/components/mlx90614/mlx90614.h +0 -1
  207. esphome/components/mqtt/__init__.py +2 -2
  208. esphome/components/mqtt/mqtt_client.cpp +1 -1
  209. esphome/components/mqtt/mqtt_sensor.cpp +7 -2
  210. esphome/components/ms5611/ms5611.cpp +7 -6
  211. esphome/components/network/__init__.py +2 -2
  212. esphome/components/nextion/nextion_upload.cpp +4 -1
  213. esphome/components/nrf52/__init__.py +49 -6
  214. esphome/components/nrf52/const.py +1 -0
  215. esphome/components/nrf52/dfu.cpp +51 -0
  216. esphome/components/nrf52/dfu.h +24 -0
  217. esphome/components/ntc/ntc.cpp +1 -1
  218. esphome/components/number/__init__.py +2 -2
  219. esphome/components/number/automation.cpp +1 -1
  220. esphome/components/number/number.cpp +21 -0
  221. esphome/components/number/number.h +4 -13
  222. esphome/components/opentherm/hub.h +6 -6
  223. esphome/components/opentherm/number/{number.cpp → opentherm_number.cpp} +2 -2
  224. esphome/components/opentherm/output/{output.cpp → opentherm_output.cpp} +1 -1
  225. esphome/components/opentherm/switch/{switch.cpp → opentherm_switch.cpp} +1 -1
  226. esphome/components/openthread/openthread.cpp +41 -7
  227. esphome/components/openthread/openthread.h +11 -0
  228. esphome/components/ota/__init__.py +2 -2
  229. esphome/components/pca6416a/__init__.py +1 -0
  230. esphome/components/pca6416a/pca6416a.cpp +20 -5
  231. esphome/components/pca6416a/pca6416a.h +12 -5
  232. esphome/components/pca9554/__init__.py +2 -1
  233. esphome/components/pca9554/pca9554.cpp +12 -18
  234. esphome/components/pca9554/pca9554.h +10 -9
  235. esphome/components/pcf8574/__init__.py +1 -0
  236. esphome/components/pcf8574/pcf8574.cpp +14 -5
  237. esphome/components/pcf8574/pcf8574.h +13 -6
  238. esphome/components/pi4ioe5v6408/pi4ioe5v6408.cpp +7 -7
  239. esphome/components/pipsolar/__init__.py +3 -3
  240. esphome/components/pipsolar/output/__init__.py +4 -4
  241. esphome/components/pulse_width/pulse_width.cpp +2 -2
  242. esphome/components/qmp6988/qmp6988.cpp +81 -126
  243. esphome/components/qmp6988/qmp6988.h +31 -37
  244. esphome/components/radon_eye_ble/__init__.py +2 -2
  245. esphome/components/remote_base/__init__.py +6 -8
  246. esphome/components/rotary_encoder/rotary_encoder.cpp +1 -1
  247. esphome/components/rp2040/__init__.py +2 -2
  248. esphome/components/runtime_stats/runtime_stats.cpp +10 -23
  249. esphome/components/runtime_stats/runtime_stats.h +4 -10
  250. esphome/components/safe_mode/__init__.py +2 -2
  251. esphome/components/safe_mode/safe_mode.cpp +33 -31
  252. esphome/components/script/script.cpp +6 -0
  253. esphome/components/script/script.h +19 -5
  254. esphome/components/sdm_meter/sensor.py +3 -1
  255. esphome/components/select/__init__.py +2 -2
  256. esphome/components/select/select.cpp +3 -3
  257. esphome/components/select/select.h +2 -2
  258. esphome/components/select/select_call.cpp +1 -1
  259. esphome/components/sen5x/sen5x.cpp +57 -55
  260. esphome/components/sen5x/sen5x.h +21 -15
  261. esphome/components/sen5x/sensor.py +67 -44
  262. esphome/components/sensirion_common/i2c_sensirion.cpp +18 -47
  263. esphome/components/sensirion_common/i2c_sensirion.h +39 -55
  264. esphome/components/sensor/__init__.py +2 -2
  265. esphome/components/sensor/automation.h +1 -1
  266. esphome/components/sensor/sensor.cpp +34 -6
  267. esphome/components/sensor/sensor.h +4 -21
  268. esphome/components/sgp30/sgp30.cpp +34 -35
  269. esphome/components/sgp30/sgp30.h +11 -10
  270. esphome/components/sgp4x/sgp4x.cpp +2 -2
  271. esphome/components/shelly_dimmer/light.py +7 -7
  272. esphome/components/sht4x/sht4x.cpp +1 -1
  273. esphome/components/sntp/sntp_component.cpp +36 -9
  274. esphome/components/sntp/sntp_component.h +7 -0
  275. esphome/components/sound_level/sound_level.cpp +1 -1
  276. esphome/components/speaker/__init__.py +2 -2
  277. esphome/components/speaker/media_player/__init__.py +2 -2
  278. esphome/components/speaker/media_player/speaker_media_player.cpp +1 -1
  279. esphome/components/spi/__init__.py +2 -2
  280. esphome/components/sprinkler/sprinkler.cpp +1 -1
  281. esphome/components/sps30/sps30.cpp +18 -23
  282. esphome/components/sps30/sps30.h +3 -3
  283. esphome/components/status_led/__init__.py +2 -2
  284. esphome/components/stepper/__init__.py +2 -2
  285. esphome/components/switch/__init__.py +2 -2
  286. esphome/components/switch/switch.cpp +5 -5
  287. esphome/components/sx1509/__init__.py +1 -1
  288. esphome/components/sx1509/sx1509.cpp +12 -7
  289. esphome/components/sx1509/sx1509.h +11 -4
  290. esphome/components/tca9555/tca9555.cpp +5 -5
  291. esphome/components/tee501/tee501.cpp +2 -21
  292. esphome/components/tee501/tee501.h +2 -4
  293. esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +1 -1
  294. esphome/components/template/datetime/template_date.cpp +1 -1
  295. esphome/components/template/datetime/template_datetime.cpp +2 -2
  296. esphome/components/template/datetime/template_time.cpp +1 -1
  297. esphome/components/template/number/template_number.cpp +1 -1
  298. esphome/components/template/select/template_select.cpp +1 -1
  299. esphome/components/template/text/template_text.cpp +1 -1
  300. esphome/components/text/__init__.py +2 -2
  301. esphome/components/text/text.h +2 -2
  302. esphome/components/text_sensor/__init__.py +2 -2
  303. esphome/components/text_sensor/text_sensor.h +4 -4
  304. esphome/components/thermostat/climate.py +11 -7
  305. esphome/components/thermostat/thermostat_climate.cpp +237 -206
  306. esphome/components/thermostat/thermostat_climate.h +52 -41
  307. esphome/components/time/__init__.py +2 -2
  308. esphome/components/tmp1075/tmp1075.cpp +1 -1
  309. esphome/components/total_daily_energy/total_daily_energy.cpp +1 -1
  310. esphome/components/touchscreen/__init__.py +2 -2
  311. esphome/components/tuya/number/tuya_number.cpp +1 -1
  312. esphome/components/udp/udp_component.cpp +3 -3
  313. esphome/components/ufire_ec/ufire_ec.cpp +4 -4
  314. esphome/components/ufire_ise/ufire_ise.cpp +4 -4
  315. esphome/components/update/__init__.py +2 -2
  316. esphome/components/usb_uart/usb_uart.cpp +1 -1
  317. esphome/components/valve/__init__.py +5 -5
  318. esphome/components/valve/valve.cpp +1 -1
  319. esphome/components/valve/valve.h +2 -2
  320. esphome/components/wake_on_lan/wake_on_lan.cpp +2 -2
  321. esphome/components/waveshare_epaper/waveshare_213v3.cpp +1 -1
  322. esphome/components/web_server/__init__.py +2 -2
  323. esphome/components/web_server/ota/__init__.py +2 -2
  324. esphome/components/web_server/ota/ota_web_server.cpp +11 -0
  325. esphome/components/web_server/server_index_v2.h +149 -149
  326. esphome/components/web_server/web_server.cpp +58 -12
  327. esphome/components/web_server_base/__init__.py +2 -2
  328. esphome/components/wifi/__init__.py +5 -5
  329. esphome/components/wifi/wifi_component.cpp +4 -4
  330. esphome/components/wifi/wifi_component_esp_idf.cpp +2 -0
  331. esphome/components/wifi_info/wifi_info_text_sensor.h +3 -2
  332. esphome/config_validation.py +2 -2
  333. esphome/const.py +3 -1
  334. esphome/core/__init__.py +1 -0
  335. esphome/core/application.cpp +89 -51
  336. esphome/core/application.h +1 -0
  337. esphome/core/component.cpp +41 -19
  338. esphome/core/component.h +17 -13
  339. esphome/core/config.py +7 -7
  340. esphome/core/defines.h +5 -0
  341. esphome/core/entity_base.cpp +22 -8
  342. esphome/core/entity_base.h +43 -0
  343. esphome/core/helpers.cpp +34 -20
  344. esphome/core/helpers.h +33 -3
  345. esphome/core/ring_buffer.cpp +6 -2
  346. esphome/core/ring_buffer.h +2 -1
  347. esphome/core/scheduler.cpp +178 -97
  348. esphome/core/scheduler.h +67 -36
  349. esphome/core/time.cpp +6 -20
  350. esphome/coroutine.py +80 -3
  351. esphome/cpp_generator.py +13 -0
  352. esphome/cpp_helpers.py +2 -2
  353. esphome/dashboard/web_server.py +69 -15
  354. esphome/espota2.py +13 -6
  355. esphome/helpers.py +68 -83
  356. esphome/resolver.py +67 -0
  357. esphome/util.py +9 -6
  358. esphome/wizard.py +81 -34
  359. esphome/writer.py +13 -0
  360. {esphome-2025.8.4.dist-info → esphome-2025.9.0.dist-info}/METADATA +9 -9
  361. {esphome-2025.8.4.dist-info → esphome-2025.9.0.dist-info}/RECORD +369 -338
  362. /esphome/components/ld2420/text_sensor/{text_sensor.h → ld2420_text_sensor.h} +0 -0
  363. /esphome/components/opentherm/number/{number.h → opentherm_number.h} +0 -0
  364. /esphome/components/opentherm/output/{output.h → opentherm_output.h} +0 -0
  365. /esphome/components/opentherm/switch/{switch.h → opentherm_switch.h} +0 -0
  366. {esphome-2025.8.4.dist-info → esphome-2025.9.0.dist-info}/WHEEL +0 -0
  367. {esphome-2025.8.4.dist-info → esphome-2025.9.0.dist-info}/entry_points.txt +0 -0
  368. {esphome-2025.8.4.dist-info → esphome-2025.9.0.dist-info}/licenses/LICENSE +0 -0
  369. {esphome-2025.8.4.dist-info → esphome-2025.9.0.dist-info}/top_level.txt +0 -0
esphome/coroutine.py CHANGED
@@ -42,7 +42,10 @@ Here everything is combined in `yield` expressions. You await other coroutines u
42
42
  the last `yield` expression defines what is returned.
43
43
  """
44
44
 
45
+ from __future__ import annotations
46
+
45
47
  from collections.abc import Awaitable, Callable, Generator, Iterator
48
+ import enum
46
49
  import functools
47
50
  import heapq
48
51
  import inspect
@@ -53,6 +56,79 @@ from typing import Any
53
56
  _LOGGER = logging.getLogger(__name__)
54
57
 
55
58
 
59
+ class CoroPriority(enum.IntEnum):
60
+ """Execution priority stages for ESPHome code generation.
61
+
62
+ Higher values run first. These stages ensure proper dependency
63
+ resolution during code generation.
64
+ """
65
+
66
+ # Platform initialization - must run first
67
+ # Examples: esp32, esp8266, rp2040
68
+ PLATFORM = 1000
69
+
70
+ # Network infrastructure setup
71
+ # Examples: network (201)
72
+ NETWORK = 201
73
+
74
+ # Network transport layer
75
+ # Examples: async_tcp (200)
76
+ NETWORK_TRANSPORT = 200
77
+
78
+ # Core system components
79
+ # Examples: esphome core, most entity base components (cover, update, datetime,
80
+ # valve, alarm_control_panel, lock, event, binary_sensor, button, climate, fan,
81
+ # light, media_player, number, select, sensor, switch, text_sensor, text),
82
+ # microphone, speaker, audio_dac, touchscreen, stepper
83
+ CORE = 100
84
+
85
+ # Diagnostic and debugging systems
86
+ # Examples: logger (90)
87
+ DIAGNOSTICS = 90
88
+
89
+ # Status and monitoring systems
90
+ # Examples: status_led (80)
91
+ STATUS = 80
92
+
93
+ # Communication protocols and services
94
+ # Examples: web_server_base (65), captive_portal (64), wifi (60), ethernet (60),
95
+ # mdns (55), ota_updates (54), web_server_ota (52)
96
+ COMMUNICATION = 60
97
+
98
+ # Application-level services
99
+ # Examples: safe_mode (50)
100
+ APPLICATION = 50
101
+
102
+ # Web and UI services
103
+ # Examples: web_server (40)
104
+ WEB = 40
105
+
106
+ # Automations and user logic
107
+ # Examples: esphome core automations (30)
108
+ AUTOMATION = 30
109
+
110
+ # Bus and peripheral setup
111
+ # Examples: i2c (1)
112
+ BUS = 1
113
+
114
+ # Standard component priority (default)
115
+ # Components without explicit priority run at 0
116
+ COMPONENT = 0
117
+
118
+ # Components that need others to be registered first
119
+ # Examples: globals (-100)
120
+ LATE = -100
121
+
122
+ # Platform-specific workarounds and fixes
123
+ # Examples: add_arduino_global_workaround (-999), esp8266 pin states (-999)
124
+ WORKAROUNDS = -999
125
+
126
+ # Final setup that requires all components to be registered
127
+ # Examples: add_includes, _add_platformio_options, _add_platform_defines (all -1000),
128
+ # esp32_ble_tracker feature defines (-1000)
129
+ FINAL = -1000
130
+
131
+
56
132
  def coroutine(func: Callable[..., Any]) -> Callable[..., Awaitable[Any]]:
57
133
  """Decorator to apply to methods to convert them to ESPHome coroutines."""
58
134
  if getattr(func, "_esphome_coroutine", False):
@@ -95,15 +171,16 @@ def coroutine(func: Callable[..., Any]) -> Callable[..., Awaitable[Any]]:
95
171
  return coro
96
172
 
97
173
 
98
- def coroutine_with_priority(priority: float):
174
+ def coroutine_with_priority(priority: float | CoroPriority):
99
175
  """Decorator to apply to functions to convert them to ESPHome coroutines.
100
176
 
101
177
  :param priority: priority with which to schedule the coroutine, higher priorities run first.
178
+ Can be a float or a CoroPriority enum value.
102
179
  """
103
180
 
104
181
  def decorator(func):
105
182
  coro = coroutine(func)
106
- coro.priority = priority
183
+ coro.priority = float(priority)
107
184
  return coro
108
185
 
109
186
  return decorator
@@ -173,7 +250,7 @@ class _Task:
173
250
  self.iterator = iterator
174
251
  self.original_function = original_function
175
252
 
176
- def with_priority(self, priority: float) -> "_Task":
253
+ def with_priority(self, priority: float) -> _Task:
177
254
  return _Task(priority, self.id_number, self.iterator, self.original_function)
178
255
 
179
256
  @property
esphome/cpp_generator.py CHANGED
@@ -253,6 +253,19 @@ class StringLiteral(Literal):
253
253
  return cpp_string_escape(self.string)
254
254
 
255
255
 
256
+ class LogStringLiteral(Literal):
257
+ """A string literal that uses LOG_STR() macro for flash storage on ESP8266."""
258
+
259
+ __slots__ = ("string",)
260
+
261
+ def __init__(self, string: str) -> None:
262
+ super().__init__()
263
+ self.string = string
264
+
265
+ def __str__(self) -> str:
266
+ return f"LOG_STR({cpp_string_escape(self.string)})"
267
+
268
+
256
269
  class IntLiteral(Literal):
257
270
  __slots__ = ("i",)
258
271
 
esphome/cpp_helpers.py CHANGED
@@ -9,7 +9,7 @@ from esphome.const import (
9
9
  )
10
10
  from esphome.core import CORE, ID, coroutine
11
11
  from esphome.coroutine import FakeAwaitable
12
- from esphome.cpp_generator import add, get_variable
12
+ from esphome.cpp_generator import LogStringLiteral, add, get_variable
13
13
  from esphome.cpp_types import App
14
14
  from esphome.types import ConfigFragmentType, ConfigType
15
15
  from esphome.util import Registry, RegistryEntry
@@ -76,7 +76,7 @@ async def register_component(var, config):
76
76
  "Error while finding name of component, please report this", exc_info=e
77
77
  )
78
78
  if name is not None:
79
- add(var.set_component_source(name))
79
+ add(var.set_component_source(LogStringLiteral(name)))
80
80
 
81
81
  add(App.register_component(var))
82
82
  return var
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import asyncio
4
4
  import base64
5
+ import binascii
5
6
  from collections.abc import Callable, Iterable
6
7
  import datetime
7
8
  import functools
@@ -490,7 +491,17 @@ class WizardRequestHandler(BaseHandler):
490
491
  kwargs = {
491
492
  k: v
492
493
  for k, v in json.loads(self.request.body.decode()).items()
493
- if k in ("name", "platform", "board", "ssid", "psk", "password")
494
+ if k
495
+ in (
496
+ "type",
497
+ "name",
498
+ "platform",
499
+ "board",
500
+ "ssid",
501
+ "psk",
502
+ "password",
503
+ "file_content",
504
+ )
494
505
  }
495
506
  if not kwargs["name"]:
496
507
  self.set_status(422)
@@ -498,19 +509,65 @@ class WizardRequestHandler(BaseHandler):
498
509
  self.write(json.dumps({"error": "Name is required"}))
499
510
  return
500
511
 
512
+ if "type" not in kwargs:
513
+ # Default to basic wizard type for backwards compatibility
514
+ kwargs["type"] = "basic"
515
+
501
516
  kwargs["friendly_name"] = kwargs["name"]
502
517
  kwargs["name"] = friendly_name_slugify(kwargs["friendly_name"])
503
-
504
- kwargs["ota_password"] = secrets.token_hex(16)
505
- noise_psk = secrets.token_bytes(32)
506
- kwargs["api_encryption_key"] = base64.b64encode(noise_psk).decode()
518
+ if kwargs["type"] == "basic":
519
+ kwargs["ota_password"] = secrets.token_hex(16)
520
+ noise_psk = secrets.token_bytes(32)
521
+ kwargs["api_encryption_key"] = base64.b64encode(noise_psk).decode()
522
+ elif kwargs["type"] == "upload":
523
+ try:
524
+ kwargs["file_text"] = base64.b64decode(kwargs["file_content"]).decode(
525
+ "utf-8"
526
+ )
527
+ except (binascii.Error, UnicodeDecodeError):
528
+ self.set_status(422)
529
+ self.set_header("content-type", "application/json")
530
+ self.write(
531
+ json.dumps({"error": "The uploaded file is not correctly encoded."})
532
+ )
533
+ return
534
+ elif kwargs["type"] != "empty":
535
+ self.set_status(422)
536
+ self.set_header("content-type", "application/json")
537
+ self.write(
538
+ json.dumps(
539
+ {"error": f"Invalid wizard type specified: {kwargs['type']}"}
540
+ )
541
+ )
542
+ return
507
543
  filename = f"{kwargs['name']}.yaml"
508
544
  destination = settings.rel_path(filename)
509
- wizard.wizard_write(path=destination, **kwargs)
510
- self.set_status(200)
511
- self.set_header("content-type", "application/json")
512
- self.write(json.dumps({"configuration": filename}))
513
- self.finish()
545
+
546
+ # Check if destination file already exists
547
+ if os.path.exists(destination):
548
+ self.set_status(409) # Conflict status code
549
+ self.set_header("content-type", "application/json")
550
+ self.write(
551
+ json.dumps({"error": f"Configuration file '{filename}' already exists"})
552
+ )
553
+ self.finish()
554
+ return
555
+
556
+ success = wizard.wizard_write(path=destination, **kwargs)
557
+ if success:
558
+ self.set_status(200)
559
+ self.set_header("content-type", "application/json")
560
+ self.write(json.dumps({"configuration": filename}))
561
+ self.finish()
562
+ else:
563
+ self.set_status(500)
564
+ self.set_header("content-type", "application/json")
565
+ self.write(
566
+ json.dumps(
567
+ {"error": "Failed to write configuration, see logs for details"}
568
+ )
569
+ )
570
+ self.finish()
514
571
 
515
572
 
516
573
  class ImportRequestHandler(BaseHandler):
@@ -981,12 +1038,9 @@ class ArchiveRequestHandler(BaseHandler):
981
1038
  shutil.move(config_file, os.path.join(archive_path, configuration))
982
1039
 
983
1040
  storage_json = StorageJSON.load(storage_path)
984
- if storage_json is not None:
1041
+ if storage_json is not None and storage_json.build_path:
985
1042
  # Delete build folder (if exists)
986
- name = storage_json.name
987
- build_folder = os.path.join(settings.config_dir, name)
988
- if build_folder is not None:
989
- shutil.rmtree(build_folder, os.path.join(archive_path, name))
1043
+ shutil.rmtree(storage_json.build_path, ignore_errors=True)
990
1044
 
991
1045
 
992
1046
  class UnArchiveRequestHandler(BaseHandler):
esphome/espota2.py CHANGED
@@ -308,8 +308,12 @@ def perform_ota(
308
308
  time.sleep(1)
309
309
 
310
310
 
311
- def run_ota_impl_(remote_host, remote_port, password, filename):
311
+ def run_ota_impl_(
312
+ remote_host: str | list[str], remote_port: int, password: str, filename: str
313
+ ) -> tuple[int, str | None]:
314
+ # Handle both single host and list of hosts
312
315
  try:
316
+ # Resolve all hosts at once for parallel DNS resolution
313
317
  res = resolve_ip_address(remote_host, remote_port)
314
318
  except EsphomeError as err:
315
319
  _LOGGER.error(
@@ -340,19 +344,22 @@ def run_ota_impl_(remote_host, remote_port, password, filename):
340
344
  perform_ota(sock, password, file_handle, filename)
341
345
  except OTAError as err:
342
346
  _LOGGER.error(str(err))
343
- return 1
347
+ return 1, None
344
348
  finally:
345
349
  sock.close()
346
350
 
347
- return 0
351
+ # Successfully uploaded to sa[0]
352
+ return 0, sa[0]
348
353
 
349
354
  _LOGGER.error("Connection failed.")
350
- return 1
355
+ return 1, None
351
356
 
352
357
 
353
- def run_ota(remote_host, remote_port, password, filename):
358
+ def run_ota(
359
+ remote_host: str | list[str], remote_port: int, password: str, filename: str
360
+ ) -> tuple[int, str | None]:
354
361
  try:
355
362
  return run_ota_impl_(remote_host, remote_port, password, filename)
356
363
  except OTAError as err:
357
364
  _LOGGER.error(err)
358
- return 1
365
+ return 1, None
esphome/helpers.py CHANGED
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import codecs
2
4
  from contextlib import suppress
3
5
  import ipaddress
@@ -11,6 +13,18 @@ from urllib.parse import urlparse
11
13
 
12
14
  from esphome.const import __version__ as ESPHOME_VERSION
13
15
 
16
+ # Type aliases for socket address information
17
+ AddrInfo = tuple[
18
+ int, # family (AF_INET, AF_INET6, etc.)
19
+ int, # type (SOCK_STREAM, SOCK_DGRAM, etc.)
20
+ int, # proto (IPPROTO_TCP, etc.)
21
+ str, # canonname
22
+ tuple[str, int] | tuple[str, int, int, int], # sockaddr (IPv4 or IPv6)
23
+ ]
24
+ IPv4SockAddr = tuple[str, int] # (host, port)
25
+ IPv6SockAddr = tuple[str, int, int, int] # (host, port, flowinfo, scope_id)
26
+ SockAddr = IPv4SockAddr | IPv6SockAddr
27
+
14
28
  _LOGGER = logging.getLogger(__name__)
15
29
 
16
30
  IS_MACOS = platform.system() == "Darwin"
@@ -147,32 +161,7 @@ def is_ip_address(host):
147
161
  return False
148
162
 
149
163
 
150
- def _resolve_with_zeroconf(host):
151
- from esphome.core import EsphomeError
152
- from esphome.zeroconf import EsphomeZeroconf
153
-
154
- try:
155
- zc = EsphomeZeroconf()
156
- except Exception as err:
157
- raise EsphomeError(
158
- "Cannot start mDNS sockets, is this a docker container without "
159
- "host network mode?"
160
- ) from err
161
- try:
162
- info = zc.resolve_host(f"{host}.")
163
- except Exception as err:
164
- raise EsphomeError(f"Error resolving mDNS hostname: {err}") from err
165
- finally:
166
- zc.close()
167
- if info is None:
168
- raise EsphomeError(
169
- "Error resolving address with mDNS: Did not respond. "
170
- "Maybe the device is offline."
171
- )
172
- return info
173
-
174
-
175
- def addr_preference_(res):
164
+ def addr_preference_(res: AddrInfo) -> int:
176
165
  # Trivial alternative to RFC6724 sorting. Put sane IPv6 first, then
177
166
  # Legacy IP, then IPv6 link-local addresses without an actual link.
178
167
  sa = res[4]
@@ -184,66 +173,70 @@ def addr_preference_(res):
184
173
  return 1
185
174
 
186
175
 
187
- def resolve_ip_address(host, port):
176
+ def resolve_ip_address(host: str | list[str], port: int) -> list[AddrInfo]:
188
177
  import socket
189
178
 
190
- from esphome.core import EsphomeError
191
-
192
179
  # There are five cases here. The host argument could be one of:
193
180
  # • a *list* of IP addresses discovered by MQTT,
194
181
  # • a single IP address specified by the user,
195
182
  # • a .local hostname to be resolved by mDNS,
196
183
  # • a normal hostname to be resolved in DNS, or
197
184
  # • A URL from which we should extract the hostname.
198
- #
199
- # In each of the first three cases, we end up with IP addresses in
200
- # string form which need to be converted to a 5-tuple to be used
201
- # for the socket connection attempt. The easiest way to construct
202
- # those is to pass the IP address string to getaddrinfo(). Which,
203
- # coincidentally, is how we do hostname lookups in the other cases
204
- # too. So first build a list which contains either IP addresses or
205
- # a single hostname, then call getaddrinfo() on each element of
206
- # that list.
207
-
208
- errs = []
185
+
186
+ hosts: list[str]
209
187
  if isinstance(host, list):
210
- addr_list = host
211
- elif is_ip_address(host):
212
- addr_list = [host]
188
+ hosts = host
213
189
  else:
214
- url = urlparse(host)
215
- if url.scheme != "":
216
- host = url.hostname
217
-
218
- addr_list = []
219
- if host.endswith(".local"):
190
+ if not is_ip_address(host):
191
+ url = urlparse(host)
192
+ if url.scheme != "":
193
+ host = url.hostname
194
+ hosts = [host]
195
+
196
+ res: list[AddrInfo] = []
197
+ if all(is_ip_address(h) for h in hosts):
198
+ # Fast path: all are IP addresses, use socket.getaddrinfo with AI_NUMERICHOST
199
+ for addr in hosts:
220
200
  try:
221
- _LOGGER.info("Resolving IP address of %s in mDNS", host)
222
- addr_list = _resolve_with_zeroconf(host)
223
- except EsphomeError as err:
224
- errs.append(str(err))
225
-
226
- # If not mDNS, or if mDNS failed, use normal DNS
227
- if not addr_list:
228
- addr_list = [host]
229
-
230
- # Now we have a list containing either IP addresses or a hostname
231
- res = []
232
- for addr in addr_list:
233
- if not is_ip_address(addr):
234
- _LOGGER.info("Resolving IP address of %s", host)
235
- try:
236
- r = socket.getaddrinfo(addr, port, proto=socket.IPPROTO_TCP)
237
- except OSError as err:
238
- errs.append(str(err))
239
- raise EsphomeError(
240
- f"Error resolving IP address: {', '.join(errs)}"
241
- ) from err
242
-
243
- res = res + r
201
+ res += socket.getaddrinfo(
202
+ addr, port, proto=socket.IPPROTO_TCP, flags=socket.AI_NUMERICHOST
203
+ )
204
+ except OSError:
205
+ _LOGGER.debug("Failed to parse IP address '%s'", addr)
206
+ # Sort by preference
207
+ res.sort(key=addr_preference_)
208
+ return res
209
+
210
+ from esphome.resolver import AsyncResolver
211
+
212
+ resolver = AsyncResolver(hosts, port)
213
+ addr_infos = resolver.resolve()
214
+ # Convert aioesphomeapi AddrInfo to our format
215
+ for addr_info in addr_infos:
216
+ sockaddr = addr_info.sockaddr
217
+ if addr_info.family == socket.AF_INET6:
218
+ # IPv6
219
+ sockaddr_tuple = (
220
+ sockaddr.address,
221
+ sockaddr.port,
222
+ sockaddr.flowinfo,
223
+ sockaddr.scope_id,
224
+ )
225
+ else:
226
+ # IPv4
227
+ sockaddr_tuple = (sockaddr.address, sockaddr.port)
228
+
229
+ res.append(
230
+ (
231
+ addr_info.family,
232
+ addr_info.type,
233
+ addr_info.proto,
234
+ "", # canonname
235
+ sockaddr_tuple,
236
+ )
237
+ )
244
238
 
245
- # Zeroconf tends to give us link-local IPv6 addresses without specifying
246
- # the link. Put those last in the list to be attempted.
239
+ # Sort by preference
247
240
  res.sort(key=addr_preference_)
248
241
  return res
249
242
 
@@ -262,15 +255,7 @@ def sort_ip_addresses(address_list: list[str]) -> list[str]:
262
255
 
263
256
  # First "resolve" all the IP addresses to getaddrinfo() tuples of the form
264
257
  # (family, type, proto, canonname, sockaddr)
265
- res: list[
266
- tuple[
267
- int,
268
- int,
269
- int,
270
- str | None,
271
- tuple[str, int] | tuple[str, int, int, int],
272
- ]
273
- ] = []
258
+ res: list[AddrInfo] = []
274
259
  for addr in address_list:
275
260
  # This should always work as these are supposed to be IP addresses
276
261
  try:
esphome/resolver.py ADDED
@@ -0,0 +1,67 @@
1
+ """DNS resolver for ESPHome using aioesphomeapi."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import threading
7
+
8
+ from aioesphomeapi.core import ResolveAPIError, ResolveTimeoutAPIError
9
+ import aioesphomeapi.host_resolver as hr
10
+
11
+ from esphome.core import EsphomeError
12
+
13
+ RESOLVE_TIMEOUT = 10.0 # seconds
14
+
15
+
16
+ class AsyncResolver(threading.Thread):
17
+ """Resolver using aioesphomeapi that runs in a thread for faster results.
18
+
19
+ This resolver uses aioesphomeapi's async_resolve_host to handle DNS resolution,
20
+ including proper .local domain fallback. Running in a thread allows us to get
21
+ the result immediately without waiting for asyncio.run() to complete its
22
+ cleanup cycle, which can take significant time.
23
+ """
24
+
25
+ def __init__(self, hosts: list[str], port: int) -> None:
26
+ """Initialize the resolver."""
27
+ super().__init__(daemon=True)
28
+ self.hosts = hosts
29
+ self.port = port
30
+ self.result: list[hr.AddrInfo] | None = None
31
+ self.exception: Exception | None = None
32
+ self.event = threading.Event()
33
+
34
+ async def _resolve(self) -> None:
35
+ """Resolve hostnames to IP addresses."""
36
+ try:
37
+ self.result = await hr.async_resolve_host(
38
+ self.hosts, self.port, timeout=RESOLVE_TIMEOUT
39
+ )
40
+ except Exception as e: # pylint: disable=broad-except
41
+ # We need to catch all exceptions to ensure the event is set
42
+ # Otherwise the thread could hang forever
43
+ self.exception = e
44
+ finally:
45
+ self.event.set()
46
+
47
+ def run(self) -> None:
48
+ """Run the DNS resolution."""
49
+ asyncio.run(self._resolve())
50
+
51
+ def resolve(self) -> list[hr.AddrInfo]:
52
+ """Start the thread and wait for the result."""
53
+ self.start()
54
+
55
+ if not self.event.wait(
56
+ timeout=RESOLVE_TIMEOUT + 1.0
57
+ ): # Give it 1 second more than the resolver timeout
58
+ raise EsphomeError("Timeout resolving IP address")
59
+
60
+ if exc := self.exception:
61
+ if isinstance(exc, ResolveTimeoutAPIError):
62
+ raise EsphomeError(f"Timeout resolving IP address: {exc}") from exc
63
+ if isinstance(exc, ResolveAPIError):
64
+ raise EsphomeError(f"Error resolving IP address: {exc}") from exc
65
+ raise exc
66
+
67
+ return self.result
esphome/util.py CHANGED
@@ -272,12 +272,15 @@ class OrderedDict(collections.OrderedDict):
272
272
  return dict(self).__repr__()
273
273
 
274
274
 
275
- def list_yaml_files(folders: list[str]) -> list[str]:
276
- files = filter_yaml_files(
277
- [os.path.join(folder, p) for folder in folders for p in os.listdir(folder)]
278
- )
279
- files.sort()
280
- return files
275
+ def list_yaml_files(configs: list[str]) -> list[str]:
276
+ files: list[str] = []
277
+ for config in configs:
278
+ if os.path.isfile(config):
279
+ files.append(config)
280
+ else:
281
+ files.extend(os.path.join(config, p) for p in os.listdir(config))
282
+ files = filter_yaml_files(files)
283
+ return sorted(files)
281
284
 
282
285
 
283
286
  def filter_yaml_files(files: list[str]) -> list[str]: