esphome 2025.8.4__py3-none-any.whl → 2025.9.0b1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (344) hide show
  1. esphome/__main__.py +36 -42
  2. esphome/components/absolute_humidity/absolute_humidity.cpp +3 -5
  3. esphome/components/adc/adc_sensor_esp32.cpp +29 -6
  4. esphome/components/ags10/ags10.cpp +3 -18
  5. esphome/components/ags10/ags10.h +2 -12
  6. esphome/components/aht10/aht10.cpp +3 -3
  7. esphome/components/airthings_ble/__init__.py +2 -2
  8. esphome/components/alarm_control_panel/__init__.py +2 -2
  9. esphome/components/am2315c/am2315c.cpp +1 -17
  10. esphome/components/am2315c/am2315c.h +2 -3
  11. esphome/components/api/__init__.py +2 -2
  12. esphome/components/api/api_connection.cpp +34 -23
  13. esphome/components/api/api_connection.h +20 -39
  14. esphome/components/api/api_frame_helper.cpp +25 -25
  15. esphome/components/api/api_frame_helper.h +3 -3
  16. esphome/components/api/api_frame_helper_noise.cpp +75 -40
  17. esphome/components/api/api_frame_helper_noise.h +3 -7
  18. esphome/components/api/api_frame_helper_plaintext.cpp +17 -4
  19. esphome/components/api/api_frame_helper_plaintext.h +1 -4
  20. esphome/components/api/api_pb2.cpp +20 -2
  21. esphome/components/api/api_pb2.h +146 -141
  22. esphome/components/api/api_pb2_dump.cpp +12 -1
  23. esphome/components/api/proto.cpp +33 -37
  24. esphome/components/async_tcp/__init__.py +2 -2
  25. esphome/components/atm90e26/sensor.py +2 -0
  26. esphome/components/atm90e32/sensor.py +4 -2
  27. esphome/components/audio_adc/__init__.py +2 -2
  28. esphome/components/audio_dac/__init__.py +2 -2
  29. esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp +1 -1
  30. esphome/components/bedjet/bedjet_hub.cpp +1 -1
  31. esphome/components/binary_sensor/__init__.py +2 -2
  32. esphome/components/binary_sensor/binary_sensor.cpp +13 -0
  33. esphome/components/binary_sensor/binary_sensor.h +4 -7
  34. esphome/components/bl0940/__init__.py +6 -1
  35. esphome/components/bl0940/bl0940.cpp +178 -41
  36. esphome/components/bl0940/bl0940.h +121 -76
  37. esphome/components/bl0940/button/__init__.py +27 -0
  38. esphome/components/bl0940/button/calibration_reset_button.cpp +20 -0
  39. esphome/components/bl0940/button/calibration_reset_button.h +19 -0
  40. esphome/components/bl0940/number/__init__.py +94 -0
  41. esphome/components/bl0940/number/calibration_number.cpp +29 -0
  42. esphome/components/bl0940/number/calibration_number.h +26 -0
  43. esphome/components/bl0940/sensor.py +151 -2
  44. esphome/components/bl0942/bl0942.cpp +1 -1
  45. esphome/components/ble_client/output/__init__.py +4 -4
  46. esphome/components/bluetooth_proxy/__init__.py +1 -1
  47. esphome/components/bluetooth_proxy/bluetooth_connection.h +1 -1
  48. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +15 -7
  49. esphome/components/bluetooth_proxy/bluetooth_proxy.h +3 -2
  50. esphome/components/button/__init__.py +2 -2
  51. esphome/components/button/button.cpp +13 -0
  52. esphome/components/button/button.h +4 -7
  53. esphome/components/camera/buffer.h +18 -0
  54. esphome/components/camera/buffer_impl.cpp +20 -0
  55. esphome/components/camera/buffer_impl.h +26 -0
  56. esphome/components/camera/camera.h +43 -0
  57. esphome/components/camera/encoder.h +69 -0
  58. esphome/components/camera_encoder/__init__.py +62 -0
  59. esphome/components/camera_encoder/encoder_buffer_impl.cpp +23 -0
  60. esphome/components/camera_encoder/encoder_buffer_impl.h +25 -0
  61. esphome/components/camera_encoder/esp32_camera_jpeg_encoder.cpp +82 -0
  62. esphome/components/camera_encoder/esp32_camera_jpeg_encoder.h +39 -0
  63. esphome/components/captive_portal/__init__.py +2 -2
  64. esphome/components/captive_portal/captive_portal.cpp +35 -12
  65. esphome/components/captive_portal/captive_portal.h +3 -3
  66. esphome/components/ccs811/ccs811.cpp +3 -3
  67. esphome/components/climate/__init__.py +2 -2
  68. esphome/components/climate/climate.cpp +1 -1
  69. esphome/components/cover/__init__.py +5 -5
  70. esphome/components/cover/cover.cpp +1 -1
  71. esphome/components/cover/cover.h +2 -2
  72. esphome/components/dallas_temp/dallas_temp.cpp +2 -2
  73. esphome/components/datetime/__init__.py +2 -2
  74. esphome/components/datetime/date_entity.h +2 -2
  75. esphome/components/datetime/datetime_entity.h +2 -2
  76. esphome/components/datetime/time_entity.h +2 -2
  77. esphome/components/debug/debug_esp32.cpp +1 -1
  78. esphome/components/display/__init__.py +4 -4
  79. esphome/components/duty_time/duty_time_sensor.cpp +1 -1
  80. esphome/components/esp32/__init__.py +0 -5
  81. esphome/components/esp32/gpio.cpp +27 -23
  82. esphome/components/esp32/gpio.h +26 -11
  83. esphome/components/esp32/preferences.cpp +8 -4
  84. esphome/components/esp32_ble/__init__.py +7 -2
  85. esphome/components/esp32_ble_client/ble_client_base.cpp +7 -3
  86. esphome/components/esp32_ble_tracker/__init__.py +2 -2
  87. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +9 -44
  88. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +2 -14
  89. esphome/components/esp8266/__init__.py +2 -2
  90. esphome/components/esp8266/core.cpp +2 -2
  91. esphome/components/esp8266/gpio.py +4 -4
  92. esphome/components/esp8266/preferences.cpp +30 -28
  93. esphome/components/esphome/ota/__init__.py +2 -2
  94. esphome/components/esphome/ota/ota_esphome.cpp +21 -19
  95. esphome/components/esphome/ota/ota_esphome.h +6 -5
  96. esphome/components/ethernet/__init__.py +7 -2
  97. esphome/components/ethernet/ethernet_component.cpp +1 -1
  98. esphome/components/event/__init__.py +2 -2
  99. esphome/components/event/event.h +4 -4
  100. esphome/components/fan/__init__.py +2 -2
  101. esphome/components/fan/fan.cpp +2 -1
  102. esphome/components/gdk101/gdk101.cpp +4 -4
  103. esphome/components/globals/__init__.py +2 -2
  104. esphome/components/gpio/binary_sensor/gpio_binary_sensor.cpp +19 -18
  105. esphome/components/gpio_expander/cached_gpio.h +36 -16
  106. esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.cpp +5 -5
  107. esphome/components/gt911/touchscreen/gt911_touchscreen.cpp +1 -1
  108. esphome/components/haier/haier_base.cpp +1 -1
  109. esphome/components/haier/hon_climate.cpp +1 -1
  110. esphome/components/hlw8012/hlw8012.cpp +5 -5
  111. esphome/components/honeywellabp2_i2c/honeywellabp2.cpp +4 -4
  112. esphome/components/host/preferences.h +3 -2
  113. esphome/components/hte501/hte501.cpp +3 -21
  114. esphome/components/hte501/hte501.h +2 -3
  115. esphome/components/http_request/ota/__init__.py +2 -2
  116. esphome/components/i2c/__init__.py +2 -2
  117. esphome/components/i2c/i2c.cpp +13 -9
  118. esphome/components/i2c/i2c_bus.h +36 -6
  119. esphome/components/i2s_audio/__init__.py +8 -2
  120. esphome/components/i2s_audio/media_player/__init__.py +1 -1
  121. esphome/components/i2s_audio/microphone/__init__.py +1 -1
  122. esphome/components/i2s_audio/speaker/__init__.py +1 -1
  123. esphome/components/inkplate/__init__.py +1 -0
  124. esphome/components/inkplate/const.py +105 -0
  125. esphome/components/inkplate/display.py +238 -0
  126. esphome/components/{inkplate6 → inkplate}/inkplate.cpp +156 -74
  127. esphome/components/{inkplate6 → inkplate}/inkplate.h +28 -68
  128. esphome/components/inkplate6/__init__.py +0 -1
  129. esphome/components/inkplate6/display.py +2 -211
  130. esphome/components/integration/integration_sensor.cpp +1 -1
  131. esphome/components/json/__init__.py +2 -2
  132. esphome/components/lc709203f/lc709203f.cpp +4 -17
  133. esphome/components/lc709203f/lc709203f.h +2 -3
  134. esphome/components/ld2420/text_sensor/{text_sensor.cpp → ld2420_text_sensor.cpp} +1 -1
  135. esphome/components/ld2450/ld2450.cpp +1 -1
  136. esphome/components/libretiny/preferences.cpp +13 -5
  137. esphome/components/light/__init__.py +2 -2
  138. esphome/components/light/addressable_light_effect.h +7 -0
  139. esphome/components/light/base_light_effects.h +8 -0
  140. esphome/components/light/light_call.cpp +22 -20
  141. esphome/components/light/light_effect.cpp +36 -0
  142. esphome/components/light/light_effect.h +14 -0
  143. esphome/components/light/light_json_schema.cpp +9 -1
  144. esphome/components/light/light_state.cpp +2 -2
  145. esphome/components/light/light_state.h +38 -0
  146. esphome/components/lock/__init__.py +2 -2
  147. esphome/components/lock/lock.h +2 -2
  148. esphome/components/logger/__init__.py +2 -2
  149. esphome/components/logger/logger.cpp +25 -4
  150. esphome/components/logger/logger.h +1 -1
  151. esphome/components/logger/logger_esp32.cpp +16 -8
  152. esphome/components/logger/logger_esp8266.cpp +11 -3
  153. esphome/components/logger/logger_libretiny.cpp +13 -3
  154. esphome/components/logger/logger_rp2040.cpp +14 -3
  155. esphome/components/logger/logger_zephyr.cpp +15 -4
  156. esphome/components/lvgl/defines.py +1 -0
  157. esphome/components/lvgl/hello_world.py +96 -33
  158. esphome/components/lvgl/number/lvgl_number.h +1 -1
  159. esphome/components/lvgl/select/lvgl_select.h +1 -1
  160. esphome/components/lvgl/widgets/__init__.py +0 -1
  161. esphome/components/lvgl/widgets/spinbox.py +20 -11
  162. esphome/components/m5stack_8angle/binary_sensor/m5stack_8angle_binary_sensor.cpp +1 -1
  163. esphome/components/m5stack_8angle/sensor/m5stack_8angle_sensor.cpp +1 -1
  164. esphome/components/mapping/__init__.py +13 -5
  165. esphome/components/mapping/mapping.h +69 -0
  166. esphome/components/max17043/max17043.cpp +2 -2
  167. esphome/components/mcp23016/__init__.py +1 -0
  168. esphome/components/mcp23016/mcp23016.cpp +20 -5
  169. esphome/components/mcp23016/mcp23016.h +10 -4
  170. esphome/components/mcp23x08_base/mcp23x08_base.cpp +1 -1
  171. esphome/components/mcp23x17_base/mcp23x17_base.cpp +2 -2
  172. esphome/components/mdns/__init__.py +2 -2
  173. esphome/components/mdns/mdns_component.cpp +145 -54
  174. esphome/components/media_player/__init__.py +2 -2
  175. esphome/components/micro_wake_word/__init__.py +2 -2
  176. esphome/components/microphone/__init__.py +2 -2
  177. esphome/components/mipi/__init__.py +77 -33
  178. esphome/components/mipi_rgb/__init__.py +2 -0
  179. esphome/components/mipi_rgb/display.py +321 -0
  180. esphome/components/mipi_rgb/mipi_rgb.cpp +388 -0
  181. esphome/components/mipi_rgb/mipi_rgb.h +127 -0
  182. esphome/components/mipi_rgb/models/guition.py +24 -0
  183. esphome/components/mipi_rgb/models/lilygo.py +228 -0
  184. esphome/components/mipi_rgb/models/rpi.py +9 -0
  185. esphome/components/mipi_rgb/models/st7701s.py +214 -0
  186. esphome/components/mipi_rgb/models/waveshare.py +64 -0
  187. esphome/components/mipi_spi/models/jc.py +229 -0
  188. esphome/components/mlx90614/mlx90614.cpp +1 -16
  189. esphome/components/mlx90614/mlx90614.h +0 -1
  190. esphome/components/mqtt/__init__.py +2 -2
  191. esphome/components/mqtt/mqtt_sensor.cpp +7 -2
  192. esphome/components/ms5611/ms5611.cpp +7 -6
  193. esphome/components/network/__init__.py +2 -2
  194. esphome/components/nextion/nextion_upload.cpp +4 -1
  195. esphome/components/nrf52/__init__.py +49 -6
  196. esphome/components/nrf52/const.py +1 -0
  197. esphome/components/nrf52/dfu.cpp +51 -0
  198. esphome/components/nrf52/dfu.h +24 -0
  199. esphome/components/ntc/ntc.cpp +1 -1
  200. esphome/components/number/__init__.py +2 -2
  201. esphome/components/number/automation.cpp +1 -1
  202. esphome/components/number/number.cpp +21 -0
  203. esphome/components/number/number.h +4 -13
  204. esphome/components/opentherm/hub.h +6 -6
  205. esphome/components/opentherm/number/{number.cpp → opentherm_number.cpp} +2 -2
  206. esphome/components/opentherm/output/{output.cpp → opentherm_output.cpp} +1 -1
  207. esphome/components/opentherm/switch/{switch.cpp → opentherm_switch.cpp} +1 -1
  208. esphome/components/ota/__init__.py +2 -2
  209. esphome/components/pca6416a/__init__.py +1 -0
  210. esphome/components/pca6416a/pca6416a.cpp +20 -5
  211. esphome/components/pca6416a/pca6416a.h +12 -5
  212. esphome/components/pca9554/__init__.py +2 -1
  213. esphome/components/pca9554/pca9554.cpp +12 -18
  214. esphome/components/pca9554/pca9554.h +10 -9
  215. esphome/components/pcf8574/__init__.py +1 -0
  216. esphome/components/pcf8574/pcf8574.cpp +14 -5
  217. esphome/components/pcf8574/pcf8574.h +13 -6
  218. esphome/components/pi4ioe5v6408/pi4ioe5v6408.cpp +7 -7
  219. esphome/components/pipsolar/__init__.py +3 -3
  220. esphome/components/pipsolar/output/__init__.py +4 -4
  221. esphome/components/pulse_width/pulse_width.cpp +2 -2
  222. esphome/components/qmp6988/qmp6988.cpp +81 -126
  223. esphome/components/qmp6988/qmp6988.h +31 -37
  224. esphome/components/radon_eye_ble/__init__.py +2 -2
  225. esphome/components/remote_base/__init__.py +6 -8
  226. esphome/components/rotary_encoder/rotary_encoder.cpp +1 -1
  227. esphome/components/rp2040/__init__.py +2 -2
  228. esphome/components/runtime_stats/runtime_stats.cpp +10 -23
  229. esphome/components/runtime_stats/runtime_stats.h +4 -10
  230. esphome/components/safe_mode/__init__.py +2 -2
  231. esphome/components/safe_mode/safe_mode.cpp +33 -31
  232. esphome/components/script/script.cpp +6 -0
  233. esphome/components/script/script.h +19 -5
  234. esphome/components/sdm_meter/sensor.py +3 -1
  235. esphome/components/select/__init__.py +2 -2
  236. esphome/components/select/select.h +2 -2
  237. esphome/components/sen5x/sen5x.cpp +57 -55
  238. esphome/components/sen5x/sen5x.h +21 -15
  239. esphome/components/sen5x/sensor.py +67 -44
  240. esphome/components/sensirion_common/i2c_sensirion.cpp +18 -47
  241. esphome/components/sensirion_common/i2c_sensirion.h +39 -55
  242. esphome/components/sensor/__init__.py +2 -2
  243. esphome/components/sensor/automation.h +1 -1
  244. esphome/components/sensor/sensor.cpp +34 -6
  245. esphome/components/sensor/sensor.h +4 -21
  246. esphome/components/sgp30/sgp30.cpp +34 -35
  247. esphome/components/sgp30/sgp30.h +11 -10
  248. esphome/components/sgp4x/sgp4x.cpp +2 -2
  249. esphome/components/shelly_dimmer/light.py +7 -7
  250. esphome/components/sht4x/sht4x.cpp +1 -1
  251. esphome/components/sntp/sntp_component.cpp +36 -9
  252. esphome/components/sntp/sntp_component.h +7 -0
  253. esphome/components/sound_level/sound_level.cpp +1 -1
  254. esphome/components/speaker/__init__.py +2 -2
  255. esphome/components/speaker/media_player/__init__.py +2 -2
  256. esphome/components/speaker/media_player/speaker_media_player.cpp +1 -1
  257. esphome/components/spi/__init__.py +2 -2
  258. esphome/components/sprinkler/sprinkler.cpp +1 -1
  259. esphome/components/sps30/sps30.cpp +18 -23
  260. esphome/components/sps30/sps30.h +3 -3
  261. esphome/components/status_led/__init__.py +2 -2
  262. esphome/components/stepper/__init__.py +2 -2
  263. esphome/components/switch/__init__.py +2 -2
  264. esphome/components/switch/switch.cpp +5 -5
  265. esphome/components/sx1509/__init__.py +1 -1
  266. esphome/components/sx1509/sx1509.cpp +12 -7
  267. esphome/components/sx1509/sx1509.h +11 -4
  268. esphome/components/tca9555/tca9555.cpp +5 -5
  269. esphome/components/tee501/tee501.cpp +2 -21
  270. esphome/components/tee501/tee501.h +2 -4
  271. esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +1 -1
  272. esphome/components/template/datetime/template_date.cpp +1 -1
  273. esphome/components/template/datetime/template_datetime.cpp +2 -2
  274. esphome/components/template/datetime/template_time.cpp +1 -1
  275. esphome/components/template/number/template_number.cpp +1 -1
  276. esphome/components/template/select/template_select.cpp +1 -1
  277. esphome/components/template/text/template_text.cpp +1 -1
  278. esphome/components/text/__init__.py +2 -2
  279. esphome/components/text/text.h +2 -2
  280. esphome/components/text_sensor/__init__.py +2 -2
  281. esphome/components/text_sensor/text_sensor.h +4 -4
  282. esphome/components/thermostat/climate.py +11 -7
  283. esphome/components/thermostat/thermostat_climate.cpp +237 -206
  284. esphome/components/thermostat/thermostat_climate.h +52 -41
  285. esphome/components/time/__init__.py +2 -2
  286. esphome/components/tmp1075/tmp1075.cpp +1 -1
  287. esphome/components/total_daily_energy/total_daily_energy.cpp +1 -1
  288. esphome/components/touchscreen/__init__.py +2 -2
  289. esphome/components/tuya/number/tuya_number.cpp +1 -1
  290. esphome/components/udp/udp_component.cpp +3 -3
  291. esphome/components/ufire_ec/ufire_ec.cpp +4 -4
  292. esphome/components/ufire_ise/ufire_ise.cpp +4 -4
  293. esphome/components/update/__init__.py +2 -2
  294. esphome/components/usb_uart/usb_uart.cpp +1 -1
  295. esphome/components/valve/__init__.py +5 -5
  296. esphome/components/valve/valve.cpp +1 -1
  297. esphome/components/valve/valve.h +2 -2
  298. esphome/components/wake_on_lan/wake_on_lan.cpp +2 -2
  299. esphome/components/waveshare_epaper/waveshare_213v3.cpp +1 -1
  300. esphome/components/web_server/__init__.py +2 -2
  301. esphome/components/web_server/ota/__init__.py +2 -2
  302. esphome/components/web_server/ota/ota_web_server.cpp +11 -0
  303. esphome/components/web_server/web_server.cpp +58 -12
  304. esphome/components/web_server_base/__init__.py +2 -2
  305. esphome/components/wifi/__init__.py +5 -5
  306. esphome/components/wifi/wifi_component.cpp +3 -3
  307. esphome/components/wifi/wifi_component_esp_idf.cpp +2 -0
  308. esphome/config_validation.py +2 -2
  309. esphome/const.py +2 -1
  310. esphome/core/__init__.py +1 -0
  311. esphome/core/application.cpp +89 -51
  312. esphome/core/application.h +1 -0
  313. esphome/core/component.cpp +41 -19
  314. esphome/core/component.h +17 -13
  315. esphome/core/config.py +7 -7
  316. esphome/core/defines.h +4 -0
  317. esphome/core/entity_base.cpp +22 -8
  318. esphome/core/entity_base.h +43 -0
  319. esphome/core/helpers.cpp +26 -13
  320. esphome/core/helpers.h +4 -3
  321. esphome/core/ring_buffer.cpp +6 -2
  322. esphome/core/ring_buffer.h +2 -1
  323. esphome/core/scheduler.cpp +175 -94
  324. esphome/core/scheduler.h +66 -35
  325. esphome/core/time.cpp +6 -20
  326. esphome/coroutine.py +80 -3
  327. esphome/cpp_generator.py +13 -0
  328. esphome/cpp_helpers.py +2 -2
  329. esphome/dashboard/web_server.py +67 -10
  330. esphome/espota2.py +13 -6
  331. esphome/helpers.py +68 -83
  332. esphome/resolver.py +67 -0
  333. esphome/util.py +9 -6
  334. esphome/wizard.py +39 -26
  335. {esphome-2025.8.4.dist-info → esphome-2025.9.0b1.dist-info}/METADATA +9 -9
  336. {esphome-2025.8.4.dist-info → esphome-2025.9.0b1.dist-info}/RECORD +344 -313
  337. /esphome/components/ld2420/text_sensor/{text_sensor.h → ld2420_text_sensor.h} +0 -0
  338. /esphome/components/opentherm/number/{number.h → opentherm_number.h} +0 -0
  339. /esphome/components/opentherm/output/{output.h → opentherm_output.h} +0 -0
  340. /esphome/components/opentherm/switch/{switch.h → opentherm_switch.h} +0 -0
  341. {esphome-2025.8.4.dist-info → esphome-2025.9.0b1.dist-info}/WHEEL +0 -0
  342. {esphome-2025.8.4.dist-info → esphome-2025.9.0b1.dist-info}/entry_points.txt +0 -0
  343. {esphome-2025.8.4.dist-info → esphome-2025.9.0b1.dist-info}/licenses/LICENSE +0 -0
  344. {esphome-2025.8.4.dist-info → esphome-2025.9.0b1.dist-info}/top_level.txt +0 -0
esphome/const.py CHANGED
@@ -4,7 +4,7 @@ from enum import Enum
4
4
 
5
5
  from esphome.enum import StrEnum
6
6
 
7
- __version__ = "2025.8.4"
7
+ __version__ = "2025.9.0b1"
8
8
 
9
9
  ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
10
10
  VALID_SUBSTITUTIONS_CHARACTERS = (
@@ -424,6 +424,7 @@ CONF_HEAD = "head"
424
424
  CONF_HEADING = "heading"
425
425
  CONF_HEARTBEAT = "heartbeat"
426
426
  CONF_HEAT_ACTION = "heat_action"
427
+ CONF_HEAT_COOL_MODE = "heat_cool_mode"
427
428
  CONF_HEAT_DEADBAND = "heat_deadband"
428
429
  CONF_HEAT_MODE = "heat_mode"
429
430
  CONF_HEAT_OVERRUN = "heat_overrun"
esphome/core/__init__.py CHANGED
@@ -29,6 +29,7 @@ from esphome.const import (
29
29
 
30
30
  # pylint: disable=unused-import
31
31
  from esphome.coroutine import ( # noqa: F401
32
+ CoroPriority,
32
33
  FakeAwaitable as _FakeAwaitable,
33
34
  FakeEventLoop as _FakeEventLoop,
34
35
  coroutine,
@@ -34,37 +34,20 @@ namespace esphome {
34
34
 
35
35
  static const char *const TAG = "app";
36
36
 
37
- // Helper function for insertion sort of components by setup priority
37
+ // Helper function for insertion sort of components by priority
38
38
  // Using insertion sort instead of std::stable_sort saves ~1.3KB of flash
39
39
  // by avoiding template instantiations (std::rotate, std::stable_sort, lambdas)
40
40
  // IMPORTANT: This sort is stable (preserves relative order of equal elements),
41
41
  // which is necessary to maintain user-defined component order for same priority
42
- template<typename Iterator> static void insertion_sort_by_setup_priority(Iterator first, Iterator last) {
42
+ template<typename Iterator, float (Component::*GetPriority)() const>
43
+ static void insertion_sort_by_priority(Iterator first, Iterator last) {
43
44
  for (auto it = first + 1; it != last; ++it) {
44
45
  auto key = *it;
45
- float key_priority = key->get_actual_setup_priority();
46
+ float key_priority = (key->*GetPriority)();
46
47
  auto j = it - 1;
47
48
 
48
49
  // Using '<' (not '<=') ensures stability - equal priority components keep their order
49
- while (j >= first && (*j)->get_actual_setup_priority() < key_priority) {
50
- *(j + 1) = *j;
51
- j--;
52
- }
53
- *(j + 1) = key;
54
- }
55
- }
56
-
57
- // Helper function for insertion sort of components by loop priority
58
- // IMPORTANT: This sort is stable (preserves relative order of equal elements),
59
- // which is required when components are re-sorted during setup() if they block
60
- template<typename Iterator> static void insertion_sort_by_loop_priority(Iterator first, Iterator last) {
61
- for (auto it = first + 1; it != last; ++it) {
62
- auto key = *it;
63
- float key_priority = key->get_loop_priority();
64
- auto j = it - 1;
65
-
66
- // Using '<' (not '<=') ensures stability - equal priority components keep their order
67
- while (j >= first && (*j)->get_loop_priority() < key_priority) {
50
+ while (j >= first && ((*j)->*GetPriority)() < key_priority) {
68
51
  *(j + 1) = *j;
69
52
  j--;
70
53
  }
@@ -80,7 +63,7 @@ void Application::register_component_(Component *comp) {
80
63
 
81
64
  for (auto *c : this->components_) {
82
65
  if (comp == c) {
83
- ESP_LOGW(TAG, "Component %s already registered! (%p)", c->get_component_source(), c);
66
+ ESP_LOGW(TAG, "Component %s already registered! (%p)", LOG_STR_ARG(c->get_component_log_str()), c);
84
67
  return;
85
68
  }
86
69
  }
@@ -91,7 +74,8 @@ void Application::setup() {
91
74
  ESP_LOGV(TAG, "Sorting components by setup priority");
92
75
 
93
76
  // Sort by setup priority using our helper function
94
- insertion_sort_by_setup_priority(this->components_.begin(), this->components_.end());
77
+ insertion_sort_by_priority<decltype(this->components_.begin()), &Component::get_actual_setup_priority>(
78
+ this->components_.begin(), this->components_.end());
95
79
 
96
80
  // Initialize looping_components_ early so enable_pending_loops_() works during setup
97
81
  this->calculate_looping_components_();
@@ -108,7 +92,8 @@ void Application::setup() {
108
92
  continue;
109
93
 
110
94
  // Sort components 0 through i by loop priority
111
- insertion_sort_by_loop_priority(this->components_.begin(), this->components_.begin() + i + 1);
95
+ insertion_sort_by_priority<decltype(this->components_.begin()), &Component::get_loop_priority>(
96
+ this->components_.begin(), this->components_.begin() + i + 1);
112
97
 
113
98
  do {
114
99
  uint8_t new_app_state = STATUS_LED_WARNING;
@@ -256,30 +241,79 @@ void Application::run_powerdown_hooks() {
256
241
  void Application::teardown_components(uint32_t timeout_ms) {
257
242
  uint32_t start_time = millis();
258
243
 
259
- // Copy all components in reverse order using reverse iterators
244
+ // Use a StaticVector instead of std::vector to avoid heap allocation
245
+ // since we know the actual size at compile time
246
+ StaticVector<Component *, ESPHOME_COMPONENT_COUNT> pending_components;
247
+
248
+ // Copy all components in reverse order
260
249
  // Reverse order matches the behavior of run_safe_shutdown_hooks() above and ensures
261
250
  // components are torn down in the opposite order of their setup_priority (which is
262
251
  // used to sort components during Application::setup())
263
- std::vector<Component *> pending_components(this->components_.rbegin(), this->components_.rend());
252
+ size_t num_components = this->components_.size();
253
+ for (size_t i = 0; i < num_components; ++i) {
254
+ pending_components[i] = this->components_[num_components - 1 - i];
255
+ }
264
256
 
265
257
  uint32_t now = start_time;
266
- while (!pending_components.empty() && (now - start_time) < timeout_ms) {
258
+ size_t pending_count = num_components;
259
+
260
+ // Teardown Algorithm
261
+ // ==================
262
+ // We iterate through pending components, calling teardown() on each.
263
+ // Components that return false (need more time) are copied forward
264
+ // in the array. Components that return true (finished) are skipped.
265
+ //
266
+ // The compaction happens in-place during iteration:
267
+ // - still_pending tracks the write position (where to put next pending component)
268
+ // - i tracks the read position (which component we're testing)
269
+ // - When teardown() returns false, we copy component[i] to component[still_pending]
270
+ // - When teardown() returns true, we just skip it (don't increment still_pending)
271
+ //
272
+ // Example with 4 components where B can teardown immediately:
273
+ //
274
+ // Start:
275
+ // pending_components: [A, B, C, D]
276
+ // pending_count: 4 ^----------^
277
+ //
278
+ // Iteration 1:
279
+ // i=0: A needs more time → keep at pos 0 (no copy needed)
280
+ // i=1: B finished → skip
281
+ // i=2: C needs more time → copy to pos 1
282
+ // i=3: D needs more time → copy to pos 2
283
+ //
284
+ // After iteration 1:
285
+ // pending_components: [A, C, D | D]
286
+ // pending_count: 3 ^--------^
287
+ //
288
+ // Iteration 2:
289
+ // i=0: A finished → skip
290
+ // i=1: C needs more time → copy to pos 0
291
+ // i=2: D finished → skip
292
+ //
293
+ // After iteration 2:
294
+ // pending_components: [C | C, D, D] (positions 1-3 have old values)
295
+ // pending_count: 1 ^--^
296
+
297
+ while (pending_count > 0 && (now - start_time) < timeout_ms) {
267
298
  // Feed watchdog during teardown to prevent triggering
268
299
  this->feed_wdt(now);
269
300
 
270
- // Use iterator to safely erase elements
271
- for (auto it = pending_components.begin(); it != pending_components.end();) {
272
- if ((*it)->teardown()) {
273
- // Component finished teardown, erase it
274
- it = pending_components.erase(it);
275
- } else {
276
- // Component still needs time
277
- ++it;
301
+ // Process components and compact the array, keeping only those still pending
302
+ size_t still_pending = 0;
303
+ for (size_t i = 0; i < pending_count; ++i) {
304
+ if (!pending_components[i]->teardown()) {
305
+ // Component still needs time, copy it forward
306
+ if (still_pending != i) {
307
+ pending_components[still_pending] = pending_components[i];
308
+ }
309
+ ++still_pending;
278
310
  }
311
+ // Component finished teardown, skip it (don't increment still_pending)
279
312
  }
313
+ pending_count = still_pending;
280
314
 
281
315
  // Give some time for I/O operations if components are still pending
282
- if (!pending_components.empty()) {
316
+ if (pending_count > 0) {
283
317
  this->yield_with_select_(1);
284
318
  }
285
319
 
@@ -287,12 +321,12 @@ void Application::teardown_components(uint32_t timeout_ms) {
287
321
  now = millis();
288
322
  }
289
323
 
290
- if (!pending_components.empty()) {
324
+ if (pending_count > 0) {
291
325
  // Note: At this point, connections are either disconnected or in a bad state,
292
326
  // so this warning will only appear via serial rather than being transmitted to clients
293
- for (auto *component : pending_components) {
294
- ESP_LOGW(TAG, "%s did not complete teardown within %" PRIu32 " ms", component->get_component_source(),
295
- timeout_ms);
327
+ for (size_t i = 0; i < pending_count; ++i) {
328
+ ESP_LOGW(TAG, "%s did not complete teardown within %" PRIu32 " ms",
329
+ LOG_STR_ARG(pending_components[i]->get_component_log_str()), timeout_ms);
296
330
  }
297
331
  }
298
332
  }
@@ -312,20 +346,19 @@ void Application::calculate_looping_components_() {
312
346
  // Add all components with loop override that aren't already LOOP_DONE
313
347
  // Some components (like logger) may call disable_loop() during initialization
314
348
  // before setup runs, so we need to respect their LOOP_DONE state
315
- for (auto *obj : this->components_) {
316
- if (obj->has_overridden_loop() &&
317
- (obj->get_component_state() & COMPONENT_STATE_MASK) != COMPONENT_STATE_LOOP_DONE) {
318
- this->looping_components_.push_back(obj);
319
- }
320
- }
349
+ this->add_looping_components_by_state_(false);
321
350
 
322
351
  this->looping_components_active_end_ = this->looping_components_.size();
323
352
 
324
353
  // Then add any components that are already LOOP_DONE to the inactive section
325
354
  // This handles components that called disable_loop() during initialization
355
+ this->add_looping_components_by_state_(true);
356
+ }
357
+
358
+ void Application::add_looping_components_by_state_(bool match_loop_done) {
326
359
  for (auto *obj : this->components_) {
327
360
  if (obj->has_overridden_loop() &&
328
- (obj->get_component_state() & COMPONENT_STATE_MASK) == COMPONENT_STATE_LOOP_DONE) {
361
+ ((obj->get_component_state() & COMPONENT_STATE_MASK) == COMPONENT_STATE_LOOP_DONE) == match_loop_done) {
329
362
  this->looping_components_.push_back(obj);
330
363
  }
331
364
  }
@@ -424,7 +457,7 @@ void Application::enable_pending_loops_() {
424
457
 
425
458
  // Clear the pending flag and enable the loop
426
459
  component->pending_enable_loop_ = false;
427
- ESP_LOGVV(TAG, "%s loop enabled from ISR", component->get_component_source());
460
+ ESP_LOGVV(TAG, "%s loop enabled from ISR", LOG_STR_ARG(component->get_component_log_str()));
428
461
  component->component_state_ &= ~COMPONENT_STATE_MASK;
429
462
  component->component_state_ |= COMPONENT_STATE_LOOP;
430
463
 
@@ -475,11 +508,16 @@ bool Application::register_socket_fd(int fd) {
475
508
  if (fd < 0)
476
509
  return false;
477
510
 
511
+ #ifndef USE_ESP32
512
+ // Only check on non-ESP32 platforms
513
+ // On ESP32 (both Arduino and ESP-IDF), CONFIG_LWIP_MAX_SOCKETS is always <= FD_SETSIZE by design
514
+ // (LWIP_SOCKET_OFFSET = FD_SETSIZE - CONFIG_LWIP_MAX_SOCKETS per lwipopts.h)
515
+ // Other platforms may not have this guarantee
478
516
  if (fd >= FD_SETSIZE) {
479
- ESP_LOGE(TAG, "Cannot monitor socket fd %d: exceeds FD_SETSIZE (%d)", fd, FD_SETSIZE);
480
- ESP_LOGE(TAG, "Socket will not be monitored for data - may cause performance issues!");
517
+ ESP_LOGE(TAG, "fd %d exceeds FD_SETSIZE %d", fd, FD_SETSIZE);
481
518
  return false;
482
519
  }
520
+ #endif
483
521
 
484
522
  this->socket_fds_.push_back(fd);
485
523
  this->socket_fds_changed_ = true;
@@ -431,6 +431,7 @@ class Application {
431
431
  void register_component_(Component *comp);
432
432
 
433
433
  void calculate_looping_components_();
434
+ void add_looping_components_by_state_(bool match_loop_done);
434
435
 
435
436
  // These methods are called by Component::disable_loop() and Component::enable_loop()
436
437
  // Components should not call these directly - use this->disable_loop() or this->enable_loop()
@@ -16,7 +16,6 @@
16
16
  namespace esphome {
17
17
 
18
18
  static const char *const TAG = "component";
19
- static const char *const UNSPECIFIED_MESSAGE = "unspecified";
20
19
 
21
20
  // Global vectors for component data that doesn't belong in every instance.
22
21
  // Using vector instead of unordered_map for both because:
@@ -142,8 +141,8 @@ void Component::call_dump_config() {
142
141
  }
143
142
  }
144
143
  }
145
- ESP_LOGE(TAG, " %s is marked FAILED: %s", this->get_component_source(),
146
- error_msg ? error_msg : UNSPECIFIED_MESSAGE);
144
+ ESP_LOGE(TAG, " %s is marked FAILED: %s", LOG_STR_ARG(this->get_component_log_str()),
145
+ error_msg ? error_msg : LOG_STR_LITERAL("unspecified"));
147
146
  }
148
147
  }
149
148
 
@@ -154,14 +153,14 @@ void Component::call() {
154
153
  case COMPONENT_STATE_CONSTRUCTION: {
155
154
  // State Construction: Call setup and set state to setup
156
155
  this->set_component_state_(COMPONENT_STATE_SETUP);
157
- ESP_LOGV(TAG, "Setup %s", this->get_component_source());
156
+ ESP_LOGV(TAG, "Setup %s", LOG_STR_ARG(this->get_component_log_str()));
158
157
  #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG
159
158
  uint32_t start_time = millis();
160
159
  #endif
161
160
  this->call_setup();
162
161
  #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG
163
162
  uint32_t setup_time = millis() - start_time;
164
- ESP_LOGCONFIG(TAG, "Setup %s took %ums", this->get_component_source(), (unsigned) setup_time);
163
+ ESP_LOGCONFIG(TAG, "Setup %s took %ums", LOG_STR_ARG(this->get_component_log_str()), (unsigned) setup_time);
165
164
  #endif
166
165
  break;
167
166
  }
@@ -182,10 +181,8 @@ void Component::call() {
182
181
  break;
183
182
  }
184
183
  }
185
- const char *Component::get_component_source() const {
186
- if (this->component_source_ == nullptr)
187
- return "<unknown>";
188
- return this->component_source_;
184
+ const LogString *Component::get_component_log_str() const {
185
+ return this->component_source_ == nullptr ? LOG_STR("<unknown>") : this->component_source_;
189
186
  }
190
187
  bool Component::should_warn_of_blocking(uint32_t blocking_time) {
191
188
  if (blocking_time > this->warn_if_blocking_over_) {
@@ -201,7 +198,7 @@ bool Component::should_warn_of_blocking(uint32_t blocking_time) {
201
198
  return false;
202
199
  }
203
200
  void Component::mark_failed() {
204
- ESP_LOGE(TAG, "%s was marked as failed", this->get_component_source());
201
+ ESP_LOGE(TAG, "%s was marked as failed", LOG_STR_ARG(this->get_component_log_str()));
205
202
  this->set_component_state_(COMPONENT_STATE_FAILED);
206
203
  this->status_set_error();
207
204
  // Also remove from loop since failed components shouldn't loop
@@ -213,14 +210,14 @@ void Component::set_component_state_(uint8_t state) {
213
210
  }
214
211
  void Component::disable_loop() {
215
212
  if ((this->component_state_ & COMPONENT_STATE_MASK) != COMPONENT_STATE_LOOP_DONE) {
216
- ESP_LOGVV(TAG, "%s loop disabled", this->get_component_source());
213
+ ESP_LOGVV(TAG, "%s loop disabled", LOG_STR_ARG(this->get_component_log_str()));
217
214
  this->set_component_state_(COMPONENT_STATE_LOOP_DONE);
218
215
  App.disable_component_loop_(this);
219
216
  }
220
217
  }
221
218
  void Component::enable_loop() {
222
219
  if ((this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_LOOP_DONE) {
223
- ESP_LOGVV(TAG, "%s loop enabled", this->get_component_source());
220
+ ESP_LOGVV(TAG, "%s loop enabled", LOG_STR_ARG(this->get_component_log_str()));
224
221
  this->set_component_state_(COMPONENT_STATE_LOOP);
225
222
  App.enable_component_loop_(this);
226
223
  }
@@ -240,7 +237,7 @@ void IRAM_ATTR HOT Component::enable_loop_soon_any_context() {
240
237
  }
241
238
  void Component::reset_to_construction_state() {
242
239
  if ((this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED) {
243
- ESP_LOGI(TAG, "%s is being reset to construction state", this->get_component_source());
240
+ ESP_LOGI(TAG, "%s is being reset to construction state", LOG_STR_ARG(this->get_component_log_str()));
244
241
  this->set_component_state_(COMPONENT_STATE_CONSTRUCTION);
245
242
  // Clear error status when resetting
246
243
  this->status_clear_error();
@@ -280,20 +277,32 @@ bool Component::is_ready() const {
280
277
  bool Component::can_proceed() { return true; }
281
278
  bool Component::status_has_warning() const { return this->component_state_ & STATUS_LED_WARNING; }
282
279
  bool Component::status_has_error() const { return this->component_state_ & STATUS_LED_ERROR; }
280
+
283
281
  void Component::status_set_warning(const char *message) {
284
282
  // Don't spam the log. This risks missing different warning messages though.
285
283
  if ((this->component_state_ & STATUS_LED_WARNING) != 0)
286
284
  return;
287
285
  this->component_state_ |= STATUS_LED_WARNING;
288
286
  App.app_state_ |= STATUS_LED_WARNING;
289
- ESP_LOGW(TAG, "%s set Warning flag: %s", this->get_component_source(), message ? message : UNSPECIFIED_MESSAGE);
287
+ ESP_LOGW(TAG, "%s set Warning flag: %s", LOG_STR_ARG(this->get_component_log_str()),
288
+ message ? message : LOG_STR_LITERAL("unspecified"));
289
+ }
290
+ void Component::status_set_warning(const LogString *message) {
291
+ // Don't spam the log. This risks missing different warning messages though.
292
+ if ((this->component_state_ & STATUS_LED_WARNING) != 0)
293
+ return;
294
+ this->component_state_ |= STATUS_LED_WARNING;
295
+ App.app_state_ |= STATUS_LED_WARNING;
296
+ ESP_LOGW(TAG, "%s set Warning flag: %s", LOG_STR_ARG(this->get_component_log_str()),
297
+ message ? LOG_STR_ARG(message) : LOG_STR_LITERAL("unspecified"));
290
298
  }
291
299
  void Component::status_set_error(const char *message) {
292
300
  if ((this->component_state_ & STATUS_LED_ERROR) != 0)
293
301
  return;
294
302
  this->component_state_ |= STATUS_LED_ERROR;
295
303
  App.app_state_ |= STATUS_LED_ERROR;
296
- ESP_LOGE(TAG, "%s set Error flag: %s", this->get_component_source(), message ? message : UNSPECIFIED_MESSAGE);
304
+ ESP_LOGE(TAG, "%s set Error flag: %s", LOG_STR_ARG(this->get_component_log_str()),
305
+ message ? message : LOG_STR_LITERAL("unspecified"));
297
306
  if (message != nullptr) {
298
307
  // Lazy allocate the error messages vector if needed
299
308
  if (!component_error_messages) {
@@ -314,13 +323,13 @@ void Component::status_clear_warning() {
314
323
  if ((this->component_state_ & STATUS_LED_WARNING) == 0)
315
324
  return;
316
325
  this->component_state_ &= ~STATUS_LED_WARNING;
317
- ESP_LOGW(TAG, "%s cleared Warning flag", this->get_component_source());
326
+ ESP_LOGW(TAG, "%s cleared Warning flag", LOG_STR_ARG(this->get_component_log_str()));
318
327
  }
319
328
  void Component::status_clear_error() {
320
329
  if ((this->component_state_ & STATUS_LED_ERROR) == 0)
321
330
  return;
322
331
  this->component_state_ &= ~STATUS_LED_ERROR;
323
- ESP_LOGE(TAG, "%s cleared Error flag", this->get_component_source());
332
+ ESP_LOGE(TAG, "%s cleared Error flag", LOG_STR_ARG(this->get_component_log_str()));
324
333
  }
325
334
  void Component::status_momentary_warning(const std::string &name, uint32_t length) {
326
335
  this->status_set_warning();
@@ -331,6 +340,18 @@ void Component::status_momentary_error(const std::string &name, uint32_t length)
331
340
  this->set_timeout(name, length, [this]() { this->status_clear_error(); });
332
341
  }
333
342
  void Component::dump_config() {}
343
+
344
+ // Function implementation of LOG_UPDATE_INTERVAL macro to reduce code size
345
+ void log_update_interval(const char *tag, PollingComponent *component) {
346
+ uint32_t update_interval = component->get_update_interval();
347
+ if (update_interval == SCHEDULER_DONT_RUN) {
348
+ ESP_LOGCONFIG(tag, " Update Interval: never");
349
+ } else if (update_interval < 100) {
350
+ ESP_LOGCONFIG(tag, " Update Interval: %.3fs", update_interval / 1000.0f);
351
+ } else {
352
+ ESP_LOGCONFIG(tag, " Update Interval: %.1fs", update_interval / 1000.0f);
353
+ }
354
+ }
334
355
  float Component::get_actual_setup_priority() const {
335
356
  // Check if there's an override in the global vector
336
357
  if (setup_priority_overrides) {
@@ -419,8 +440,9 @@ uint32_t WarnIfComponentBlockingGuard::finish() {
419
440
  should_warn = blocking_time > WARN_IF_BLOCKING_OVER_MS;
420
441
  }
421
442
  if (should_warn) {
422
- const char *src = component_ == nullptr ? "<null>" : component_->get_component_source();
423
- ESP_LOGW(TAG, "%s took a long time for an operation (%" PRIu32 " ms)", src, blocking_time);
443
+ ESP_LOGW(TAG, "%s took a long time for an operation (%" PRIu32 " ms)",
444
+ component_ == nullptr ? LOG_STR_LITERAL("<null>") : LOG_STR_ARG(component_->get_component_log_str()),
445
+ blocking_time);
424
446
  ESP_LOGW(TAG, "Components should block for at most 30 ms");
425
447
  }
426
448
 
esphome/core/component.h CHANGED
@@ -5,10 +5,14 @@
5
5
  #include <functional>
6
6
  #include <string>
7
7
 
8
+ #include "esphome/core/log.h"
8
9
  #include "esphome/core/optional.h"
9
10
 
10
11
  namespace esphome {
11
12
 
13
+ // Forward declaration for LogString
14
+ struct LogString;
15
+
12
16
  /** Default setup priorities for components of different types.
13
17
  *
14
18
  * Components should return one of these setup priorities in get_setup_priority.
@@ -44,14 +48,13 @@ extern const float LATE;
44
48
 
45
49
  static const uint32_t SCHEDULER_DONT_RUN = 4294967295UL;
46
50
 
47
- #define LOG_UPDATE_INTERVAL(this) \
48
- if (this->get_update_interval() == SCHEDULER_DONT_RUN) { \
49
- ESP_LOGCONFIG(TAG, " Update Interval: never"); \
50
- } else if (this->get_update_interval() < 100) { \
51
- ESP_LOGCONFIG(TAG, " Update Interval: %.3fs", this->get_update_interval() / 1000.0f); \
52
- } else { \
53
- ESP_LOGCONFIG(TAG, " Update Interval: %.1fs", this->get_update_interval() / 1000.0f); \
54
- }
51
+ // Forward declaration
52
+ class PollingComponent;
53
+
54
+ // Function declaration for LOG_UPDATE_INTERVAL
55
+ void log_update_interval(const char *tag, PollingComponent *component);
56
+
57
+ #define LOG_UPDATE_INTERVAL(this) log_update_interval(TAG, this)
55
58
 
56
59
  extern const uint8_t COMPONENT_STATE_MASK;
57
60
  extern const uint8_t COMPONENT_STATE_CONSTRUCTION;
@@ -203,6 +206,7 @@ class Component {
203
206
  bool status_has_error() const;
204
207
 
205
208
  void status_set_warning(const char *message = nullptr);
209
+ void status_set_warning(const LogString *message);
206
210
 
207
211
  void status_set_error(const char *message = nullptr);
208
212
 
@@ -220,12 +224,12 @@ class Component {
220
224
  *
221
225
  * This is set by the ESPHome core, and should not be called manually.
222
226
  */
223
- void set_component_source(const char *source) { component_source_ = source; }
224
- /** Get the integration where this component was declared as a string.
227
+ void set_component_source(const LogString *source) { component_source_ = source; }
228
+ /** Get the integration where this component was declared as a LogString for logging.
225
229
  *
226
- * Returns "<unknown>" if source not set
230
+ * Returns LOG_STR("<unknown>") if source not set
227
231
  */
228
- const char *get_component_source() const;
232
+ const LogString *get_component_log_str() const;
229
233
 
230
234
  bool should_warn_of_blocking(uint32_t blocking_time);
231
235
 
@@ -405,7 +409,7 @@ class Component {
405
409
  bool cancel_defer(const std::string &name); // NOLINT
406
410
 
407
411
  // Ordered for optimal packing on 32-bit systems
408
- const char *component_source_{nullptr};
412
+ const LogString *component_source_{nullptr};
409
413
  uint16_t warn_if_blocking_over_{WARN_IF_BLOCKING_OVER_MS}; ///< Warn if blocked for this many ms (max 65.5s)
410
414
  /// State of this component - each bit has a purpose:
411
415
  /// Bits 0-2: Component state (0x00=CONSTRUCTION, 0x01=SETUP, 0x02=LOOP, 0x03=FAILED, 0x04=LOOP_DONE)
esphome/core/config.py CHANGED
@@ -39,7 +39,7 @@ from esphome.const import (
39
39
  PlatformFramework,
40
40
  __version__ as ESPHOME_VERSION,
41
41
  )
42
- from esphome.core import CORE, coroutine_with_priority
42
+ from esphome.core import CORE, CoroPriority, coroutine_with_priority
43
43
  from esphome.helpers import (
44
44
  copy_file_if_changed,
45
45
  fnv1a_32bit_hash,
@@ -359,7 +359,7 @@ ARDUINO_GLUE_CODE = """\
359
359
  """
360
360
 
361
361
 
362
- @coroutine_with_priority(-999.0)
362
+ @coroutine_with_priority(CoroPriority.WORKAROUNDS)
363
363
  async def add_arduino_global_workaround():
364
364
  # The Arduino framework defined these itself in the global
365
365
  # namespace. For the esphome codebase that is not a problem,
@@ -376,7 +376,7 @@ async def add_arduino_global_workaround():
376
376
  cg.add_global(cg.RawStatement(line))
377
377
 
378
378
 
379
- @coroutine_with_priority(-1000.0)
379
+ @coroutine_with_priority(CoroPriority.FINAL)
380
380
  async def add_includes(includes):
381
381
  # Add includes at the very end, so that the included files can access global variables
382
382
  for include in includes:
@@ -392,7 +392,7 @@ async def add_includes(includes):
392
392
  include_file(path, basename)
393
393
 
394
394
 
395
- @coroutine_with_priority(-1000.0)
395
+ @coroutine_with_priority(CoroPriority.FINAL)
396
396
  async def _add_platformio_options(pio_options):
397
397
  # Add includes at the very end, so that they override everything
398
398
  for key, val in pio_options.items():
@@ -401,7 +401,7 @@ async def _add_platformio_options(pio_options):
401
401
  cg.add_platformio_option(key, val)
402
402
 
403
403
 
404
- @coroutine_with_priority(30.0)
404
+ @coroutine_with_priority(CoroPriority.AUTOMATION)
405
405
  async def _add_automations(config):
406
406
  for conf in config.get(CONF_ON_BOOT, []):
407
407
  trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], conf.get(CONF_PRIORITY))
@@ -423,7 +423,7 @@ async def _add_automations(config):
423
423
  DATETIME_SUBTYPES = {"date", "time", "datetime"}
424
424
 
425
425
 
426
- @coroutine_with_priority(-1000.0)
426
+ @coroutine_with_priority(CoroPriority.FINAL)
427
427
  async def _add_platform_defines() -> None:
428
428
  # Generate compile-time defines for platforms that have actual entities
429
429
  # Only add USE_* and count defines when there are entities
@@ -442,7 +442,7 @@ async def _add_platform_defines() -> None:
442
442
  cg.add_define(f"USE_{platform_name.upper()}")
443
443
 
444
444
 
445
- @coroutine_with_priority(100.0)
445
+ @coroutine_with_priority(CoroPriority.CORE)
446
446
  async def to_code(config: ConfigType) -> None:
447
447
  cg.add_global(cg.global_ns.namespace("esphome").using)
448
448
  # These can be used by user lambdas, put them to default scope
esphome/core/defines.h CHANGED
@@ -240,6 +240,10 @@
240
240
  #define USE_SOCKET_SELECT_SUPPORT
241
241
  #endif
242
242
 
243
+ #ifdef USE_NRF52
244
+ #define USE_NRF52_DFU
245
+ #endif
246
+
243
247
  // Disabled feature flags
244
248
  // #define USE_BSEC // Requires a library with proprietary license
245
249
  // #define USE_BSEC2 // Requires a library with proprietary license
@@ -1,6 +1,7 @@
1
1
  #include "esphome/core/entity_base.h"
2
2
  #include "esphome/core/application.h"
3
3
  #include "esphome/core/helpers.h"
4
+ #include "esphome/core/string_ref.h"
4
5
 
5
6
  namespace esphome {
6
7
 
@@ -44,19 +45,29 @@ void EntityBase::set_icon(const char *icon) {
44
45
  #endif
45
46
  }
46
47
 
48
+ // Check if the object_id is dynamic (changes with MAC suffix)
49
+ bool EntityBase::is_object_id_dynamic_() const {
50
+ return !this->flags_.has_own_name && App.is_name_add_mac_suffix_enabled();
51
+ }
52
+
47
53
  // Entity Object ID
48
54
  std::string EntityBase::get_object_id() const {
49
55
  // Check if `App.get_friendly_name()` is constant or dynamic.
50
- if (!this->flags_.has_own_name && App.is_name_add_mac_suffix_enabled()) {
56
+ if (this->is_object_id_dynamic_()) {
51
57
  // `App.get_friendly_name()` is dynamic.
52
58
  return str_sanitize(str_snake_case(App.get_friendly_name()));
53
- } else {
54
- // `App.get_friendly_name()` is constant.
55
- if (this->object_id_c_str_ == nullptr) {
56
- return "";
57
- }
58
- return this->object_id_c_str_;
59
59
  }
60
+ // `App.get_friendly_name()` is constant.
61
+ return this->object_id_c_str_ == nullptr ? "" : this->object_id_c_str_;
62
+ }
63
+ StringRef EntityBase::get_object_id_ref_for_api_() const {
64
+ static constexpr auto EMPTY_STRING = StringRef::from_lit("");
65
+ // Return empty for dynamic case (MAC suffix)
66
+ if (this->is_object_id_dynamic_()) {
67
+ return EMPTY_STRING;
68
+ }
69
+ // For static case, return the string or empty if null
70
+ return this->object_id_c_str_ == nullptr ? EMPTY_STRING : StringRef(this->object_id_c_str_);
60
71
  }
61
72
  void EntityBase::set_object_id(const char *object_id) {
62
73
  this->object_id_c_str_ = object_id;
@@ -64,7 +75,10 @@ void EntityBase::set_object_id(const char *object_id) {
64
75
  }
65
76
 
66
77
  // Calculate Object ID Hash from Entity Name
67
- void EntityBase::calc_object_id_() { this->object_id_hash_ = fnv1_hash(this->get_object_id()); }
78
+ void EntityBase::calc_object_id_() {
79
+ this->object_id_hash_ =
80
+ fnv1_hash(this->is_object_id_dynamic_() ? this->get_object_id().c_str() : this->object_id_c_str_);
81
+ }
68
82
 
69
83
  uint32_t EntityBase::get_object_id_hash() { return this->object_id_hash_; }
70
84