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