esphome 2025.7.5__py3-none-any.whl → 2025.8.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 (756) hide show
  1. esphome/__main__.py +190 -83
  2. esphome/automation.py +2 -4
  3. esphome/build_gen/__init__.py +0 -0
  4. esphome/build_gen/platformio.py +102 -0
  5. esphome/components/a4988/a4988.cpp +0 -1
  6. esphome/components/absolute_humidity/absolute_humidity.cpp +0 -2
  7. esphome/components/absolute_humidity/sensor.py +2 -2
  8. esphome/components/adc/__init__.py +123 -85
  9. esphome/components/adc/adc_sensor.h +98 -35
  10. esphome/components/adc/adc_sensor_common.cpp +10 -4
  11. esphome/components/adc/adc_sensor_esp32.cpp +291 -123
  12. esphome/components/adc/adc_sensor_esp8266.cpp +1 -4
  13. esphome/components/adc/adc_sensor_libretiny.cpp +1 -2
  14. esphome/components/adc/adc_sensor_rp2040.cpp +1 -2
  15. esphome/components/adc/adc_sensor_zephyr.cpp +207 -0
  16. esphome/components/adc/sensor.py +61 -27
  17. esphome/components/adc128s102/adc128s102.cpp +1 -4
  18. esphome/components/ade7880/sensor.py +75 -49
  19. esphome/components/ads1115/ads1115.cpp +0 -1
  20. esphome/components/ads1118/ads1118.cpp +0 -1
  21. esphome/components/ags10/ags10.cpp +0 -4
  22. esphome/components/aht10/aht10.cpp +0 -4
  23. esphome/components/aic3204/aic3204.cpp +0 -2
  24. esphome/components/airthings_wave_plus/__init__.py +1 -1
  25. esphome/components/airthings_wave_plus/airthings_wave_plus.cpp +22 -4
  26. esphome/components/airthings_wave_plus/airthings_wave_plus.h +10 -1
  27. esphome/components/airthings_wave_plus/sensor.py +55 -28
  28. esphome/components/alarm_control_panel/__init__.py +4 -9
  29. esphome/components/am2315c/am2315c.cpp +0 -2
  30. esphome/components/am2320/am2320.cpp +0 -1
  31. esphome/components/animation/__init__.py +14 -11
  32. esphome/components/apds9306/apds9306.cpp +0 -4
  33. esphome/components/apds9960/apds9960.cpp +0 -1
  34. esphome/components/api/__init__.py +29 -4
  35. esphome/components/api/api_connection.cpp +378 -401
  36. esphome/components/api/api_connection.h +102 -52
  37. esphome/components/api/api_frame_helper.cpp +69 -896
  38. esphome/components/api/api_frame_helper.h +31 -126
  39. esphome/components/api/api_frame_helper_noise.cpp +583 -0
  40. esphome/components/api/api_frame_helper_noise.h +68 -0
  41. esphome/components/api/api_frame_helper_plaintext.cpp +290 -0
  42. esphome/components/api/api_frame_helper_plaintext.h +53 -0
  43. esphome/components/api/api_noise_context.h +2 -4
  44. esphome/components/api/api_pb2.cpp +1601 -1808
  45. esphome/components/api/api_pb2.h +367 -323
  46. esphome/components/api/api_pb2_dump.cpp +1137 -3466
  47. esphome/components/api/api_pb2_includes.h +34 -0
  48. esphome/components/api/api_pb2_service.cpp +94 -105
  49. esphome/components/api/api_pb2_service.h +27 -16
  50. esphome/components/api/api_server.cpp +18 -17
  51. esphome/components/api/api_server.h +8 -5
  52. esphome/components/api/client.py +16 -8
  53. esphome/components/api/custom_api_device.h +68 -14
  54. esphome/components/api/homeassistant_service.h +24 -19
  55. esphome/components/api/list_entities.cpp +3 -5
  56. esphome/components/api/list_entities.h +2 -4
  57. esphome/components/api/proto.cpp +3 -5
  58. esphome/components/api/proto.h +239 -274
  59. esphome/components/api/subscribe_state.cpp +2 -4
  60. esphome/components/api/subscribe_state.h +2 -4
  61. esphome/components/api/user_services.cpp +2 -4
  62. esphome/components/api/user_services.h +8 -8
  63. esphome/components/as3935/as3935.cpp +0 -2
  64. esphome/components/as3935_spi/as3935_spi.cpp +0 -2
  65. esphome/components/as5600/__init__.py +1 -1
  66. esphome/components/as5600/as5600.cpp +0 -2
  67. esphome/components/as5600/sensor/__init__.py +0 -1
  68. esphome/components/as7341/as7341.cpp +0 -1
  69. esphome/components/async_tcp/__init__.py +1 -1
  70. esphome/components/at581x/at581x.cpp +1 -1
  71. esphome/components/atm90e26/atm90e26.cpp +0 -1
  72. esphome/components/atm90e32/atm90e32.cpp +475 -116
  73. esphome/components/atm90e32/atm90e32.h +43 -5
  74. esphome/components/audio/audio.h +2 -2
  75. esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp +0 -2
  76. esphome/components/beken_spi_led_strip/led_strip.cpp +0 -2
  77. esphome/components/bh1750/bh1750.cpp +0 -1
  78. esphome/components/binary_sensor/__init__.py +14 -12
  79. esphome/components/ble_client/__init__.py +4 -7
  80. esphome/components/bluetooth_proxy/__init__.py +40 -3
  81. esphome/components/bluetooth_proxy/bluetooth_connection.cpp +392 -81
  82. esphome/components/bluetooth_proxy/bluetooth_connection.h +16 -5
  83. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +102 -311
  84. esphome/components/bluetooth_proxy/bluetooth_proxy.h +30 -14
  85. esphome/components/bme280_base/bme280_base.cpp +15 -16
  86. esphome/components/bme680/bme680.cpp +2 -3
  87. esphome/components/bme680_bsec/bme680_bsec.cpp +0 -2
  88. esphome/components/bme68x_bsec2/bme68x_bsec2.cpp +0 -2
  89. esphome/components/bmi160/bmi160.cpp +0 -1
  90. esphome/components/bmp085/bmp085.cpp +0 -1
  91. esphome/components/bmp280_base/bmp280_base.cpp +13 -14
  92. esphome/components/bmp3xx_base/bmp3xx_base.cpp +0 -1
  93. esphome/components/bmp581/bmp581.cpp +0 -2
  94. esphome/components/bp1658cj/bp1658cj.cpp +0 -1
  95. esphome/components/bp5758d/bp5758d.cpp +0 -1
  96. esphome/components/button/__init__.py +0 -1
  97. esphome/components/canbus/__init__.py +2 -3
  98. esphome/components/canbus/canbus.cpp +0 -1
  99. esphome/components/cap1188/cap1188.cpp +0 -2
  100. esphome/components/captive_portal/__init__.py +1 -1
  101. esphome/components/cd74hc4067/cd74hc4067.cpp +0 -2
  102. esphome/components/ch422g/ch422g.cpp +0 -1
  103. esphome/components/chsc6x/chsc6x_touchscreen.cpp +0 -3
  104. esphome/components/climate/__init__.py +0 -1
  105. esphome/components/climate/climate_traits.h +24 -0
  106. esphome/components/cm1106/cm1106.cpp +0 -1
  107. esphome/components/const/__init__.py +6 -0
  108. esphome/components/cover/__init__.py +0 -1
  109. esphome/components/cover/cover.cpp +9 -13
  110. esphome/components/cs5460a/cs5460a.cpp +0 -2
  111. esphome/components/cse7761/cse7761.cpp +0 -1
  112. esphome/components/cst226/touchscreen/cst226_touchscreen.cpp +0 -2
  113. esphome/components/cst816/touchscreen/cst816_touchscreen.cpp +0 -2
  114. esphome/components/dac7678/dac7678_output.cpp +0 -2
  115. esphome/components/dallas_temp/dallas_temp.cpp +0 -1
  116. esphome/components/datetime/__init__.py +0 -2
  117. esphome/components/debug/__init__.py +15 -1
  118. esphome/components/debug/debug_zephyr.cpp +281 -0
  119. esphome/components/debug/sensor.py +2 -1
  120. esphome/components/deep_sleep/deep_sleep_component.cpp +0 -1
  121. esphome/components/deep_sleep/deep_sleep_esp32.cpp +20 -1
  122. esphome/components/dfrobot_sen0395/__init__.py +1 -2
  123. esphome/components/dht/dht.cpp +0 -1
  124. esphome/components/dht12/dht12.cpp +0 -1
  125. esphome/components/display/__init__.py +16 -3
  126. esphome/components/display_menu_base/__init__.py +1 -1
  127. esphome/components/dps310/dps310.cpp +0 -2
  128. esphome/components/ds1307/ds1307.cpp +0 -1
  129. esphome/components/ds2484/ds2484.cpp +0 -1
  130. esphome/components/duty_cycle/duty_cycle_sensor.cpp +0 -1
  131. esphome/components/ee895/ee895.cpp +0 -1
  132. esphome/components/ektf2232/touchscreen/ektf2232.cpp +0 -1
  133. esphome/components/emc2101/emc2101.cpp +0 -2
  134. esphome/components/ens160_base/ens160_base.cpp +0 -2
  135. esphome/components/ens210/ens210.cpp +0 -1
  136. esphome/components/es7210/es7210.cpp +0 -2
  137. esphome/components/es7243e/es7243e.cpp +0 -2
  138. esphome/components/es8156/es8156.cpp +0 -2
  139. esphome/components/es8311/es8311.cpp +0 -2
  140. esphome/components/es8388/es8388.cpp +0 -2
  141. esphome/components/esp32/__init__.py +203 -60
  142. esphome/components/esp32/boards.py +17 -0
  143. esphome/components/esp32/gpio.cpp +0 -1
  144. esphome/components/esp32/gpio.py +1 -2
  145. esphome/components/esp32/gpio_esp32_h2.py +2 -7
  146. esphome/components/esp32/gpio_esp32_p4.py +2 -7
  147. esphome/components/esp32/post_build.py.script +112 -61
  148. esphome/components/esp32_ble/__init__.py +41 -2
  149. esphome/components/esp32_ble/ble.cpp +14 -10
  150. esphome/components/esp32_ble/ble.h +18 -18
  151. esphome/components/esp32_ble/ble_advertising.cpp +5 -5
  152. esphome/components/esp32_ble/ble_advertising.h +7 -5
  153. esphome/components/esp32_ble/ble_event.h +139 -73
  154. esphome/components/esp32_ble/ble_scan_result.h +2 -4
  155. esphome/components/esp32_ble/ble_uuid.cpp +5 -5
  156. esphome/components/esp32_ble/ble_uuid.h +6 -5
  157. esphome/components/esp32_ble_beacon/__init__.py +4 -0
  158. esphome/components/esp32_ble_client/__init__.py +1 -1
  159. esphome/components/esp32_ble_client/ble_characteristic.cpp +4 -4
  160. esphome/components/esp32_ble_client/ble_characteristic.h +6 -4
  161. esphome/components/esp32_ble_client/ble_client_base.cpp +155 -104
  162. esphome/components/esp32_ble_client/ble_client_base.h +17 -6
  163. esphome/components/esp32_ble_client/ble_descriptor.h +6 -4
  164. esphome/components/esp32_ble_client/ble_service.cpp +4 -4
  165. esphome/components/esp32_ble_client/ble_service.h +6 -4
  166. esphome/components/esp32_ble_server/__init__.py +15 -12
  167. esphome/components/esp32_ble_tracker/__init__.py +79 -11
  168. esphome/components/esp32_ble_tracker/automation.h +4 -4
  169. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +264 -261
  170. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +103 -37
  171. esphome/components/esp32_camera/__init__.py +13 -1
  172. esphome/components/esp32_camera/esp32_camera.cpp +7 -2
  173. esphome/components/esp32_camera/esp32_camera.h +1 -0
  174. esphome/components/esp32_dac/esp32_dac.cpp +3 -19
  175. esphome/components/esp32_dac/esp32_dac.h +4 -8
  176. esphome/components/esp32_rmt_led_strip/led_strip.cpp +1 -6
  177. esphome/components/esp32_rmt_led_strip/light.py +1 -1
  178. esphome/components/esp32_touch/__init__.py +2 -3
  179. esphome/components/esp32_touch/esp32_touch.h +9 -6
  180. esphome/components/esp32_touch/esp32_touch_common.cpp +2 -0
  181. esphome/components/esp32_touch/esp32_touch_v1.cpp +7 -9
  182. esphome/components/esp32_touch/esp32_touch_v2.cpp +10 -6
  183. esphome/components/esp8266/__init__.py +3 -1
  184. esphome/components/esp8266_pwm/esp8266_pwm.cpp +0 -1
  185. esphome/components/esphome/ota/__init__.py +1 -2
  186. esphome/components/esphome/ota/ota_esphome.cpp +150 -77
  187. esphome/components/esphome/ota/ota_esphome.h +8 -1
  188. esphome/components/espnow/__init__.py +309 -0
  189. esphome/components/espnow/automation.h +175 -0
  190. esphome/components/espnow/espnow_component.cpp +468 -0
  191. esphome/components/espnow/espnow_component.h +183 -0
  192. esphome/components/espnow/espnow_err.h +19 -0
  193. esphome/components/espnow/espnow_packet.h +166 -0
  194. esphome/components/ethernet/__init__.py +7 -1
  195. esphome/components/ethernet/esp_eth_phy_jl1101.c +5 -0
  196. esphome/components/ethernet/ethernet_component.cpp +0 -1
  197. esphome/components/ethernet/ethernet_component.h +4 -0
  198. esphome/components/ethernet_info/ethernet_info_text_sensor.h +0 -3
  199. esphome/components/event/__init__.py +0 -1
  200. esphome/components/factory_reset/__init__.py +92 -0
  201. esphome/components/factory_reset/factory_reset.cpp +76 -0
  202. esphome/components/factory_reset/factory_reset.h +43 -0
  203. esphome/components/fan/__init__.py +0 -1
  204. esphome/components/fan/fan_traits.h +16 -0
  205. esphome/components/fastled_base/fastled_light.cpp +0 -1
  206. esphome/components/fastled_spi/light.py +1 -3
  207. esphome/components/fingerprint_grow/fingerprint_grow.cpp +0 -2
  208. esphome/components/fs3000/fs3000.cpp +0 -2
  209. esphome/components/ft5x06/touchscreen/ft5x06_touchscreen.cpp +0 -2
  210. esphome/components/ft63x6/ft63x6.cpp +0 -1
  211. esphome/components/gdk101/gdk101.cpp +0 -1
  212. esphome/components/gl_r01_i2c/gl_r01_i2c.cpp +0 -1
  213. esphome/components/gl_r01_i2c/sensor.py +1 -1
  214. esphome/components/gpio/one_wire/gpio_one_wire.cpp +0 -1
  215. esphome/components/gpio/switch/gpio_switch.cpp +0 -2
  216. esphome/components/gpio_expander/cached_gpio.h +24 -15
  217. esphome/components/gps/__init__.py +6 -2
  218. esphome/components/gps/gps.cpp +50 -49
  219. esphome/components/gps/gps.h +4 -8
  220. esphome/components/gps/time/gps_time.cpp +3 -9
  221. esphome/components/gps/time/gps_time.h +4 -7
  222. esphome/components/graph/__init__.py +1 -1
  223. esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.cpp +0 -1
  224. esphome/components/grove_tb6612fng/grove_tb6612fng.cpp +0 -1
  225. esphome/components/gt911/touchscreen/gt911_touchscreen.cpp +21 -12
  226. esphome/components/gt911/touchscreen/gt911_touchscreen.h +26 -2
  227. esphome/components/haier/climate.py +5 -10
  228. esphome/components/haier/haier_base.cpp +0 -1
  229. esphome/components/hbridge/switch/hbridge_switch.cpp +0 -2
  230. esphome/components/hdc1080/hdc1080.cpp +0 -2
  231. esphome/components/heatpumpir/climate.py +2 -2
  232. esphome/components/hlw8012/hlw8012.cpp +0 -1
  233. esphome/components/hm3301/hm3301.cpp +0 -1
  234. esphome/components/hmc5883l/hmc5883l.cpp +0 -1
  235. esphome/components/homeassistant/__init__.py +1 -0
  236. esphome/components/homeassistant/number/__init__.py +1 -0
  237. esphome/components/homeassistant/number/homeassistant_number.cpp +11 -7
  238. esphome/components/homeassistant/switch/__init__.py +1 -0
  239. esphome/components/homeassistant/switch/homeassistant_switch.cpp +9 -5
  240. esphome/components/honeywellabp/honeywellabp.cpp +1 -4
  241. esphome/components/host/__init__.py +2 -0
  242. esphome/components/hte501/hte501.cpp +0 -1
  243. esphome/components/http_request/__init__.py +2 -3
  244. esphome/components/http_request/http_request_idf.cpp +2 -2
  245. esphome/components/htu21d/htu21d.cpp +0 -2
  246. esphome/components/htu31d/htu31d.cpp +0 -2
  247. esphome/components/hx711/hx711.cpp +0 -1
  248. esphome/components/hydreon_rgxx/hydreon_rgxx.cpp +0 -1
  249. esphome/components/hydreon_rgxx/sensor.py +4 -5
  250. esphome/components/i2c/i2c_bus.h +1 -1
  251. esphome/components/i2c/i2c_bus_arduino.cpp +1 -2
  252. esphome/components/i2c/i2c_bus_esp_idf.cpp +192 -17
  253. esphome/components/i2c/i2c_bus_esp_idf.h +11 -1
  254. esphome/components/i2s_audio/__init__.py +6 -5
  255. esphome/components/i2s_audio/i2s_audio.cpp +0 -2
  256. esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp +1 -4
  257. esphome/components/i2s_audio/microphone/__init__.py +4 -6
  258. esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +0 -1
  259. esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +2 -2
  260. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +273 -269
  261. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +19 -34
  262. esphome/components/ili9xxx/display.py +4 -3
  263. esphome/components/ili9xxx/ili9xxx_display.cpp +0 -2
  264. esphome/components/image/__init__.py +123 -92
  265. esphome/components/improv_serial/__init__.py +7 -8
  266. esphome/components/ina219/ina219.cpp +0 -1
  267. esphome/components/ina226/ina226.cpp +0 -2
  268. esphome/components/ina260/ina260.cpp +0 -2
  269. esphome/components/ina2xx_base/__init__.py +2 -5
  270. esphome/components/ina2xx_base/ina2xx_base.cpp +0 -2
  271. esphome/components/ina3221/ina3221.cpp +0 -1
  272. esphome/components/internal_temperature/internal_temperature.cpp +0 -2
  273. esphome/components/interval/interval.h +5 -9
  274. esphome/components/json/__init__.py +1 -1
  275. esphome/components/kmeteriso/kmeteriso.cpp +0 -2
  276. esphome/components/lc709203f/lc709203f.cpp +0 -2
  277. esphome/components/lcd_gpio/display.py +1 -3
  278. esphome/components/lcd_gpio/gpio_lcd_display.cpp +0 -1
  279. esphome/components/lcd_pcf8574/pcf8574_display.cpp +0 -1
  280. esphome/components/ld2410/__init__.py +4 -6
  281. esphome/components/ld2410/binary_sensor.py +4 -0
  282. esphome/components/ld2410/ld2410.cpp +56 -100
  283. esphome/components/ld2410/ld2410.h +17 -15
  284. esphome/components/ld2410/sensor.py +24 -10
  285. esphome/components/ld2412/__init__.py +46 -0
  286. esphome/components/ld2412/binary_sensor.py +70 -0
  287. esphome/components/ld2412/button/__init__.py +74 -0
  288. esphome/components/ld2412/button/factory_reset_button.cpp +9 -0
  289. esphome/components/ld2412/button/factory_reset_button.h +18 -0
  290. esphome/components/ld2412/button/query_button.cpp +9 -0
  291. esphome/components/ld2412/button/query_button.h +18 -0
  292. esphome/components/ld2412/button/restart_button.cpp +9 -0
  293. esphome/components/ld2412/button/restart_button.h +18 -0
  294. esphome/components/ld2412/button/start_dynamic_background_correction_button.cpp +11 -0
  295. esphome/components/ld2412/button/start_dynamic_background_correction_button.h +18 -0
  296. esphome/components/ld2412/ld2412.cpp +861 -0
  297. esphome/components/ld2412/ld2412.h +141 -0
  298. esphome/components/ld2412/number/__init__.py +126 -0
  299. esphome/components/ld2412/number/gate_threshold_number.cpp +14 -0
  300. esphome/components/ld2412/number/gate_threshold_number.h +19 -0
  301. esphome/components/ld2412/number/light_threshold_number.cpp +12 -0
  302. esphome/components/ld2412/number/light_threshold_number.h +18 -0
  303. esphome/components/ld2412/number/max_distance_timeout_number.cpp +12 -0
  304. esphome/components/ld2412/number/max_distance_timeout_number.h +18 -0
  305. esphome/components/ld2412/select/__init__.py +82 -0
  306. esphome/components/ld2412/select/baud_rate_select.cpp +12 -0
  307. esphome/components/ld2412/select/baud_rate_select.h +18 -0
  308. esphome/components/ld2412/select/distance_resolution_select.cpp +12 -0
  309. esphome/components/ld2412/select/distance_resolution_select.h +18 -0
  310. esphome/components/ld2412/select/light_out_control_select.cpp +12 -0
  311. esphome/components/ld2412/select/light_out_control_select.h +18 -0
  312. esphome/components/ld2412/sensor.py +124 -0
  313. esphome/components/ld2412/switch/__init__.py +45 -0
  314. esphome/components/ld2412/switch/bluetooth_switch.cpp +12 -0
  315. esphome/components/ld2412/switch/bluetooth_switch.h +18 -0
  316. esphome/components/ld2412/switch/engineering_mode_switch.cpp +12 -0
  317. esphome/components/ld2412/switch/engineering_mode_switch.h +18 -0
  318. esphome/components/ld2412/text_sensor.py +34 -0
  319. esphome/components/ld2420/ld2420.cpp +0 -1
  320. esphome/components/ld2450/__init__.py +3 -4
  321. esphome/components/ld2450/binary_sensor.py +3 -0
  322. esphome/components/ld2450/ld2450.cpp +77 -165
  323. esphome/components/ld2450/ld2450.h +26 -54
  324. esphome/components/ld2450/sensor.py +120 -6
  325. esphome/components/ld2450/text_sensor.py +5 -4
  326. esphome/components/ld24xx/__init__.py +1 -0
  327. esphome/components/ld24xx/ld24xx.h +65 -0
  328. esphome/components/ledc/ledc_output.cpp +0 -1
  329. esphome/components/libretiny/__init__.py +2 -0
  330. esphome/components/light/__init__.py +0 -1
  331. esphome/components/light/effects.py +70 -45
  332. esphome/components/light/light_call.cpp +101 -66
  333. esphome/components/light/light_color_values.h +16 -11
  334. esphome/components/light/light_json_schema.cpp +46 -44
  335. esphome/components/light/light_state.cpp +8 -11
  336. esphome/components/light/light_traits.h +17 -0
  337. esphome/components/lightwaverf/lightwaverf.cpp +0 -2
  338. esphome/components/lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.cpp +0 -1
  339. esphome/components/lock/__init__.py +0 -1
  340. esphome/components/logger/__init__.py +31 -9
  341. esphome/components/logger/logger.cpp +12 -7
  342. esphome/components/logger/logger.h +25 -14
  343. esphome/components/logger/logger_esp32.cpp +2 -7
  344. esphome/components/logger/logger_esp8266.cpp +2 -4
  345. esphome/components/logger/logger_host.cpp +2 -4
  346. esphome/components/logger/logger_libretiny.cpp +2 -4
  347. esphome/components/logger/logger_rp2040.cpp +2 -4
  348. esphome/components/logger/logger_zephyr.cpp +86 -0
  349. esphome/components/logger/select/logger_level_select.cpp +2 -4
  350. esphome/components/logger/select/logger_level_select.h +2 -4
  351. esphome/components/logger/task_log_buffer.cpp +2 -4
  352. esphome/components/logger/task_log_buffer.h +2 -4
  353. esphome/components/lps22/sensor.py +5 -5
  354. esphome/components/ltr390/ltr390.cpp +0 -2
  355. esphome/components/ltr501/ltr501.cpp +0 -1
  356. esphome/components/ltr_als_ps/ltr_als_ps.cpp +0 -1
  357. esphome/components/lvgl/__init__.py +14 -13
  358. esphome/components/lvgl/automation.py +2 -4
  359. esphome/components/lvgl/defines.py +0 -2
  360. esphome/components/lvgl/helpers.py +1 -1
  361. esphome/components/lvgl/lv_validation.py +7 -4
  362. esphome/components/lvgl/lvgl_esphome.cpp +2 -3
  363. esphome/components/lvgl/styles.py +2 -2
  364. esphome/components/lvgl/types.py +1 -1
  365. esphome/components/lvgl/widgets/__init__.py +2 -2
  366. esphome/components/lvgl/widgets/arc.py +14 -11
  367. esphome/components/lvgl/widgets/buttonmatrix.py +1 -1
  368. esphome/components/lvgl/widgets/qrcode.py +7 -7
  369. esphome/components/lvgl/widgets/spinner.py +6 -6
  370. esphome/components/lvgl/widgets/switch.py +2 -2
  371. esphome/components/lvgl/widgets/tabview.py +3 -3
  372. esphome/components/m5stack_8angle/m5stack_8angle.cpp +0 -1
  373. esphome/components/matrix_keypad/__init__.py +4 -3
  374. esphome/components/max17043/max17043.cpp +0 -2
  375. esphome/components/max31855/max31855.cpp +1 -4
  376. esphome/components/max31856/max31856.cpp +0 -4
  377. esphome/components/max31865/max31865.cpp +0 -1
  378. esphome/components/max44009/max44009.cpp +0 -1
  379. esphome/components/max6675/max6675.cpp +1 -4
  380. esphome/components/max6956/max6956.cpp +0 -1
  381. esphome/components/max7219/max7219.cpp +0 -1
  382. esphome/components/max7219digit/display.py +1 -1
  383. esphome/components/max7219digit/max7219digit.cpp +0 -1
  384. esphome/components/max9611/max9611.cpp +0 -1
  385. esphome/components/mcp23008/__init__.py +1 -1
  386. esphome/components/mcp23008/mcp23008.cpp +0 -1
  387. esphome/components/mcp23016/mcp23016.cpp +0 -1
  388. esphome/components/mcp23017/__init__.py +1 -1
  389. esphome/components/mcp23017/mcp23017.cpp +0 -1
  390. esphome/components/mcp23s08/__init__.py +1 -1
  391. esphome/components/mcp23s08/mcp23s08.cpp +0 -1
  392. esphome/components/mcp23s17/__init__.py +1 -1
  393. esphome/components/mcp23s17/mcp23s17.cpp +0 -1
  394. esphome/components/mcp23x08_base/__init__.py +2 -0
  395. esphome/components/mcp23x08_base/mcp23x08_base.cpp +9 -7
  396. esphome/components/mcp23x08_base/mcp23x08_base.h +9 -4
  397. esphome/components/mcp23x17_base/__init__.py +2 -0
  398. esphome/components/mcp23x17_base/mcp23x17_base.cpp +20 -7
  399. esphome/components/mcp23x17_base/mcp23x17_base.h +9 -4
  400. esphome/components/mcp23xxx_base/__init__.py +11 -5
  401. esphome/components/mcp23xxx_base/mcp23xxx_base.cpp +15 -12
  402. esphome/components/mcp23xxx_base/mcp23xxx_base.h +8 -7
  403. esphome/components/mcp3008/mcp3008.cpp +1 -4
  404. esphome/components/mcp3204/mcp3204.cpp +1 -4
  405. esphome/components/mcp4461/mcp4461.cpp +0 -1
  406. esphome/components/mcp4725/mcp4725.cpp +0 -1
  407. esphome/components/mcp4728/mcp4728.cpp +0 -1
  408. esphome/components/mcp9600/mcp9600.cpp +0 -2
  409. esphome/components/mcp9808/mcp9808.cpp +0 -2
  410. esphome/components/mdns/__init__.py +3 -0
  411. esphome/components/mdns/mdns_component.cpp +2 -0
  412. esphome/components/mdns/mdns_component.h +4 -0
  413. esphome/components/media_player/__init__.py +40 -0
  414. esphome/components/media_player/automation.h +16 -0
  415. esphome/components/media_player/media_player.cpp +13 -0
  416. esphome/components/media_player/media_player.h +50 -3
  417. esphome/components/micro_wake_word/micro_wake_word.cpp +0 -3
  418. esphome/components/mics_4514/mics_4514.cpp +1 -6
  419. esphome/components/midea/ir_transmitter.h +4 -4
  420. esphome/components/mipi/__init__.py +416 -0
  421. esphome/components/mipi_dsi/__init__.py +5 -0
  422. esphome/components/mipi_dsi/display.py +233 -0
  423. esphome/components/mipi_dsi/mipi_dsi.cpp +379 -0
  424. esphome/components/mipi_dsi/mipi_dsi.h +123 -0
  425. esphome/components/mipi_dsi/models/__init__.py +0 -0
  426. esphome/components/mipi_dsi/models/guition.py +38 -0
  427. esphome/components/mipi_dsi/models/m5stack.py +57 -0
  428. esphome/components/mipi_dsi/models/waveshare.py +105 -0
  429. esphome/components/mipi_rgb/models/lilygo.py +0 -0
  430. esphome/components/mipi_spi/__init__.py +0 -9
  431. esphome/components/mipi_spi/display.py +220 -256
  432. esphome/components/mipi_spi/mipi_spi.cpp +1 -485
  433. esphome/components/mipi_spi/mipi_spi.h +556 -108
  434. esphome/components/mipi_spi/models/__init__.py +0 -65
  435. esphome/components/mipi_spi/models/adafruit.py +30 -0
  436. esphome/components/mipi_spi/models/amoled.py +41 -5
  437. esphome/components/mipi_spi/models/ili.py +5 -5
  438. esphome/components/mipi_spi/models/jc.py +1 -3
  439. esphome/components/mipi_spi/models/lilygo.py +1 -1
  440. esphome/components/mipi_spi/models/waveshare.py +16 -1
  441. esphome/components/mixer/speaker/__init__.py +4 -5
  442. esphome/components/mlx90393/sensor.py +7 -5
  443. esphome/components/mlx90393/sensor_mlx90393.cpp +0 -1
  444. esphome/components/mlx90614/mlx90614.cpp +0 -1
  445. esphome/components/mmc5603/mmc5603.cpp +0 -1
  446. esphome/components/mmc5983/mmc5983.cpp +0 -2
  447. esphome/components/mpl3115a2/mpl3115a2.cpp +0 -2
  448. esphome/components/mpr121/__init__.py +7 -6
  449. esphome/components/mpr121/mpr121.cpp +0 -1
  450. esphome/components/mpu6050/mpu6050.cpp +0 -1
  451. esphome/components/mpu6886/mpu6886.cpp +0 -1
  452. esphome/components/mqtt/__init__.py +1 -2
  453. esphome/components/mqtt/mqtt_button.cpp +1 -1
  454. esphome/components/mqtt/mqtt_client.cpp +0 -1
  455. esphome/components/mqtt/mqtt_component.cpp +8 -14
  456. esphome/components/mqtt/mqtt_component.h +0 -7
  457. esphome/components/mqtt/mqtt_sensor.cpp +0 -1
  458. esphome/components/mqtt/mqtt_sensor.h +0 -1
  459. esphome/components/mqtt/mqtt_text_sensor.cpp +0 -1
  460. esphome/components/mqtt/mqtt_text_sensor.h +0 -1
  461. esphome/components/ms5611/ms5611.cpp +0 -1
  462. esphome/components/ms8607/ms8607.cpp +0 -1
  463. esphome/components/msa3xx/msa3xx.cpp +0 -2
  464. esphome/components/my9231/my9231.cpp +0 -2
  465. esphome/components/nau7802/nau7802.cpp +0 -1
  466. esphome/components/neopixelbus/light.py +3 -0
  467. esphome/components/network/util.cpp +29 -0
  468. esphome/components/nextion/nextion.cpp +2 -2
  469. esphome/components/nfc/binary_sensor/{binary_sensor.cpp → nfc_binary_sensor.cpp} +1 -1
  470. esphome/components/npi19/npi19.cpp +0 -2
  471. esphome/components/nrf52/__init__.py +223 -0
  472. esphome/components/nrf52/boards.py +34 -0
  473. esphome/components/nrf52/const.py +18 -0
  474. esphome/components/nrf52/gpio.py +79 -0
  475. esphome/components/number/__init__.py +2 -1
  476. esphome/components/one_wire/__init__.py +1 -2
  477. esphome/components/one_wire/one_wire.cpp +0 -2
  478. esphome/components/one_wire/one_wire.h +0 -2
  479. esphome/components/opentherm/hub.cpp +0 -1
  480. esphome/components/opentherm/number/__init__.py +2 -2
  481. esphome/components/openthread/__init__.py +2 -3
  482. esphome/components/openthread/openthread.cpp +30 -13
  483. esphome/components/openthread/openthread.h +3 -0
  484. esphome/components/openthread/openthread_esp.cpp +3 -1
  485. esphome/components/opt3001/sensor.py +2 -6
  486. esphome/components/output/__init__.py +38 -0
  487. esphome/components/output/automation.h +24 -0
  488. esphome/components/output/switch/output_switch.cpp +0 -2
  489. esphome/components/packages/__init__.py +1 -2
  490. esphome/components/packet_transport/__init__.py +4 -3
  491. esphome/components/pca6416a/pca6416a.cpp +0 -1
  492. esphome/components/pca9554/pca9554.cpp +0 -1
  493. esphome/components/pca9685/pca9685_output.cpp +0 -2
  494. esphome/components/pcf85063/pcf85063.cpp +0 -1
  495. esphome/components/pcf8563/pcf8563.cpp +0 -1
  496. esphome/components/pcf8574/pcf8574.cpp +0 -1
  497. esphome/components/pi4ioe5v6408/pi4ioe5v6408.cpp +0 -1
  498. esphome/components/pipsolar/pipsolar.cpp +54 -42
  499. esphome/components/pipsolar/pipsolar.h +5 -4
  500. esphome/components/pipsolar/sensor/__init__.py +1 -1
  501. esphome/components/pm2005/pm2005.cpp +0 -1
  502. esphome/components/pmsa003i/pmsa003i.cpp +0 -2
  503. esphome/components/pmwcs3/sensor.py +1 -2
  504. esphome/components/pn532/pn532.cpp +0 -2
  505. esphome/components/pn532_spi/pn532_spi.cpp +0 -2
  506. esphome/components/power_supply/power_supply.cpp +7 -10
  507. esphome/components/power_supply/power_supply.h +1 -1
  508. esphome/components/psram/__init__.py +6 -1
  509. esphome/components/pulse_counter/pulse_counter_sensor.cpp +0 -1
  510. esphome/components/pulse_counter/sensor.py +9 -6
  511. esphome/components/pylontech/pylontech.cpp +0 -1
  512. esphome/components/qmc5883l/qmc5883l.cpp +0 -1
  513. esphome/components/qmp6988/qmp6988.cpp +0 -2
  514. esphome/components/qspi_dbi/display.py +2 -3
  515. esphome/components/qspi_dbi/qspi_dbi.cpp +0 -2
  516. esphome/components/qwiic_pir/binary_sensor.py +2 -3
  517. esphome/components/qwiic_pir/qwiic_pir.cpp +0 -2
  518. esphome/components/rc522/rc522.cpp +9 -31
  519. esphome/components/rc522_spi/rc522_spi.cpp +0 -1
  520. esphome/components/remote_base/__init__.py +5 -6
  521. esphome/components/remote_receiver/remote_receiver_esp32.cpp +0 -1
  522. esphome/components/remote_receiver/remote_receiver_esp8266.cpp +0 -1
  523. esphome/components/remote_receiver/remote_receiver_libretiny.cpp +0 -1
  524. esphome/components/remote_transmitter/__init__.py +26 -0
  525. esphome/components/remote_transmitter/automation.h +18 -0
  526. esphome/components/remote_transmitter/remote_transmitter.h +2 -1
  527. esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +0 -1
  528. esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +2 -0
  529. esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp +2 -0
  530. esphome/components/resampler/speaker/__init__.py +4 -5
  531. esphome/components/rf_bridge/__init__.py +4 -8
  532. esphome/components/rotary_encoder/rotary_encoder.cpp +0 -2
  533. esphome/components/rp2040/__init__.py +3 -1
  534. esphome/components/rp2040_pio_led_strip/led_strip.cpp +0 -2
  535. esphome/components/rp2040_pio_led_strip/light.py +1 -2
  536. esphome/components/rp2040_pwm/rp2040_pwm.cpp +1 -5
  537. esphome/components/rpi_dpi_rgb/display.py +13 -15
  538. esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp +0 -3
  539. esphome/components/runtime_stats/__init__.py +34 -0
  540. esphome/components/runtime_stats/runtime_stats.cpp +102 -0
  541. esphome/components/runtime_stats/runtime_stats.h +132 -0
  542. esphome/components/scd30/scd30.cpp +0 -2
  543. esphome/components/scd30/sensor.py +1 -2
  544. esphome/components/scd4x/scd4x.cpp +0 -1
  545. esphome/components/scd4x/sensor.py +1 -3
  546. esphome/components/sdl/display.py +3 -1
  547. esphome/components/sdl/sdl_esphome.cpp +0 -2
  548. esphome/components/sdp3x/sdp3x.cpp +0 -2
  549. esphome/components/seeed_mr24hpc1/seeed_mr24hpc1.cpp +0 -2
  550. esphome/components/seeed_mr60fda2/seeed_mr60fda2.cpp +0 -3
  551. esphome/components/select/__init__.py +2 -3
  552. esphome/components/select/select_traits.cpp +1 -1
  553. esphome/components/select/select_traits.h +1 -1
  554. esphome/components/sen0321/sen0321.cpp +0 -1
  555. esphome/components/sen5x/sen5x.cpp +0 -2
  556. esphome/components/senseair/senseair.cpp +7 -3
  557. esphome/components/senseair/senseair.h +11 -0
  558. esphome/components/sensor/__init__.py +36 -4
  559. esphome/components/sensor/filter.cpp +49 -10
  560. esphome/components/sensor/filter.h +22 -7
  561. esphome/components/sensor/sensor.cpp +0 -1
  562. esphome/components/sensor/sensor.h +0 -9
  563. esphome/components/sfa30/sfa30.cpp +0 -4
  564. esphome/components/sgp30/sgp30.cpp +0 -2
  565. esphome/components/sgp4x/sensor.py +1 -1
  566. esphome/components/sgp4x/sgp4x.cpp +0 -2
  567. esphome/components/shelly_dimmer/shelly_dimmer.cpp +0 -2
  568. esphome/components/sht3xd/sht3xd.cpp +0 -2
  569. esphome/components/sht4x/sht4x.cpp +0 -2
  570. esphome/components/shtcx/shtcx.cpp +0 -1
  571. esphome/components/sim800l/__init__.py +2 -4
  572. esphome/components/sm16716/sm16716.cpp +0 -1
  573. esphome/components/sm2135/sm2135.cpp +0 -1
  574. esphome/components/sm2235/sm2235.cpp +0 -1
  575. esphome/components/sm2335/sm2335.cpp +0 -1
  576. esphome/components/sn74hc165/sn74hc165.cpp +0 -1
  577. esphome/components/sn74hc595/sn74hc595.cpp +0 -1
  578. esphome/components/sntp/sntp_component.cpp +0 -1
  579. esphome/components/sound_level/sensor.py +1 -1
  580. esphome/components/speaker/media_player/__init__.py +21 -33
  581. esphome/components/speaker/media_player/audio_pipeline.cpp +4 -7
  582. esphome/components/spi/__init__.py +29 -13
  583. esphome/components/spi/spi.cpp +0 -2
  584. esphome/components/spi_device/spi_device.cpp +1 -4
  585. esphome/components/sprinkler/__init__.py +4 -4
  586. esphome/components/sps30/sps30.cpp +0 -1
  587. esphome/components/ssd1306_base/__init__.py +11 -11
  588. esphome/components/ssd1306_base/ssd1306_base.cpp +1 -1
  589. esphome/components/ssd1306_i2c/ssd1306_i2c.cpp +0 -1
  590. esphome/components/ssd1306_spi/ssd1306_spi.cpp +0 -1
  591. esphome/components/ssd1322_spi/ssd1322_spi.cpp +0 -1
  592. esphome/components/ssd1325_spi/ssd1325_spi.cpp +0 -1
  593. esphome/components/ssd1327_i2c/ssd1327_i2c.cpp +0 -1
  594. esphome/components/ssd1327_spi/ssd1327_spi.cpp +0 -1
  595. esphome/components/ssd1331_spi/ssd1331_spi.cpp +0 -1
  596. esphome/components/ssd1351_spi/ssd1351_spi.cpp +0 -1
  597. esphome/components/st7567_i2c/st7567_i2c.cpp +0 -1
  598. esphome/components/st7567_spi/st7567_spi.cpp +0 -1
  599. esphome/components/st7701s/display.py +10 -14
  600. esphome/components/st7701s/st7701s.cpp +0 -3
  601. esphome/components/st7735/st7735.cpp +0 -1
  602. esphome/components/st7789v/st7789v.cpp +0 -1
  603. esphome/components/st7920/st7920.cpp +0 -1
  604. esphome/components/status_led/light/status_led_light.cpp +0 -2
  605. esphome/components/status_led/status_led.cpp +0 -1
  606. esphome/components/stepper/__init__.py +2 -4
  607. esphome/components/sts3x/sts3x.cpp +0 -1
  608. esphome/components/substitutions/__init__.py +10 -16
  609. esphome/components/substitutions/jinja.py +24 -1
  610. esphome/components/sun/__init__.py +2 -3
  611. esphome/components/switch/__init__.py +31 -1
  612. esphome/components/switch/automation.h +24 -0
  613. esphome/components/switch/switch.cpp +8 -0
  614. esphome/components/switch/switch.h +8 -0
  615. esphome/components/sx126x/sx126x.cpp +0 -2
  616. esphome/components/sx127x/sx127x.cpp +0 -2
  617. esphome/components/sx1509/__init__.py +7 -5
  618. esphome/components/sx1509/output/sx1509_float_output.cpp +1 -1
  619. esphome/components/sx1509/sx1509.cpp +0 -2
  620. esphome/components/syslog/esphome_syslog.cpp +1 -1
  621. esphome/components/tc74/tc74.cpp +0 -1
  622. esphome/components/tca9548a/tca9548a.cpp +0 -1
  623. esphome/components/tca9555/tca9555.cpp +0 -1
  624. esphome/components/tcs34725/tcs34725.cpp +0 -1
  625. esphome/components/tee501/tee501.cpp +0 -1
  626. esphome/components/tem3200/tem3200.cpp +0 -2
  627. esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +0 -1
  628. esphome/components/template/cover/template_cover.cpp +0 -1
  629. esphome/components/template/select/template_select.cpp +0 -1
  630. esphome/components/template/text/template_text.cpp +0 -2
  631. esphome/components/template/valve/template_valve.cpp +0 -1
  632. esphome/components/text/__init__.py +0 -1
  633. esphome/components/text/text_traits.h +2 -0
  634. esphome/components/text_sensor/__init__.py +2 -1
  635. esphome/components/text_sensor/text_sensor.cpp +0 -2
  636. esphome/components/text_sensor/text_sensor.h +0 -8
  637. esphome/components/thermostat/climate.py +4 -4
  638. esphome/components/time/__init__.py +7 -4
  639. esphome/components/time/real_time_clock.cpp +16 -3
  640. esphome/components/tlc59208f/tlc59208f_output.cpp +0 -2
  641. esphome/components/tlc5947/tlc5947.cpp +0 -2
  642. esphome/components/tlc5971/tlc5971.cpp +0 -2
  643. esphome/components/tm1621/tm1621.cpp +0 -2
  644. esphome/components/tm1637/tm1637.cpp +0 -2
  645. esphome/components/tm1638/tm1638.cpp +0 -2
  646. esphome/components/tm1651/__init__.py +45 -48
  647. esphome/components/tm1651/tm1651.cpp +213 -47
  648. esphome/components/tm1651/tm1651.h +37 -32
  649. esphome/components/tmp117/tmp117.cpp +0 -2
  650. esphome/components/tsl2561/tsl2561.cpp +0 -1
  651. esphome/components/tsl2591/tsl2591.cpp +0 -1
  652. esphome/components/tt21100/touchscreen/tt21100.cpp +0 -2
  653. esphome/components/ttp229_bsf/ttp229_bsf.cpp +0 -1
  654. esphome/components/ttp229_lsf/ttp229_lsf.cpp +0 -1
  655. esphome/components/tuya/climate/__init__.py +9 -10
  656. esphome/components/tuya/number/__init__.py +8 -6
  657. esphome/components/tx20/tx20.cpp +0 -1
  658. esphome/components/uart/uart_component_esp32_arduino.cpp +0 -1
  659. esphome/components/uart/uart_component_esp8266.cpp +0 -1
  660. esphome/components/uart/uart_component_esp_idf.cpp +0 -2
  661. esphome/components/uart/uart_component_libretiny.cpp +0 -2
  662. esphome/components/uart/uart_component_rp2040.cpp +0 -2
  663. esphome/components/udp/__init__.py +1 -1
  664. esphome/components/ufire_ec/sensor.py +1 -2
  665. esphome/components/ufire_ec/ufire_ec.cpp +0 -2
  666. esphome/components/ufire_ise/sensor.py +1 -2
  667. esphome/components/ufire_ise/ufire_ise.cpp +0 -2
  668. esphome/components/ultrasonic/ultrasonic_sensor.cpp +0 -1
  669. esphome/components/update/__init__.py +0 -1
  670. esphome/components/uptime/sensor/uptime_seconds_sensor.cpp +0 -1
  671. esphome/components/uptime/sensor/uptime_seconds_sensor.h +0 -2
  672. esphome/components/usb_host/usb_host_client.cpp +0 -1
  673. esphome/components/usb_host/usb_host_component.cpp +0 -1
  674. esphome/components/valve/__init__.py +0 -1
  675. esphome/components/veml3235/veml3235.cpp +0 -3
  676. esphome/components/veml7700/veml7700.cpp +0 -2
  677. esphome/components/version/version_text_sensor.cpp +0 -1
  678. esphome/components/version/version_text_sensor.h +0 -1
  679. esphome/components/vl53l0x/vl53l0x_sensor.cpp +0 -4
  680. esphome/components/voice_assistant/voice_assistant.cpp +9 -8
  681. esphome/components/web_server/__init__.py +13 -0
  682. esphome/components/web_server/web_server.cpp +188 -353
  683. esphome/components/web_server/web_server.h +61 -1
  684. esphome/components/web_server_base/__init__.py +1 -1
  685. esphome/components/web_server_base/web_server_base.cpp +2 -0
  686. esphome/components/web_server_base/web_server_base.h +6 -0
  687. esphome/components/web_server_idf/web_server_idf.cpp +10 -8
  688. esphome/components/web_server_idf/web_server_idf.h +2 -0
  689. esphome/components/weikai_i2c/weikai_i2c.cpp +1 -2
  690. esphome/components/weikai_spi/weikai_spi.cpp +1 -1
  691. esphome/components/wifi/__init__.py +17 -43
  692. esphome/components/wifi/wifi_component.cpp +100 -36
  693. esphome/components/wifi/wifi_component.h +5 -1
  694. esphome/components/wifi/wifi_component_esp32_arduino.cpp +30 -0
  695. esphome/components/wifi/wifi_component_esp_idf.cpp +30 -0
  696. esphome/components/wifi_info/wifi_info_text_sensor.h +0 -6
  697. esphome/components/wifi_signal/wifi_signal_sensor.h +0 -1
  698. esphome/components/wireguard/wireguard.cpp +0 -2
  699. esphome/components/x9c/x9c.cpp +0 -2
  700. esphome/components/xgzp68xx/xgzp68xx.cpp +0 -1
  701. esphome/components/xl9535/xl9535.cpp +0 -2
  702. esphome/components/zephyr/__init__.py +252 -0
  703. esphome/components/zephyr/const.py +16 -0
  704. esphome/components/zephyr/core.cpp +90 -0
  705. esphome/components/zephyr/gpio.cpp +120 -0
  706. esphome/components/zephyr/gpio.h +38 -0
  707. esphome/components/zephyr/pre_build.py.script +4 -0
  708. esphome/components/zephyr/preferences.cpp +156 -0
  709. esphome/components/zephyr/preferences.h +13 -0
  710. esphome/config.py +38 -16
  711. esphome/config_helpers.py +1 -2
  712. esphome/config_validation.py +12 -16
  713. esphome/const.py +26 -1
  714. esphome/core/__init__.py +92 -51
  715. esphome/core/application.cpp +75 -21
  716. esphome/core/application.h +106 -171
  717. esphome/core/color.h +10 -0
  718. esphome/core/component.cpp +41 -25
  719. esphome/core/component.h +9 -6
  720. esphome/core/component_iterator.cpp +61 -261
  721. esphome/core/component_iterator.h +15 -0
  722. esphome/core/config.py +26 -11
  723. esphome/core/defines.h +40 -2
  724. esphome/core/entity_base.h +18 -0
  725. esphome/core/entity_helpers.py +45 -10
  726. esphome/core/helpers.cpp +8 -15
  727. esphome/core/helpers.h +60 -6
  728. esphome/core/lock_free_queue.h +1 -1
  729. esphome/core/scheduler.cpp +311 -77
  730. esphome/core/scheduler.h +141 -28
  731. esphome/cpp_generator.py +2 -6
  732. esphome/cpp_helpers.py +1 -1
  733. esphome/dashboard/dashboard.py +2 -3
  734. esphome/dashboard/dns.py +2 -8
  735. esphome/dashboard/web_server.py +34 -19
  736. esphome/espota2.py +1 -4
  737. esphome/git.py +3 -1
  738. esphome/helpers.py +23 -4
  739. esphome/log.py +3 -1
  740. esphome/mqtt.py +3 -5
  741. esphome/platformio_api.py +7 -4
  742. esphome/types.py +12 -0
  743. esphome/util.py +20 -8
  744. esphome/voluptuous_schema.py +4 -3
  745. esphome/vscode.py +1 -2
  746. esphome/wizard.py +1 -4
  747. esphome/writer.py +16 -108
  748. esphome/yaml_util.py +7 -5
  749. {esphome-2025.7.5.dist-info → esphome-2025.8.0.dist-info}/METADATA +13 -14
  750. {esphome-2025.7.5.dist-info → esphome-2025.8.0.dist-info}/RECORD +755 -675
  751. esphome/components/mipi_spi/models/commands.py +0 -82
  752. /esphome/components/nfc/binary_sensor/{binary_sensor.h → nfc_binary_sensor.h} +0 -0
  753. {esphome-2025.7.5.dist-info → esphome-2025.8.0.dist-info}/WHEEL +0 -0
  754. {esphome-2025.7.5.dist-info → esphome-2025.8.0.dist-info}/entry_points.txt +0 -0
  755. {esphome-2025.7.5.dist-info → esphome-2025.8.0.dist-info}/licenses/LICENSE +0 -0
  756. {esphome-2025.7.5.dist-info → esphome-2025.8.0.dist-info}/top_level.txt +0 -0
@@ -8,12 +8,17 @@
8
8
  #include <algorithm>
9
9
  #include <cinttypes>
10
10
  #include <cstring>
11
+ #include <limits>
11
12
 
12
13
  namespace esphome {
13
14
 
14
15
  static const char *const TAG = "scheduler";
15
16
 
16
17
  static const uint32_t MAX_LOGICALLY_DELETED_ITEMS = 10;
18
+ // Half the 32-bit range - used to detect rollovers vs normal time progression
19
+ static constexpr uint32_t HALF_MAX_UINT32 = std::numeric_limits<uint32_t>::max() / 2;
20
+ // max delay to start an interval sequence
21
+ static constexpr uint32_t MAX_INTERVAL_DELAY = 5000;
17
22
 
18
23
  // Uncomment to debug scheduler
19
24
  // #define ESPHOME_DEBUG_SCHEDULER
@@ -51,7 +56,7 @@ static void validate_static_string(const char *name) {
51
56
  ESP_LOGW(TAG, "WARNING: Scheduler name '%s' at %p might be on heap (static ref at %p)", name, name, static_str);
52
57
  }
53
58
  }
54
- #endif
59
+ #endif /* ESPHOME_DEBUG_SCHEDULER */
55
60
 
56
61
  // A note on locking: the `lock_` lock protects the `items_` and `to_add_` containers. It must be taken when writing to
57
62
  // them (i.e. when adding/removing items, but not when changing items). As items are only deleted from the loop task,
@@ -60,7 +65,7 @@ static void validate_static_string(const char *name) {
60
65
 
61
66
  // Common implementation for both timeout and interval
62
67
  void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type type, bool is_static_string,
63
- const void *name_ptr, uint32_t delay, std::function<void()> func) {
68
+ const void *name_ptr, uint32_t delay, std::function<void()> func, bool is_retry) {
64
69
  // Get the name as const char*
65
70
  const char *name_cstr = this->get_name_cstr_(is_static_string, name_ptr);
66
71
 
@@ -77,11 +82,18 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type
77
82
  item->set_name(name_cstr, !is_static_string);
78
83
  item->type = type;
79
84
  item->callback = std::move(func);
85
+ // Initialize remove to false (though it should already be from constructor)
86
+ // Not using mark_item_removed_ helper since we're setting to false, not true
87
+ #ifdef ESPHOME_THREAD_MULTI_ATOMICS
88
+ item->remove.store(false, std::memory_order_relaxed);
89
+ #else
80
90
  item->remove = false;
91
+ #endif
92
+ item->is_retry = is_retry;
81
93
 
82
- #if !defined(USE_ESP8266) && !defined(USE_RP2040)
94
+ #ifndef ESPHOME_THREAD_SINGLE
83
95
  // Special handling for defer() (delay = 0, type = TIMEOUT)
84
- // ESP8266 and RP2040 are excluded because they don't need thread-safe defer handling
96
+ // Single-core platforms don't need thread-safe defer handling
85
97
  if (delay == 0 && type == SchedulerItem::TIMEOUT) {
86
98
  // Put in defer queue for guaranteed FIFO execution
87
99
  LockGuard guard{this->lock_};
@@ -89,16 +101,20 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type
89
101
  this->defer_queue_.push_back(std::move(item));
90
102
  return;
91
103
  }
92
- #endif
104
+ #endif /* not ESPHOME_THREAD_SINGLE */
93
105
 
94
- const auto now = this->millis_();
106
+ // Get fresh timestamp for new timer/interval - ensures accurate scheduling
107
+ const auto now = this->millis_64_(millis()); // Fresh millis() call
95
108
 
96
109
  // Type-specific setup
97
110
  if (type == SchedulerItem::INTERVAL) {
98
111
  item->interval = delay;
99
- // Calculate random offset (0 to interval/2)
100
- uint32_t offset = (delay != 0) ? (random_uint32() % delay) / 2 : 0;
112
+ // first execution happens immediately after a random smallish offset
113
+ // Calculate random offset (0 to min(interval/2, 5s))
114
+ uint32_t offset = (uint32_t) (std::min(delay / 2, MAX_INTERVAL_DELAY) * random_float());
101
115
  item->next_execution_ = now + offset;
116
+ ESP_LOGV(TAG, "Scheduler interval for %s is %" PRIu32 "ms, offset %" PRIu32 "ms", name_cstr ? name_cstr : "", delay,
117
+ offset);
102
118
  } else {
103
119
  item->interval = 0;
104
120
  item->next_execution_ = now + delay;
@@ -119,9 +135,21 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type
119
135
  ESP_LOGD(TAG, "set_%s(name='%s/%s', %s=%" PRIu32 ", offset=%" PRIu32 ")", type_str, item->get_source(),
120
136
  name_cstr ? name_cstr : "(null)", type_str, delay, static_cast<uint32_t>(item->next_execution_ - now));
121
137
  }
122
- #endif
138
+ #endif /* ESPHOME_DEBUG_SCHEDULER */
123
139
 
124
140
  LockGuard guard{this->lock_};
141
+
142
+ // For retries, check if there's a cancelled timeout first
143
+ if (is_retry && name_cstr != nullptr && type == SchedulerItem::TIMEOUT &&
144
+ (has_cancelled_timeout_in_container_(this->items_, component, name_cstr, /* match_retry= */ true) ||
145
+ has_cancelled_timeout_in_container_(this->to_add_, component, name_cstr, /* match_retry= */ true))) {
146
+ // Skip scheduling - the retry was cancelled
147
+ #ifdef ESPHOME_DEBUG_SCHEDULER
148
+ ESP_LOGD(TAG, "Skipping retry '%s' - found cancelled item", name_cstr);
149
+ #endif
150
+ return;
151
+ }
152
+
125
153
  // If name is provided, do atomic cancel-and-add
126
154
  // Cancel existing items
127
155
  this->cancel_item_locked_(component, name_cstr, type);
@@ -170,32 +198,34 @@ struct RetryArgs {
170
198
  Scheduler *scheduler;
171
199
  };
172
200
 
173
- static void retry_handler(const std::shared_ptr<RetryArgs> &args) {
201
+ void retry_handler(const std::shared_ptr<RetryArgs> &args) {
174
202
  RetryResult const retry_result = args->func(--args->retry_countdown);
175
203
  if (retry_result == RetryResult::DONE || args->retry_countdown <= 0)
176
204
  return;
177
205
  // second execution of `func` happens after `initial_wait_time`
178
- args->scheduler->set_timeout(args->component, args->name, args->current_interval, [args]() { retry_handler(args); });
206
+ args->scheduler->set_timer_common_(
207
+ args->component, Scheduler::SchedulerItem::TIMEOUT, false, &args->name, args->current_interval,
208
+ [args]() { retry_handler(args); }, /* is_retry= */ true);
179
209
  // backoff_increase_factor applied to third & later executions
180
210
  args->current_interval *= args->backoff_increase_factor;
181
211
  }
182
212
 
183
- void HOT Scheduler::set_retry(Component *component, const std::string &name, uint32_t initial_wait_time,
184
- uint8_t max_attempts, std::function<RetryResult(uint8_t)> func,
185
- float backoff_increase_factor) {
186
- if (!name.empty())
187
- this->cancel_retry(component, name);
213
+ void HOT Scheduler::set_retry_common_(Component *component, bool is_static_string, const void *name_ptr,
214
+ uint32_t initial_wait_time, uint8_t max_attempts,
215
+ std::function<RetryResult(uint8_t)> func, float backoff_increase_factor) {
216
+ const char *name_cstr = this->get_name_cstr_(is_static_string, name_ptr);
217
+
218
+ if (name_cstr != nullptr)
219
+ this->cancel_retry(component, name_cstr);
188
220
 
189
221
  if (initial_wait_time == SCHEDULER_DONT_RUN)
190
222
  return;
191
223
 
192
224
  ESP_LOGVV(TAG, "set_retry(name='%s', initial_wait_time=%" PRIu32 ", max_attempts=%u, backoff_factor=%0.1f)",
193
- name.c_str(), initial_wait_time, max_attempts, backoff_increase_factor);
225
+ name_cstr ? name_cstr : "", initial_wait_time, max_attempts, backoff_increase_factor);
194
226
 
195
227
  if (backoff_increase_factor < 0.0001) {
196
- ESP_LOGE(TAG,
197
- "set_retry(name='%s'): backoff_factor cannot be close to zero nor negative (%0.1f). Using 1.0 instead",
198
- name.c_str(), backoff_increase_factor);
228
+ ESP_LOGE(TAG, "backoff_factor %0.1f too small, using 1.0: %s", backoff_increase_factor, name_cstr ? name_cstr : "");
199
229
  backoff_increase_factor = 1;
200
230
  }
201
231
 
@@ -204,31 +234,56 @@ void HOT Scheduler::set_retry(Component *component, const std::string &name, uin
204
234
  args->retry_countdown = max_attempts;
205
235
  args->current_interval = initial_wait_time;
206
236
  args->component = component;
207
- args->name = "retry$" + name;
237
+ args->name = name_cstr ? name_cstr : ""; // Convert to std::string for RetryArgs
208
238
  args->backoff_increase_factor = backoff_increase_factor;
209
239
  args->scheduler = this;
210
240
 
211
- // First execution of `func` immediately
212
- this->set_timeout(component, args->name, 0, [args]() { retry_handler(args); });
241
+ // First execution of `func` immediately - use set_timer_common_ with is_retry=true
242
+ this->set_timer_common_(
243
+ component, SchedulerItem::TIMEOUT, false, &args->name, 0, [args]() { retry_handler(args); },
244
+ /* is_retry= */ true);
245
+ }
246
+
247
+ void HOT Scheduler::set_retry(Component *component, const std::string &name, uint32_t initial_wait_time,
248
+ uint8_t max_attempts, std::function<RetryResult(uint8_t)> func,
249
+ float backoff_increase_factor) {
250
+ this->set_retry_common_(component, false, &name, initial_wait_time, max_attempts, std::move(func),
251
+ backoff_increase_factor);
252
+ }
253
+
254
+ void HOT Scheduler::set_retry(Component *component, const char *name, uint32_t initial_wait_time, uint8_t max_attempts,
255
+ std::function<RetryResult(uint8_t)> func, float backoff_increase_factor) {
256
+ this->set_retry_common_(component, true, name, initial_wait_time, max_attempts, std::move(func),
257
+ backoff_increase_factor);
213
258
  }
214
259
  bool HOT Scheduler::cancel_retry(Component *component, const std::string &name) {
215
- return this->cancel_timeout(component, "retry$" + name);
260
+ return this->cancel_retry(component, name.c_str());
261
+ }
262
+
263
+ bool HOT Scheduler::cancel_retry(Component *component, const char *name) {
264
+ // Cancel timeouts that have is_retry flag set
265
+ LockGuard guard{this->lock_};
266
+ return this->cancel_item_locked_(component, name, SchedulerItem::TIMEOUT, /* match_retry= */ true);
216
267
  }
217
268
 
218
- optional<uint32_t> HOT Scheduler::next_schedule_in() {
269
+ optional<uint32_t> HOT Scheduler::next_schedule_in(uint32_t now) {
219
270
  // IMPORTANT: This method should only be called from the main thread (loop task).
220
- // It calls empty_() and accesses items_[0] without holding a lock, which is only
271
+ // It performs cleanup and accesses items_[0] without holding a lock, which is only
221
272
  // safe when called from the main thread. Other threads must not call this method.
222
- if (this->empty_())
273
+
274
+ // If no items, return empty optional
275
+ if (this->cleanup_() == 0)
223
276
  return {};
277
+
224
278
  auto &item = this->items_[0];
225
- const auto now = this->millis_();
226
- if (item->next_execution_ < now)
279
+ // Convert the fresh timestamp from caller (usually Application::loop()) to 64-bit
280
+ const auto now_64 = this->millis_64_(now); // 'now' from parameter - fresh from caller
281
+ if (item->next_execution_ < now_64)
227
282
  return 0;
228
- return item->next_execution_ - now;
283
+ return item->next_execution_ - now_64;
229
284
  }
230
- void HOT Scheduler::call() {
231
- #if !defined(USE_ESP8266) && !defined(USE_RP2040)
285
+ void HOT Scheduler::call(uint32_t now) {
286
+ #ifndef ESPHOME_THREAD_SINGLE
232
287
  // Process defer queue first to guarantee FIFO execution order for deferred items.
233
288
  // Previously, defer() used the heap which gave undefined order for equal timestamps,
234
289
  // causing race conditions on multi-core systems (ESP32, BK7200).
@@ -236,8 +291,7 @@ void HOT Scheduler::call() {
236
291
  // - Deferred items (delay=0) go directly to defer_queue_ in set_timer_common_
237
292
  // - Items execute in exact order they were deferred (FIFO guarantee)
238
293
  // - No deferred items exist in to_add_, so processing order doesn't affect correctness
239
- // ESP8266 and RP2040 don't use this queue - they fall back to the heap-based approach
240
- // (ESP8266: single-core, RP2040: empty mutex implementation).
294
+ // Single-core platforms don't use this queue and fall back to the heap-based approach.
241
295
  //
242
296
  // Note: Items cancelled via cancel_item_locked_() are marked with remove=true but still
243
297
  // processed here. They are removed from the queue normally via pop_front() but skipped
@@ -256,23 +310,33 @@ void HOT Scheduler::call() {
256
310
  // Execute callback without holding lock to prevent deadlocks
257
311
  // if the callback tries to call defer() again
258
312
  if (!this->should_skip_item_(item.get())) {
259
- this->execute_item_(item.get());
313
+ this->execute_item_(item.get(), now);
260
314
  }
261
315
  }
262
- #endif
316
+ #endif /* not ESPHOME_THREAD_SINGLE */
263
317
 
264
- const auto now = this->millis_();
318
+ // Convert the fresh timestamp from main loop to 64-bit for scheduler operations
319
+ const auto now_64 = this->millis_64_(now); // 'now' from parameter - fresh from Application::loop()
265
320
  this->process_to_add();
266
321
 
267
322
  #ifdef ESPHOME_DEBUG_SCHEDULER
268
323
  static uint64_t last_print = 0;
269
324
 
270
- if (now - last_print > 2000) {
271
- last_print = now;
325
+ if (now_64 - last_print > 2000) {
326
+ last_print = now_64;
272
327
  std::vector<std::unique_ptr<SchedulerItem>> old_items;
273
- ESP_LOGD(TAG, "Items: count=%zu, now=%" PRIu64 " (%u, %" PRIu32 ")", this->items_.size(), now, this->millis_major_,
274
- this->last_millis_);
275
- while (!this->empty_()) {
328
+ #ifdef ESPHOME_THREAD_MULTI_ATOMICS
329
+ const auto last_dbg = this->last_millis_.load(std::memory_order_relaxed);
330
+ const auto major_dbg = this->millis_major_.load(std::memory_order_relaxed);
331
+ ESP_LOGD(TAG, "Items: count=%zu, now=%" PRIu64 " (%" PRIu16 ", %" PRIu32 ")", this->items_.size(), now_64,
332
+ major_dbg, last_dbg);
333
+ #else /* not ESPHOME_THREAD_MULTI_ATOMICS */
334
+ ESP_LOGD(TAG, "Items: count=%zu, now=%" PRIu64 " (%" PRIu16 ", %" PRIu32 ")", this->items_.size(), now_64,
335
+ this->millis_major_, this->last_millis_);
336
+ #endif /* else ESPHOME_THREAD_MULTI_ATOMICS */
337
+ // Cleanup before debug output
338
+ this->cleanup_();
339
+ while (!this->items_.empty()) {
276
340
  std::unique_ptr<SchedulerItem> item;
277
341
  {
278
342
  LockGuard guard{this->lock_};
@@ -283,7 +347,7 @@ void HOT Scheduler::call() {
283
347
  const char *name = item->get_name();
284
348
  ESP_LOGD(TAG, " %s '%s/%s' interval=%" PRIu32 " next_execution in %" PRIu64 "ms at %" PRIu64,
285
349
  item->get_type_str(), item->get_source(), name ? name : "(null)", item->interval,
286
- item->next_execution_ - now, item->next_execution_);
350
+ item->next_execution_ - now_64, item->next_execution_);
287
351
 
288
352
  old_items.push_back(std::move(item));
289
353
  }
@@ -296,7 +360,7 @@ void HOT Scheduler::call() {
296
360
  std::make_heap(this->items_.begin(), this->items_.end(), SchedulerItem::cmp);
297
361
  }
298
362
  }
299
- #endif // ESPHOME_DEBUG_SCHEDULER
363
+ #endif /* ESPHOME_DEBUG_SCHEDULER */
300
364
 
301
365
  // If we have too many items to remove
302
366
  if (this->to_remove_ > MAX_LOGICALLY_DELETED_ITEMS) {
@@ -323,12 +387,14 @@ void HOT Scheduler::call() {
323
387
  this->to_remove_ = 0;
324
388
  }
325
389
 
326
- while (!this->empty_()) {
390
+ // Cleanup removed items before processing
391
+ this->cleanup_();
392
+ while (!this->items_.empty()) {
327
393
  // use scoping to indicate visibility of `item` variable
328
394
  {
329
395
  // Don't copy-by value yet
330
396
  auto &item = this->items_[0];
331
- if (item->next_execution_ > now) {
397
+ if (item->next_execution_ > now_64) {
332
398
  // Not reached timeout yet, done for this call
333
399
  break;
334
400
  }
@@ -338,17 +404,42 @@ void HOT Scheduler::call() {
338
404
  this->pop_raw_();
339
405
  continue;
340
406
  }
407
+
408
+ // Check if item is marked for removal
409
+ // This handles two cases:
410
+ // 1. Item was marked for removal after cleanup_() but before we got here
411
+ // 2. Item is marked for removal but wasn't at the front of the heap during cleanup_()
412
+ #ifdef ESPHOME_THREAD_MULTI_NO_ATOMICS
413
+ // Multi-threaded platforms without atomics: must take lock to safely read remove flag
414
+ {
415
+ LockGuard guard{this->lock_};
416
+ if (is_item_removed_(item.get())) {
417
+ this->pop_raw_();
418
+ this->to_remove_--;
419
+ continue;
420
+ }
421
+ }
422
+ #else
423
+ // Single-threaded or multi-threaded with atomics: can check without lock
424
+ if (is_item_removed_(item.get())) {
425
+ LockGuard guard{this->lock_};
426
+ this->pop_raw_();
427
+ this->to_remove_--;
428
+ continue;
429
+ }
430
+ #endif
431
+
341
432
  #ifdef ESPHOME_DEBUG_SCHEDULER
342
433
  const char *item_name = item->get_name();
343
434
  ESP_LOGV(TAG, "Running %s '%s/%s' with interval=%" PRIu32 " next_execution=%" PRIu64 " (now=%" PRIu64 ")",
344
435
  item->get_type_str(), item->get_source(), item_name ? item_name : "(null)", item->interval,
345
- item->next_execution_, now);
346
- #endif
436
+ item->next_execution_, now_64);
437
+ #endif /* ESPHOME_DEBUG_SCHEDULER */
347
438
 
348
439
  // Warning: During callback(), a lot of stuff can happen, including:
349
440
  // - timeouts/intervals get added, potentially invalidating vector pointers
350
441
  // - timeouts/intervals get cancelled
351
- this->execute_item_(item.get());
442
+ this->execute_item_(item.get(), now);
352
443
  }
353
444
 
354
445
  {
@@ -367,7 +458,7 @@ void HOT Scheduler::call() {
367
458
  }
368
459
 
369
460
  if (item->type == SchedulerItem::INTERVAL) {
370
- item->next_execution_ = now + item->interval;
461
+ item->next_execution_ = now_64 + item->interval;
371
462
  // Add new item directly to to_add_
372
463
  // since we have the lock held
373
464
  this->to_add_.push_back(std::move(item));
@@ -389,8 +480,8 @@ void HOT Scheduler::process_to_add() {
389
480
  }
390
481
  this->to_add_.clear();
391
482
  }
392
- void HOT Scheduler::cleanup_() {
393
- // Fast path: if nothing to remove, just return
483
+ size_t HOT Scheduler::cleanup_() {
484
+ // Fast path: if nothing to remove, just return the current size
394
485
  // Reading to_remove_ without lock is safe because:
395
486
  // 1. We only call this from the main thread during call()
396
487
  // 2. If it's 0, there's definitely nothing to cleanup
@@ -398,7 +489,7 @@ void HOT Scheduler::cleanup_() {
398
489
  // 4. Not all platforms support atomics, so we accept this race in favor of performance
399
490
  // 5. The worst case is a one-loop-iteration delay in cleanup, which is harmless
400
491
  if (this->to_remove_ == 0)
401
- return;
492
+ return this->items_.size();
402
493
 
403
494
  // We must hold the lock for the entire cleanup operation because:
404
495
  // 1. We're modifying items_ (via pop_raw_) which requires exclusive access
@@ -412,10 +503,11 @@ void HOT Scheduler::cleanup_() {
412
503
  while (!this->items_.empty()) {
413
504
  auto &item = this->items_[0];
414
505
  if (!item->remove)
415
- return;
506
+ break;
416
507
  this->to_remove_--;
417
508
  this->pop_raw_();
418
509
  }
510
+ return this->items_.size();
419
511
  }
420
512
  void HOT Scheduler::pop_raw_() {
421
513
  std::pop_heap(this->items_.begin(), this->items_.end(), SchedulerItem::cmp);
@@ -423,11 +515,9 @@ void HOT Scheduler::pop_raw_() {
423
515
  }
424
516
 
425
517
  // Helper to execute a scheduler item
426
- void HOT Scheduler::execute_item_(SchedulerItem *item) {
518
+ void HOT Scheduler::execute_item_(SchedulerItem *item, uint32_t now) {
427
519
  App.set_current_component(item->component);
428
-
429
- uint32_t now_ms = millis();
430
- WarnIfComponentBlockingGuard guard{item->component, now_ms};
520
+ WarnIfComponentBlockingGuard guard{item->component, now};
431
521
  item->callback();
432
522
  guard.finish();
433
523
  }
@@ -444,7 +534,8 @@ bool HOT Scheduler::cancel_item_(Component *component, bool is_static_string, co
444
534
  }
445
535
 
446
536
  // Helper to cancel items by name - must be called with lock held
447
- bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_cstr, SchedulerItem::Type type) {
537
+ bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_cstr, SchedulerItem::Type type,
538
+ bool match_retry) {
448
539
  // Early return if name is invalid - no items to cancel
449
540
  if (name_cstr == nullptr) {
450
541
  return false;
@@ -453,22 +544,22 @@ bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_c
453
544
  size_t total_cancelled = 0;
454
545
 
455
546
  // Check all containers for matching items
456
- #if !defined(USE_ESP8266) && !defined(USE_RP2040)
547
+ #ifndef ESPHOME_THREAD_SINGLE
457
548
  // Only check defer queue for timeouts (intervals never go there)
458
549
  if (type == SchedulerItem::TIMEOUT) {
459
550
  for (auto &item : this->defer_queue_) {
460
- if (this->matches_item_(item, component, name_cstr, type)) {
461
- item->remove = true;
551
+ if (this->matches_item_(item, component, name_cstr, type, match_retry)) {
552
+ this->mark_item_removed_(item.get());
462
553
  total_cancelled++;
463
554
  }
464
555
  }
465
556
  }
466
- #endif
557
+ #endif /* not ESPHOME_THREAD_SINGLE */
467
558
 
468
559
  // Cancel items in the main heap
469
560
  for (auto &item : this->items_) {
470
- if (this->matches_item_(item, component, name_cstr, type)) {
471
- item->remove = true;
561
+ if (this->matches_item_(item, component, name_cstr, type, match_retry)) {
562
+ this->mark_item_removed_(item.get());
472
563
  total_cancelled++;
473
564
  this->to_remove_++; // Track removals for heap items
474
565
  }
@@ -476,8 +567,8 @@ bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_c
476
567
 
477
568
  // Cancel items in to_add_
478
569
  for (auto &item : this->to_add_) {
479
- if (this->matches_item_(item, component, name_cstr, type)) {
480
- item->remove = true;
570
+ if (this->matches_item_(item, component, name_cstr, type, match_retry)) {
571
+ this->mark_item_removed_(item.get());
481
572
  total_cancelled++;
482
573
  // Don't track removals for to_add_ items
483
574
  }
@@ -486,19 +577,162 @@ bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_c
486
577
  return total_cancelled > 0;
487
578
  }
488
579
 
489
- uint64_t Scheduler::millis_() {
490
- // Get the current 32-bit millis value
491
- const uint32_t now = millis();
492
- // Check for rollover by comparing with last value
493
- if (now < this->last_millis_) {
494
- // Detected rollover (happens every ~49.7 days)
580
+ uint64_t Scheduler::millis_64_(uint32_t now) {
581
+ // THREAD SAFETY NOTE:
582
+ // This function has three implementations, based on the precompiler flags
583
+ // - ESPHOME_THREAD_SINGLE - Runs on single-threaded platforms (ESP8266, RP2040, etc.)
584
+ // - ESPHOME_THREAD_MULTI_NO_ATOMICS - Runs on multi-threaded platforms without atomics (LibreTiny)
585
+ // - ESPHOME_THREAD_MULTI_ATOMICS - Runs on multi-threaded platforms with atomics (ESP32, HOST, etc.)
586
+ //
587
+ // Make sure all changes are synchronized if you edit this function.
588
+ //
589
+ // IMPORTANT: Always pass fresh millis() values to this function. The implementation
590
+ // handles out-of-order timestamps between threads, but minimizing time differences
591
+ // helps maintain accuracy.
592
+ //
593
+
594
+ #ifdef ESPHOME_THREAD_SINGLE
595
+ // This is the single core implementation.
596
+ //
597
+ // Single-core platforms have no concurrency, so this is a simple implementation
598
+ // that just tracks 32-bit rollover (every 49.7 days) without any locking or atomics.
599
+
600
+ uint16_t major = this->millis_major_;
601
+ uint32_t last = this->last_millis_;
602
+
603
+ // Check for rollover
604
+ if (now < last && (last - now) > HALF_MAX_UINT32) {
495
605
  this->millis_major_++;
496
- ESP_LOGD(TAG, "Incrementing scheduler major at %" PRIu64 "ms",
497
- now + (static_cast<uint64_t>(this->millis_major_) << 32));
606
+ major++;
607
+ #ifdef ESPHOME_DEBUG_SCHEDULER
608
+ ESP_LOGD(TAG, "Detected true 32-bit rollover at %" PRIu32 "ms (was %" PRIu32 ")", now, last);
609
+ #endif /* ESPHOME_DEBUG_SCHEDULER */
498
610
  }
499
- this->last_millis_ = now;
611
+
612
+ // Only update if time moved forward
613
+ if (now > last) {
614
+ this->last_millis_ = now;
615
+ }
616
+
500
617
  // Combine major (high 32 bits) and now (low 32 bits) into 64-bit time
501
- return now + (static_cast<uint64_t>(this->millis_major_) << 32);
618
+ return now + (static_cast<uint64_t>(major) << 32);
619
+
620
+ #elif defined(ESPHOME_THREAD_MULTI_NO_ATOMICS)
621
+ // This is the multi core no atomics implementation.
622
+ //
623
+ // Without atomics, this implementation uses locks more aggressively:
624
+ // 1. Always locks when near the rollover boundary (within 10 seconds)
625
+ // 2. Always locks when detecting a large backwards jump
626
+ // 3. Updates without lock in normal forward progression (accepting minor races)
627
+ // This is less efficient but necessary without atomic operations.
628
+ uint16_t major = this->millis_major_;
629
+ uint32_t last = this->last_millis_;
630
+
631
+ // Define a safe window around the rollover point (10 seconds)
632
+ // This covers any reasonable scheduler delays or thread preemption
633
+ static const uint32_t ROLLOVER_WINDOW = 10000; // 10 seconds in milliseconds
634
+
635
+ // Check if we're near the rollover boundary (close to std::numeric_limits<uint32_t>::max() or just past 0)
636
+ bool near_rollover = (last > (std::numeric_limits<uint32_t>::max() - ROLLOVER_WINDOW)) || (now < ROLLOVER_WINDOW);
637
+
638
+ if (near_rollover || (now < last && (last - now) > HALF_MAX_UINT32)) {
639
+ // Near rollover or detected a rollover - need lock for safety
640
+ LockGuard guard{this->lock_};
641
+ // Re-read with lock held
642
+ last = this->last_millis_;
643
+
644
+ if (now < last && (last - now) > HALF_MAX_UINT32) {
645
+ // True rollover detected (happens every ~49.7 days)
646
+ this->millis_major_++;
647
+ major++;
648
+ #ifdef ESPHOME_DEBUG_SCHEDULER
649
+ ESP_LOGD(TAG, "Detected true 32-bit rollover at %" PRIu32 "ms (was %" PRIu32 ")", now, last);
650
+ #endif /* ESPHOME_DEBUG_SCHEDULER */
651
+ }
652
+ // Update last_millis_ while holding lock
653
+ this->last_millis_ = now;
654
+ } else if (now > last) {
655
+ // Normal case: Not near rollover and time moved forward
656
+ // Update without lock. While this may cause minor races (microseconds of
657
+ // backwards time movement), they're acceptable because:
658
+ // 1. The scheduler operates at millisecond resolution, not microsecond
659
+ // 2. We've already prevented the critical rollover race condition
660
+ // 3. Any backwards movement is orders of magnitude smaller than scheduler delays
661
+ this->last_millis_ = now;
662
+ }
663
+ // If now <= last and we're not near rollover, don't update
664
+ // This minimizes backwards time movement
665
+
666
+ // Combine major (high 32 bits) and now (low 32 bits) into 64-bit time
667
+ return now + (static_cast<uint64_t>(major) << 32);
668
+
669
+ #elif defined(ESPHOME_THREAD_MULTI_ATOMICS)
670
+ // This is the multi core with atomics implementation.
671
+ //
672
+ // Uses atomic operations with acquire/release semantics to ensure coherent
673
+ // reads of millis_major_ and last_millis_ across cores. Features:
674
+ // 1. Epoch-coherency retry loop to handle concurrent updates
675
+ // 2. Lock only taken for actual rollover detection and update
676
+ // 3. Lock-free CAS updates for normal forward time progression
677
+ // 4. Memory ordering ensures cores see consistent time values
678
+
679
+ for (;;) {
680
+ uint16_t major = this->millis_major_.load(std::memory_order_acquire);
681
+
682
+ /*
683
+ * Acquire so that if we later decide **not** to take the lock we still
684
+ * observe a `millis_major_` value coherent with the loaded `last_millis_`.
685
+ * The acquire load ensures any later read of `millis_major_` sees its
686
+ * corresponding increment.
687
+ */
688
+ uint32_t last = this->last_millis_.load(std::memory_order_acquire);
689
+
690
+ // If we might be near a rollover (large backwards jump), take the lock for the entire operation
691
+ // This ensures rollover detection and last_millis_ update are atomic together
692
+ if (now < last && (last - now) > HALF_MAX_UINT32) {
693
+ // Potential rollover - need lock for atomic rollover detection + update
694
+ LockGuard guard{this->lock_};
695
+ // Re-read with lock held; mutex already provides ordering
696
+ last = this->last_millis_.load(std::memory_order_relaxed);
697
+
698
+ if (now < last && (last - now) > HALF_MAX_UINT32) {
699
+ // True rollover detected (happens every ~49.7 days)
700
+ this->millis_major_.fetch_add(1, std::memory_order_relaxed);
701
+ major++;
702
+ #ifdef ESPHOME_DEBUG_SCHEDULER
703
+ ESP_LOGD(TAG, "Detected true 32-bit rollover at %" PRIu32 "ms (was %" PRIu32 ")", now, last);
704
+ #endif /* ESPHOME_DEBUG_SCHEDULER */
705
+ }
706
+ /*
707
+ * Update last_millis_ while holding the lock to prevent races
708
+ * Publish the new low-word *after* bumping `millis_major_` (done above)
709
+ * so readers never see a mismatched pair.
710
+ */
711
+ this->last_millis_.store(now, std::memory_order_release);
712
+ } else {
713
+ // Normal case: Try lock-free update, but only allow forward movement within same epoch
714
+ // This prevents accidentally moving backwards across a rollover boundary
715
+ while (now > last && (now - last) < HALF_MAX_UINT32) {
716
+ if (this->last_millis_.compare_exchange_weak(last, now,
717
+ std::memory_order_release, // success
718
+ std::memory_order_relaxed)) { // failure
719
+ break;
720
+ }
721
+ // CAS failure means no data was published; relaxed is fine
722
+ // last is automatically updated by compare_exchange_weak if it fails
723
+ }
724
+ }
725
+ uint16_t major_end = this->millis_major_.load(std::memory_order_relaxed);
726
+ if (major_end == major)
727
+ return now + (static_cast<uint64_t>(major) << 32);
728
+ }
729
+ // Unreachable - the loop always returns when major_end == major
730
+ __builtin_unreachable();
731
+
732
+ #else
733
+ #error \
734
+ "No platform threading model defined. One of ESPHOME_THREAD_SINGLE, ESPHOME_THREAD_MULTI_NO_ATOMICS, or ESPHOME_THREAD_MULTI_ATOMICS must be defined."
735
+ #endif
502
736
  }
503
737
 
504
738
  bool HOT Scheduler::SchedulerItem::cmp(const std::unique_ptr<SchedulerItem> &a,