esphome 2025.6.3__py3-none-any.whl → 2025.7.0b2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (606) 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 +57 -21
  22. esphome/components/api/api_connection.cpp +344 -539
  23. esphome/components/api/api_connection.h +224 -141
  24. esphome/components/api/api_frame_helper.cpp +91 -127
  25. esphome/components/api/api_frame_helper.h +64 -54
  26. esphome/components/api/api_pb2.cpp +1837 -9044
  27. esphome/components/api/api_pb2.h +532 -685
  28. esphome/components/api/api_pb2_dump.cpp +4432 -0
  29. esphome/components/api/api_pb2_service.cpp +184 -425
  30. esphome/components/api/api_pb2_service.h +13 -6
  31. esphome/components/api/api_server.cpp +131 -167
  32. esphome/components/api/api_server.h +38 -10
  33. esphome/components/api/client.py +8 -2
  34. esphome/components/api/custom_api_device.h +8 -0
  35. esphome/components/api/list_entities.cpp +37 -104
  36. esphome/components/api/list_entities.h +33 -23
  37. esphome/components/api/proto.h +532 -26
  38. esphome/components/api/subscribe_state.cpp +23 -29
  39. esphome/components/api/subscribe_state.h +26 -19
  40. esphome/components/api/user_services.h +2 -0
  41. esphome/components/as5600/as5600.h +0 -1
  42. esphome/components/async_tcp/__init__.py +14 -5
  43. esphome/components/atc_mithermometer/atc_mithermometer.h +0 -1
  44. esphome/components/atm90e32/atm90e32.cpp +2 -1
  45. esphome/components/audio/audio_decoder.cpp +1 -1
  46. esphome/components/audio/audio_transfer_buffer.cpp +2 -2
  47. esphome/components/b_parasite/b_parasite.h +0 -1
  48. esphome/components/bedjet/bedjet_hub.cpp +5 -1
  49. esphome/components/bedjet/climate/bedjet_climate.cpp +5 -1
  50. esphome/components/beken_spi_led_strip/led_strip.cpp +4 -2
  51. esphome/components/bh1750/bh1750.cpp +5 -5
  52. esphome/components/binary_sensor/__init__.py +82 -5
  53. esphome/components/binary_sensor/automation.h +19 -1
  54. esphome/components/binary_sensor/binary_sensor.cpp +12 -30
  55. esphome/components/binary_sensor/binary_sensor.h +11 -25
  56. esphome/components/binary_sensor/filter.cpp +29 -24
  57. esphome/components/binary_sensor/filter.h +20 -10
  58. esphome/components/ble_client/output/ble_binary_output.h +0 -1
  59. esphome/components/ble_client/sensor/ble_rssi_sensor.cpp +5 -1
  60. esphome/components/ble_client/sensor/ble_rssi_sensor.h +0 -1
  61. esphome/components/ble_client/sensor/ble_sensor.cpp +5 -1
  62. esphome/components/ble_client/sensor/ble_sensor.h +0 -1
  63. esphome/components/ble_client/switch/ble_switch.h +0 -1
  64. esphome/components/ble_client/text_sensor/ble_text_sensor.cpp +5 -1
  65. esphome/components/ble_client/text_sensor/ble_text_sensor.h +0 -1
  66. esphome/components/ble_presence/ble_presence_device.h +0 -1
  67. esphome/components/ble_rssi/ble_rssi_sensor.h +0 -1
  68. esphome/components/ble_scanner/ble_scanner.h +0 -1
  69. esphome/components/bluetooth_proxy/bluetooth_connection.h +9 -2
  70. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +16 -6
  71. esphome/components/bluetooth_proxy/bluetooth_proxy.h +8 -2
  72. esphome/components/bme680/sensor.py +1 -1
  73. esphome/components/bmp581/bmp581.h +0 -2
  74. esphome/components/button/__init__.py +5 -2
  75. esphome/components/camera/__init__.py +1 -0
  76. esphome/components/camera/camera.cpp +22 -0
  77. esphome/components/camera/camera.h +80 -0
  78. esphome/components/canbus/__init__.py +1 -0
  79. esphome/components/cap1188/cap1188.h +0 -1
  80. esphome/components/captive_portal/__init__.py +12 -2
  81. esphome/components/captive_portal/captive_portal.cpp +12 -2
  82. esphome/components/captive_portal/captive_portal.h +5 -2
  83. esphome/components/ccs811/ccs811.h +0 -2
  84. esphome/components/climate/__init__.py +5 -2
  85. esphome/components/cm1106/sensor.py +2 -2
  86. esphome/components/const/__init__.py +2 -0
  87. esphome/components/copy/binary_sensor/copy_binary_sensor.h +0 -1
  88. esphome/components/copy/button/copy_button.h +0 -1
  89. esphome/components/copy/cover/copy_cover.h +0 -1
  90. esphome/components/copy/fan/copy_fan.h +0 -1
  91. esphome/components/copy/lock/copy_lock.h +0 -1
  92. esphome/components/copy/number/copy_number.h +0 -1
  93. esphome/components/copy/select/copy_select.h +0 -1
  94. esphome/components/copy/sensor/copy_sensor.h +0 -1
  95. esphome/components/copy/switch/copy_switch.h +0 -1
  96. esphome/components/copy/text/copy_text.h +0 -1
  97. esphome/components/copy/text_sensor/copy_text_sensor.h +0 -1
  98. esphome/components/cover/__init__.py +5 -2
  99. esphome/components/cs5460a/cs5460a.h +0 -1
  100. esphome/components/datetime/__init__.py +4 -2
  101. esphome/components/debug/__init__.py +20 -0
  102. esphome/components/debug/debug_esp32.cpp +2 -0
  103. esphome/components/deep_sleep/__init__.py +43 -9
  104. esphome/components/demo/__init__.py +2 -2
  105. esphome/components/display/display.cpp +4 -3
  106. esphome/components/display/display.h +0 -2
  107. esphome/components/display/display_buffer.cpp +1 -1
  108. esphome/components/ds2484/__init__.py +1 -0
  109. esphome/components/ds2484/ds2484.cpp +209 -0
  110. esphome/components/ds2484/ds2484.h +43 -0
  111. esphome/components/ds2484/one_wire.py +37 -0
  112. esphome/components/duty_time/duty_time_sensor.h +0 -1
  113. esphome/components/ens160_base/ens160_base.h +0 -1
  114. esphome/components/es7210/es7210.h +0 -1
  115. esphome/components/es7243e/es7243e.h +0 -1
  116. esphome/components/es8156/es8156.h +0 -1
  117. esphome/components/es8311/es8311.h +0 -1
  118. esphome/components/es8388/es8388.h +0 -1
  119. esphome/components/esp32/__init__.py +103 -135
  120. esphome/components/esp32/core.cpp +0 -4
  121. esphome/components/esp32/gpio.h +1 -1
  122. esphome/components/esp32/helpers.cpp +69 -0
  123. esphome/components/esp32_ble/ble.cpp +5 -6
  124. esphome/components/esp32_ble/ble.h +29 -14
  125. esphome/components/esp32_ble/ble_event.h +6 -6
  126. esphome/components/esp32_ble_client/ble_client_base.cpp +21 -6
  127. esphome/components/esp32_ble_client/ble_client_base.h +24 -9
  128. esphome/components/esp32_ble_tracker/__init__.py +2 -8
  129. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +5 -5
  130. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +11 -7
  131. esphome/components/esp32_camera/__init__.py +112 -98
  132. esphome/components/esp32_camera/esp32_camera.cpp +41 -31
  133. esphome/components/esp32_camera/esp32_camera.h +35 -30
  134. esphome/components/esp32_camera_web_server/__init__.py +2 -1
  135. esphome/components/esp32_camera_web_server/camera_web_server.cpp +8 -8
  136. esphome/components/esp32_camera_web_server/camera_web_server.h +3 -3
  137. esphome/components/esp32_hall/sensor.py +2 -21
  138. esphome/components/esp32_hosted/__init__.py +101 -0
  139. esphome/components/esp32_hosted/esp32_hosted.py.script +12 -0
  140. esphome/components/esp32_improv/esp32_improv_component.cpp +3 -0
  141. esphome/components/esp32_rmt/__init__.py +0 -58
  142. esphome/components/esp32_rmt_led_strip/led_strip.cpp +77 -63
  143. esphome/components/esp32_rmt_led_strip/led_strip.h +11 -17
  144. esphome/components/esp32_rmt_led_strip/light.py +14 -76
  145. esphome/components/esp32_touch/esp32_touch.h +174 -28
  146. esphome/components/esp32_touch/esp32_touch_common.cpp +162 -0
  147. esphome/components/esp32_touch/esp32_touch_v1.cpp +240 -0
  148. esphome/components/esp32_touch/esp32_touch_v2.cpp +397 -0
  149. esphome/components/esp8266/__init__.py +2 -0
  150. esphome/components/esp8266/gpio.cpp +10 -10
  151. esphome/components/esp8266/helpers.cpp +31 -0
  152. esphome/components/esphome/ota/__init__.py +1 -0
  153. esphome/components/esphome/ota/ota_esphome.cpp +24 -19
  154. esphome/components/ethernet/__init__.py +42 -23
  155. esphome/components/ethernet/esp_eth_phy_jl1101.c +0 -16
  156. esphome/components/ethernet/ethernet_component.cpp +69 -29
  157. esphome/components/ethernet/ethernet_component.h +18 -10
  158. esphome/components/event/__init__.py +5 -2
  159. esphome/components/ezo/ezo.h +0 -1
  160. esphome/components/ezo_pmp/ezo_pmp.h +0 -1
  161. esphome/components/fan/__init__.py +5 -2
  162. esphome/components/feedback/feedback_cover.h +0 -1
  163. esphome/components/font/__init__.py +92 -82
  164. esphome/components/font/font.cpp +9 -2
  165. esphome/components/font/font.h +20 -5
  166. esphome/components/fs3000/fs3000.h +0 -1
  167. esphome/components/gcja5/gcja5.h +0 -1
  168. esphome/components/gl_r01_i2c/__init__.py +0 -0
  169. esphome/components/gl_r01_i2c/gl_r01_i2c.cpp +68 -0
  170. esphome/components/gl_r01_i2c/gl_r01_i2c.h +22 -0
  171. esphome/components/gl_r01_i2c/sensor.py +36 -0
  172. esphome/components/gp8403/gp8403.h +0 -1
  173. esphome/components/gpio/binary_sensor/__init__.py +17 -0
  174. esphome/components/gpio/binary_sensor/gpio_binary_sensor.cpp +77 -3
  175. esphome/components/gpio/binary_sensor/gpio_binary_sensor.h +40 -0
  176. esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.h +0 -2
  177. esphome/components/he60r/he60r.h +0 -1
  178. esphome/components/heatpumpir/climate.py +2 -1
  179. esphome/components/heatpumpir/heatpumpir.cpp +1 -0
  180. esphome/components/heatpumpir/heatpumpir.h +1 -0
  181. esphome/components/honeywellabp2_i2c/honeywellabp2.h +0 -1
  182. esphome/components/host/__init__.py +3 -1
  183. esphome/components/host/helpers.cpp +57 -0
  184. esphome/components/http_request/__init__.py +19 -1
  185. esphome/components/http_request/http_request.h +1 -1
  186. esphome/components/http_request/http_request_arduino.h +1 -0
  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 +421 -268
  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 +5 -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 +132 -47
  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 +34 -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 +2 -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 +0 -4
  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/usb_host/usb_host_client.cpp +10 -10
  477. esphome/components/usb_uart/cp210x.cpp +1 -1
  478. esphome/components/usb_uart/usb_uart.cpp +41 -44
  479. esphome/components/usb_uart/usb_uart.h +4 -3
  480. esphome/components/valve/__init__.py +5 -2
  481. esphome/components/vbus/vbus.h +0 -1
  482. esphome/components/veml3235/veml3235.h +0 -1
  483. esphome/components/veml7700/veml7700.h +0 -1
  484. esphome/components/vl53l0x/vl53l0x_sensor.h +0 -1
  485. esphome/components/voice_assistant/voice_assistant.cpp +4 -4
  486. esphome/components/watchdog/watchdog.cpp +0 -4
  487. esphome/components/waveshare_epaper/waveshare_epaper.cpp +6 -6
  488. esphome/components/web_server/__init__.py +34 -19
  489. esphome/components/web_server/ota/__init__.py +32 -0
  490. esphome/components/web_server/ota/ota_web_server.cpp +210 -0
  491. esphome/components/web_server/ota/ota_web_server.h +26 -0
  492. esphome/components/web_server/web_server.cpp +305 -427
  493. esphome/components/web_server/web_server.h +33 -23
  494. esphome/components/web_server/web_server_v1.cpp +4 -5
  495. esphome/components/web_server_base/__init__.py +5 -2
  496. esphome/components/web_server_base/web_server_base.cpp +2 -94
  497. esphome/components/web_server_base/web_server_base.h +5 -25
  498. esphome/components/web_server_idf/multipart.cpp +254 -0
  499. esphome/components/web_server_idf/multipart.h +86 -0
  500. esphome/components/web_server_idf/utils.cpp +32 -0
  501. esphome/components/web_server_idf/utils.h +10 -0
  502. esphome/components/web_server_idf/web_server_idf.cpp +162 -16
  503. esphome/components/web_server_idf/web_server_idf.h +11 -10
  504. esphome/components/wiegand/wiegand.cpp +2 -2
  505. esphome/components/wifi/__init__.py +18 -0
  506. esphome/components/wifi/wifi_component.cpp +17 -22
  507. esphome/components/wifi/wifi_component.h +27 -23
  508. esphome/components/wifi/wifi_component_esp32_arduino.cpp +52 -59
  509. esphome/components/wifi/wifi_component_esp8266.cpp +46 -46
  510. esphome/components/wifi/wifi_component_esp_idf.cpp +35 -36
  511. esphome/components/wifi/wifi_component_libretiny.cpp +26 -27
  512. esphome/components/wifi/wifi_component_pico_w.cpp +3 -3
  513. esphome/components/wifi_info/wifi_info_text_sensor.cpp +6 -6
  514. esphome/components/wireguard/__init__.py +2 -11
  515. esphome/components/xiaomi_ble/xiaomi_ble.cpp +13 -1
  516. esphome/components/xiaomi_ble/xiaomi_ble.h +1 -0
  517. esphome/components/xiaomi_cgd1/xiaomi_cgd1.h +0 -1
  518. esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.h +0 -1
  519. esphome/components/xiaomi_cgg1/xiaomi_cgg1.h +0 -1
  520. esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.h +0 -1
  521. esphome/components/xiaomi_gcls002/xiaomi_gcls002.h +0 -1
  522. esphome/components/xiaomi_hhccjcy01/xiaomi_hhccjcy01.h +0 -1
  523. esphome/components/xiaomi_hhccjcy10/xiaomi_hhccjcy10.h +0 -1
  524. esphome/components/xiaomi_hhccpot002/xiaomi_hhccpot002.h +0 -1
  525. esphome/components/xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.h +0 -1
  526. esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.h +0 -1
  527. esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.h +0 -1
  528. esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.h +0 -1
  529. esphome/components/xiaomi_lywsdcgq/xiaomi_lywsdcgq.h +0 -1
  530. esphome/components/xiaomi_mhoc303/xiaomi_mhoc303.h +0 -1
  531. esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.h +0 -1
  532. esphome/components/xiaomi_miscale/xiaomi_miscale.h +0 -1
  533. esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.h +0 -1
  534. esphome/components/xiaomi_mue4094rt/xiaomi_mue4094rt.h +0 -1
  535. esphome/components/xiaomi_rtcgq02lm/xiaomi_rtcgq02lm.h +0 -1
  536. esphome/components/xiaomi_wx08zm/xiaomi_wx08zm.h +0 -1
  537. esphome/components/xiaomi_xmwsdj04mmc/__init__.py +0 -0
  538. esphome/components/xiaomi_xmwsdj04mmc/sensor.py +77 -0
  539. esphome/components/xiaomi_xmwsdj04mmc/xiaomi_xmwsdj04mmc.cpp +77 -0
  540. esphome/components/xiaomi_xmwsdj04mmc/xiaomi_xmwsdj04mmc.h +36 -0
  541. esphome/components/zio_ultrasonic/zio_ultrasonic.h +0 -2
  542. esphome/components/zyaura/zyaura.h +0 -1
  543. esphome/config.py +88 -22
  544. esphome/config_helpers.py +74 -1
  545. esphome/config_validation.py +12 -1
  546. esphome/const.py +65 -10
  547. esphome/core/__init__.py +18 -2
  548. esphome/core/application.cpp +163 -10
  549. esphome/core/application.h +145 -165
  550. esphome/core/area.h +19 -0
  551. esphome/core/automation.h +58 -9
  552. esphome/core/color.cpp +3 -5
  553. esphome/core/color.h +16 -16
  554. esphome/core/component.cpp +151 -18
  555. esphome/core/component.h +98 -4
  556. esphome/core/component_iterator.cpp +11 -9
  557. esphome/core/component_iterator.h +12 -10
  558. esphome/core/config.py +155 -6
  559. esphome/core/controller.cpp +4 -2
  560. esphome/core/controller.h +1 -1
  561. esphome/core/datatypes.h +2 -2
  562. esphome/core/defines.h +17 -2
  563. esphome/core/device.h +20 -0
  564. esphome/core/entity_base.cpp +20 -15
  565. esphome/core/entity_base.h +76 -0
  566. esphome/core/entity_helpers.py +168 -1
  567. esphome/core/event_pool.h +81 -0
  568. esphome/core/helpers.cpp +75 -230
  569. esphome/core/helpers.h +164 -104
  570. esphome/core/lock_free_queue.h +151 -0
  571. esphome/core/log.cpp +2 -2
  572. esphome/core/log.h +2 -0
  573. esphome/core/optional.h +5 -0
  574. esphome/core/ring_buffer.cpp +2 -2
  575. esphome/core/scheduler.cpp +275 -103
  576. esphome/core/scheduler.h +154 -17
  577. esphome/core/time.cpp +5 -5
  578. esphome/core/time.h +5 -5
  579. esphome/cpp_generator.py +17 -0
  580. esphome/cpp_helpers.py +0 -22
  581. esphome/cpp_types.py +3 -1
  582. esphome/dashboard/entries.py +1 -1
  583. esphome/dashboard/util/text.py +5 -21
  584. esphome/dashboard/web_server.py +9 -1
  585. esphome/helpers.py +47 -0
  586. esphome/loader.py +15 -1
  587. esphome/pins.py +14 -8
  588. esphome/wizard.py +17 -4
  589. esphome/writer.py +21 -3
  590. esphome/yaml_util.py +0 -2
  591. {esphome-2025.6.3.dist-info → esphome-2025.7.0b2.dist-info}/METADATA +10 -9
  592. {esphome-2025.6.3.dist-info → esphome-2025.7.0b2.dist-info}/RECORD +597 -538
  593. esphome/components/api/api_pb2_size.h +0 -361
  594. esphome/components/esp32_ble/ble_event_pool.h +0 -72
  595. esphome/components/esp32_ble/queue.h +0 -85
  596. esphome/components/esp32_hall/esp32_hall.cpp +0 -25
  597. esphome/components/esp32_hall/esp32_hall.h +0 -23
  598. esphome/components/esp32_touch/esp32_touch.cpp +0 -355
  599. esphome/components/ld2410/button/reset_button.cpp +0 -9
  600. esphome/components/ld2450/button/reset_button.cpp +0 -9
  601. esphome/components/openthread/tlv.py +0 -65
  602. /esphome/{dashboard/enum.py → enum.py} +0 -0
  603. {esphome-2025.6.3.dist-info → esphome-2025.7.0b2.dist-info}/WHEEL +0 -0
  604. {esphome-2025.6.3.dist-info → esphome-2025.7.0b2.dist-info}/entry_points.txt +0 -0
  605. {esphome-2025.6.3.dist-info → esphome-2025.7.0b2.dist-info}/licenses/LICENSE +0 -0
  606. {esphome-2025.6.3.dist-info → esphome-2025.7.0b2.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,146 @@ 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
+ LockGuard guard{this->lock_};
70
+ this->cancel_item_locked_(component, name_cstr, type);
33
71
  return;
72
+ }
34
73
 
74
+ // Create and populate the scheduler item
35
75
  auto item = make_unique<SchedulerItem>();
36
76
  item->component = component;
37
- item->name = name;
38
- item->type = SchedulerItem::TIMEOUT;
39
- item->next_execution_ = now + timeout;
77
+ item->set_name(name_cstr, !is_static_string);
78
+ item->type = type;
40
79
  item->callback = std::move(func);
41
80
  item->remove = false;
81
+
82
+ #if !defined(USE_ESP8266) && !defined(USE_RP2040)
83
+ // Special handling for defer() (delay = 0, type = TIMEOUT)
84
+ // ESP8266 and RP2040 are excluded because they don't need thread-safe defer handling
85
+ if (delay == 0 && type == SchedulerItem::TIMEOUT) {
86
+ // Put in defer queue for guaranteed FIFO execution
87
+ LockGuard guard{this->lock_};
88
+ this->cancel_item_locked_(component, name_cstr, type);
89
+ this->defer_queue_.push_back(std::move(item));
90
+ return;
91
+ }
92
+ #endif
93
+
94
+ const auto now = this->millis_();
95
+
96
+ // Type-specific setup
97
+ if (type == SchedulerItem::INTERVAL) {
98
+ item->interval = delay;
99
+ // Calculate random offset (0 to interval/2)
100
+ uint32_t offset = (delay != 0) ? (random_uint32() % delay) / 2 : 0;
101
+ item->next_execution_ = now + offset;
102
+ } else {
103
+ item->interval = 0;
104
+ item->next_execution_ = now + delay;
105
+ }
106
+
42
107
  #ifdef ESPHOME_DEBUG_SCHEDULER
43
- ESP_LOGD(TAG, "set_timeout(name='%s/%s', timeout=%" PRIu32 ")", item->get_source(), name.c_str(), timeout);
108
+ // Validate static strings in debug mode
109
+ if (is_static_string && name_cstr != nullptr) {
110
+ validate_static_string(name_cstr);
111
+ }
112
+
113
+ // Debug logging
114
+ const char *type_str = (type == SchedulerItem::TIMEOUT) ? "timeout" : "interval";
115
+ if (type == SchedulerItem::TIMEOUT) {
116
+ ESP_LOGD(TAG, "set_%s(name='%s/%s', %s=%" PRIu32 ")", type_str, item->get_source(),
117
+ name_cstr ? name_cstr : "(null)", type_str, delay);
118
+ } else {
119
+ ESP_LOGD(TAG, "set_%s(name='%s/%s', %s=%" PRIu32 ", offset=%" PRIu32 ")", type_str, item->get_source(),
120
+ name_cstr ? name_cstr : "(null)", type_str, delay, static_cast<uint32_t>(item->next_execution_ - now));
121
+ }
44
122
  #endif
45
- this->push_(std::move(item));
123
+
124
+ LockGuard guard{this->lock_};
125
+ // If name is provided, do atomic cancel-and-add
126
+ // Cancel existing items
127
+ this->cancel_item_locked_(component, name_cstr, type);
128
+ // Add new item directly to to_add_
129
+ // since we have the lock held
130
+ this->to_add_.push_back(std::move(item));
131
+ }
132
+
133
+ void HOT Scheduler::set_timeout(Component *component, const char *name, uint32_t timeout, std::function<void()> func) {
134
+ this->set_timer_common_(component, SchedulerItem::TIMEOUT, true, name, timeout, std::move(func));
135
+ }
136
+
137
+ void HOT Scheduler::set_timeout(Component *component, const std::string &name, uint32_t timeout,
138
+ std::function<void()> func) {
139
+ this->set_timer_common_(component, SchedulerItem::TIMEOUT, false, &name, timeout, std::move(func));
46
140
  }
47
141
  bool HOT Scheduler::cancel_timeout(Component *component, const std::string &name) {
48
- return this->cancel_item_(component, name, SchedulerItem::TIMEOUT);
142
+ return this->cancel_item_(component, false, &name, SchedulerItem::TIMEOUT);
143
+ }
144
+ bool HOT Scheduler::cancel_timeout(Component *component, const char *name) {
145
+ return this->cancel_item_(component, true, name, SchedulerItem::TIMEOUT);
49
146
  }
50
147
  void HOT Scheduler::set_interval(Component *component, const std::string &name, uint32_t interval,
51
148
  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;
149
+ this->set_timer_common_(component, SchedulerItem::INTERVAL, false, &name, interval, std::move(func));
150
+ }
64
151
 
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));
152
+ void HOT Scheduler::set_interval(Component *component, const char *name, uint32_t interval,
153
+ std::function<void()> func) {
154
+ this->set_timer_common_(component, SchedulerItem::INTERVAL, true, name, interval, std::move(func));
78
155
  }
79
156
  bool HOT Scheduler::cancel_interval(Component *component, const std::string &name) {
80
- return this->cancel_item_(component, name, SchedulerItem::INTERVAL);
157
+ return this->cancel_item_(component, false, &name, SchedulerItem::INTERVAL);
158
+ }
159
+ bool HOT Scheduler::cancel_interval(Component *component, const char *name) {
160
+ return this->cancel_item_(component, true, name, SchedulerItem::INTERVAL);
81
161
  }
82
162
 
83
163
  struct RetryArgs {
@@ -85,7 +165,7 @@ struct RetryArgs {
85
165
  uint8_t retry_countdown;
86
166
  uint32_t current_interval;
87
167
  Component *component;
88
- std::string name;
168
+ std::string name; // Keep as std::string since retry uses it dynamically
89
169
  float backoff_increase_factor;
90
170
  Scheduler *scheduler;
91
171
  };
@@ -136,6 +216,9 @@ bool HOT Scheduler::cancel_retry(Component *component, const std::string &name)
136
216
  }
137
217
 
138
218
  optional<uint32_t> HOT Scheduler::next_schedule_in() {
219
+ // 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
221
+ // safe when called from the main thread. Other threads must not call this method.
139
222
  if (this->empty_())
140
223
  return {};
141
224
  auto &item = this->items_[0];
@@ -145,6 +228,39 @@ optional<uint32_t> HOT Scheduler::next_schedule_in() {
145
228
  return item->next_execution_ - now;
146
229
  }
147
230
  void HOT Scheduler::call() {
231
+ #if !defined(USE_ESP8266) && !defined(USE_RP2040)
232
+ // Process defer queue first to guarantee FIFO execution order for deferred items.
233
+ // Previously, defer() used the heap which gave undefined order for equal timestamps,
234
+ // causing race conditions on multi-core systems (ESP32, BK7200).
235
+ // With the defer queue:
236
+ // - Deferred items (delay=0) go directly to defer_queue_ in set_timer_common_
237
+ // - Items execute in exact order they were deferred (FIFO guarantee)
238
+ // - 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).
241
+ //
242
+ // Note: Items cancelled via cancel_item_locked_() are marked with remove=true but still
243
+ // processed here. They are removed from the queue normally via pop_front() but skipped
244
+ // during execution by should_skip_item_(). This is intentional - no memory leak occurs.
245
+ while (!this->defer_queue_.empty()) {
246
+ // The outer check is done without a lock for performance. If the queue
247
+ // appears non-empty, we lock and process an item. We don't need to check
248
+ // empty() again inside the lock because only this thread can remove items.
249
+ std::unique_ptr<SchedulerItem> item;
250
+ {
251
+ LockGuard lock(this->lock_);
252
+ item = std::move(this->defer_queue_.front());
253
+ this->defer_queue_.pop_front();
254
+ }
255
+
256
+ // Execute callback without holding lock to prevent deadlocks
257
+ // if the callback tries to call defer() again
258
+ if (!this->should_skip_item_(item.get())) {
259
+ this->execute_item_(item.get());
260
+ }
261
+ }
262
+ #endif
263
+
148
264
  const auto now = this->millis_();
149
265
  this->process_to_add();
150
266
 
@@ -154,16 +270,19 @@ void HOT Scheduler::call() {
154
270
  if (now - last_print > 2000) {
155
271
  last_print = now;
156
272
  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_,
273
+ ESP_LOGD(TAG, "Items: count=%zu, now=%" PRIu64 " (%u, %" PRIu32 ")", this->items_.size(), now, this->millis_major_,
158
274
  this->last_millis_);
159
275
  while (!this->empty_()) {
160
- this->lock_.lock();
161
- auto item = std::move(this->items_[0]);
162
- this->pop_raw_();
163
- this->lock_.unlock();
276
+ std::unique_ptr<SchedulerItem> item;
277
+ {
278
+ LockGuard guard{this->lock_};
279
+ item = std::move(this->items_[0]);
280
+ this->pop_raw_();
281
+ }
164
282
 
283
+ const char *name = item->get_name();
165
284
  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,
285
+ item->get_type_str(), item->get_source(), name ? name : "(null)", item->interval,
167
286
  item->next_execution_ - now, item->next_execution_);
168
287
 
169
288
  old_items.push_back(std::move(item));
@@ -173,33 +292,35 @@ void HOT Scheduler::call() {
173
292
  {
174
293
  LockGuard guard{this->lock_};
175
294
  this->items_ = std::move(old_items);
295
+ // Rebuild heap after moving items back
296
+ std::make_heap(this->items_.begin(), this->items_.end(), SchedulerItem::cmp);
176
297
  }
177
298
  }
178
299
  #endif // ESPHOME_DEBUG_SCHEDULER
179
300
 
180
- auto to_remove_was = to_remove_;
181
- auto items_was = this->items_.size();
182
301
  // If we have too many items to remove
183
- if (to_remove_ > MAX_LOGICALLY_DELETED_ITEMS) {
302
+ if (this->to_remove_ > MAX_LOGICALLY_DELETED_ITEMS) {
303
+ // We hold the lock for the entire cleanup operation because:
304
+ // 1. We're rebuilding the entire items_ list, so we need exclusive access throughout
305
+ // 2. Other threads must see either the old state or the new state, not intermediate states
306
+ // 3. The operation is already expensive (O(n)), so lock overhead is negligible
307
+ // 4. No operations inside can block or take other locks, so no deadlock risk
308
+ LockGuard guard{this->lock_};
309
+
184
310
  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
311
 
192
- {
193
- LockGuard guard{this->lock_};
194
- this->items_ = std::move(valid_items);
312
+ // Move all non-removed items to valid_items
313
+ for (auto &item : this->items_) {
314
+ if (!item->remove) {
315
+ valid_items.push_back(std::move(item));
316
+ }
195
317
  }
196
318
 
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
- }
319
+ // Replace items_ with the filtered list
320
+ this->items_ = std::move(valid_items);
321
+ // Rebuild the heap structure since items are no longer in heap order
322
+ std::make_heap(this->items_.begin(), this->items_.end(), SchedulerItem::cmp);
323
+ this->to_remove_ = 0;
203
324
  }
204
325
 
205
326
  while (!this->empty_()) {
@@ -217,47 +338,39 @@ void HOT Scheduler::call() {
217
338
  this->pop_raw_();
218
339
  continue;
219
340
  }
220
- App.set_current_component(item->component);
221
-
222
341
  #ifdef ESPHOME_DEBUG_SCHEDULER
342
+ const char *item_name = item->get_name();
223
343
  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);
344
+ item->get_type_str(), item->get_source(), item_name ? item_name : "(null)", item->interval,
345
+ item->next_execution_, now);
226
346
  #endif
227
347
 
228
348
  // Warning: During callback(), a lot of stuff can happen, including:
229
349
  // - timeouts/intervals get added, potentially invalidating vector pointers
230
350
  // - 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
- }
351
+ this->execute_item_(item.get());
238
352
  }
239
353
 
240
354
  {
241
- this->lock_.lock();
355
+ LockGuard guard{this->lock_};
242
356
 
243
357
  // new scope, item from before might have been moved in the vector
244
358
  auto item = std::move(this->items_[0]);
245
-
246
359
  // Only pop after function call, this ensures we were reachable
247
360
  // during the function call and know if we were cancelled.
248
361
  this->pop_raw_();
249
362
 
250
- this->lock_.unlock();
251
-
252
363
  if (item->remove) {
253
364
  // We were removed/cancelled in the function call, stop
254
- to_remove_--;
365
+ this->to_remove_--;
255
366
  continue;
256
367
  }
257
368
 
258
369
  if (item->type == SchedulerItem::INTERVAL) {
259
370
  item->next_execution_ = now + item->interval;
260
- this->push_(std::move(item));
371
+ // Add new item directly to to_add_
372
+ // since we have the lock held
373
+ this->to_add_.push_back(std::move(item));
261
374
  }
262
375
  }
263
376
  }
@@ -277,55 +390,114 @@ void HOT Scheduler::process_to_add() {
277
390
  this->to_add_.clear();
278
391
  }
279
392
  void HOT Scheduler::cleanup_() {
393
+ // Fast path: if nothing to remove, just return
394
+ // Reading to_remove_ without lock is safe because:
395
+ // 1. We only call this from the main thread during call()
396
+ // 2. If it's 0, there's definitely nothing to cleanup
397
+ // 3. If it becomes non-zero after we check, cleanup will happen on the next loop iteration
398
+ // 4. Not all platforms support atomics, so we accept this race in favor of performance
399
+ // 5. The worst case is a one-loop-iteration delay in cleanup, which is harmless
400
+ if (this->to_remove_ == 0)
401
+ return;
402
+
403
+ // We must hold the lock for the entire cleanup operation because:
404
+ // 1. We're modifying items_ (via pop_raw_) which requires exclusive access
405
+ // 2. We're decrementing to_remove_ which is also modified by other threads
406
+ // (though all modifications are already under lock)
407
+ // 3. Other threads read items_ when searching for items to cancel in cancel_item_locked_()
408
+ // 4. We need a consistent view of items_ and to_remove_ throughout the operation
409
+ // Without the lock, we could access items_ while another thread is reading it,
410
+ // leading to race conditions
411
+ LockGuard guard{this->lock_};
280
412
  while (!this->items_.empty()) {
281
413
  auto &item = this->items_[0];
282
414
  if (!item->remove)
283
415
  return;
284
-
285
- to_remove_--;
286
-
287
- {
288
- LockGuard guard{this->lock_};
289
- this->pop_raw_();
290
- }
416
+ this->to_remove_--;
417
+ this->pop_raw_();
291
418
  }
292
419
  }
293
420
  void HOT Scheduler::pop_raw_() {
294
421
  std::pop_heap(this->items_.begin(), this->items_.end(), SchedulerItem::cmp);
295
422
  this->items_.pop_back();
296
423
  }
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));
424
+
425
+ // Helper to execute a scheduler item
426
+ void HOT Scheduler::execute_item_(SchedulerItem *item) {
427
+ App.set_current_component(item->component);
428
+
429
+ uint32_t now_ms = millis();
430
+ WarnIfComponentBlockingGuard guard{item->component, now_ms};
431
+ item->callback();
432
+ guard.finish();
300
433
  }
301
- bool HOT Scheduler::cancel_item_(Component *component, const std::string &name, Scheduler::SchedulerItem::Type type) {
434
+
435
+ // Common implementation for cancel operations
436
+ bool HOT Scheduler::cancel_item_(Component *component, bool is_static_string, const void *name_ptr,
437
+ SchedulerItem::Type type) {
438
+ // Get the name as const char*
439
+ const char *name_cstr = this->get_name_cstr_(is_static_string, name_ptr);
440
+
302
441
  // obtain lock because this function iterates and can be called from non-loop task context
303
442
  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;
443
+ return this->cancel_item_locked_(component, name_cstr, type);
444
+ }
445
+
446
+ // 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) {
448
+ // Early return if name is invalid - no items to cancel
449
+ if (name_cstr == nullptr || name_cstr[0] == '\0') {
450
+ return false;
451
+ }
452
+
453
+ size_t total_cancelled = 0;
454
+
455
+ // Check all containers for matching items
456
+ #if !defined(USE_ESP8266) && !defined(USE_RP2040)
457
+ // Only check defer queue for timeouts (intervals never go there)
458
+ if (type == SchedulerItem::TIMEOUT) {
459
+ for (auto &item : this->defer_queue_) {
460
+ if (this->matches_item_(item, component, name_cstr, type)) {
461
+ item->remove = true;
462
+ total_cancelled++;
463
+ }
310
464
  }
311
465
  }
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;
466
+ #endif
467
+
468
+ // Cancel items in the main heap
469
+ for (auto &item : this->items_) {
470
+ if (this->matches_item_(item, component, name_cstr, type)) {
471
+ item->remove = true;
472
+ total_cancelled++;
473
+ this->to_remove_++; // Track removals for heap items
474
+ }
475
+ }
476
+
477
+ // Cancel items in to_add_
478
+ for (auto &item : this->to_add_) {
479
+ if (this->matches_item_(item, component, name_cstr, type)) {
480
+ item->remove = true;
481
+ total_cancelled++;
482
+ // Don't track removals for to_add_ items
316
483
  }
317
484
  }
318
485
 
319
- return ret;
486
+ return total_cancelled > 0;
320
487
  }
488
+
321
489
  uint64_t Scheduler::millis_() {
490
+ // Get the current 32-bit millis value
322
491
  const uint32_t now = millis();
492
+ // Check for rollover by comparing with last value
323
493
  if (now < this->last_millis_) {
494
+ // Detected rollover (happens every ~49.7 days)
324
495
  this->millis_major_++;
325
496
  ESP_LOGD(TAG, "Incrementing scheduler major at %" PRIu64 "ms",
326
497
  now + (static_cast<uint64_t>(this->millis_major_) << 32));
327
498
  }
328
499
  this->last_millis_ = now;
500
+ // Combine major (high 32 bits) and now (low 32 bits) into 64-bit time
329
501
  return now + (static_cast<uint64_t>(this->millis_major_) << 32);
330
502
  }
331
503