esphome 2025.6.2__py3-none-any.whl → 2025.7.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 (601) hide show
  1. esphome/__main__.py +1 -3
  2. esphome/codegen.py +2 -0
  3. esphome/components/ac_dimmer/ac_dimmer.cpp +6 -6
  4. esphome/components/adc/__init__.py +25 -1
  5. esphome/components/adc/adc_sensor.h +11 -11
  6. esphome/components/adc/adc_sensor_common.cpp +1 -1
  7. esphome/components/adc/adc_sensor_esp32.cpp +16 -8
  8. esphome/components/ade7880/ade7880.h +0 -2
  9. esphome/components/ads1115/ads1115.h +0 -1
  10. esphome/components/ads1118/ads1118.h +0 -1
  11. esphome/components/ags10/ags10.h +0 -2
  12. esphome/components/aic3204/aic3204.h +0 -1
  13. esphome/components/alarm_control_panel/__init__.py +5 -2
  14. esphome/components/alpha3/alpha3.h +0 -1
  15. esphome/components/am43/cover/am43_cover.h +0 -1
  16. esphome/components/am43/sensor/am43_sensor.h +0 -1
  17. esphome/components/analog_threshold/analog_threshold_binary_sensor.h +0 -2
  18. esphome/components/anova/anova.cpp +5 -1
  19. esphome/components/anova/anova.h +0 -1
  20. esphome/components/apds9960/apds9960.cpp +1 -1
  21. esphome/components/api/__init__.py +42 -20
  22. esphome/components/api/api_connection.cpp +318 -391
  23. esphome/components/api/api_connection.h +206 -126
  24. esphome/components/api/api_frame_helper.cpp +89 -124
  25. esphome/components/api/api_frame_helper.h +57 -45
  26. esphome/components/api/api_pb2.cpp +414 -4350
  27. esphome/components/api/api_pb2.h +287 -198
  28. esphome/components/api/api_pb2_dump.cpp +4333 -0
  29. esphome/components/api/api_pb2_service.cpp +180 -425
  30. esphome/components/api/api_pb2_service.h +7 -6
  31. esphome/components/api/api_pb2_size.h +2 -4
  32. esphome/components/api/api_server.cpp +138 -167
  33. esphome/components/api/api_server.h +66 -12
  34. esphome/components/api/client.py +10 -4
  35. esphome/components/api/list_entities.cpp +36 -105
  36. esphome/components/api/list_entities.h +31 -23
  37. esphome/components/api/proto.h +26 -3
  38. esphome/components/api/subscribe_state.cpp +23 -29
  39. esphome/components/api/subscribe_state.h +26 -19
  40. esphome/components/as5600/as5600.h +0 -1
  41. esphome/components/async_tcp/__init__.py +14 -5
  42. esphome/components/atc_mithermometer/atc_mithermometer.h +0 -1
  43. esphome/components/atm90e32/atm90e32.cpp +2 -1
  44. esphome/components/audio/audio_decoder.cpp +1 -1
  45. esphome/components/audio/audio_transfer_buffer.cpp +2 -2
  46. esphome/components/b_parasite/b_parasite.h +0 -1
  47. esphome/components/bedjet/bedjet_hub.cpp +5 -1
  48. esphome/components/bedjet/climate/bedjet_climate.cpp +5 -1
  49. esphome/components/beken_spi_led_strip/led_strip.cpp +4 -2
  50. esphome/components/bh1750/bh1750.cpp +5 -5
  51. esphome/components/binary_sensor/__init__.py +82 -5
  52. esphome/components/binary_sensor/automation.h +19 -1
  53. esphome/components/binary_sensor/binary_sensor.cpp +12 -30
  54. esphome/components/binary_sensor/binary_sensor.h +11 -25
  55. esphome/components/binary_sensor/filter.cpp +29 -24
  56. esphome/components/binary_sensor/filter.h +20 -10
  57. esphome/components/ble_client/output/ble_binary_output.h +0 -1
  58. esphome/components/ble_client/sensor/ble_rssi_sensor.cpp +5 -1
  59. esphome/components/ble_client/sensor/ble_rssi_sensor.h +0 -1
  60. esphome/components/ble_client/sensor/ble_sensor.cpp +5 -1
  61. esphome/components/ble_client/sensor/ble_sensor.h +0 -1
  62. esphome/components/ble_client/switch/ble_switch.h +0 -1
  63. esphome/components/ble_client/text_sensor/ble_text_sensor.cpp +5 -1
  64. esphome/components/ble_client/text_sensor/ble_text_sensor.h +0 -1
  65. esphome/components/ble_presence/ble_presence_device.h +0 -1
  66. esphome/components/ble_rssi/ble_rssi_sensor.h +0 -1
  67. esphome/components/ble_scanner/ble_scanner.h +0 -1
  68. esphome/components/bluetooth_proxy/bluetooth_connection.h +9 -2
  69. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +16 -6
  70. esphome/components/bluetooth_proxy/bluetooth_proxy.h +8 -2
  71. esphome/components/bme680/sensor.py +1 -1
  72. esphome/components/bmp581/bmp581.h +0 -2
  73. esphome/components/button/__init__.py +5 -2
  74. esphome/components/camera/__init__.py +1 -0
  75. esphome/components/camera/camera.cpp +22 -0
  76. esphome/components/camera/camera.h +80 -0
  77. esphome/components/canbus/__init__.py +1 -0
  78. esphome/components/cap1188/cap1188.h +0 -1
  79. esphome/components/captive_portal/__init__.py +12 -2
  80. esphome/components/captive_portal/captive_portal.cpp +12 -2
  81. esphome/components/captive_portal/captive_portal.h +5 -2
  82. esphome/components/ccs811/ccs811.h +0 -2
  83. esphome/components/climate/__init__.py +5 -2
  84. esphome/components/cm1106/sensor.py +2 -2
  85. esphome/components/const/__init__.py +2 -0
  86. esphome/components/copy/binary_sensor/copy_binary_sensor.h +0 -1
  87. esphome/components/copy/button/copy_button.h +0 -1
  88. esphome/components/copy/cover/copy_cover.h +0 -1
  89. esphome/components/copy/fan/copy_fan.h +0 -1
  90. esphome/components/copy/lock/copy_lock.h +0 -1
  91. esphome/components/copy/number/copy_number.h +0 -1
  92. esphome/components/copy/select/copy_select.h +0 -1
  93. esphome/components/copy/sensor/copy_sensor.h +0 -1
  94. esphome/components/copy/switch/copy_switch.h +0 -1
  95. esphome/components/copy/text/copy_text.h +0 -1
  96. esphome/components/copy/text_sensor/copy_text_sensor.h +0 -1
  97. esphome/components/cover/__init__.py +5 -2
  98. esphome/components/cs5460a/cs5460a.h +0 -1
  99. esphome/components/datetime/__init__.py +4 -2
  100. esphome/components/debug/__init__.py +20 -0
  101. esphome/components/deep_sleep/__init__.py +43 -9
  102. esphome/components/demo/__init__.py +2 -2
  103. esphome/components/display/display.cpp +4 -3
  104. esphome/components/display/display.h +0 -2
  105. esphome/components/display/display_buffer.cpp +1 -1
  106. esphome/components/ds2484/__init__.py +1 -0
  107. esphome/components/ds2484/ds2484.cpp +209 -0
  108. esphome/components/ds2484/ds2484.h +43 -0
  109. esphome/components/ds2484/one_wire.py +37 -0
  110. esphome/components/duty_time/duty_time_sensor.h +0 -1
  111. esphome/components/ens160_base/ens160_base.h +0 -1
  112. esphome/components/es7210/es7210.h +0 -1
  113. esphome/components/es7243e/es7243e.h +0 -1
  114. esphome/components/es8156/es8156.h +0 -1
  115. esphome/components/es8311/es8311.h +0 -1
  116. esphome/components/es8388/es8388.h +0 -1
  117. esphome/components/esp32/__init__.py +102 -135
  118. esphome/components/esp32/core.cpp +0 -4
  119. esphome/components/esp32/gpio.h +1 -1
  120. esphome/components/esp32/helpers.cpp +69 -0
  121. esphome/components/esp32_ble/ble.cpp +5 -6
  122. esphome/components/esp32_ble/ble.h +29 -14
  123. esphome/components/esp32_ble/ble_event.h +6 -6
  124. esphome/components/esp32_ble_client/ble_client_base.cpp +21 -6
  125. esphome/components/esp32_ble_client/ble_client_base.h +24 -9
  126. esphome/components/esp32_ble_tracker/__init__.py +2 -8
  127. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +5 -5
  128. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +11 -7
  129. esphome/components/esp32_camera/__init__.py +111 -97
  130. esphome/components/esp32_camera/esp32_camera.cpp +41 -31
  131. esphome/components/esp32_camera/esp32_camera.h +35 -30
  132. esphome/components/esp32_camera_web_server/__init__.py +2 -1
  133. esphome/components/esp32_camera_web_server/camera_web_server.cpp +8 -8
  134. esphome/components/esp32_camera_web_server/camera_web_server.h +3 -3
  135. esphome/components/esp32_hall/sensor.py +2 -21
  136. esphome/components/esp32_hosted/__init__.py +101 -0
  137. esphome/components/esp32_hosted/esp32_hosted.py.script +12 -0
  138. esphome/components/esp32_improv/esp32_improv_component.cpp +3 -0
  139. esphome/components/esp32_rmt/__init__.py +0 -58
  140. esphome/components/esp32_rmt_led_strip/led_strip.cpp +77 -63
  141. esphome/components/esp32_rmt_led_strip/led_strip.h +11 -17
  142. esphome/components/esp32_rmt_led_strip/light.py +14 -76
  143. esphome/components/esp32_touch/esp32_touch.h +174 -28
  144. esphome/components/esp32_touch/esp32_touch_common.cpp +162 -0
  145. esphome/components/esp32_touch/esp32_touch_v1.cpp +238 -0
  146. esphome/components/esp32_touch/esp32_touch_v2.cpp +397 -0
  147. esphome/components/esp8266/__init__.py +1 -0
  148. esphome/components/esp8266/gpio.cpp +10 -10
  149. esphome/components/esp8266/helpers.cpp +31 -0
  150. esphome/components/esphome/ota/__init__.py +1 -0
  151. esphome/components/esphome/ota/ota_esphome.cpp +24 -19
  152. esphome/components/ethernet/__init__.py +42 -23
  153. esphome/components/ethernet/esp_eth_phy_jl1101.c +0 -16
  154. esphome/components/ethernet/ethernet_component.cpp +69 -29
  155. esphome/components/ethernet/ethernet_component.h +18 -10
  156. esphome/components/event/__init__.py +5 -2
  157. esphome/components/ezo/ezo.h +0 -1
  158. esphome/components/ezo_pmp/ezo_pmp.h +0 -1
  159. esphome/components/fan/__init__.py +5 -2
  160. esphome/components/feedback/feedback_cover.h +0 -1
  161. esphome/components/font/__init__.py +92 -82
  162. esphome/components/font/font.cpp +9 -2
  163. esphome/components/font/font.h +20 -5
  164. esphome/components/fs3000/fs3000.h +0 -1
  165. esphome/components/gcja5/gcja5.h +0 -1
  166. esphome/components/gl_r01_i2c/__init__.py +0 -0
  167. esphome/components/gl_r01_i2c/gl_r01_i2c.cpp +68 -0
  168. esphome/components/gl_r01_i2c/gl_r01_i2c.h +22 -0
  169. esphome/components/gl_r01_i2c/sensor.py +36 -0
  170. esphome/components/gp8403/gp8403.h +0 -1
  171. esphome/components/gpio/binary_sensor/__init__.py +17 -0
  172. esphome/components/gpio/binary_sensor/gpio_binary_sensor.cpp +77 -3
  173. esphome/components/gpio/binary_sensor/gpio_binary_sensor.h +40 -0
  174. esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.h +0 -2
  175. esphome/components/he60r/he60r.h +0 -1
  176. esphome/components/heatpumpir/climate.py +2 -1
  177. esphome/components/heatpumpir/heatpumpir.cpp +1 -0
  178. esphome/components/heatpumpir/heatpumpir.h +1 -0
  179. esphome/components/honeywellabp2_i2c/honeywellabp2.h +0 -1
  180. esphome/components/host/__init__.py +2 -1
  181. esphome/components/host/helpers.cpp +57 -0
  182. esphome/components/http_request/__init__.py +19 -1
  183. esphome/components/http_request/http_request.h +1 -1
  184. esphome/components/http_request/http_request_arduino.cpp +0 -1
  185. esphome/components/http_request/http_request_arduino.h +1 -0
  186. esphome/components/http_request/http_request_idf.cpp +0 -1
  187. esphome/components/http_request/ota/ota_http_request.cpp +1 -1
  188. esphome/components/http_request/update/http_request_update.cpp +28 -9
  189. esphome/components/hydreon_rgxx/hydreon_rgxx.cpp +3 -9
  190. esphome/components/hydreon_rgxx/sensor.py +1 -1
  191. esphome/components/i2c/__init__.py +23 -11
  192. esphome/components/i2c/i2c_bus.h +8 -1
  193. esphome/components/i2c/i2c_bus_arduino.cpp +4 -3
  194. esphome/components/i2c/i2c_bus_arduino.h +6 -3
  195. esphome/components/i2c/i2c_bus_esp_idf.h +5 -3
  196. esphome/components/i2c_device/i2c_device.h +0 -1
  197. esphome/components/i2s_audio/__init__.py +2 -10
  198. esphome/components/i2s_audio/i2s_audio.cpp +1 -5
  199. esphome/components/i2s_audio/media_player/__init__.py +2 -2
  200. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +2 -2
  201. esphome/components/iaqcore/iaqcore.h +0 -2
  202. esphome/components/image/__init__.py +123 -24
  203. esphome/components/improv_serial/improv_serial_component.cpp +0 -4
  204. esphome/components/ina219/ina219.cpp +7 -0
  205. esphome/components/ina219/ina219.h +1 -0
  206. esphome/components/ina260/ina260.h +0 -2
  207. esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.h +0 -1
  208. esphome/components/inkplate6/display.py +15 -0
  209. esphome/components/inkplate6/inkplate.cpp +2 -2
  210. esphome/components/integration/integration_sensor.h +0 -1
  211. esphome/components/internal_temperature/internal_temperature.cpp +8 -27
  212. esphome/components/internal_temperature/sensor.py +0 -26
  213. esphome/components/interval/interval.h +0 -2
  214. esphome/components/ld2410/button/__init__.py +3 -3
  215. esphome/components/ld2410/button/factory_reset_button.cpp +9 -0
  216. esphome/components/ld2410/button/{reset_button.h → factory_reset_button.h} +2 -2
  217. esphome/components/ld2410/ld2410.cpp +430 -261
  218. esphome/components/ld2410/ld2410.h +44 -146
  219. esphome/components/ld2410/number/__init__.py +2 -2
  220. esphome/components/ld2410/sensor.py +1 -1
  221. esphome/components/ld2410/switch/__init__.py +1 -1
  222. esphome/components/ld2420/ld2420.cpp +196 -100
  223. esphome/components/ld2420/ld2420.h +46 -118
  224. esphome/components/ld2420/number/__init__.py +2 -2
  225. esphome/components/ld2420/sensor/__init__.py +6 -2
  226. esphome/components/ld2420/sensor/ld2420_sensor.h +1 -1
  227. esphome/components/ld2450/button/__init__.py +3 -3
  228. esphome/components/ld2450/button/factory_reset_button.cpp +9 -0
  229. esphome/components/ld2450/button/{reset_button.h → factory_reset_button.h} +2 -2
  230. esphome/components/ld2450/ld2450.cpp +384 -232
  231. esphome/components/ld2450/ld2450.h +60 -69
  232. esphome/components/ld2450/switch/__init__.py +1 -1
  233. esphome/components/ledc/ledc_output.cpp +1 -63
  234. esphome/components/libretiny/__init__.py +4 -3
  235. esphome/components/libretiny/const.py +5 -0
  236. esphome/components/libretiny/generate_components.py +1 -0
  237. esphome/components/libretiny/helpers.cpp +35 -0
  238. esphome/components/libretiny/lt_component.cpp +5 -3
  239. esphome/components/light/__init__.py +4 -2
  240. esphome/components/light/addressable_light.h +3 -3
  241. esphome/components/light/light_call.cpp +180 -243
  242. esphome/components/light/light_call.h +72 -20
  243. esphome/components/light/light_color_values.h +14 -14
  244. esphome/components/light/light_state.h +15 -13
  245. esphome/components/light/transformers.h +2 -2
  246. esphome/components/ln882x/__init__.py +52 -0
  247. esphome/components/ln882x/boards.py +285 -0
  248. esphome/components/lock/__init__.py +5 -2
  249. esphome/components/logger/__init__.py +40 -3
  250. esphome/components/logger/logger.cpp +47 -12
  251. esphome/components/logger/logger.h +80 -49
  252. esphome/components/logger/logger_esp32.cpp +3 -3
  253. esphome/components/lps22/__init__.py +0 -0
  254. esphome/components/lps22/lps22.cpp +75 -0
  255. esphome/components/lps22/lps22.h +27 -0
  256. esphome/components/lps22/sensor.py +58 -0
  257. esphome/components/ltr390/ltr390.h +0 -1
  258. esphome/components/ltr501/ltr501.h +0 -1
  259. esphome/components/ltr_als_ps/ltr_als_ps.h +0 -1
  260. esphome/components/lvgl/__init__.py +1 -1
  261. esphome/components/lvgl/schemas.py +66 -6
  262. esphome/components/lvgl/styles.py +24 -16
  263. esphome/components/lvgl/widgets/__init__.py +12 -2
  264. esphome/components/lvgl/widgets/lv_bar.py +40 -19
  265. esphome/components/m5stack_8angle/light/m5stack_8angle_light.cpp +1 -1
  266. esphome/components/max9611/max9611.h +0 -1
  267. esphome/components/mcp23016/__init__.py +1 -1
  268. esphome/components/mcp23xxx_base/__init__.py +1 -1
  269. esphome/components/mcp4461/__init__.py +1 -1
  270. esphome/components/mcp4461/output/__init__.py +3 -2
  271. esphome/components/mcp9600/mcp9600.h +0 -2
  272. esphome/components/md5/md5.cpp +3 -3
  273. esphome/components/md5/md5.h +1 -6
  274. esphome/components/mdns/__init__.py +22 -11
  275. esphome/components/media_player/__init__.py +4 -3
  276. esphome/components/micro_wake_word/__init__.py +1 -5
  277. esphome/components/micro_wake_word/streaming_model.cpp +2 -2
  278. esphome/components/microphone/microphone.cpp +7 -9
  279. esphome/components/microphone/microphone.h +0 -2
  280. esphome/components/mipi_spi/display.py +1 -0
  281. esphome/components/mmc5603/mmc5603.cpp +1 -1
  282. esphome/components/modbus/modbus.cpp +33 -15
  283. esphome/components/modbus/modbus.h +9 -0
  284. esphome/components/modbus_controller/__init__.py +42 -10
  285. esphome/components/modbus_controller/modbus_controller.cpp +92 -11
  286. esphome/components/modbus_controller/modbus_controller.h +61 -7
  287. esphome/components/mopeka_pro_check/mopeka_pro_check.h +0 -1
  288. esphome/components/mopeka_std_check/mopeka_std_check.h +0 -1
  289. esphome/components/mpl3115a2/mpl3115a2.h +0 -2
  290. esphome/components/mqtt/__init__.py +16 -0
  291. esphome/components/mqtt/mqtt_backend.h +2 -1
  292. esphome/components/mqtt/mqtt_backend_esp32.cpp +126 -45
  293. esphome/components/mqtt/mqtt_backend_esp32.h +106 -4
  294. esphome/components/mqtt/mqtt_client.cpp +15 -9
  295. esphome/components/mqtt/mqtt_client.h +8 -3
  296. esphome/components/ms8607/ms8607.h +0 -1
  297. esphome/components/neopixelbus/light.py +4 -1
  298. esphome/components/neopixelbus/neopixelbus_light.h +1 -1
  299. esphome/components/network/__init__.py +4 -1
  300. esphome/components/network/ip_address.h +1 -0
  301. esphome/components/nextion/__init__.py +16 -0
  302. esphome/components/nextion/base_component.py +1 -0
  303. esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp +1 -1
  304. esphome/components/nextion/display.py +14 -4
  305. esphome/components/nextion/nextion.cpp +166 -101
  306. esphome/components/nextion/nextion.h +84 -53
  307. esphome/components/nextion/nextion_commands.cpp +11 -10
  308. esphome/components/nextion/nextion_component.cpp +28 -28
  309. esphome/components/nextion/nextion_component.h +53 -18
  310. esphome/components/nextion/nextion_component_base.h +3 -0
  311. esphome/components/nextion/nextion_upload.cpp +36 -0
  312. esphome/components/nextion/nextion_upload_arduino.cpp +10 -35
  313. esphome/components/nextion/nextion_upload_idf.cpp +9 -33
  314. esphome/components/nextion/sensor/nextion_sensor.cpp +1 -1
  315. esphome/components/nextion/switch/nextion_switch.cpp +1 -1
  316. esphome/components/nextion/text_sensor/nextion_textsensor.cpp +1 -1
  317. esphome/components/nfc/nfc.cpp +3 -22
  318. esphome/components/nfc/nfc.h +3 -3
  319. esphome/components/number/__init__.py +5 -2
  320. esphome/components/online_image/__init__.py +5 -0
  321. esphome/components/online_image/online_image.cpp +6 -2
  322. esphome/components/online_image/online_image.h +4 -1
  323. esphome/components/opentherm/opentherm.cpp +7 -12
  324. esphome/components/openthread/__init__.py +47 -40
  325. esphome/components/openthread/const.py +1 -0
  326. esphome/components/openthread/openthread_esp.cpp +27 -5
  327. esphome/components/opt3001/__init__.py +0 -0
  328. esphome/components/opt3001/opt3001.cpp +122 -0
  329. esphome/components/opt3001/opt3001.h +27 -0
  330. esphome/components/opt3001/sensor.py +35 -0
  331. esphome/components/ota/__init__.py +17 -0
  332. esphome/components/ota/ota_backend.h +27 -1
  333. esphome/components/ota/ota_backend_arduino_esp32.cpp +12 -2
  334. esphome/components/ota/ota_backend_arduino_esp32.h +3 -0
  335. esphome/components/ota/ota_backend_arduino_esp8266.cpp +18 -4
  336. esphome/components/ota/ota_backend_arduino_esp8266.h +3 -0
  337. esphome/components/ota/ota_backend_arduino_libretiny.cpp +12 -2
  338. esphome/components/ota/ota_backend_arduino_libretiny.h +3 -0
  339. esphome/components/ota/ota_backend_arduino_rp2040.cpp +9 -2
  340. esphome/components/ota/ota_backend_arduino_rp2040.h +3 -0
  341. esphome/components/ota/ota_backend_esp_idf.cpp +10 -16
  342. esphome/components/ota/ota_backend_esp_idf.h +1 -0
  343. esphome/components/packages/__init__.py +5 -2
  344. esphome/components/packet_transport/binary_sensor.py +61 -4
  345. esphome/components/packet_transport/packet_transport.cpp +31 -1
  346. esphome/components/packet_transport/packet_transport.h +11 -5
  347. esphome/components/pcf8574/__init__.py +1 -1
  348. esphome/components/pi4ioe5v6408/__init__.py +84 -0
  349. esphome/components/pi4ioe5v6408/pi4ioe5v6408.cpp +171 -0
  350. esphome/components/pi4ioe5v6408/pi4ioe5v6408.h +70 -0
  351. esphome/components/pmsa003i/pmsa003i.h +0 -1
  352. esphome/components/pmsx003/pmsx003.h +0 -1
  353. esphome/components/pn7150/pn7150.cpp +7 -7
  354. esphome/components/pn7150/pn7150.h +0 -1
  355. esphome/components/pn7160/pn7160.cpp +7 -7
  356. esphome/components/pn7160/pn7160.h +0 -1
  357. esphome/components/preferences/syncer.h +2 -0
  358. esphome/components/prometheus/prometheus_handler.h +1 -1
  359. esphome/components/psram/psram.cpp +0 -20
  360. esphome/components/pulse_counter/pulse_counter_sensor.h +0 -1
  361. esphome/components/pulse_meter/pulse_meter_sensor.cpp +8 -4
  362. esphome/components/pulse_width/pulse_width.h +0 -1
  363. esphome/components/pvvx_mithermometer/display/pvvx_display.cpp +0 -4
  364. esphome/components/pvvx_mithermometer/display/pvvx_display.h +0 -2
  365. esphome/components/pvvx_mithermometer/pvvx_mithermometer.h +0 -1
  366. esphome/components/qr_code/__init__.py +13 -10
  367. esphome/components/qwiic_pir/qwiic_pir.h +0 -1
  368. esphome/components/radon_eye_ble/radon_eye_listener.cpp +1 -1
  369. esphome/components/rc522/rc522.h +0 -1
  370. esphome/components/rdm6300/rdm6300.h +0 -2
  371. esphome/components/remote_base/__init__.py +7 -5
  372. esphome/components/remote_base/remote_base.cpp +24 -21
  373. esphome/components/remote_base/remote_base.h +3 -26
  374. esphome/components/remote_receiver/__init__.py +40 -46
  375. esphome/components/remote_receiver/remote_receiver.h +4 -18
  376. esphome/components/remote_receiver/remote_receiver_esp32.cpp +0 -87
  377. esphome/components/remote_receiver/remote_receiver_esp8266.cpp +1 -1
  378. esphome/components/remote_transmitter/__init__.py +42 -43
  379. esphome/components/remote_transmitter/remote_transmitter.h +2 -14
  380. esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +0 -77
  381. esphome/components/resistance/resistance_sensor.h +0 -1
  382. esphome/components/rp2040/__init__.py +1 -0
  383. esphome/components/rp2040/helpers.cpp +55 -0
  384. esphome/components/rp2040_pio_led_strip/led_strip.cpp +2 -2
  385. esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp +0 -4
  386. esphome/components/rtttl/__init__.py +4 -4
  387. esphome/components/rtttl/rtttl.cpp +10 -1
  388. esphome/components/ruuvitag/ruuvitag.h +0 -1
  389. esphome/components/safe_mode/safe_mode.cpp +2 -0
  390. esphome/components/safe_mode/safe_mode.h +4 -1
  391. esphome/components/scd30/scd30.h +0 -1
  392. esphome/components/scd30/sensor.py +2 -2
  393. esphome/components/scd4x/scd4x.cpp +61 -54
  394. esphome/components/scd4x/scd4x.h +17 -15
  395. esphome/components/scd4x/sensor.py +4 -4
  396. esphome/components/script/script.h +0 -2
  397. esphome/components/sdp3x/sensor.py +1 -1
  398. esphome/components/select/__init__.py +5 -2
  399. esphome/components/sen5x/sen5x.h +0 -1
  400. esphome/components/senseair/senseair.h +0 -1
  401. esphome/components/sensor/__init__.py +4 -2
  402. esphome/components/sensor/filter.cpp +1 -1
  403. esphome/components/sensor/sensor.cpp +12 -6
  404. esphome/components/sensor/sensor.h +13 -5
  405. esphome/components/servo/servo.h +0 -1
  406. esphome/components/sfa30/sfa30.h +0 -1
  407. esphome/components/sgp30/sgp30.h +0 -1
  408. esphome/components/sgp4x/sgp4x.h +0 -1
  409. esphome/components/shelly_dimmer/stm32flash.cpp +1 -2
  410. esphome/components/sht4x/sht4x.h +0 -1
  411. esphome/components/sm300d2/sm300d2.h +0 -2
  412. esphome/components/smt100/sensor.py +8 -4
  413. esphome/components/smt100/smt100.cpp +5 -5
  414. esphome/components/smt100/smt100.h +3 -3
  415. esphome/components/sn74hc595/__init__.py +1 -1
  416. esphome/components/sn74hc595/sn74hc595.cpp +5 -4
  417. esphome/components/sntp/sntp_component.cpp +9 -3
  418. esphome/components/sntp/time.py +2 -0
  419. esphome/components/socket/__init__.py +17 -0
  420. esphome/components/spi/__init__.py +27 -6
  421. esphome/components/spi/spi.cpp +3 -2
  422. esphome/components/spi/spi.h +9 -3
  423. esphome/components/spi/spi_arduino.cpp +3 -5
  424. esphome/components/spi/spi_esp_idf.cpp +40 -21
  425. esphome/components/spi_led_strip/spi_led_strip.cpp +1 -1
  426. esphome/components/sps30/sps30.h +0 -1
  427. esphome/components/ssd1306_base/ssd1306_base.cpp +1 -1
  428. esphome/components/st7701s/st7701s.cpp +0 -4
  429. esphome/components/status/status_binary_sensor.h +0 -2
  430. esphome/components/substitutions/__init__.py +76 -19
  431. esphome/components/substitutions/jinja.py +99 -0
  432. esphome/components/sun/sun.cpp +3 -4
  433. esphome/components/switch/__init__.py +5 -2
  434. esphome/components/switch/binary_sensor/switch_binary_sensor.h +0 -1
  435. esphome/components/sx126x/__init__.py +317 -0
  436. esphome/components/sx126x/automation.h +62 -0
  437. esphome/components/sx126x/packet_transport/__init__.py +26 -0
  438. esphome/components/sx126x/packet_transport/sx126x_transport.cpp +26 -0
  439. esphome/components/sx126x/packet_transport/sx126x_transport.h +25 -0
  440. esphome/components/sx126x/sx126x.cpp +523 -0
  441. esphome/components/sx126x/sx126x.h +140 -0
  442. esphome/components/sx126x/sx126x_reg.h +163 -0
  443. esphome/components/sx127x/__init__.py +325 -0
  444. esphome/components/sx127x/automation.h +62 -0
  445. esphome/components/sx127x/packet_transport/__init__.py +26 -0
  446. esphome/components/sx127x/packet_transport/sx127x_transport.cpp +26 -0
  447. esphome/components/sx127x/packet_transport/sx127x_transport.h +25 -0
  448. esphome/components/sx127x/sx127x.cpp +498 -0
  449. esphome/components/sx127x/sx127x.h +128 -0
  450. esphome/components/sx127x/sx127x_reg.h +295 -0
  451. esphome/components/syslog/esphome_syslog.cpp +5 -3
  452. esphome/components/syslog/esphome_syslog.h +1 -1
  453. esphome/components/tca9555/__init__.py +1 -1
  454. esphome/components/template/binary_sensor/template_binary_sensor.cpp +1 -9
  455. esphome/components/text/__init__.py +5 -2
  456. esphome/components/text_sensor/__init__.py +5 -2
  457. esphome/components/thermostat/thermostat_climate.cpp +34 -31
  458. esphome/components/thermostat/thermostat_climate.h +43 -39
  459. esphome/components/time/__init__.py +16 -2
  460. esphome/components/time/real_time_clock.cpp +4 -0
  461. esphome/components/time/real_time_clock.h +5 -1
  462. esphome/components/tlc5971/tlc5971.cpp +4 -1
  463. esphome/components/tmp1075/tmp1075.h +0 -2
  464. esphome/components/tof10120/tof10120_sensor.h +0 -1
  465. esphome/components/tormatic/tormatic_cover.h +0 -1
  466. esphome/components/total_daily_energy/total_daily_energy.h +0 -1
  467. esphome/components/tsl2591/tsl2591.cpp +1 -1
  468. esphome/components/ttp229_bsf/ttp229_bsf.h +0 -1
  469. esphome/components/ttp229_lsf/ttp229_lsf.h +0 -1
  470. esphome/components/tx20/tx20.cpp +2 -2
  471. esphome/components/uart/__init__.py +18 -0
  472. esphome/components/uart/uart_component_esp_idf.cpp +1 -5
  473. esphome/components/update/__init__.py +5 -2
  474. esphome/components/update/update_entity.h +8 -0
  475. esphome/components/usb_host/__init__.py +5 -2
  476. esphome/components/valve/__init__.py +5 -2
  477. esphome/components/vbus/vbus.h +0 -1
  478. esphome/components/veml3235/veml3235.h +0 -1
  479. esphome/components/veml7700/veml7700.h +0 -1
  480. esphome/components/vl53l0x/vl53l0x_sensor.h +0 -1
  481. esphome/components/voice_assistant/voice_assistant.cpp +4 -4
  482. esphome/components/watchdog/watchdog.cpp +0 -4
  483. esphome/components/waveshare_epaper/waveshare_epaper.cpp +6 -6
  484. esphome/components/web_server/__init__.py +34 -19
  485. esphome/components/web_server/ota/__init__.py +32 -0
  486. esphome/components/web_server/ota/ota_web_server.cpp +210 -0
  487. esphome/components/web_server/ota/ota_web_server.h +26 -0
  488. esphome/components/web_server/web_server.cpp +311 -430
  489. esphome/components/web_server/web_server.h +33 -23
  490. esphome/components/web_server/web_server_v1.cpp +4 -5
  491. esphome/components/web_server_base/__init__.py +5 -2
  492. esphome/components/web_server_base/web_server_base.cpp +2 -94
  493. esphome/components/web_server_base/web_server_base.h +5 -25
  494. esphome/components/web_server_idf/multipart.cpp +254 -0
  495. esphome/components/web_server_idf/multipart.h +86 -0
  496. esphome/components/web_server_idf/utils.cpp +32 -0
  497. esphome/components/web_server_idf/utils.h +10 -0
  498. esphome/components/web_server_idf/web_server_idf.cpp +162 -16
  499. esphome/components/web_server_idf/web_server_idf.h +11 -10
  500. esphome/components/wiegand/wiegand.cpp +2 -2
  501. esphome/components/wifi/__init__.py +18 -0
  502. esphome/components/wifi/wifi_component.cpp +17 -22
  503. esphome/components/wifi/wifi_component.h +27 -23
  504. esphome/components/wifi/wifi_component_esp32_arduino.cpp +52 -59
  505. esphome/components/wifi/wifi_component_esp8266.cpp +46 -46
  506. esphome/components/wifi/wifi_component_esp_idf.cpp +35 -36
  507. esphome/components/wifi/wifi_component_libretiny.cpp +26 -27
  508. esphome/components/wifi/wifi_component_pico_w.cpp +3 -3
  509. esphome/components/wifi_info/wifi_info_text_sensor.cpp +6 -6
  510. esphome/components/wireguard/__init__.py +2 -11
  511. esphome/components/xiaomi_ble/xiaomi_ble.cpp +13 -1
  512. esphome/components/xiaomi_ble/xiaomi_ble.h +1 -0
  513. esphome/components/xiaomi_cgd1/xiaomi_cgd1.h +0 -1
  514. esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.h +0 -1
  515. esphome/components/xiaomi_cgg1/xiaomi_cgg1.h +0 -1
  516. esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.h +0 -1
  517. esphome/components/xiaomi_gcls002/xiaomi_gcls002.h +0 -1
  518. esphome/components/xiaomi_hhccjcy01/xiaomi_hhccjcy01.h +0 -1
  519. esphome/components/xiaomi_hhccjcy10/xiaomi_hhccjcy10.h +0 -1
  520. esphome/components/xiaomi_hhccpot002/xiaomi_hhccpot002.h +0 -1
  521. esphome/components/xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.h +0 -1
  522. esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.h +0 -1
  523. esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.h +0 -1
  524. esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.h +0 -1
  525. esphome/components/xiaomi_lywsdcgq/xiaomi_lywsdcgq.h +0 -1
  526. esphome/components/xiaomi_mhoc303/xiaomi_mhoc303.h +0 -1
  527. esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.h +0 -1
  528. esphome/components/xiaomi_miscale/xiaomi_miscale.h +0 -1
  529. esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.h +0 -1
  530. esphome/components/xiaomi_mue4094rt/xiaomi_mue4094rt.h +0 -1
  531. esphome/components/xiaomi_rtcgq02lm/xiaomi_rtcgq02lm.h +0 -1
  532. esphome/components/xiaomi_wx08zm/xiaomi_wx08zm.h +0 -1
  533. esphome/components/xiaomi_xmwsdj04mmc/__init__.py +0 -0
  534. esphome/components/xiaomi_xmwsdj04mmc/sensor.py +77 -0
  535. esphome/components/xiaomi_xmwsdj04mmc/xiaomi_xmwsdj04mmc.cpp +77 -0
  536. esphome/components/xiaomi_xmwsdj04mmc/xiaomi_xmwsdj04mmc.h +36 -0
  537. esphome/components/zio_ultrasonic/zio_ultrasonic.h +0 -2
  538. esphome/components/zyaura/zyaura.h +0 -1
  539. esphome/config.py +88 -22
  540. esphome/config_helpers.py +74 -1
  541. esphome/config_validation.py +12 -1
  542. esphome/const.py +65 -10
  543. esphome/core/__init__.py +18 -2
  544. esphome/core/application.cpp +163 -10
  545. esphome/core/application.h +145 -165
  546. esphome/core/area.h +19 -0
  547. esphome/core/automation.h +58 -9
  548. esphome/core/color.cpp +3 -5
  549. esphome/core/color.h +16 -16
  550. esphome/core/component.cpp +151 -18
  551. esphome/core/component.h +98 -4
  552. esphome/core/component_iterator.cpp +7 -7
  553. esphome/core/component_iterator.h +9 -7
  554. esphome/core/config.py +155 -6
  555. esphome/core/controller.cpp +4 -2
  556. esphome/core/controller.h +1 -1
  557. esphome/core/datatypes.h +2 -2
  558. esphome/core/defines.h +17 -2
  559. esphome/core/device.h +20 -0
  560. esphome/core/entity_base.cpp +20 -15
  561. esphome/core/entity_base.h +76 -0
  562. esphome/core/entity_helpers.py +162 -1
  563. esphome/core/event_pool.h +81 -0
  564. esphome/core/helpers.cpp +75 -230
  565. esphome/core/helpers.h +164 -104
  566. esphome/core/lock_free_queue.h +151 -0
  567. esphome/core/log.cpp +2 -2
  568. esphome/core/log.h +2 -0
  569. esphome/core/optional.h +5 -0
  570. esphome/core/ring_buffer.cpp +2 -2
  571. esphome/core/scheduler.cpp +278 -103
  572. esphome/core/scheduler.h +157 -17
  573. esphome/core/time.cpp +5 -5
  574. esphome/core/time.h +5 -5
  575. esphome/cpp_generator.py +17 -0
  576. esphome/cpp_helpers.py +0 -22
  577. esphome/cpp_types.py +3 -1
  578. esphome/dashboard/entries.py +1 -1
  579. esphome/dashboard/util/text.py +5 -21
  580. esphome/dashboard/web_server.py +9 -1
  581. esphome/helpers.py +47 -0
  582. esphome/loader.py +15 -1
  583. esphome/pins.py +14 -8
  584. esphome/wizard.py +16 -3
  585. esphome/writer.py +21 -3
  586. esphome/yaml_util.py +0 -2
  587. {esphome-2025.6.2.dist-info → esphome-2025.7.0b1.dist-info}/METADATA +10 -9
  588. {esphome-2025.6.2.dist-info → esphome-2025.7.0b1.dist-info}/RECORD +593 -533
  589. esphome/components/esp32_ble/ble_event_pool.h +0 -72
  590. esphome/components/esp32_ble/queue.h +0 -85
  591. esphome/components/esp32_hall/esp32_hall.cpp +0 -25
  592. esphome/components/esp32_hall/esp32_hall.h +0 -23
  593. esphome/components/esp32_touch/esp32_touch.cpp +0 -355
  594. esphome/components/ld2410/button/reset_button.cpp +0 -9
  595. esphome/components/ld2450/button/reset_button.cpp +0 -9
  596. esphome/components/openthread/tlv.py +0 -65
  597. /esphome/{dashboard/enum.py → enum.py} +0 -0
  598. {esphome-2025.6.2.dist-info → esphome-2025.7.0b1.dist-info}/WHEEL +0 -0
  599. {esphome-2025.6.2.dist-info → esphome-2025.7.0b1.dist-info}/entry_points.txt +0 -0
  600. {esphome-2025.6.2.dist-info → esphome-2025.7.0b1.dist-info}/licenses/LICENSE +0 -0
  601. {esphome-2025.6.2.dist-info → esphome-2025.7.0b1.dist-info}/top_level.txt +0 -0
@@ -7,6 +7,7 @@
7
7
  #include "esphome/core/log.h"
8
8
  #include <algorithm>
9
9
  #include <cinttypes>
10
+ #include <cstring>
10
11
 
11
12
  namespace esphome {
12
13
 
@@ -17,67 +18,150 @@ static const uint32_t MAX_LOGICALLY_DELETED_ITEMS = 10;
17
18
  // Uncomment to debug scheduler
18
19
  // #define ESPHOME_DEBUG_SCHEDULER
19
20
 
21
+ #ifdef ESPHOME_DEBUG_SCHEDULER
22
+ // Helper to validate that a pointer looks like it's in static memory
23
+ static void validate_static_string(const char *name) {
24
+ if (name == nullptr)
25
+ return;
26
+
27
+ // This is a heuristic check - stack and heap pointers are typically
28
+ // much higher in memory than static data
29
+ uintptr_t addr = reinterpret_cast<uintptr_t>(name);
30
+
31
+ // Create a stack variable to compare against
32
+ int stack_var;
33
+ uintptr_t stack_addr = reinterpret_cast<uintptr_t>(&stack_var);
34
+
35
+ // If the string pointer is near our stack variable, it's likely on the stack
36
+ // Using 8KB range as ESP32 main task stack is typically 8192 bytes
37
+ if (addr > (stack_addr - 0x2000) && addr < (stack_addr + 0x2000)) {
38
+ ESP_LOGW(TAG,
39
+ "WARNING: Scheduler name '%s' at %p appears to be on the stack - this is unsafe!\n"
40
+ " Stack reference at %p",
41
+ name, name, &stack_var);
42
+ }
43
+
44
+ // Also check if it might be on the heap by seeing if it's in a very different range
45
+ // This is platform-specific but generally heap is allocated far from static memory
46
+ static const char *static_str = "test";
47
+ uintptr_t static_addr = reinterpret_cast<uintptr_t>(static_str);
48
+
49
+ // If the address is very far from known static memory, it might be heap
50
+ if (addr > static_addr + 0x100000 || (static_addr > 0x100000 && addr < static_addr - 0x100000)) {
51
+ ESP_LOGW(TAG, "WARNING: Scheduler name '%s' at %p might be on heap (static ref at %p)", name, name, static_str);
52
+ }
53
+ }
54
+ #endif
55
+
20
56
  // A note on locking: the `lock_` lock protects the `items_` and `to_add_` containers. It must be taken when writing to
21
57
  // them (i.e. when adding/removing items, but not when changing items). As items are only deleted from the loop task,
22
58
  // iterating over them from the loop task is fine; but iterating from any other context requires the lock to be held to
23
59
  // avoid the main thread modifying the list while it is being accessed.
24
60
 
25
- void HOT Scheduler::set_timeout(Component *component, const std::string &name, uint32_t timeout,
26
- std::function<void()> func) {
27
- const auto now = this->millis_();
28
-
29
- if (!name.empty())
30
- this->cancel_timeout(component, name);
61
+ // Common implementation for both timeout and interval
62
+ 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) {
64
+ // Get the name as const char*
65
+ const char *name_cstr = this->get_name_cstr_(is_static_string, name_ptr);
31
66
 
32
- if (timeout == SCHEDULER_DONT_RUN)
67
+ if (delay == SCHEDULER_DONT_RUN) {
68
+ // Still need to cancel existing timer if name is not empty
69
+ if (this->is_name_valid_(name_cstr)) {
70
+ LockGuard guard{this->lock_};
71
+ this->cancel_item_locked_(component, name_cstr, type);
72
+ }
33
73
  return;
74
+ }
34
75
 
76
+ // Create and populate the scheduler item
35
77
  auto item = make_unique<SchedulerItem>();
36
78
  item->component = component;
37
- item->name = name;
38
- item->type = SchedulerItem::TIMEOUT;
39
- item->next_execution_ = now + timeout;
79
+ item->set_name(name_cstr, !is_static_string);
80
+ item->type = type;
40
81
  item->callback = std::move(func);
41
82
  item->remove = false;
83
+
84
+ #if !defined(USE_ESP8266) && !defined(USE_RP2040)
85
+ // Special handling for defer() (delay = 0, type = TIMEOUT)
86
+ // ESP8266 and RP2040 are excluded because they don't need thread-safe defer handling
87
+ if (delay == 0 && type == SchedulerItem::TIMEOUT) {
88
+ // Put in defer queue for guaranteed FIFO execution
89
+ LockGuard guard{this->lock_};
90
+ this->cancel_item_locked_(component, name_cstr, type);
91
+ this->defer_queue_.push_back(std::move(item));
92
+ return;
93
+ }
94
+ #endif
95
+
96
+ const auto now = this->millis_();
97
+
98
+ // Type-specific setup
99
+ if (type == SchedulerItem::INTERVAL) {
100
+ item->interval = delay;
101
+ // Calculate random offset (0 to interval/2)
102
+ uint32_t offset = (delay != 0) ? (random_uint32() % delay) / 2 : 0;
103
+ item->next_execution_ = now + offset;
104
+ } else {
105
+ item->interval = 0;
106
+ item->next_execution_ = now + delay;
107
+ }
108
+
42
109
  #ifdef ESPHOME_DEBUG_SCHEDULER
43
- ESP_LOGD(TAG, "set_timeout(name='%s/%s', timeout=%" PRIu32 ")", item->get_source(), name.c_str(), timeout);
110
+ // Validate static strings in debug mode
111
+ if (is_static_string && name_cstr != nullptr) {
112
+ validate_static_string(name_cstr);
113
+ }
114
+
115
+ // Debug logging
116
+ const char *type_str = (type == SchedulerItem::TIMEOUT) ? "timeout" : "interval";
117
+ if (type == SchedulerItem::TIMEOUT) {
118
+ ESP_LOGD(TAG, "set_%s(name='%s/%s', %s=%" PRIu32 ")", type_str, item->get_source(),
119
+ name_cstr ? name_cstr : "(null)", type_str, delay);
120
+ } else {
121
+ ESP_LOGD(TAG, "set_%s(name='%s/%s', %s=%" PRIu32 ", offset=%" PRIu32 ")", type_str, item->get_source(),
122
+ name_cstr ? name_cstr : "(null)", type_str, delay, static_cast<uint32_t>(item->next_execution_ - now));
123
+ }
44
124
  #endif
45
- this->push_(std::move(item));
125
+
126
+ LockGuard guard{this->lock_};
127
+ // If name is provided, do atomic cancel-and-add
128
+ if (this->is_name_valid_(name_cstr)) {
129
+ // Cancel existing items
130
+ this->cancel_item_locked_(component, name_cstr, type);
131
+ }
132
+ // Add new item directly to to_add_
133
+ // since we have the lock held
134
+ this->to_add_.push_back(std::move(item));
135
+ }
136
+
137
+ void HOT Scheduler::set_timeout(Component *component, const char *name, uint32_t timeout, std::function<void()> func) {
138
+ this->set_timer_common_(component, SchedulerItem::TIMEOUT, true, name, timeout, std::move(func));
139
+ }
140
+
141
+ void HOT Scheduler::set_timeout(Component *component, const std::string &name, uint32_t timeout,
142
+ std::function<void()> func) {
143
+ this->set_timer_common_(component, SchedulerItem::TIMEOUT, false, &name, timeout, std::move(func));
46
144
  }
47
145
  bool HOT Scheduler::cancel_timeout(Component *component, const std::string &name) {
48
- return this->cancel_item_(component, name, SchedulerItem::TIMEOUT);
146
+ return this->cancel_item_(component, false, &name, SchedulerItem::TIMEOUT);
147
+ }
148
+ bool HOT Scheduler::cancel_timeout(Component *component, const char *name) {
149
+ return this->cancel_item_(component, true, name, SchedulerItem::TIMEOUT);
49
150
  }
50
151
  void HOT Scheduler::set_interval(Component *component, const std::string &name, uint32_t interval,
51
152
  std::function<void()> func) {
52
- const auto now = this->millis_();
53
-
54
- if (!name.empty())
55
- this->cancel_interval(component, name);
56
-
57
- if (interval == SCHEDULER_DONT_RUN)
58
- return;
59
-
60
- // only put offset in lower half
61
- uint32_t offset = 0;
62
- if (interval != 0)
63
- offset = (random_uint32() % interval) / 2;
153
+ this->set_timer_common_(component, SchedulerItem::INTERVAL, false, &name, interval, std::move(func));
154
+ }
64
155
 
65
- auto item = make_unique<SchedulerItem>();
66
- item->component = component;
67
- item->name = name;
68
- item->type = SchedulerItem::INTERVAL;
69
- item->interval = interval;
70
- item->next_execution_ = now + offset;
71
- item->callback = std::move(func);
72
- item->remove = false;
73
- #ifdef ESPHOME_DEBUG_SCHEDULER
74
- ESP_LOGD(TAG, "set_interval(name='%s/%s', interval=%" PRIu32 ", offset=%" PRIu32 ")", item->get_source(),
75
- name.c_str(), interval, offset);
76
- #endif
77
- this->push_(std::move(item));
156
+ void HOT Scheduler::set_interval(Component *component, const char *name, uint32_t interval,
157
+ std::function<void()> func) {
158
+ this->set_timer_common_(component, SchedulerItem::INTERVAL, true, name, interval, std::move(func));
78
159
  }
79
160
  bool HOT Scheduler::cancel_interval(Component *component, const std::string &name) {
80
- return this->cancel_item_(component, name, SchedulerItem::INTERVAL);
161
+ return this->cancel_item_(component, false, &name, SchedulerItem::INTERVAL);
162
+ }
163
+ bool HOT Scheduler::cancel_interval(Component *component, const char *name) {
164
+ return this->cancel_item_(component, true, name, SchedulerItem::INTERVAL);
81
165
  }
82
166
 
83
167
  struct RetryArgs {
@@ -85,7 +169,7 @@ struct RetryArgs {
85
169
  uint8_t retry_countdown;
86
170
  uint32_t current_interval;
87
171
  Component *component;
88
- std::string name;
172
+ std::string name; // Keep as std::string since retry uses it dynamically
89
173
  float backoff_increase_factor;
90
174
  Scheduler *scheduler;
91
175
  };
@@ -136,6 +220,9 @@ bool HOT Scheduler::cancel_retry(Component *component, const std::string &name)
136
220
  }
137
221
 
138
222
  optional<uint32_t> HOT Scheduler::next_schedule_in() {
223
+ // IMPORTANT: This method should only be called from the main thread (loop task).
224
+ // It calls empty_() and accesses items_[0] without holding a lock, which is only
225
+ // safe when called from the main thread. Other threads must not call this method.
139
226
  if (this->empty_())
140
227
  return {};
141
228
  auto &item = this->items_[0];
@@ -145,6 +232,39 @@ optional<uint32_t> HOT Scheduler::next_schedule_in() {
145
232
  return item->next_execution_ - now;
146
233
  }
147
234
  void HOT Scheduler::call() {
235
+ #if !defined(USE_ESP8266) && !defined(USE_RP2040)
236
+ // Process defer queue first to guarantee FIFO execution order for deferred items.
237
+ // Previously, defer() used the heap which gave undefined order for equal timestamps,
238
+ // causing race conditions on multi-core systems (ESP32, BK7200).
239
+ // With the defer queue:
240
+ // - Deferred items (delay=0) go directly to defer_queue_ in set_timer_common_
241
+ // - Items execute in exact order they were deferred (FIFO guarantee)
242
+ // - No deferred items exist in to_add_, so processing order doesn't affect correctness
243
+ // ESP8266 and RP2040 don't use this queue - they fall back to the heap-based approach
244
+ // (ESP8266: single-core, RP2040: empty mutex implementation).
245
+ //
246
+ // Note: Items cancelled via cancel_item_locked_() are marked with remove=true but still
247
+ // processed here. They are removed from the queue normally via pop_front() but skipped
248
+ // during execution by should_skip_item_(). This is intentional - no memory leak occurs.
249
+ while (!this->defer_queue_.empty()) {
250
+ // The outer check is done without a lock for performance. If the queue
251
+ // appears non-empty, we lock and process an item. We don't need to check
252
+ // empty() again inside the lock because only this thread can remove items.
253
+ std::unique_ptr<SchedulerItem> item;
254
+ {
255
+ LockGuard lock(this->lock_);
256
+ item = std::move(this->defer_queue_.front());
257
+ this->defer_queue_.pop_front();
258
+ }
259
+
260
+ // Execute callback without holding lock to prevent deadlocks
261
+ // if the callback tries to call defer() again
262
+ if (!this->should_skip_item_(item.get())) {
263
+ this->execute_item_(item.get());
264
+ }
265
+ }
266
+ #endif
267
+
148
268
  const auto now = this->millis_();
149
269
  this->process_to_add();
150
270
 
@@ -154,16 +274,19 @@ void HOT Scheduler::call() {
154
274
  if (now - last_print > 2000) {
155
275
  last_print = now;
156
276
  std::vector<std::unique_ptr<SchedulerItem>> old_items;
157
- ESP_LOGD(TAG, "Items: count=%u, now=%" PRIu64 " (%u, %" PRIu32 ")", this->items_.size(), now, this->millis_major_,
277
+ ESP_LOGD(TAG, "Items: count=%zu, now=%" PRIu64 " (%u, %" PRIu32 ")", this->items_.size(), now, this->millis_major_,
158
278
  this->last_millis_);
159
279
  while (!this->empty_()) {
160
- this->lock_.lock();
161
- auto item = std::move(this->items_[0]);
162
- this->pop_raw_();
163
- this->lock_.unlock();
280
+ std::unique_ptr<SchedulerItem> item;
281
+ {
282
+ LockGuard guard{this->lock_};
283
+ item = std::move(this->items_[0]);
284
+ this->pop_raw_();
285
+ }
164
286
 
287
+ const char *name = item->get_name();
165
288
  ESP_LOGD(TAG, " %s '%s/%s' interval=%" PRIu32 " next_execution in %" PRIu64 "ms at %" PRIu64,
166
- item->get_type_str(), item->get_source(), item->name.c_str(), item->interval,
289
+ item->get_type_str(), item->get_source(), name ? name : "(null)", item->interval,
167
290
  item->next_execution_ - now, item->next_execution_);
168
291
 
169
292
  old_items.push_back(std::move(item));
@@ -173,33 +296,35 @@ void HOT Scheduler::call() {
173
296
  {
174
297
  LockGuard guard{this->lock_};
175
298
  this->items_ = std::move(old_items);
299
+ // Rebuild heap after moving items back
300
+ std::make_heap(this->items_.begin(), this->items_.end(), SchedulerItem::cmp);
176
301
  }
177
302
  }
178
303
  #endif // ESPHOME_DEBUG_SCHEDULER
179
304
 
180
- auto to_remove_was = to_remove_;
181
- auto items_was = this->items_.size();
182
305
  // If we have too many items to remove
183
- if (to_remove_ > MAX_LOGICALLY_DELETED_ITEMS) {
306
+ if (this->to_remove_ > MAX_LOGICALLY_DELETED_ITEMS) {
307
+ // We hold the lock for the entire cleanup operation because:
308
+ // 1. We're rebuilding the entire items_ list, so we need exclusive access throughout
309
+ // 2. Other threads must see either the old state or the new state, not intermediate states
310
+ // 3. The operation is already expensive (O(n)), so lock overhead is negligible
311
+ // 4. No operations inside can block or take other locks, so no deadlock risk
312
+ LockGuard guard{this->lock_};
313
+
184
314
  std::vector<std::unique_ptr<SchedulerItem>> valid_items;
185
- while (!this->empty_()) {
186
- LockGuard guard{this->lock_};
187
- auto item = std::move(this->items_[0]);
188
- this->pop_raw_();
189
- valid_items.push_back(std::move(item));
190
- }
191
315
 
192
- {
193
- LockGuard guard{this->lock_};
194
- this->items_ = std::move(valid_items);
316
+ // Move all non-removed items to valid_items
317
+ for (auto &item : this->items_) {
318
+ if (!item->remove) {
319
+ valid_items.push_back(std::move(item));
320
+ }
195
321
  }
196
322
 
197
- // The following should not happen unless I'm missing something
198
- if (to_remove_ != 0) {
199
- ESP_LOGW(TAG, "to_remove_ was %" PRIu32 " now: %" PRIu32 " items where %zu now %zu. Please report this",
200
- to_remove_was, to_remove_, items_was, items_.size());
201
- to_remove_ = 0;
202
- }
323
+ // Replace items_ with the filtered list
324
+ this->items_ = std::move(valid_items);
325
+ // Rebuild the heap structure since items are no longer in heap order
326
+ std::make_heap(this->items_.begin(), this->items_.end(), SchedulerItem::cmp);
327
+ this->to_remove_ = 0;
203
328
  }
204
329
 
205
330
  while (!this->empty_()) {
@@ -217,47 +342,39 @@ void HOT Scheduler::call() {
217
342
  this->pop_raw_();
218
343
  continue;
219
344
  }
220
- App.set_current_component(item->component);
221
-
222
345
  #ifdef ESPHOME_DEBUG_SCHEDULER
346
+ const char *item_name = item->get_name();
223
347
  ESP_LOGV(TAG, "Running %s '%s/%s' with interval=%" PRIu32 " next_execution=%" PRIu64 " (now=%" PRIu64 ")",
224
- item->get_type_str(), item->get_source(), item->name.c_str(), item->interval, item->next_execution_,
225
- now);
348
+ item->get_type_str(), item->get_source(), item_name ? item_name : "(null)", item->interval,
349
+ item->next_execution_, now);
226
350
  #endif
227
351
 
228
352
  // Warning: During callback(), a lot of stuff can happen, including:
229
353
  // - timeouts/intervals get added, potentially invalidating vector pointers
230
354
  // - timeouts/intervals get cancelled
231
- {
232
- uint32_t now_ms = millis();
233
- WarnIfComponentBlockingGuard guard{item->component, now_ms};
234
- item->callback();
235
- // Call finish to ensure blocking time is properly calculated and reported
236
- guard.finish();
237
- }
355
+ this->execute_item_(item.get());
238
356
  }
239
357
 
240
358
  {
241
- this->lock_.lock();
359
+ LockGuard guard{this->lock_};
242
360
 
243
361
  // new scope, item from before might have been moved in the vector
244
362
  auto item = std::move(this->items_[0]);
245
-
246
363
  // Only pop after function call, this ensures we were reachable
247
364
  // during the function call and know if we were cancelled.
248
365
  this->pop_raw_();
249
366
 
250
- this->lock_.unlock();
251
-
252
367
  if (item->remove) {
253
368
  // We were removed/cancelled in the function call, stop
254
- to_remove_--;
369
+ this->to_remove_--;
255
370
  continue;
256
371
  }
257
372
 
258
373
  if (item->type == SchedulerItem::INTERVAL) {
259
374
  item->next_execution_ = now + item->interval;
260
- this->push_(std::move(item));
375
+ // Add new item directly to to_add_
376
+ // since we have the lock held
377
+ this->to_add_.push_back(std::move(item));
261
378
  }
262
379
  }
263
380
  }
@@ -277,55 +394,113 @@ void HOT Scheduler::process_to_add() {
277
394
  this->to_add_.clear();
278
395
  }
279
396
  void HOT Scheduler::cleanup_() {
397
+ // Fast path: if nothing to remove, just return
398
+ // Reading to_remove_ without lock is safe because:
399
+ // 1. We only call this from the main thread during call()
400
+ // 2. If it's 0, there's definitely nothing to cleanup
401
+ // 3. If it becomes non-zero after we check, cleanup will happen on the next loop iteration
402
+ // 4. Not all platforms support atomics, so we accept this race in favor of performance
403
+ // 5. The worst case is a one-loop-iteration delay in cleanup, which is harmless
404
+ if (this->to_remove_ == 0)
405
+ return;
406
+
407
+ // We must hold the lock for the entire cleanup operation because:
408
+ // 1. We're modifying items_ (via pop_raw_) which requires exclusive access
409
+ // 2. We're decrementing to_remove_ which is also modified by other threads
410
+ // (though all modifications are already under lock)
411
+ // 3. Other threads read items_ when searching for items to cancel in cancel_item_locked_()
412
+ // 4. We need a consistent view of items_ and to_remove_ throughout the operation
413
+ // Without the lock, we could access items_ while another thread is reading it,
414
+ // leading to race conditions
415
+ LockGuard guard{this->lock_};
280
416
  while (!this->items_.empty()) {
281
417
  auto &item = this->items_[0];
282
418
  if (!item->remove)
283
419
  return;
284
-
285
- to_remove_--;
286
-
287
- {
288
- LockGuard guard{this->lock_};
289
- this->pop_raw_();
290
- }
420
+ this->to_remove_--;
421
+ this->pop_raw_();
291
422
  }
292
423
  }
293
424
  void HOT Scheduler::pop_raw_() {
294
425
  std::pop_heap(this->items_.begin(), this->items_.end(), SchedulerItem::cmp);
295
426
  this->items_.pop_back();
296
427
  }
297
- void HOT Scheduler::push_(std::unique_ptr<Scheduler::SchedulerItem> item) {
298
- LockGuard guard{this->lock_};
299
- this->to_add_.push_back(std::move(item));
428
+
429
+ // Helper to execute a scheduler item
430
+ void HOT Scheduler::execute_item_(SchedulerItem *item) {
431
+ App.set_current_component(item->component);
432
+
433
+ uint32_t now_ms = millis();
434
+ WarnIfComponentBlockingGuard guard{item->component, now_ms};
435
+ item->callback();
436
+ guard.finish();
300
437
  }
301
- bool HOT Scheduler::cancel_item_(Component *component, const std::string &name, Scheduler::SchedulerItem::Type type) {
438
+
439
+ // Common implementation for cancel operations
440
+ bool HOT Scheduler::cancel_item_(Component *component, bool is_static_string, const void *name_ptr,
441
+ SchedulerItem::Type type) {
442
+ // Get the name as const char*
443
+ const char *name_cstr = this->get_name_cstr_(is_static_string, name_ptr);
444
+
445
+ // Handle null or empty names
446
+ if (!this->is_name_valid_(name_cstr))
447
+ return false;
448
+
302
449
  // obtain lock because this function iterates and can be called from non-loop task context
303
450
  LockGuard guard{this->lock_};
304
- bool ret = false;
305
- for (auto &it : this->items_) {
306
- if (it->component == component && it->name == name && it->type == type && !it->remove) {
307
- to_remove_++;
308
- it->remove = true;
309
- ret = true;
451
+ return this->cancel_item_locked_(component, name_cstr, type);
452
+ }
453
+
454
+ // Helper to cancel items by name - must be called with lock held
455
+ bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_cstr, SchedulerItem::Type type) {
456
+ size_t total_cancelled = 0;
457
+
458
+ // Check all containers for matching items
459
+ #if !defined(USE_ESP8266) && !defined(USE_RP2040)
460
+ // Only check defer queue for timeouts (intervals never go there)
461
+ if (type == SchedulerItem::TIMEOUT) {
462
+ for (auto &item : this->defer_queue_) {
463
+ if (this->matches_item_(item, component, name_cstr, type)) {
464
+ item->remove = true;
465
+ total_cancelled++;
466
+ }
310
467
  }
311
468
  }
312
- for (auto &it : this->to_add_) {
313
- if (it->component == component && it->name == name && it->type == type) {
314
- it->remove = true;
315
- ret = true;
469
+ #endif
470
+
471
+ // Cancel items in the main heap
472
+ for (auto &item : this->items_) {
473
+ if (this->matches_item_(item, component, name_cstr, type)) {
474
+ item->remove = true;
475
+ total_cancelled++;
476
+ this->to_remove_++; // Track removals for heap items
477
+ }
478
+ }
479
+
480
+ // Cancel items in to_add_
481
+ for (auto &item : this->to_add_) {
482
+ if (this->matches_item_(item, component, name_cstr, type)) {
483
+ item->remove = true;
484
+ total_cancelled++;
485
+ // Don't track removals for to_add_ items
316
486
  }
317
487
  }
318
488
 
319
- return ret;
489
+ return total_cancelled > 0;
320
490
  }
491
+
321
492
  uint64_t Scheduler::millis_() {
493
+ // Get the current 32-bit millis value
322
494
  const uint32_t now = millis();
495
+ // Check for rollover by comparing with last value
323
496
  if (now < this->last_millis_) {
497
+ // Detected rollover (happens every ~49.7 days)
324
498
  this->millis_major_++;
325
499
  ESP_LOGD(TAG, "Incrementing scheduler major at %" PRIu64 "ms",
326
500
  now + (static_cast<uint64_t>(this->millis_major_) << 32));
327
501
  }
328
502
  this->last_millis_ = now;
503
+ // Combine major (high 32 bits) and now (low 32 bits) into 64-bit time
329
504
  return now + (static_cast<uint64_t>(this->millis_major_) << 32);
330
505
  }
331
506