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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (646) 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/as3935_spi/as3935_spi.h +0 -2
  42. esphome/components/as5600/as5600.h +0 -1
  43. esphome/components/async_tcp/__init__.py +14 -5
  44. esphome/components/atc_mithermometer/atc_mithermometer.h +0 -1
  45. esphome/components/atm90e32/atm90e32.cpp +2 -1
  46. esphome/components/audio/audio_decoder.cpp +1 -1
  47. esphome/components/audio/audio_transfer_buffer.cpp +2 -2
  48. esphome/components/b_parasite/b_parasite.h +0 -1
  49. esphome/components/bedjet/bedjet_hub.cpp +5 -1
  50. esphome/components/bedjet/climate/bedjet_climate.cpp +5 -1
  51. esphome/components/beken_spi_led_strip/led_strip.cpp +4 -2
  52. esphome/components/bh1750/bh1750.cpp +5 -5
  53. esphome/components/binary_sensor/__init__.py +82 -5
  54. esphome/components/binary_sensor/automation.h +19 -1
  55. esphome/components/binary_sensor/binary_sensor.cpp +12 -30
  56. esphome/components/binary_sensor/binary_sensor.h +11 -25
  57. esphome/components/binary_sensor/filter.cpp +29 -24
  58. esphome/components/binary_sensor/filter.h +20 -10
  59. esphome/components/ble_client/output/ble_binary_output.h +0 -1
  60. esphome/components/ble_client/sensor/ble_rssi_sensor.cpp +5 -1
  61. esphome/components/ble_client/sensor/ble_rssi_sensor.h +0 -1
  62. esphome/components/ble_client/sensor/ble_sensor.cpp +5 -1
  63. esphome/components/ble_client/sensor/ble_sensor.h +0 -1
  64. esphome/components/ble_client/switch/ble_switch.h +0 -1
  65. esphome/components/ble_client/text_sensor/ble_text_sensor.cpp +5 -1
  66. esphome/components/ble_client/text_sensor/ble_text_sensor.h +0 -1
  67. esphome/components/ble_presence/ble_presence_device.h +0 -1
  68. esphome/components/ble_rssi/ble_rssi_sensor.h +0 -1
  69. esphome/components/ble_scanner/ble_scanner.h +0 -1
  70. esphome/components/bluetooth_proxy/bluetooth_connection.h +9 -2
  71. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +16 -6
  72. esphome/components/bluetooth_proxy/bluetooth_proxy.h +8 -2
  73. esphome/components/bme680/sensor.py +1 -1
  74. esphome/components/bmp581/bmp581.h +0 -2
  75. esphome/components/button/__init__.py +5 -2
  76. esphome/components/camera/__init__.py +1 -0
  77. esphome/components/camera/camera.cpp +22 -0
  78. esphome/components/camera/camera.h +80 -0
  79. esphome/components/canbus/__init__.py +1 -0
  80. esphome/components/cap1188/cap1188.h +0 -1
  81. esphome/components/captive_portal/__init__.py +12 -2
  82. esphome/components/captive_portal/captive_portal.cpp +12 -2
  83. esphome/components/captive_portal/captive_portal.h +5 -2
  84. esphome/components/ccs811/ccs811.h +0 -2
  85. esphome/components/climate/__init__.py +5 -2
  86. esphome/components/cm1106/sensor.py +2 -2
  87. esphome/components/const/__init__.py +2 -0
  88. esphome/components/copy/binary_sensor/copy_binary_sensor.h +0 -1
  89. esphome/components/copy/button/copy_button.h +0 -1
  90. esphome/components/copy/cover/copy_cover.h +0 -1
  91. esphome/components/copy/fan/copy_fan.h +0 -1
  92. esphome/components/copy/lock/copy_lock.h +0 -1
  93. esphome/components/copy/number/copy_number.h +0 -1
  94. esphome/components/copy/select/copy_select.h +0 -1
  95. esphome/components/copy/sensor/copy_sensor.h +0 -1
  96. esphome/components/copy/switch/copy_switch.h +0 -1
  97. esphome/components/copy/text/copy_text.h +0 -1
  98. esphome/components/copy/text_sensor/copy_text_sensor.h +0 -1
  99. esphome/components/cover/__init__.py +5 -2
  100. esphome/components/cs5460a/cs5460a.h +0 -1
  101. esphome/components/datetime/__init__.py +4 -2
  102. esphome/components/debug/__init__.py +20 -0
  103. esphome/components/debug/debug_esp32.cpp +2 -0
  104. esphome/components/deep_sleep/__init__.py +43 -9
  105. esphome/components/demo/__init__.py +2 -2
  106. esphome/components/display/display.cpp +4 -3
  107. esphome/components/display/display.h +0 -2
  108. esphome/components/display/display_buffer.cpp +1 -1
  109. esphome/components/ds2484/__init__.py +1 -0
  110. esphome/components/ds2484/ds2484.cpp +209 -0
  111. esphome/components/ds2484/ds2484.h +43 -0
  112. esphome/components/ds2484/one_wire.py +37 -0
  113. esphome/components/duty_time/duty_time_sensor.h +0 -1
  114. esphome/components/ens160_base/ens160_base.h +0 -1
  115. esphome/components/es7210/es7210.h +0 -1
  116. esphome/components/es7243e/es7243e.h +0 -1
  117. esphome/components/es8156/es8156.h +0 -1
  118. esphome/components/es8311/es8311.h +0 -1
  119. esphome/components/es8388/es8388.h +0 -1
  120. esphome/components/esp32/__init__.py +103 -135
  121. esphome/components/esp32/core.cpp +0 -4
  122. esphome/components/esp32/gpio.h +1 -1
  123. esphome/components/esp32/helpers.cpp +69 -0
  124. esphome/components/esp32_ble/ble.cpp +5 -6
  125. esphome/components/esp32_ble/ble.h +29 -14
  126. esphome/components/esp32_ble/ble_event.h +6 -6
  127. esphome/components/esp32_ble_client/ble_client_base.cpp +21 -6
  128. esphome/components/esp32_ble_client/ble_client_base.h +24 -9
  129. esphome/components/esp32_ble_tracker/__init__.py +2 -8
  130. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +5 -5
  131. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +11 -7
  132. esphome/components/esp32_camera/__init__.py +112 -98
  133. esphome/components/esp32_camera/esp32_camera.cpp +41 -31
  134. esphome/components/esp32_camera/esp32_camera.h +35 -30
  135. esphome/components/esp32_camera_web_server/__init__.py +2 -1
  136. esphome/components/esp32_camera_web_server/camera_web_server.cpp +8 -8
  137. esphome/components/esp32_camera_web_server/camera_web_server.h +3 -3
  138. esphome/components/esp32_hall/sensor.py +2 -21
  139. esphome/components/esp32_hosted/__init__.py +101 -0
  140. esphome/components/esp32_hosted/esp32_hosted.py.script +12 -0
  141. esphome/components/esp32_improv/esp32_improv_component.cpp +3 -0
  142. esphome/components/esp32_rmt/__init__.py +0 -58
  143. esphome/components/esp32_rmt_led_strip/led_strip.cpp +77 -63
  144. esphome/components/esp32_rmt_led_strip/led_strip.h +11 -17
  145. esphome/components/esp32_rmt_led_strip/light.py +14 -76
  146. esphome/components/esp32_touch/esp32_touch.h +174 -28
  147. esphome/components/esp32_touch/esp32_touch_common.cpp +162 -0
  148. esphome/components/esp32_touch/esp32_touch_v1.cpp +240 -0
  149. esphome/components/esp32_touch/esp32_touch_v2.cpp +397 -0
  150. esphome/components/esp8266/__init__.py +2 -0
  151. esphome/components/esp8266/gpio.cpp +10 -10
  152. esphome/components/esp8266/helpers.cpp +31 -0
  153. esphome/components/esp_ldo/__init__.py +10 -8
  154. esphome/components/esp_ldo/esp_ldo.h +3 -0
  155. esphome/components/esphome/ota/__init__.py +1 -0
  156. esphome/components/esphome/ota/ota_esphome.cpp +24 -19
  157. esphome/components/ethernet/__init__.py +42 -23
  158. esphome/components/ethernet/esp_eth_phy_jl1101.c +0 -16
  159. esphome/components/ethernet/ethernet_component.cpp +69 -29
  160. esphome/components/ethernet/ethernet_component.h +18 -10
  161. esphome/components/event/__init__.py +5 -2
  162. esphome/components/ezo/ezo.h +0 -1
  163. esphome/components/ezo_pmp/ezo_pmp.h +0 -1
  164. esphome/components/fan/__init__.py +5 -2
  165. esphome/components/fan/fan.cpp +4 -0
  166. esphome/components/feedback/feedback_cover.h +0 -1
  167. esphome/components/font/__init__.py +92 -82
  168. esphome/components/font/font.cpp +9 -2
  169. esphome/components/font/font.h +20 -5
  170. esphome/components/fs3000/fs3000.h +0 -1
  171. esphome/components/gcja5/gcja5.h +0 -1
  172. esphome/components/gl_r01_i2c/__init__.py +0 -0
  173. esphome/components/gl_r01_i2c/gl_r01_i2c.cpp +68 -0
  174. esphome/components/gl_r01_i2c/gl_r01_i2c.h +22 -0
  175. esphome/components/gl_r01_i2c/sensor.py +36 -0
  176. esphome/components/gp8403/gp8403.h +0 -1
  177. esphome/components/gpio/binary_sensor/__init__.py +39 -1
  178. esphome/components/gpio/binary_sensor/gpio_binary_sensor.cpp +77 -3
  179. esphome/components/gpio/binary_sensor/gpio_binary_sensor.h +40 -0
  180. esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.h +0 -2
  181. esphome/components/he60r/he60r.h +0 -1
  182. esphome/components/heatpumpir/climate.py +2 -1
  183. esphome/components/heatpumpir/heatpumpir.cpp +1 -0
  184. esphome/components/heatpumpir/heatpumpir.h +1 -0
  185. esphome/components/honeywellabp2_i2c/honeywellabp2.h +0 -1
  186. esphome/components/host/__init__.py +3 -1
  187. esphome/components/host/helpers.cpp +57 -0
  188. esphome/components/http_request/__init__.py +19 -1
  189. esphome/components/http_request/http_request.h +1 -1
  190. esphome/components/http_request/http_request_arduino.h +1 -0
  191. esphome/components/http_request/ota/ota_http_request.cpp +1 -1
  192. esphome/components/http_request/update/http_request_update.cpp +35 -16
  193. esphome/components/hydreon_rgxx/hydreon_rgxx.cpp +3 -9
  194. esphome/components/hydreon_rgxx/sensor.py +1 -1
  195. esphome/components/i2c/__init__.py +23 -11
  196. esphome/components/i2c/i2c_bus.h +8 -1
  197. esphome/components/i2c/i2c_bus_arduino.cpp +4 -3
  198. esphome/components/i2c/i2c_bus_arduino.h +6 -3
  199. esphome/components/i2c/i2c_bus_esp_idf.h +5 -3
  200. esphome/components/i2c_device/i2c_device.h +0 -1
  201. esphome/components/i2s_audio/__init__.py +2 -10
  202. esphome/components/i2s_audio/i2s_audio.cpp +1 -5
  203. esphome/components/i2s_audio/media_player/__init__.py +2 -2
  204. esphome/components/i2s_audio/speaker/__init__.py +1 -1
  205. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +2 -2
  206. esphome/components/iaqcore/iaqcore.h +0 -2
  207. esphome/components/image/__init__.py +123 -24
  208. esphome/components/improv_serial/improv_serial_component.cpp +0 -4
  209. esphome/components/ina219/ina219.cpp +7 -0
  210. esphome/components/ina219/ina219.h +1 -0
  211. esphome/components/ina260/ina260.h +0 -2
  212. esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.h +0 -1
  213. esphome/components/inkplate6/display.py +15 -0
  214. esphome/components/inkplate6/inkplate.cpp +2 -2
  215. esphome/components/integration/integration_sensor.h +0 -1
  216. esphome/components/internal_temperature/internal_temperature.cpp +8 -27
  217. esphome/components/internal_temperature/sensor.py +0 -26
  218. esphome/components/interval/interval.h +0 -2
  219. esphome/components/json/__init__.py +1 -1
  220. esphome/components/json/json_util.cpp +56 -63
  221. esphome/components/ld2410/button/__init__.py +3 -3
  222. esphome/components/ld2410/button/factory_reset_button.cpp +9 -0
  223. esphome/components/ld2410/button/{reset_button.h → factory_reset_button.h} +2 -2
  224. esphome/components/ld2410/ld2410.cpp +421 -268
  225. esphome/components/ld2410/ld2410.h +44 -146
  226. esphome/components/ld2410/number/__init__.py +2 -2
  227. esphome/components/ld2410/sensor.py +1 -1
  228. esphome/components/ld2410/switch/__init__.py +1 -1
  229. esphome/components/ld2420/binary_sensor/ld2420_binary_sensor.cpp +2 -2
  230. esphome/components/ld2420/button/reconfig_buttons.cpp +1 -1
  231. esphome/components/ld2420/ld2420.cpp +252 -147
  232. esphome/components/ld2420/ld2420.h +52 -126
  233. esphome/components/ld2420/number/__init__.py +2 -2
  234. esphome/components/ld2420/number/gate_config_number.cpp +1 -1
  235. esphome/components/ld2420/select/operating_mode_select.cpp +1 -1
  236. esphome/components/ld2420/sensor/__init__.py +6 -2
  237. esphome/components/ld2420/sensor/ld2420_sensor.cpp +2 -2
  238. esphome/components/ld2420/sensor/ld2420_sensor.h +1 -1
  239. esphome/components/ld2420/text_sensor/text_sensor.cpp +2 -2
  240. esphome/components/ld2450/button/__init__.py +3 -3
  241. esphome/components/ld2450/button/factory_reset_button.cpp +9 -0
  242. esphome/components/ld2450/button/{reset_button.h → factory_reset_button.h} +2 -2
  243. esphome/components/ld2450/ld2450.cpp +384 -232
  244. esphome/components/ld2450/ld2450.h +60 -69
  245. esphome/components/ld2450/switch/__init__.py +1 -1
  246. esphome/components/ledc/ledc_output.cpp +1 -63
  247. esphome/components/libretiny/__init__.py +5 -3
  248. esphome/components/libretiny/const.py +5 -0
  249. esphome/components/libretiny/generate_components.py +1 -0
  250. esphome/components/libretiny/helpers.cpp +35 -0
  251. esphome/components/libretiny/lt_component.cpp +5 -3
  252. esphome/components/light/__init__.py +4 -2
  253. esphome/components/light/addressable_light.h +3 -3
  254. esphome/components/light/light_call.cpp +180 -243
  255. esphome/components/light/light_call.h +72 -20
  256. esphome/components/light/light_color_values.h +14 -14
  257. esphome/components/light/light_json_schema.cpp +17 -16
  258. esphome/components/light/light_state.h +15 -13
  259. esphome/components/light/transformers.h +2 -2
  260. esphome/components/ln882x/__init__.py +52 -0
  261. esphome/components/ln882x/boards.py +285 -0
  262. esphome/components/lock/__init__.py +5 -2
  263. esphome/components/logger/__init__.py +40 -3
  264. esphome/components/logger/logger.cpp +47 -12
  265. esphome/components/logger/logger.h +80 -49
  266. esphome/components/logger/logger_esp32.cpp +3 -3
  267. esphome/components/lps22/__init__.py +0 -0
  268. esphome/components/lps22/lps22.cpp +75 -0
  269. esphome/components/lps22/lps22.h +27 -0
  270. esphome/components/lps22/sensor.py +58 -0
  271. esphome/components/ltr390/ltr390.h +0 -1
  272. esphome/components/ltr501/ltr501.h +0 -1
  273. esphome/components/ltr_als_ps/ltr_als_ps.h +0 -1
  274. esphome/components/lvgl/__init__.py +1 -1
  275. esphome/components/lvgl/schemas.py +66 -6
  276. esphome/components/lvgl/styles.py +24 -16
  277. esphome/components/lvgl/widgets/__init__.py +12 -2
  278. esphome/components/lvgl/widgets/lv_bar.py +40 -19
  279. esphome/components/lvgl/widgets/meter.py +20 -13
  280. esphome/components/m5stack_8angle/light/m5stack_8angle_light.cpp +1 -1
  281. esphome/components/max9611/max9611.h +0 -1
  282. esphome/components/mcp23016/__init__.py +1 -1
  283. esphome/components/mcp23xxx_base/__init__.py +1 -1
  284. esphome/components/mcp4461/__init__.py +1 -1
  285. esphome/components/mcp4461/output/__init__.py +3 -2
  286. esphome/components/mcp9600/mcp9600.h +0 -2
  287. esphome/components/md5/md5.cpp +3 -3
  288. esphome/components/md5/md5.h +1 -6
  289. esphome/components/mdns/__init__.py +22 -11
  290. esphome/components/media_player/__init__.py +4 -3
  291. esphome/components/micro_wake_word/__init__.py +1 -5
  292. esphome/components/micro_wake_word/streaming_model.cpp +2 -2
  293. esphome/components/microphone/microphone.cpp +7 -9
  294. esphome/components/microphone/microphone.h +0 -2
  295. esphome/components/mipi_spi/display.py +1 -0
  296. esphome/components/mmc5603/mmc5603.cpp +1 -1
  297. esphome/components/modbus/modbus.cpp +33 -15
  298. esphome/components/modbus/modbus.h +9 -0
  299. esphome/components/modbus_controller/__init__.py +42 -10
  300. esphome/components/modbus_controller/modbus_controller.cpp +92 -11
  301. esphome/components/modbus_controller/modbus_controller.h +61 -7
  302. esphome/components/mopeka_pro_check/mopeka_pro_check.h +0 -1
  303. esphome/components/mopeka_std_check/mopeka_std_check.h +0 -1
  304. esphome/components/mpl3115a2/mpl3115a2.h +0 -2
  305. esphome/components/mqtt/__init__.py +16 -0
  306. esphome/components/mqtt/mqtt_alarm_control_panel.cpp +2 -1
  307. esphome/components/mqtt/mqtt_backend.h +2 -1
  308. esphome/components/mqtt/mqtt_backend_esp32.cpp +132 -47
  309. esphome/components/mqtt/mqtt_backend_esp32.h +106 -4
  310. esphome/components/mqtt/mqtt_binary_sensor.cpp +1 -0
  311. esphome/components/mqtt/mqtt_button.cpp +4 -1
  312. esphome/components/mqtt/mqtt_client.cpp +17 -9
  313. esphome/components/mqtt/mqtt_client.h +8 -3
  314. esphome/components/mqtt/mqtt_climate.cpp +6 -4
  315. esphome/components/mqtt/mqtt_component.cpp +3 -1
  316. esphome/components/mqtt/mqtt_cover.cpp +1 -0
  317. esphome/components/mqtt/mqtt_date.cpp +4 -3
  318. esphome/components/mqtt/mqtt_datetime.cpp +7 -6
  319. esphome/components/mqtt/mqtt_event.cpp +6 -3
  320. esphome/components/mqtt/mqtt_fan.cpp +1 -0
  321. esphome/components/mqtt/mqtt_light.cpp +8 -4
  322. esphome/components/mqtt/mqtt_lock.cpp +3 -1
  323. esphome/components/mqtt/mqtt_number.cpp +1 -0
  324. esphome/components/mqtt/mqtt_select.cpp +2 -1
  325. esphome/components/mqtt/mqtt_sensor.cpp +3 -1
  326. esphome/components/mqtt/mqtt_switch.cpp +3 -1
  327. esphome/components/mqtt/mqtt_text.cpp +1 -0
  328. esphome/components/mqtt/mqtt_text_sensor.cpp +3 -1
  329. esphome/components/mqtt/mqtt_time.cpp +4 -3
  330. esphome/components/mqtt/mqtt_update.cpp +1 -0
  331. esphome/components/mqtt/mqtt_valve.cpp +3 -1
  332. esphome/components/ms8607/ms8607.cpp +1 -1
  333. esphome/components/ms8607/ms8607.h +0 -1
  334. esphome/components/neopixelbus/light.py +4 -1
  335. esphome/components/neopixelbus/neopixelbus_light.h +1 -1
  336. esphome/components/network/__init__.py +4 -1
  337. esphome/components/network/ip_address.h +1 -0
  338. esphome/components/nextion/__init__.py +16 -0
  339. esphome/components/nextion/base_component.py +1 -0
  340. esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp +1 -1
  341. esphome/components/nextion/display.py +14 -4
  342. esphome/components/nextion/nextion.cpp +166 -101
  343. esphome/components/nextion/nextion.h +84 -53
  344. esphome/components/nextion/nextion_commands.cpp +11 -10
  345. esphome/components/nextion/nextion_component.cpp +28 -28
  346. esphome/components/nextion/nextion_component.h +53 -18
  347. esphome/components/nextion/nextion_component_base.h +3 -0
  348. esphome/components/nextion/nextion_upload.cpp +36 -0
  349. esphome/components/nextion/nextion_upload_arduino.cpp +10 -35
  350. esphome/components/nextion/nextion_upload_idf.cpp +9 -33
  351. esphome/components/nextion/sensor/nextion_sensor.cpp +1 -1
  352. esphome/components/nextion/switch/nextion_switch.cpp +1 -1
  353. esphome/components/nextion/text_sensor/nextion_textsensor.cpp +1 -1
  354. esphome/components/nfc/nfc.cpp +3 -22
  355. esphome/components/nfc/nfc.h +3 -3
  356. esphome/components/number/__init__.py +5 -2
  357. esphome/components/online_image/__init__.py +9 -1
  358. esphome/components/online_image/online_image.cpp +17 -7
  359. esphome/components/online_image/online_image.h +10 -2
  360. esphome/components/opentherm/opentherm.cpp +7 -12
  361. esphome/components/opentherm/output/output.cpp +1 -1
  362. esphome/components/openthread/__init__.py +47 -40
  363. esphome/components/openthread/const.py +1 -0
  364. esphome/components/openthread/openthread_esp.cpp +27 -5
  365. esphome/components/opt3001/__init__.py +0 -0
  366. esphome/components/opt3001/opt3001.cpp +122 -0
  367. esphome/components/opt3001/opt3001.h +27 -0
  368. esphome/components/opt3001/sensor.py +35 -0
  369. esphome/components/ota/__init__.py +17 -0
  370. esphome/components/ota/ota_backend.h +27 -1
  371. esphome/components/ota/ota_backend_arduino_esp32.cpp +12 -2
  372. esphome/components/ota/ota_backend_arduino_esp32.h +3 -0
  373. esphome/components/ota/ota_backend_arduino_esp8266.cpp +18 -4
  374. esphome/components/ota/ota_backend_arduino_esp8266.h +3 -0
  375. esphome/components/ota/ota_backend_arduino_libretiny.cpp +12 -2
  376. esphome/components/ota/ota_backend_arduino_libretiny.h +3 -0
  377. esphome/components/ota/ota_backend_arduino_rp2040.cpp +9 -2
  378. esphome/components/ota/ota_backend_arduino_rp2040.h +3 -0
  379. esphome/components/ota/ota_backend_esp_idf.cpp +10 -16
  380. esphome/components/ota/ota_backend_esp_idf.h +1 -0
  381. esphome/components/packages/__init__.py +5 -2
  382. esphome/components/packet_transport/binary_sensor.py +61 -4
  383. esphome/components/packet_transport/packet_transport.cpp +34 -1
  384. esphome/components/packet_transport/packet_transport.h +11 -5
  385. esphome/components/pcf8574/__init__.py +1 -1
  386. esphome/components/pi4ioe5v6408/__init__.py +84 -0
  387. esphome/components/pi4ioe5v6408/pi4ioe5v6408.cpp +171 -0
  388. esphome/components/pi4ioe5v6408/pi4ioe5v6408.h +70 -0
  389. esphome/components/pmsa003i/pmsa003i.h +0 -1
  390. esphome/components/pmsx003/pmsx003.h +0 -1
  391. esphome/components/pn7150/pn7150.cpp +7 -7
  392. esphome/components/pn7150/pn7150.h +0 -1
  393. esphome/components/pn7160/pn7160.cpp +7 -7
  394. esphome/components/pn7160/pn7160.h +0 -1
  395. esphome/components/preferences/syncer.h +2 -0
  396. esphome/components/prometheus/prometheus_handler.h +1 -1
  397. esphome/components/psram/psram.cpp +0 -20
  398. esphome/components/pulse_counter/pulse_counter_sensor.h +0 -1
  399. esphome/components/pulse_meter/pulse_meter_sensor.cpp +8 -4
  400. esphome/components/pulse_width/pulse_width.h +0 -1
  401. esphome/components/pvvx_mithermometer/display/pvvx_display.cpp +0 -4
  402. esphome/components/pvvx_mithermometer/display/pvvx_display.h +0 -2
  403. esphome/components/pvvx_mithermometer/pvvx_mithermometer.h +0 -1
  404. esphome/components/qr_code/__init__.py +13 -10
  405. esphome/components/qwiic_pir/qwiic_pir.h +0 -1
  406. esphome/components/radon_eye_ble/radon_eye_listener.cpp +1 -1
  407. esphome/components/rc522/rc522.h +0 -1
  408. esphome/components/rdm6300/rdm6300.h +0 -2
  409. esphome/components/remote_base/__init__.py +7 -5
  410. esphome/components/remote_base/remote_base.cpp +24 -21
  411. esphome/components/remote_base/remote_base.h +3 -26
  412. esphome/components/remote_receiver/__init__.py +40 -46
  413. esphome/components/remote_receiver/remote_receiver.h +4 -18
  414. esphome/components/remote_receiver/remote_receiver_esp32.cpp +0 -87
  415. esphome/components/remote_receiver/remote_receiver_esp8266.cpp +1 -1
  416. esphome/components/remote_transmitter/__init__.py +42 -43
  417. esphome/components/remote_transmitter/remote_transmitter.h +2 -14
  418. esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +0 -77
  419. esphome/components/resistance/resistance_sensor.h +0 -1
  420. esphome/components/rp2040/__init__.py +2 -0
  421. esphome/components/rp2040/helpers.cpp +55 -0
  422. esphome/components/rp2040_pio_led_strip/led_strip.cpp +2 -2
  423. esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp +0 -4
  424. esphome/components/rtttl/__init__.py +4 -4
  425. esphome/components/rtttl/rtttl.cpp +10 -1
  426. esphome/components/ruuvitag/ruuvitag.h +0 -1
  427. esphome/components/safe_mode/safe_mode.cpp +2 -0
  428. esphome/components/safe_mode/safe_mode.h +4 -1
  429. esphome/components/scd30/scd30.h +0 -1
  430. esphome/components/scd30/sensor.py +2 -2
  431. esphome/components/scd4x/scd4x.cpp +61 -54
  432. esphome/components/scd4x/scd4x.h +17 -15
  433. esphome/components/scd4x/sensor.py +4 -4
  434. esphome/components/script/script.h +0 -2
  435. esphome/components/sdp3x/sensor.py +1 -1
  436. esphome/components/select/__init__.py +5 -2
  437. esphome/components/sen5x/sen5x.h +0 -1
  438. esphome/components/senseair/senseair.h +0 -1
  439. esphome/components/sensor/__init__.py +4 -2
  440. esphome/components/sensor/filter.cpp +1 -1
  441. esphome/components/sensor/sensor.cpp +12 -6
  442. esphome/components/sensor/sensor.h +13 -5
  443. esphome/components/servo/servo.cpp +2 -2
  444. esphome/components/servo/servo.h +0 -1
  445. esphome/components/sfa30/sfa30.h +0 -1
  446. esphome/components/sgp30/sgp30.h +0 -1
  447. esphome/components/sgp4x/sgp4x.h +0 -1
  448. esphome/components/shelly_dimmer/stm32flash.cpp +1 -2
  449. esphome/components/sht4x/sht4x.h +0 -1
  450. esphome/components/sm300d2/sm300d2.h +0 -2
  451. esphome/components/smt100/sensor.py +8 -4
  452. esphome/components/smt100/smt100.cpp +5 -5
  453. esphome/components/smt100/smt100.h +3 -3
  454. esphome/components/sn74hc595/__init__.py +1 -1
  455. esphome/components/sn74hc595/sn74hc595.cpp +5 -4
  456. esphome/components/sntp/sntp_component.cpp +9 -3
  457. esphome/components/sntp/time.py +2 -0
  458. esphome/components/socket/__init__.py +17 -0
  459. esphome/components/spi/__init__.py +27 -6
  460. esphome/components/spi/spi.cpp +3 -2
  461. esphome/components/spi/spi.h +9 -3
  462. esphome/components/spi/spi_arduino.cpp +3 -5
  463. esphome/components/spi/spi_esp_idf.cpp +40 -21
  464. esphome/components/spi_led_strip/spi_led_strip.cpp +1 -1
  465. esphome/components/sps30/sps30.h +0 -1
  466. esphome/components/ssd1306_base/ssd1306_base.cpp +1 -1
  467. esphome/components/st7701s/st7701s.cpp +0 -4
  468. esphome/components/status/status_binary_sensor.h +0 -2
  469. esphome/components/substitutions/__init__.py +81 -21
  470. esphome/components/substitutions/jinja.py +99 -0
  471. esphome/components/sun/sun.cpp +3 -4
  472. esphome/components/switch/__init__.py +5 -2
  473. esphome/components/switch/binary_sensor/switch_binary_sensor.h +0 -1
  474. esphome/components/sx126x/__init__.py +317 -0
  475. esphome/components/sx126x/automation.h +62 -0
  476. esphome/components/sx126x/packet_transport/__init__.py +26 -0
  477. esphome/components/sx126x/packet_transport/sx126x_transport.cpp +26 -0
  478. esphome/components/sx126x/packet_transport/sx126x_transport.h +25 -0
  479. esphome/components/sx126x/sx126x.cpp +523 -0
  480. esphome/components/sx126x/sx126x.h +140 -0
  481. esphome/components/sx126x/sx126x_reg.h +163 -0
  482. esphome/components/sx127x/__init__.py +325 -0
  483. esphome/components/sx127x/automation.h +62 -0
  484. esphome/components/sx127x/packet_transport/__init__.py +26 -0
  485. esphome/components/sx127x/packet_transport/sx127x_transport.cpp +26 -0
  486. esphome/components/sx127x/packet_transport/sx127x_transport.h +25 -0
  487. esphome/components/sx127x/sx127x.cpp +498 -0
  488. esphome/components/sx127x/sx127x.h +128 -0
  489. esphome/components/sx127x/sx127x_reg.h +295 -0
  490. esphome/components/syslog/esphome_syslog.cpp +5 -3
  491. esphome/components/syslog/esphome_syslog.h +1 -1
  492. esphome/components/tca9555/__init__.py +1 -1
  493. esphome/components/template/binary_sensor/template_binary_sensor.cpp +1 -9
  494. esphome/components/text/__init__.py +5 -2
  495. esphome/components/text_sensor/__init__.py +5 -2
  496. esphome/components/thermostat/thermostat_climate.cpp +34 -31
  497. esphome/components/thermostat/thermostat_climate.h +43 -39
  498. esphome/components/time/__init__.py +16 -2
  499. esphome/components/time/real_time_clock.cpp +4 -0
  500. esphome/components/time/real_time_clock.h +5 -1
  501. esphome/components/tlc5971/tlc5971.cpp +4 -1
  502. esphome/components/tmp1075/tmp1075.h +0 -2
  503. esphome/components/tof10120/tof10120_sensor.h +0 -1
  504. esphome/components/tormatic/tormatic_cover.h +0 -1
  505. esphome/components/total_daily_energy/total_daily_energy.h +0 -1
  506. esphome/components/tsl2591/tsl2591.cpp +1 -1
  507. esphome/components/ttp229_bsf/ttp229_bsf.h +0 -1
  508. esphome/components/ttp229_lsf/ttp229_lsf.h +0 -1
  509. esphome/components/tx20/tx20.cpp +2 -2
  510. esphome/components/uart/__init__.py +18 -0
  511. esphome/components/uart/uart_component_esp_idf.cpp +0 -4
  512. esphome/components/update/__init__.py +5 -2
  513. esphome/components/update/update_entity.h +8 -0
  514. esphome/components/usb_host/__init__.py +5 -2
  515. esphome/components/usb_host/usb_host_client.cpp +10 -10
  516. esphome/components/usb_uart/cp210x.cpp +1 -1
  517. esphome/components/usb_uart/usb_uart.cpp +41 -44
  518. esphome/components/usb_uart/usb_uart.h +4 -3
  519. esphome/components/valve/__init__.py +5 -2
  520. esphome/components/vbus/vbus.h +0 -1
  521. esphome/components/veml3235/veml3235.h +0 -1
  522. esphome/components/veml7700/veml7700.h +0 -1
  523. esphome/components/vl53l0x/vl53l0x_sensor.h +0 -1
  524. esphome/components/voice_assistant/voice_assistant.cpp +4 -4
  525. esphome/components/watchdog/watchdog.cpp +0 -4
  526. esphome/components/waveshare_epaper/waveshare_epaper.cpp +6 -6
  527. esphome/components/web_server/__init__.py +34 -19
  528. esphome/components/web_server/ota/__init__.py +32 -0
  529. esphome/components/web_server/ota/ota_web_server.cpp +210 -0
  530. esphome/components/web_server/ota/ota_web_server.h +26 -0
  531. esphome/components/web_server/web_server.cpp +318 -436
  532. esphome/components/web_server/web_server.h +33 -23
  533. esphome/components/web_server/web_server_v1.cpp +4 -5
  534. esphome/components/web_server_base/__init__.py +5 -2
  535. esphome/components/web_server_base/web_server_base.cpp +2 -94
  536. esphome/components/web_server_base/web_server_base.h +5 -25
  537. esphome/components/web_server_idf/multipart.cpp +254 -0
  538. esphome/components/web_server_idf/multipart.h +86 -0
  539. esphome/components/web_server_idf/utils.cpp +32 -0
  540. esphome/components/web_server_idf/utils.h +10 -0
  541. esphome/components/web_server_idf/web_server_idf.cpp +164 -16
  542. esphome/components/web_server_idf/web_server_idf.h +11 -10
  543. esphome/components/wiegand/wiegand.cpp +2 -2
  544. esphome/components/wifi/__init__.py +18 -0
  545. esphome/components/wifi/wifi_component.cpp +17 -22
  546. esphome/components/wifi/wifi_component.h +27 -23
  547. esphome/components/wifi/wifi_component_esp32_arduino.cpp +52 -59
  548. esphome/components/wifi/wifi_component_esp8266.cpp +46 -46
  549. esphome/components/wifi/wifi_component_esp_idf.cpp +35 -36
  550. esphome/components/wifi/wifi_component_libretiny.cpp +26 -27
  551. esphome/components/wifi/wifi_component_pico_w.cpp +3 -3
  552. esphome/components/wifi_info/wifi_info_text_sensor.cpp +6 -6
  553. esphome/components/wireguard/__init__.py +2 -11
  554. esphome/components/xiaomi_ble/xiaomi_ble.cpp +13 -1
  555. esphome/components/xiaomi_ble/xiaomi_ble.h +1 -0
  556. esphome/components/xiaomi_cgd1/xiaomi_cgd1.h +0 -1
  557. esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.h +0 -1
  558. esphome/components/xiaomi_cgg1/xiaomi_cgg1.h +0 -1
  559. esphome/components/xiaomi_cgpr1/xiaomi_cgpr1.h +0 -1
  560. esphome/components/xiaomi_gcls002/xiaomi_gcls002.h +0 -1
  561. esphome/components/xiaomi_hhccjcy01/xiaomi_hhccjcy01.h +0 -1
  562. esphome/components/xiaomi_hhccjcy10/xiaomi_hhccjcy10.h +0 -1
  563. esphome/components/xiaomi_hhccpot002/xiaomi_hhccpot002.h +0 -1
  564. esphome/components/xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.h +0 -1
  565. esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.h +0 -1
  566. esphome/components/xiaomi_lywsd02mmc/xiaomi_lywsd02mmc.h +0 -1
  567. esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.h +0 -1
  568. esphome/components/xiaomi_lywsdcgq/xiaomi_lywsdcgq.h +0 -1
  569. esphome/components/xiaomi_mhoc303/xiaomi_mhoc303.h +0 -1
  570. esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.h +0 -1
  571. esphome/components/xiaomi_miscale/xiaomi_miscale.h +0 -1
  572. esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.h +0 -1
  573. esphome/components/xiaomi_mue4094rt/xiaomi_mue4094rt.h +0 -1
  574. esphome/components/xiaomi_rtcgq02lm/xiaomi_rtcgq02lm.h +0 -1
  575. esphome/components/xiaomi_wx08zm/xiaomi_wx08zm.h +0 -1
  576. esphome/components/xiaomi_xmwsdj04mmc/__init__.py +0 -0
  577. esphome/components/xiaomi_xmwsdj04mmc/sensor.py +77 -0
  578. esphome/components/xiaomi_xmwsdj04mmc/xiaomi_xmwsdj04mmc.cpp +77 -0
  579. esphome/components/xiaomi_xmwsdj04mmc/xiaomi_xmwsdj04mmc.h +36 -0
  580. esphome/components/zio_ultrasonic/zio_ultrasonic.h +0 -2
  581. esphome/components/zyaura/zyaura.h +0 -1
  582. esphome/config.py +88 -22
  583. esphome/config_helpers.py +74 -1
  584. esphome/config_validation.py +12 -1
  585. esphome/const.py +65 -10
  586. esphome/core/__init__.py +18 -2
  587. esphome/core/application.cpp +169 -10
  588. esphome/core/application.h +145 -165
  589. esphome/core/area.h +19 -0
  590. esphome/core/automation.h +58 -9
  591. esphome/core/color.cpp +3 -5
  592. esphome/core/color.h +16 -16
  593. esphome/core/component.cpp +156 -22
  594. esphome/core/component.h +98 -4
  595. esphome/core/component_iterator.cpp +11 -9
  596. esphome/core/component_iterator.h +12 -10
  597. esphome/core/config.py +155 -6
  598. esphome/core/controller.cpp +4 -2
  599. esphome/core/controller.h +1 -1
  600. esphome/core/datatypes.h +2 -2
  601. esphome/core/defines.h +17 -2
  602. esphome/core/device.h +20 -0
  603. esphome/core/entity_base.cpp +20 -15
  604. esphome/core/entity_base.h +76 -0
  605. esphome/core/entity_helpers.py +168 -1
  606. esphome/core/event_pool.h +81 -0
  607. esphome/core/helpers.cpp +75 -230
  608. esphome/core/helpers.h +165 -105
  609. esphome/core/lock_free_queue.h +151 -0
  610. esphome/core/log.cpp +2 -2
  611. esphome/core/log.h +2 -0
  612. esphome/core/optional.h +5 -0
  613. esphome/core/ring_buffer.cpp +2 -2
  614. esphome/core/scheduler.cpp +275 -103
  615. esphome/core/scheduler.h +154 -17
  616. esphome/core/time.cpp +5 -5
  617. esphome/core/time.h +5 -5
  618. esphome/cpp_generator.py +17 -0
  619. esphome/cpp_helpers.py +0 -22
  620. esphome/cpp_types.py +3 -1
  621. esphome/dashboard/entries.py +1 -1
  622. esphome/dashboard/util/text.py +5 -21
  623. esphome/dashboard/web_server.py +9 -1
  624. esphome/helpers.py +47 -0
  625. esphome/loader.py +15 -1
  626. esphome/pins.py +14 -8
  627. esphome/platformio_api.py +2 -0
  628. esphome/wizard.py +17 -4
  629. esphome/writer.py +44 -3
  630. esphome/yaml_util.py +0 -2
  631. {esphome-2025.6.3.dist-info → esphome-2025.7.0.dist-info}/METADATA +10 -9
  632. {esphome-2025.6.3.dist-info → esphome-2025.7.0.dist-info}/RECORD +637 -578
  633. esphome/components/api/api_pb2_size.h +0 -361
  634. esphome/components/esp32_ble/ble_event_pool.h +0 -72
  635. esphome/components/esp32_ble/queue.h +0 -85
  636. esphome/components/esp32_hall/esp32_hall.cpp +0 -25
  637. esphome/components/esp32_hall/esp32_hall.h +0 -23
  638. esphome/components/esp32_touch/esp32_touch.cpp +0 -355
  639. esphome/components/ld2410/button/reset_button.cpp +0 -9
  640. esphome/components/ld2450/button/reset_button.cpp +0 -9
  641. esphome/components/openthread/tlv.py +0 -65
  642. /esphome/{dashboard/enum.py → enum.py} +0 -0
  643. {esphome-2025.6.3.dist-info → esphome-2025.7.0.dist-info}/WHEEL +0 -0
  644. {esphome-2025.6.3.dist-info → esphome-2025.7.0.dist-info}/entry_points.txt +0 -0
  645. {esphome-2025.6.3.dist-info → esphome-2025.7.0.dist-info}/licenses/LICENSE +0 -0
  646. {esphome-2025.6.3.dist-info → esphome-2025.7.0.dist-info}/top_level.txt +0 -0
@@ -3,6 +3,7 @@
3
3
  #include "esphome/core/version.h"
4
4
  #include "esphome/core/hal.h"
5
5
  #include <algorithm>
6
+ #include <ranges>
6
7
 
7
8
  #ifdef USE_STATUS_LED
8
9
  #include "esphome/components/status_led/status_led.h"
@@ -83,6 +84,10 @@ void Application::setup() {
83
84
  }
84
85
 
85
86
  ESP_LOGI(TAG, "setup() finished successfully!");
87
+
88
+ // Clear setup priority overrides to free memory
89
+ clear_setup_priority_overrides();
90
+
86
91
  this->schedule_dump_config();
87
92
  this->calculate_looping_components_();
88
93
  }
@@ -97,7 +102,27 @@ void Application::loop() {
97
102
  // Feed WDT with time
98
103
  this->feed_wdt(last_op_end_time);
99
104
 
100
- for (Component *component : this->looping_components_) {
105
+ // Process any pending enable_loop requests from ISRs
106
+ // This must be done before marking in_loop_ = true to avoid race conditions
107
+ if (this->has_pending_enable_loop_requests_) {
108
+ // Clear flag BEFORE processing to avoid race condition
109
+ // If ISR sets it during processing, we'll catch it next loop iteration
110
+ // This is safe because:
111
+ // 1. Each component has its own pending_enable_loop_ flag that we check
112
+ // 2. If we can't process a component (wrong state), enable_pending_loops_()
113
+ // will set this flag back to true
114
+ // 3. Any new ISR requests during processing will set the flag again
115
+ this->has_pending_enable_loop_requests_ = false;
116
+ this->enable_pending_loops_();
117
+ }
118
+
119
+ // Mark that we're in the loop for safe reentrant modifications
120
+ this->in_loop_ = true;
121
+
122
+ for (this->current_loop_index_ = 0; this->current_loop_index_ < this->looping_components_active_end_;
123
+ this->current_loop_index_++) {
124
+ Component *component = this->looping_components_[this->current_loop_index_];
125
+
101
126
  // Update the cached time before each component runs
102
127
  this->loop_component_start_time_ = last_op_end_time;
103
128
 
@@ -112,6 +137,8 @@ void Application::loop() {
112
137
  this->app_state_ |= new_app_state;
113
138
  this->feed_wdt(last_op_end_time);
114
139
  }
140
+
141
+ this->in_loop_ = false;
115
142
  this->app_state_ = new_app_state;
116
143
 
117
144
  // Use the last component's end time instead of calling millis() again
@@ -162,8 +189,8 @@ void IRAM_ATTR HOT Application::feed_wdt(uint32_t time) {
162
189
  }
163
190
  void Application::reboot() {
164
191
  ESP_LOGI(TAG, "Forcing a reboot");
165
- for (auto it = this->components_.rbegin(); it != this->components_.rend(); ++it) {
166
- (*it)->on_shutdown();
192
+ for (auto &component : std::ranges::reverse_view(this->components_)) {
193
+ component->on_shutdown();
167
194
  }
168
195
  arch_restart();
169
196
  }
@@ -176,17 +203,17 @@ void Application::safe_reboot() {
176
203
  }
177
204
 
178
205
  void Application::run_safe_shutdown_hooks() {
179
- for (auto it = this->components_.rbegin(); it != this->components_.rend(); ++it) {
180
- (*it)->on_safe_shutdown();
206
+ for (auto &component : std::ranges::reverse_view(this->components_)) {
207
+ component->on_safe_shutdown();
181
208
  }
182
- for (auto it = this->components_.rbegin(); it != this->components_.rend(); ++it) {
183
- (*it)->on_shutdown();
209
+ for (auto &component : std::ranges::reverse_view(this->components_)) {
210
+ component->on_shutdown();
184
211
  }
185
212
  }
186
213
 
187
214
  void Application::run_powerdown_hooks() {
188
- for (auto it = this->components_.rbegin(); it != this->components_.rend(); ++it) {
189
- (*it)->on_powerdown();
215
+ for (auto &component : std::ranges::reverse_view(this->components_)) {
216
+ component->on_powerdown();
190
217
  }
191
218
  }
192
219
 
@@ -235,9 +262,141 @@ void Application::teardown_components(uint32_t timeout_ms) {
235
262
  }
236
263
 
237
264
  void Application::calculate_looping_components_() {
265
+ // Count total components that need looping
266
+ size_t total_looping = 0;
267
+ for (auto *obj : this->components_) {
268
+ if (obj->has_overridden_loop()) {
269
+ total_looping++;
270
+ }
271
+ }
272
+
273
+ // Pre-reserve vector to avoid reallocations
274
+ this->looping_components_.reserve(total_looping);
275
+
276
+ // First add all active components
238
277
  for (auto *obj : this->components_) {
239
- if (obj->has_overridden_loop())
278
+ if (obj->has_overridden_loop() &&
279
+ (obj->get_component_state() & COMPONENT_STATE_MASK) != COMPONENT_STATE_LOOP_DONE) {
240
280
  this->looping_components_.push_back(obj);
281
+ }
282
+ }
283
+
284
+ this->looping_components_active_end_ = this->looping_components_.size();
285
+
286
+ // Then add all inactive (LOOP_DONE) components
287
+ // This handles components that called disable_loop() during setup, before this method runs
288
+ for (auto *obj : this->components_) {
289
+ if (obj->has_overridden_loop() &&
290
+ (obj->get_component_state() & COMPONENT_STATE_MASK) == COMPONENT_STATE_LOOP_DONE) {
291
+ this->looping_components_.push_back(obj);
292
+ }
293
+ }
294
+ }
295
+
296
+ void Application::disable_component_loop_(Component *component) {
297
+ // This method must be reentrant - components can disable themselves during their own loop() call
298
+ // Linear search to find component in active section
299
+ // Most configs have 10-30 looping components (30 is on the high end)
300
+ // O(n) is acceptable here as we optimize for memory, not complexity
301
+ for (uint16_t i = 0; i < this->looping_components_active_end_; i++) {
302
+ if (this->looping_components_[i] == component) {
303
+ // Move last active component to this position
304
+ this->looping_components_active_end_--;
305
+ if (i != this->looping_components_active_end_) {
306
+ std::swap(this->looping_components_[i], this->looping_components_[this->looping_components_active_end_]);
307
+
308
+ // If we're currently iterating and just swapped the current position
309
+ if (this->in_loop_ && i == this->current_loop_index_) {
310
+ // Decrement so we'll process the swapped component next
311
+ this->current_loop_index_--;
312
+ // Update the loop start time to current time so the swapped component
313
+ // gets correct timing instead of inheriting stale timing.
314
+ // This prevents integer underflow in timing calculations by ensuring
315
+ // the swapped component starts with a fresh timing reference, avoiding
316
+ // errors caused by stale or wrapped timing values.
317
+ this->loop_component_start_time_ = millis();
318
+ }
319
+ }
320
+ return;
321
+ }
322
+ }
323
+ }
324
+
325
+ void Application::activate_looping_component_(uint16_t index) {
326
+ // Helper to move component from inactive to active section
327
+ if (index != this->looping_components_active_end_) {
328
+ std::swap(this->looping_components_[index], this->looping_components_[this->looping_components_active_end_]);
329
+ }
330
+ this->looping_components_active_end_++;
331
+ }
332
+
333
+ void Application::enable_component_loop_(Component *component) {
334
+ // This method is only called when component state is LOOP_DONE, so we know
335
+ // the component must be in the inactive section (if it exists in looping_components_)
336
+ // Only search the inactive portion for better performance
337
+ // With typical 0-5 inactive components, O(k) is much faster than O(n)
338
+ const uint16_t size = this->looping_components_.size();
339
+ for (uint16_t i = this->looping_components_active_end_; i < size; i++) {
340
+ if (this->looping_components_[i] == component) {
341
+ // Found in inactive section - move to active
342
+ this->activate_looping_component_(i);
343
+ return;
344
+ }
345
+ }
346
+ // Component not found in looping_components_ - this is normal for components
347
+ // that don't have loop() or were not included in the partitioned vector
348
+ }
349
+
350
+ void Application::enable_pending_loops_() {
351
+ // Process components that requested enable_loop from ISR context
352
+ // Only iterate through inactive looping_components_ (typically 0-5) instead of all components
353
+ //
354
+ // Race condition handling:
355
+ // 1. We check if component is already in LOOP state first - if so, just clear the flag
356
+ // This handles reentrancy where enable_loop() was called between ISR and processing
357
+ // 2. We only clear pending_enable_loop_ after checking state, preventing lost requests
358
+ // 3. If any components aren't in LOOP_DONE state, we set has_pending_enable_loop_requests_
359
+ // back to true to ensure we check again next iteration
360
+ // 4. ISRs can safely set flags at any time - worst case is we process them next iteration
361
+ // 5. The global flag (has_pending_enable_loop_requests_) is cleared before this method,
362
+ // so any ISR that fires during processing will be caught in the next loop
363
+ const uint16_t size = this->looping_components_.size();
364
+ bool has_pending = false;
365
+
366
+ for (uint16_t i = this->looping_components_active_end_; i < size; i++) {
367
+ Component *component = this->looping_components_[i];
368
+ if (!component->pending_enable_loop_) {
369
+ continue; // Skip components without pending requests
370
+ }
371
+
372
+ // Check current state
373
+ uint8_t state = component->component_state_ & COMPONENT_STATE_MASK;
374
+
375
+ // If already in LOOP state, nothing to do - clear flag and continue
376
+ if (state == COMPONENT_STATE_LOOP) {
377
+ component->pending_enable_loop_ = false;
378
+ continue;
379
+ }
380
+
381
+ // If not in LOOP_DONE state, can't enable yet - keep flag set
382
+ if (state != COMPONENT_STATE_LOOP_DONE) {
383
+ has_pending = true; // Keep tracking this component
384
+ continue; // Keep the flag set - try again next iteration
385
+ }
386
+
387
+ // Clear the pending flag and enable the loop
388
+ component->pending_enable_loop_ = false;
389
+ ESP_LOGVV(TAG, "%s loop enabled from ISR", component->get_component_source());
390
+ component->component_state_ &= ~COMPONENT_STATE_MASK;
391
+ component->component_state_ |= COMPONENT_STATE_LOOP;
392
+
393
+ // Move to active section
394
+ this->activate_looping_component_(i);
395
+ }
396
+
397
+ // If we couldn't process some requests, ensure we check again next iteration
398
+ if (has_pending) {
399
+ this->has_pending_enable_loop_requests_ = true;
241
400
  }
242
401
  }
243
402