esphome 2025.4.1__py3-none-any.whl → 2025.5.0b2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (412) hide show
  1. esphome/components/ac_dimmer/ac_dimmer.cpp +3 -2
  2. esphome/components/adc/__init__.py +51 -34
  3. esphome/components/airthings_wave_base/__init__.py +1 -1
  4. esphome/components/alarm_control_panel/__init__.py +37 -2
  5. esphome/components/am43/cover/__init__.py +4 -5
  6. esphome/components/analog_threshold/analog_threshold_binary_sensor.cpp +6 -4
  7. esphome/components/analog_threshold/analog_threshold_binary_sensor.h +4 -5
  8. esphome/components/analog_threshold/binary_sensor.py +10 -8
  9. esphome/components/anova/climate.py +4 -5
  10. esphome/components/api/__init__.py +25 -8
  11. esphome/components/api/api_connection.cpp +77 -10
  12. esphome/components/api/api_connection.h +6 -1
  13. esphome/components/api/api_frame_helper.cpp +98 -130
  14. esphome/components/api/api_frame_helper.h +12 -2
  15. esphome/components/api/api_noise_context.h +13 -4
  16. esphome/components/api/api_pb2.cpp +1422 -1
  17. esphome/components/api/api_pb2.h +255 -1
  18. esphome/components/api/api_pb2_service.cpp +162 -49
  19. esphome/components/api/api_pb2_service.h +90 -51
  20. esphome/components/api/api_pb2_size.h +361 -0
  21. esphome/components/api/api_server.cpp +110 -34
  22. esphome/components/api/api_server.h +8 -0
  23. esphome/components/api/proto.h +38 -9
  24. esphome/components/as3935_i2c/as3935_i2c.h +0 -3
  25. esphome/components/as7341/as7341.h +1 -1
  26. esphome/components/atm90e32/__init__.py +1 -0
  27. esphome/components/atm90e32/atm90e32.cpp +576 -199
  28. esphome/components/atm90e32/atm90e32.h +128 -31
  29. esphome/components/atm90e32/atm90e32_reg.h +4 -2
  30. esphome/components/atm90e32/button/__init__.py +62 -10
  31. esphome/components/atm90e32/button/atm90e32_button.cpp +63 -4
  32. esphome/components/atm90e32/button/atm90e32_button.h +36 -4
  33. esphome/components/atm90e32/number/__init__.py +130 -0
  34. esphome/components/atm90e32/number/atm90e32_number.h +16 -0
  35. esphome/components/atm90e32/sensor.py +21 -4
  36. esphome/components/atm90e32/text_sensor/__init__.py +48 -0
  37. esphome/components/audio/__init__.py +96 -49
  38. esphome/components/audio/audio.h +48 -0
  39. esphome/components/audio/audio_decoder.cpp +1 -1
  40. esphome/components/audio/audio_resampler.cpp +2 -0
  41. esphome/components/audio/audio_resampler.h +1 -0
  42. esphome/components/ballu/climate.py +2 -9
  43. esphome/components/bang_bang/climate.py +5 -6
  44. esphome/components/bedjet/climate/__init__.py +3 -8
  45. esphome/components/bedjet/fan/__init__.py +2 -11
  46. esphome/components/binary/fan/__init__.py +13 -16
  47. esphome/components/binary_sensor/__init__.py +13 -10
  48. esphome/components/binary_sensor/binary_sensor.cpp +6 -10
  49. esphome/components/binary_sensor/binary_sensor.h +1 -1
  50. esphome/components/binary_sensor/filter.cpp +21 -21
  51. esphome/components/binary_sensor/filter.h +10 -10
  52. esphome/components/bl0906/constants.h +16 -16
  53. esphome/components/ble_client/text_sensor/__init__.py +3 -5
  54. esphome/components/bluetooth_proxy/bluetooth_connection.cpp +4 -6
  55. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +135 -21
  56. esphome/components/bluetooth_proxy/bluetooth_proxy.h +7 -0
  57. esphome/components/button/__init__.py +11 -8
  58. esphome/components/canbus/canbus.cpp +3 -0
  59. esphome/components/canbus/canbus.h +16 -0
  60. esphome/components/climate/__init__.py +35 -2
  61. esphome/components/climate/climate_mode.h +1 -1
  62. esphome/components/climate/climate_traits.h +63 -57
  63. esphome/components/climate_ir/__init__.py +57 -17
  64. esphome/components/climate_ir_lg/climate.py +2 -5
  65. esphome/components/climate_ir_lg/climate_ir_lg.cpp +7 -7
  66. esphome/components/climate_ir_lg/climate_ir_lg.h +1 -1
  67. esphome/components/color/__init__.py +2 -0
  68. esphome/components/const/__init__.py +5 -0
  69. esphome/components/coolix/climate.py +2 -9
  70. esphome/components/copy/cover/__init__.py +10 -9
  71. esphome/components/copy/fan/__init__.py +11 -9
  72. esphome/components/copy/lock/__init__.py +11 -9
  73. esphome/components/copy/text/__init__.py +9 -6
  74. esphome/components/cover/__init__.py +37 -2
  75. esphome/components/cst226/binary_sensor/__init__.py +28 -0
  76. esphome/components/cst226/binary_sensor/cs226_button.h +22 -0
  77. esphome/components/cst226/binary_sensor/cstt6_button.cpp +19 -0
  78. esphome/components/cst226/touchscreen/cst226_touchscreen.cpp +27 -5
  79. esphome/components/cst226/touchscreen/cst226_touchscreen.h +10 -10
  80. esphome/components/current_based/cover.py +37 -36
  81. esphome/components/daikin/climate.py +2 -9
  82. esphome/components/daikin/daikin.cpp +15 -9
  83. esphome/components/daikin/daikin.h +5 -5
  84. esphome/components/daikin_arc/climate.py +2 -7
  85. esphome/components/daikin_brc/climate.py +3 -5
  86. esphome/components/dallas_temp/dallas_temp.cpp +17 -24
  87. esphome/components/dallas_temp/dallas_temp.h +0 -1
  88. esphome/components/debug/debug_component.cpp +5 -0
  89. esphome/components/debug/debug_component.h +6 -0
  90. esphome/components/debug/debug_esp32.cpp +109 -254
  91. esphome/components/debug/sensor.py +14 -0
  92. esphome/components/deep_sleep/deep_sleep_esp32.cpp +13 -1
  93. esphome/components/delonghi/climate.py +2 -9
  94. esphome/components/demo/__init__.py +18 -20
  95. esphome/components/dfrobot_sen0395/switch/__init__.py +21 -22
  96. esphome/components/display/rect.cpp +4 -9
  97. esphome/components/display/rect.h +1 -1
  98. esphome/components/emmeti/climate.py +2 -9
  99. esphome/components/endstop/cover.py +17 -16
  100. esphome/components/esp32/__init__.py +60 -3
  101. esphome/components/esp32/core.cpp +11 -5
  102. esphome/components/esp32/gpio.cpp +86 -24
  103. esphome/components/esp32/gpio.py +15 -16
  104. esphome/components/esp32/gpio_esp32.py +1 -2
  105. esphome/components/esp32/gpio_esp32_c2.py +1 -1
  106. esphome/components/esp32/gpio_esp32_c3.py +1 -1
  107. esphome/components/esp32/gpio_esp32_c6.py +1 -1
  108. esphome/components/esp32/gpio_esp32_h2.py +1 -1
  109. esphome/components/esp32_ble/ble.cpp +1 -8
  110. esphome/components/esp32_ble/ble.h +5 -3
  111. esphome/components/esp32_ble/ble_advertising.h +1 -0
  112. esphome/components/esp32_ble_server/__init__.py +3 -0
  113. esphome/components/esp32_ble_tracker/__init__.py +7 -1
  114. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +192 -118
  115. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +29 -3
  116. esphome/components/esp32_can/esp32_can.cpp +1 -1
  117. esphome/components/esp32_rmt_led_strip/led_strip.cpp +1 -1
  118. esphome/components/esp32_rmt_led_strip/led_strip.h +7 -5
  119. esphome/components/esp32_rmt_led_strip/light.py +9 -1
  120. esphome/components/esp8266/gpio.cpp +69 -8
  121. esphome/components/event/__init__.py +13 -10
  122. esphome/components/factory_reset/switch/__init__.py +7 -21
  123. esphome/components/fan/__init__.py +52 -5
  124. esphome/components/fastled_base/__init__.py +1 -4
  125. esphome/components/fastled_base/fastled_light.cpp +1 -1
  126. esphome/components/feedback/cover.py +38 -33
  127. esphome/components/fujitsu_general/climate.py +2 -9
  128. esphome/components/gpio/one_wire/gpio_one_wire.cpp +45 -43
  129. esphome/components/gpio/one_wire/gpio_one_wire.h +2 -1
  130. esphome/components/gpio_expander/cached_gpio.h +22 -7
  131. esphome/components/gps/__init__.py +11 -2
  132. esphome/components/gps/gps.cpp +11 -8
  133. esphome/components/gps/gps.h +9 -6
  134. esphome/components/graph/__init__.py +1 -2
  135. esphome/components/gree/climate.py +4 -6
  136. esphome/components/gree/gree.cpp +16 -2
  137. esphome/components/gree/gree.h +2 -2
  138. esphome/components/haier/climate.py +37 -34
  139. esphome/components/hbridge/fan/__init__.py +19 -17
  140. esphome/components/he60r/cover.py +4 -5
  141. esphome/components/heatpumpir/climate.py +3 -6
  142. esphome/components/hitachi_ac344/climate.py +2 -9
  143. esphome/components/hitachi_ac424/climate.py +2 -9
  144. esphome/components/hlw8012/hlw8012.cpp +1 -1
  145. esphome/components/hm3301/hm3301.h +1 -1
  146. esphome/components/http_request/__init__.py +39 -6
  147. esphome/components/http_request/http_request.cpp +20 -0
  148. esphome/components/http_request/http_request.h +57 -15
  149. esphome/components/http_request/http_request_arduino.cpp +22 -6
  150. esphome/components/http_request/http_request_arduino.h +4 -3
  151. esphome/components/http_request/http_request_host.cpp +141 -0
  152. esphome/components/http_request/http_request_host.h +37 -0
  153. esphome/components/http_request/http_request_idf.cpp +35 -3
  154. esphome/components/http_request/http_request_idf.h +10 -3
  155. esphome/components/http_request/httplib.h +9691 -0
  156. esphome/components/http_request/update/__init__.py +11 -8
  157. esphome/components/i2c/i2c.h +4 -0
  158. esphome/components/i2c/i2c_bus_esp_idf.cpp +1 -1
  159. esphome/components/i2s_audio/__init__.py +131 -22
  160. esphome/components/i2s_audio/i2s_audio.h +44 -4
  161. esphome/components/i2s_audio/media_player/__init__.py +19 -9
  162. esphome/components/i2s_audio/microphone/__init__.py +63 -5
  163. esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +351 -61
  164. esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +40 -6
  165. esphome/components/i2s_audio/speaker/__init__.py +31 -5
  166. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +155 -19
  167. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +17 -4
  168. esphome/components/ili9xxx/ili9xxx_init.h +1 -1
  169. esphome/components/image/__init__.py +37 -17
  170. esphome/components/image/image.cpp +25 -8
  171. esphome/components/internal_temperature/internal_temperature.cpp +6 -4
  172. esphome/components/key_collector/__init__.py +35 -0
  173. esphome/components/key_collector/key_collector.cpp +8 -0
  174. esphome/components/key_collector/key_collector.h +10 -0
  175. esphome/components/ld2410/ld2410.h +1 -1
  176. esphome/components/ld2450/ld2450.h +1 -1
  177. esphome/components/light/__init__.py +57 -0
  178. esphome/components/lock/__init__.py +51 -4
  179. esphome/components/lock/automation.h +2 -13
  180. esphome/components/logger/__init__.py +21 -0
  181. esphome/components/logger/logger.cpp +125 -95
  182. esphome/components/logger/logger.h +160 -35
  183. esphome/components/logger/task_log_buffer.cpp +138 -0
  184. esphome/components/logger/task_log_buffer.h +69 -0
  185. esphome/components/lvgl/__init__.py +13 -5
  186. esphome/components/lvgl/automation.py +50 -1
  187. esphome/components/lvgl/defines.py +0 -1
  188. esphome/components/lvgl/lv_validation.py +10 -1
  189. esphome/components/lvgl/lvgl_esphome.cpp +5 -1
  190. esphome/components/lvgl/schemas.py +14 -14
  191. esphome/components/lvgl/text/__init__.py +1 -2
  192. esphome/components/lvgl/widgets/arc.py +7 -6
  193. esphome/components/lvgl/widgets/buttonmatrix.py +3 -3
  194. esphome/components/lvgl/widgets/checkbox.py +2 -2
  195. esphome/components/lvgl/widgets/dropdown.py +2 -1
  196. esphome/components/lvgl/widgets/img.py +15 -12
  197. esphome/components/mapping/__init__.py +134 -0
  198. esphome/components/max7219digit/max7219digit.cpp +27 -27
  199. esphome/components/mdns/__init__.py +11 -5
  200. esphome/components/mdns/mdns_component.cpp +11 -5
  201. esphome/components/mdns/mdns_component.h +3 -2
  202. esphome/components/mdns/mdns_esp32.cpp +4 -3
  203. esphome/components/mdns/mdns_esp8266.cpp +4 -2
  204. esphome/components/mdns/mdns_libretiny.cpp +4 -2
  205. esphome/components/mdns/mdns_rp2040.cpp +4 -2
  206. esphome/components/media_player/__init__.py +40 -6
  207. esphome/components/micro_wake_word/__init__.py +99 -31
  208. esphome/components/micro_wake_word/automation.h +54 -0
  209. esphome/components/micro_wake_word/micro_wake_word.cpp +331 -319
  210. esphome/components/micro_wake_word/micro_wake_word.h +58 -105
  211. esphome/components/micro_wake_word/preprocessor_settings.h +19 -2
  212. esphome/components/micro_wake_word/streaming_model.cpp +158 -41
  213. esphome/components/micro_wake_word/streaming_model.h +85 -13
  214. esphome/components/microphone/__init__.py +139 -9
  215. esphome/components/microphone/automation.h +14 -2
  216. esphome/components/microphone/microphone.cpp +21 -0
  217. esphome/components/microphone/microphone.h +14 -5
  218. esphome/components/microphone/microphone_source.cpp +95 -0
  219. esphome/components/microphone/microphone_source.h +80 -0
  220. esphome/components/mics_4514/sensor.py +25 -14
  221. esphome/components/midea/climate.py +3 -4
  222. esphome/components/midea_ir/climate.py +3 -5
  223. esphome/components/mipi_spi/__init__.py +15 -0
  224. esphome/components/mipi_spi/display.py +474 -0
  225. esphome/components/mipi_spi/mipi_spi.cpp +481 -0
  226. esphome/components/mipi_spi/mipi_spi.h +171 -0
  227. esphome/components/mipi_spi/models/__init__.py +65 -0
  228. esphome/components/mipi_spi/models/amoled.py +72 -0
  229. esphome/components/mipi_spi/models/commands.py +82 -0
  230. esphome/components/mipi_spi/models/cyd.py +10 -0
  231. esphome/components/mipi_spi/models/ili.py +749 -0
  232. esphome/components/mipi_spi/models/jc.py +260 -0
  233. esphome/components/mipi_spi/models/lanbon.py +15 -0
  234. esphome/components/mipi_spi/models/lilygo.py +60 -0
  235. esphome/components/mipi_spi/models/waveshare.py +139 -0
  236. esphome/components/mitsubishi/climate.py +2 -5
  237. esphome/components/mitsubishi/mitsubishi.cpp +9 -9
  238. esphome/components/mixer/speaker/mixer_speaker.cpp +12 -22
  239. esphome/components/mixer/speaker/mixer_speaker.h +1 -3
  240. esphome/components/mlx90393/sensor.py +5 -0
  241. esphome/components/mlx90393/sensor_mlx90393.cpp +195 -13
  242. esphome/components/mlx90393/sensor_mlx90393.h +21 -4
  243. esphome/components/mqtt/__init__.py +1 -1
  244. esphome/components/mqtt/mqtt_client.cpp +5 -1
  245. esphome/components/mqtt/mqtt_const.h +4 -0
  246. esphome/components/mqtt/mqtt_fan.cpp +39 -0
  247. esphome/components/mqtt/mqtt_fan.h +2 -0
  248. esphome/components/network/__init__.py +1 -1
  249. esphome/components/nextion/base_component.py +17 -16
  250. esphome/components/nextion/display.py +11 -2
  251. esphome/components/nextion/nextion.cpp +39 -1
  252. esphome/components/nextion/nextion.h +50 -0
  253. esphome/components/noblex/climate.py +2 -9
  254. esphome/components/number/__init__.py +12 -9
  255. esphome/components/one_wire/one_wire_bus.cpp +14 -10
  256. esphome/components/one_wire/one_wire_bus.h +14 -8
  257. esphome/components/online_image/bmp_image.cpp +48 -11
  258. esphome/components/online_image/bmp_image.h +2 -0
  259. esphome/components/opentherm/binary_sensor/__init__.py +2 -4
  260. esphome/components/opentherm/number/__init__.py +11 -20
  261. esphome/components/opentherm/sensor/__init__.py +3 -3
  262. esphome/components/opentherm/switch/__init__.py +3 -5
  263. esphome/components/output/lock/__init__.py +11 -9
  264. esphome/components/packages/__init__.py +33 -31
  265. esphome/components/packet_transport/__init__.py +201 -0
  266. esphome/components/packet_transport/binary_sensor.py +19 -0
  267. esphome/components/packet_transport/packet_transport.cpp +534 -0
  268. esphome/components/packet_transport/packet_transport.h +154 -0
  269. esphome/components/packet_transport/sensor.py +19 -0
  270. esphome/components/pca9685/pca9685_output.cpp +2 -1
  271. esphome/components/pid/climate.py +2 -4
  272. esphome/components/pm2005/__init__.py +1 -0
  273. esphome/components/pm2005/pm2005.cpp +123 -0
  274. esphome/components/pm2005/pm2005.h +46 -0
  275. esphome/components/pm2005/sensor.py +86 -0
  276. esphome/components/pmsa003i/pmsa003i.cpp +43 -16
  277. esphome/components/pmsa003i/pmsa003i.h +25 -25
  278. esphome/components/pmsx003/pmsx003.cpp +193 -229
  279. esphome/components/pmsx003/pmsx003.h +51 -33
  280. esphome/components/pmsx003/sensor.py +21 -11
  281. esphome/components/pn7150/pn7150.h +2 -2
  282. esphome/components/pn7160/pn7160.h +2 -2
  283. esphome/components/prometheus/prometheus_handler.cpp +174 -0
  284. esphome/components/prometheus/prometheus_handler.h +17 -0
  285. esphome/components/psram/__init__.py +7 -5
  286. esphome/components/pulse_meter/pulse_meter_sensor.cpp +32 -12
  287. esphome/components/pulse_meter/pulse_meter_sensor.h +5 -5
  288. esphome/components/qspi_dbi/__init__.py +0 -1
  289. esphome/components/qspi_dbi/display.py +2 -1
  290. esphome/components/qspi_dbi/models.py +1 -2
  291. esphome/components/remote_base/__init__.py +91 -0
  292. esphome/components/remote_base/beo4_protocol.cpp +153 -0
  293. esphome/components/remote_base/beo4_protocol.h +43 -0
  294. esphome/components/remote_base/gobox_protocol.cpp +131 -0
  295. esphome/components/remote_base/gobox_protocol.h +54 -0
  296. esphome/components/remote_receiver/remote_receiver_esp32.cpp +16 -9
  297. esphome/components/resampler/speaker/resampler_speaker.cpp +12 -10
  298. esphome/components/resampler/speaker/resampler_speaker.h +1 -1
  299. esphome/components/scd30/sensor.py +2 -3
  300. esphome/components/scd4x/sensor.py +4 -5
  301. esphome/components/sdp3x/sensor.py +2 -1
  302. esphome/components/select/__init__.py +19 -20
  303. esphome/components/sen5x/sensor.py +1 -1
  304. esphome/components/sensor/__init__.py +158 -14
  305. esphome/components/sensor/filter.cpp +23 -0
  306. esphome/components/sensor/filter.h +22 -0
  307. esphome/components/sgp4x/sensor.py +1 -1
  308. esphome/components/sht4x/sht4x.cpp +43 -22
  309. esphome/components/sht4x/sht4x.h +1 -1
  310. esphome/components/sml/text_sensor/__init__.py +4 -6
  311. esphome/components/sound_level/__init__.py +0 -0
  312. esphome/components/sound_level/sensor.py +97 -0
  313. esphome/components/sound_level/sound_level.cpp +194 -0
  314. esphome/components/sound_level/sound_level.h +73 -0
  315. esphome/components/speaker/media_player/__init__.py +4 -8
  316. esphome/components/speaker/media_player/speaker_media_player.cpp +0 -18
  317. esphome/components/speaker/media_player/speaker_media_player.h +0 -11
  318. esphome/components/speaker/speaker.h +4 -7
  319. esphome/components/speed/fan/__init__.py +17 -16
  320. esphome/components/spi/spi.h +11 -1
  321. esphome/components/sprinkler/__init__.py +18 -19
  322. esphome/components/switch/__init__.py +32 -42
  323. esphome/components/syslog/__init__.py +41 -0
  324. esphome/components/syslog/esphome_syslog.cpp +49 -0
  325. esphome/components/syslog/esphome_syslog.h +27 -0
  326. esphome/components/tca9555/tca9555.cpp +11 -6
  327. esphome/components/tcl112/climate.py +2 -9
  328. esphome/components/template/alarm_control_panel/__init__.py +7 -6
  329. esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +21 -17
  330. esphome/components/template/alarm_control_panel/template_alarm_control_panel.h +2 -1
  331. esphome/components/template/cover/__init__.py +27 -21
  332. esphome/components/template/fan/__init__.py +14 -12
  333. esphome/components/template/lock/__init__.py +20 -25
  334. esphome/components/template/lock/automation.h +18 -0
  335. esphome/components/template/text/__init__.py +4 -3
  336. esphome/components/template/valve/__init__.py +32 -21
  337. esphome/components/template/valve/automation.h +24 -0
  338. esphome/components/text/__init__.py +32 -1
  339. esphome/components/text_sensor/__init__.py +24 -29
  340. esphome/components/thermostat/climate.py +5 -5
  341. esphome/components/time_based/cover.py +17 -16
  342. esphome/components/tm1638/switch/__init__.py +10 -7
  343. esphome/components/tormatic/cover.py +4 -5
  344. esphome/components/toshiba/climate.py +3 -5
  345. esphome/components/touchscreen/touchscreen.cpp +3 -1
  346. esphome/components/tt21100/touchscreen/tt21100.cpp +1 -1
  347. esphome/components/tuya/climate/__init__.py +5 -6
  348. esphome/components/tuya/cover/__init__.py +6 -11
  349. esphome/components/tuya/select/__init__.py +15 -5
  350. esphome/components/tuya/select/tuya_select.cpp +6 -1
  351. esphome/components/tuya/select/tuya_select.h +5 -1
  352. esphome/components/uart/packet_transport/__init__.py +20 -0
  353. esphome/components/uart/packet_transport/uart_transport.cpp +88 -0
  354. esphome/components/uart/packet_transport/uart_transport.h +41 -0
  355. esphome/components/udp/__init__.py +126 -128
  356. esphome/components/udp/automation.h +40 -0
  357. esphome/components/udp/binary_sensor.py +3 -25
  358. esphome/components/udp/packet_transport/__init__.py +29 -0
  359. esphome/components/udp/packet_transport/udp_transport.cpp +36 -0
  360. esphome/components/udp/packet_transport/udp_transport.h +28 -0
  361. esphome/components/udp/sensor.py +3 -25
  362. esphome/components/udp/udp_component.cpp +26 -470
  363. esphome/components/udp/udp_component.h +21 -128
  364. esphome/components/update/__init__.py +31 -1
  365. esphome/components/uponor_smatrix/climate/__init__.py +4 -9
  366. esphome/components/uptime/text_sensor/__init__.py +47 -7
  367. esphome/components/uptime/text_sensor/uptime_text_sensor.cpp +12 -7
  368. esphome/components/uptime/text_sensor/uptime_text_sensor.h +19 -0
  369. esphome/components/valve/__init__.py +34 -3
  370. esphome/components/valve/automation.h +1 -19
  371. esphome/components/vl53l0x/sensor.py +11 -0
  372. esphome/components/vl53l0x/vl53l0x_sensor.cpp +5 -1
  373. esphome/components/vl53l0x/vl53l0x_sensor.h +2 -1
  374. esphome/components/voice_assistant/__init__.py +36 -10
  375. esphome/components/voice_assistant/voice_assistant.cpp +170 -144
  376. esphome/components/voice_assistant/voice_assistant.h +26 -25
  377. esphome/components/waveshare_epaper/display.py +6 -0
  378. esphome/components/waveshare_epaper/waveshare_epaper.cpp +439 -37
  379. esphome/components/waveshare_epaper/waveshare_epaper.h +60 -11
  380. esphome/components/whirlpool/climate.py +3 -5
  381. esphome/components/whynter/climate.py +3 -5
  382. esphome/components/xpt2046/touchscreen/xpt2046.cpp +1 -1
  383. esphome/components/yashima/climate.py +6 -6
  384. esphome/components/zhlt01/climate.py +2 -7
  385. esphome/config_validation.py +38 -58
  386. esphome/const.py +15 -1
  387. esphome/core/__init__.py +2 -0
  388. esphome/core/application.cpp +1 -0
  389. esphome/core/application.h +4 -0
  390. esphome/core/automation.h +4 -3
  391. esphome/core/component.cpp +19 -3
  392. esphome/core/component.h +5 -0
  393. esphome/core/defines.h +23 -17
  394. esphome/core/macros.h +4 -0
  395. esphome/core/scheduler.cpp +3 -0
  396. esphome/cpp_generator.py +6 -2
  397. esphome/dashboard/web_server.py +3 -3
  398. esphome/helpers.py +39 -0
  399. esphome/loader.py +4 -0
  400. esphome/mqtt.py +21 -8
  401. esphome/platformio_api.py +1 -1
  402. esphome/schema_extractors.py +0 -1
  403. esphome/vscode.py +15 -0
  404. esphome/wizard.py +2 -2
  405. esphome/zeroconf.py +7 -3
  406. {esphome-2025.4.1.dist-info → esphome-2025.5.0b2.dist-info}/METADATA +10 -11
  407. {esphome-2025.4.1.dist-info → esphome-2025.5.0b2.dist-info}/RECORD +411 -352
  408. {esphome-2025.4.1.dist-info → esphome-2025.5.0b2.dist-info}/WHEEL +1 -1
  409. esphome/components/esp32_ble/const_esp32c6.h +0 -74
  410. {esphome-2025.4.1.dist-info → esphome-2025.5.0b2.dist-info}/entry_points.txt +0 -0
  411. {esphome-2025.4.1.dist-info → esphome-2025.5.0b2.dist-info}/licenses/LICENSE +0 -0
  412. {esphome-2025.4.1.dist-info → esphome-2025.5.0b2.dist-info}/top_level.txt +0 -0
@@ -1,3 +1,5 @@
1
+ import enum
2
+
1
3
  import esphome.automation as auto
2
4
  import esphome.codegen as cg
3
5
  from esphome.components import mqtt, power_supply, web_server
@@ -13,15 +15,18 @@ from esphome.const import (
13
15
  CONF_COLOR_TEMPERATURE,
14
16
  CONF_DEFAULT_TRANSITION_LENGTH,
15
17
  CONF_EFFECTS,
18
+ CONF_ENTITY_CATEGORY,
16
19
  CONF_FLASH_TRANSITION_LENGTH,
17
20
  CONF_GAMMA_CORRECT,
18
21
  CONF_GREEN,
22
+ CONF_ICON,
19
23
  CONF_ID,
20
24
  CONF_INITIAL_STATE,
21
25
  CONF_MQTT_ID,
22
26
  CONF_ON_STATE,
23
27
  CONF_ON_TURN_OFF,
24
28
  CONF_ON_TURN_ON,
29
+ CONF_OUTPUT_ID,
25
30
  CONF_POWER_SUPPLY,
26
31
  CONF_RED,
27
32
  CONF_RESTORE_MODE,
@@ -33,6 +38,7 @@ from esphome.const import (
33
38
  CONF_WHITE,
34
39
  )
35
40
  from esphome.core import coroutine_with_priority
41
+ from esphome.cpp_generator import MockObjClass
36
42
  from esphome.cpp_helpers import setup_entity
37
43
 
38
44
  from .automation import LIGHT_STATE_SCHEMA
@@ -141,6 +147,51 @@ ADDRESSABLE_LIGHT_SCHEMA = RGB_LIGHT_SCHEMA.extend(
141
147
  )
142
148
 
143
149
 
150
+ class LightType(enum.IntEnum):
151
+ """Light type enum."""
152
+
153
+ BINARY = 0
154
+ BRIGHTNESS_ONLY = 1
155
+ RGB = 2
156
+ ADDRESSABLE = 3
157
+
158
+
159
+ def light_schema(
160
+ class_: MockObjClass,
161
+ type_: LightType,
162
+ *,
163
+ entity_category: str = cv.UNDEFINED,
164
+ icon: str = cv.UNDEFINED,
165
+ default_restore_mode: str = cv.UNDEFINED,
166
+ ) -> cv.Schema:
167
+ schema = {
168
+ cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(class_),
169
+ }
170
+
171
+ for key, default, validator in [
172
+ (CONF_ENTITY_CATEGORY, entity_category, cv.entity_category),
173
+ (CONF_ICON, icon, cv.icon),
174
+ (
175
+ CONF_RESTORE_MODE,
176
+ default_restore_mode,
177
+ cv.enum(RESTORE_MODES, upper=True, space="_"),
178
+ ),
179
+ ]:
180
+ if default is not cv.UNDEFINED:
181
+ schema[cv.Optional(key, default=default)] = validator
182
+
183
+ if type_ == LightType.BINARY:
184
+ return BINARY_LIGHT_SCHEMA.extend(schema)
185
+ if type_ == LightType.BRIGHTNESS_ONLY:
186
+ return BRIGHTNESS_ONLY_LIGHT_SCHEMA.extend(schema)
187
+ if type_ == LightType.RGB:
188
+ return RGB_LIGHT_SCHEMA.extend(schema)
189
+ if type_ == LightType.ADDRESSABLE:
190
+ return ADDRESSABLE_LIGHT_SCHEMA.extend(schema)
191
+
192
+ raise ValueError(f"Invalid light type: {type_}")
193
+
194
+
144
195
  def validate_color_temperature_channels(value):
145
196
  if (
146
197
  CONF_COLD_WHITE_COLOR_TEMPERATURE in value
@@ -223,6 +274,12 @@ async def register_light(output_var, config):
223
274
  await setup_light_core_(light_var, output_var, config)
224
275
 
225
276
 
277
+ async def new_light(config, *args):
278
+ output_var = cg.new_Pvariable(config[CONF_OUTPUT_ID], *args)
279
+ await register_light(output_var, config)
280
+ return output_var
281
+
282
+
226
283
  @coroutine_with_priority(100.0)
227
284
  async def to_code(config):
228
285
  cg.add_define("USE_LIGHT")
@@ -4,6 +4,8 @@ import esphome.codegen as cg
4
4
  from esphome.components import mqtt, web_server
5
5
  import esphome.config_validation as cv
6
6
  from esphome.const import (
7
+ CONF_ENTITY_CATEGORY,
8
+ CONF_ICON,
7
9
  CONF_ID,
8
10
  CONF_MQTT_ID,
9
11
  CONF_ON_LOCK,
@@ -12,6 +14,7 @@ from esphome.const import (
12
14
  CONF_WEB_SERVER,
13
15
  )
14
16
  from esphome.core import CORE, coroutine_with_priority
17
+ from esphome.cpp_generator import MockObjClass
15
18
  from esphome.cpp_helpers import setup_entity
16
19
 
17
20
  CODEOWNERS = ["@esphome/core"]
@@ -31,7 +34,19 @@ LockCondition = lock_ns.class_("LockCondition", Condition)
31
34
  LockLockTrigger = lock_ns.class_("LockLockTrigger", automation.Trigger.template())
32
35
  LockUnlockTrigger = lock_ns.class_("LockUnlockTrigger", automation.Trigger.template())
33
36
 
34
- LOCK_SCHEMA = (
37
+ LockState = lock_ns.enum("LockState")
38
+
39
+ LOCK_STATES = {
40
+ "LOCKED": LockState.LOCK_STATE_LOCKED,
41
+ "UNLOCKED": LockState.LOCK_STATE_UNLOCKED,
42
+ "JAMMED": LockState.LOCK_STATE_JAMMED,
43
+ "LOCKING": LockState.LOCK_STATE_LOCKING,
44
+ "UNLOCKING": LockState.LOCK_STATE_UNLOCKING,
45
+ }
46
+
47
+ validate_lock_state = cv.enum(LOCK_STATES, upper=True)
48
+
49
+ _LOCK_SCHEMA = (
35
50
  cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
36
51
  .extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
37
52
  .extend(
@@ -52,7 +67,33 @@ LOCK_SCHEMA = (
52
67
  )
53
68
 
54
69
 
55
- async def setup_lock_core_(var, config):
70
+ def lock_schema(
71
+ class_: MockObjClass = cv.UNDEFINED,
72
+ *,
73
+ icon: str = cv.UNDEFINED,
74
+ entity_category: str = cv.UNDEFINED,
75
+ ) -> cv.Schema:
76
+ schema = {}
77
+
78
+ if class_ is not cv.UNDEFINED:
79
+ schema[cv.GenerateID()] = cv.declare_id(class_)
80
+
81
+ for key, default, validator in [
82
+ (CONF_ICON, icon, cv.icon),
83
+ (CONF_ENTITY_CATEGORY, entity_category, cv.entity_category),
84
+ ]:
85
+ if default is not cv.UNDEFINED:
86
+ schema[cv.Optional(key, default=default)] = validator
87
+
88
+ return _LOCK_SCHEMA.extend(schema)
89
+
90
+
91
+ # Remove before 2025.11.0
92
+ LOCK_SCHEMA = lock_schema()
93
+ LOCK_SCHEMA.add_extra(cv.deprecated_schema_constant("lock"))
94
+
95
+
96
+ async def _setup_lock_core(var, config):
56
97
  await setup_entity(var, config)
57
98
 
58
99
  for conf in config.get(CONF_ON_LOCK, []):
@@ -74,12 +115,18 @@ async def register_lock(var, config):
74
115
  if not CORE.has_id(config[CONF_ID]):
75
116
  var = cg.Pvariable(config[CONF_ID], var)
76
117
  cg.add(cg.App.register_lock(var))
77
- await setup_lock_core_(var, config)
118
+ await _setup_lock_core(var, config)
119
+
120
+
121
+ async def new_lock(config, *args):
122
+ var = cg.new_Pvariable(config[CONF_ID], *args)
123
+ await register_lock(var, config)
124
+ return var
78
125
 
79
126
 
80
127
  LOCK_ACTION_SCHEMA = maybe_simple_id(
81
128
  {
82
- cv.Required(CONF_ID): cv.use_id(Lock),
129
+ cv.GenerateID(CONF_ID): cv.use_id(Lock),
83
130
  }
84
131
  )
85
132
 
@@ -1,8 +1,8 @@
1
1
  #pragma once
2
2
 
3
- #include "esphome/core/component.h"
4
- #include "esphome/core/automation.h"
5
3
  #include "esphome/components/lock/lock.h"
4
+ #include "esphome/core/automation.h"
5
+ #include "esphome/core/component.h"
6
6
 
7
7
  namespace esphome {
8
8
  namespace lock {
@@ -72,16 +72,5 @@ class LockUnlockTrigger : public Trigger<> {
72
72
  }
73
73
  };
74
74
 
75
- template<typename... Ts> class LockPublishAction : public Action<Ts...> {
76
- public:
77
- LockPublishAction(Lock *a_lock) : lock_(a_lock) {}
78
- TEMPLATABLE_VALUE(LockState, state)
79
-
80
- void play(Ts... x) override { this->lock_->publish_state(this->state_.value(x...)); }
81
-
82
- protected:
83
- Lock *lock_;
84
- };
85
-
86
75
  } // namespace lock
87
76
  } // namespace esphome
@@ -79,6 +79,7 @@ DEFAULT = "DEFAULT"
79
79
 
80
80
  CONF_INITIAL_LEVEL = "initial_level"
81
81
  CONF_LOGGER_ID = "logger_id"
82
+ CONF_TASK_LOG_BUFFER_SIZE = "task_log_buffer_size"
82
83
 
83
84
  UART_SELECTION_ESP32 = {
84
85
  VARIANT_ESP32: [UART0, UART1, UART2],
@@ -180,6 +181,20 @@ CONFIG_SCHEMA = cv.All(
180
181
  cv.Optional(CONF_BAUD_RATE, default=115200): cv.positive_int,
181
182
  cv.Optional(CONF_TX_BUFFER_SIZE, default=512): cv.validate_bytes,
182
183
  cv.Optional(CONF_DEASSERT_RTS_DTR, default=False): cv.boolean,
184
+ cv.SplitDefault(
185
+ CONF_TASK_LOG_BUFFER_SIZE,
186
+ esp32=768, # Default: 768 bytes (~5-6 messages with 70-byte text plus thread names)
187
+ ): cv.All(
188
+ cv.only_on_esp32,
189
+ cv.validate_bytes,
190
+ cv.Any(
191
+ cv.int_(0), # Disabled
192
+ cv.int_range(
193
+ min=640, # Min: ~4-5 messages with 70-byte text plus thread names
194
+ max=32768, # Max: Depends on message sizes, typically ~300 messages with default size
195
+ ),
196
+ ),
197
+ ),
183
198
  cv.SplitDefault(
184
199
  CONF_HARDWARE_UART,
185
200
  esp8266=UART0,
@@ -238,6 +253,12 @@ async def to_code(config):
238
253
  baud_rate,
239
254
  config[CONF_TX_BUFFER_SIZE],
240
255
  )
256
+ if CORE.is_esp32:
257
+ task_log_buffer_size = config[CONF_TASK_LOG_BUFFER_SIZE]
258
+ if task_log_buffer_size > 0:
259
+ cg.add_define("USE_ESPHOME_TASK_LOG_BUFFER")
260
+ cg.add(log.init_log_buffer(task_log_buffer_size))
261
+
241
262
  cg.add(log.set_log_level(initial_level))
242
263
  if CONF_HARDWARE_UART in config:
243
264
  cg.add(
@@ -1,5 +1,8 @@
1
1
  #include "logger.h"
2
2
  #include <cinttypes>
3
+ #ifdef USE_ESPHOME_TASK_LOG_BUFFER
4
+ #include <memory> // For unique_ptr
5
+ #endif
3
6
 
4
7
  #include "esphome/core/hal.h"
5
8
  #include "esphome/core/log.h"
@@ -10,127 +13,121 @@ namespace logger {
10
13
 
11
14
  static const char *const TAG = "logger";
12
15
 
13
- static const char *const LOG_LEVEL_COLORS[] = {
14
- "", // NONE
15
- ESPHOME_LOG_BOLD(ESPHOME_LOG_COLOR_RED), // ERROR
16
- ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_YELLOW), // WARNING
17
- ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_GREEN), // INFO
18
- ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_MAGENTA), // CONFIG
19
- ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_CYAN), // DEBUG
20
- ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_GRAY), // VERBOSE
21
- ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_WHITE), // VERY_VERBOSE
22
- };
23
- static const char *const LOG_LEVEL_LETTERS[] = {
24
- "", // NONE
25
- "E", // ERROR
26
- "W", // WARNING
27
- "I", // INFO
28
- "C", // CONFIG
29
- "D", // DEBUG
30
- "V", // VERBOSE
31
- "VV", // VERY_VERBOSE
32
- };
33
-
34
- void Logger::write_header_(int level, const char *tag, int line) {
35
- if (level < 0)
36
- level = 0;
37
- if (level > 7)
38
- level = 7;
39
-
40
- const char *color = LOG_LEVEL_COLORS[level];
41
- const char *letter = LOG_LEVEL_LETTERS[level];
42
- #if defined(USE_ESP32) || defined(USE_LIBRETINY)
16
+ #ifdef USE_ESP32
17
+ // Implementation for ESP32 (multi-core with atomic support)
18
+ // Main thread: synchronous logging with direct buffer access
19
+ // Other threads: console output with stack buffer, callbacks via async buffer
20
+ void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *format, va_list args) { // NOLINT
21
+ if (level > this->level_for(tag) || recursion_guard_.load(std::memory_order_relaxed))
22
+ return;
23
+ recursion_guard_.store(true, std::memory_order_relaxed);
24
+
43
25
  TaskHandle_t current_task = xTaskGetCurrentTaskHandle();
44
- #else
45
- void *current_task = nullptr;
46
- #endif
26
+
27
+ // For main task: call log_message_to_buffer_and_send_ which does console and callback logging
47
28
  if (current_task == main_task_) {
48
- this->printf_to_buffer_("%s[%s][%s:%03u]: ", color, letter, tag, line);
49
- } else {
50
- const char *thread_name = ""; // NOLINT(clang-analyzer-deadcode.DeadStores)
51
- #if defined(USE_ESP32)
52
- thread_name = pcTaskGetName(current_task);
53
- #elif defined(USE_LIBRETINY)
54
- thread_name = pcTaskGetTaskName(current_task);
55
- #endif
56
- this->printf_to_buffer_("%s[%s][%s:%03u]%s[%s]%s: ", color, letter, tag, line,
57
- ESPHOME_LOG_BOLD(ESPHOME_LOG_COLOR_RED), thread_name, color);
29
+ this->log_message_to_buffer_and_send_(level, tag, line, format, args);
30
+ recursion_guard_.store(false, std::memory_order_release);
31
+ return;
58
32
  }
33
+
34
+ // For non-main tasks: use stack-allocated buffer only for console output
35
+ if (this->baud_rate_ > 0) { // If logging is enabled, write to console
36
+ // Maximum size for console log messages (includes null terminator)
37
+ static const size_t MAX_CONSOLE_LOG_MSG_SIZE = 144;
38
+ char console_buffer[MAX_CONSOLE_LOG_MSG_SIZE]; // MUST be stack allocated for thread safety
39
+ int buffer_at = 0; // Initialize buffer position
40
+ this->format_log_to_buffer_with_terminator_(level, tag, line, format, args, console_buffer, &buffer_at,
41
+ MAX_CONSOLE_LOG_MSG_SIZE);
42
+ this->write_msg_(console_buffer);
43
+ }
44
+
45
+ #ifdef USE_ESPHOME_TASK_LOG_BUFFER
46
+ // For non-main tasks, queue the message for callbacks - but only if we have any callbacks registered
47
+ if (this->log_callback_.size() > 0) {
48
+ // This will be processed in the main loop
49
+ this->log_buffer_->send_message_thread_safe(static_cast<uint8_t>(level), tag, static_cast<uint16_t>(line),
50
+ current_task, format, args);
51
+ }
52
+ #endif // USE_ESPHOME_TASK_LOG_BUFFER
53
+
54
+ recursion_guard_.store(false, std::memory_order_release);
59
55
  }
56
+ #endif // USE_ESP32
60
57
 
58
+ #ifndef USE_ESP32
59
+ // Implementation for platforms that do not support atomic operations
60
+ // or have to consider logging in other tasks
61
61
  void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *format, va_list args) { // NOLINT
62
62
  if (level > this->level_for(tag) || recursion_guard_)
63
63
  return;
64
64
 
65
65
  recursion_guard_ = true;
66
- this->reset_buffer_();
67
- this->write_header_(level, tag, line);
68
- this->vprintf_to_buffer_(format, args);
69
- this->write_footer_();
70
- this->log_message_(level, tag);
66
+
67
+ // Format and send to both console and callbacks
68
+ this->log_message_to_buffer_and_send_(level, tag, line, format, args);
69
+
71
70
  recursion_guard_ = false;
72
71
  }
72
+ #endif // !USE_ESP32
73
+
73
74
  #ifdef USE_STORE_LOG_STR_IN_FLASH
75
+ // Implementation for ESP8266 with flash string support.
76
+ // Note: USE_STORE_LOG_STR_IN_FLASH is only defined for ESP8266.
74
77
  void Logger::log_vprintf_(int level, const char *tag, int line, const __FlashStringHelper *format,
75
78
  va_list args) { // NOLINT
76
79
  if (level > this->level_for(tag) || recursion_guard_)
77
80
  return;
78
81
 
79
82
  recursion_guard_ = true;
80
- this->reset_buffer_();
81
- // copy format string
83
+ this->tx_buffer_at_ = 0;
84
+
85
+ // Copy format string from progmem
82
86
  auto *format_pgm_p = reinterpret_cast<const uint8_t *>(format);
83
- size_t len = 0;
84
87
  char ch = '.';
85
- while (!this->is_buffer_full_() && ch != '\0') {
88
+ while (this->tx_buffer_at_ < this->tx_buffer_size_ && ch != '\0') {
86
89
  this->tx_buffer_[this->tx_buffer_at_++] = ch = (char) progmem_read_byte(format_pgm_p++);
87
90
  }
88
- // Buffer full form copying format
89
- if (this->is_buffer_full_())
91
+
92
+ // Buffer full from copying format
93
+ if (this->tx_buffer_at_ >= this->tx_buffer_size_) {
94
+ recursion_guard_ = false; // Make sure to reset the recursion guard before returning
90
95
  return;
96
+ }
91
97
 
92
- // length of format string, includes null terminator
93
- uint32_t offset = this->tx_buffer_at_;
98
+ // Save the offset before calling format_log_to_buffer_with_terminator_
99
+ // since it will increment tx_buffer_at_ to the end of the formatted string
100
+ uint32_t msg_start = this->tx_buffer_at_;
101
+ this->format_log_to_buffer_with_terminator_(level, tag, line, this->tx_buffer_, args, this->tx_buffer_,
102
+ &this->tx_buffer_at_, this->tx_buffer_size_);
103
+
104
+ // Write to console and send callback starting at the msg_start
105
+ if (this->baud_rate_ > 0) {
106
+ this->write_msg_(this->tx_buffer_ + msg_start);
107
+ }
108
+ this->call_log_callbacks_(level, tag, this->tx_buffer_ + msg_start);
94
109
 
95
- // now apply vsnprintf
96
- this->write_header_(level, tag, line);
97
- this->vprintf_to_buffer_(this->tx_buffer_, args);
98
- this->write_footer_();
99
- this->log_message_(level, tag, offset);
100
110
  recursion_guard_ = false;
101
111
  }
102
- #endif
112
+ #endif // USE_STORE_LOG_STR_IN_FLASH
103
113
 
104
- int HOT Logger::level_for(const char *tag) {
105
- if (this->log_levels_.count(tag) != 0)
106
- return this->log_levels_[tag];
114
+ inline int Logger::level_for(const char *tag) {
115
+ auto it = this->log_levels_.find(tag);
116
+ if (it != this->log_levels_.end())
117
+ return it->second;
107
118
  return this->current_level_;
108
119
  }
109
120
 
110
- void HOT Logger::log_message_(int level, const char *tag, int offset) {
111
- // remove trailing newline
112
- if (this->tx_buffer_[this->tx_buffer_at_ - 1] == '\n') {
113
- this->tx_buffer_at_--;
114
- }
115
- // make sure null terminator is present
116
- this->set_null_terminator_();
117
-
118
- const char *msg = this->tx_buffer_ + offset;
119
-
120
- if (this->baud_rate_ > 0) {
121
- this->write_msg_(msg);
122
- }
123
-
121
+ void HOT Logger::call_log_callbacks_(int level, const char *tag, const char *msg) {
124
122
  #ifdef USE_ESP32
125
- // Suppress network-logging if memory constrained, but still log to serial
126
- // ports. In some configurations (eg BLE enabled) there may be some transient
123
+ // Suppress network-logging if memory constrained
124
+ // In some configurations (eg BLE enabled) there may be some transient
127
125
  // memory exhaustion, and trying to log when OOM can lead to a crash. Skipping
128
126
  // here usually allows the stack to recover instead.
129
127
  // See issue #1234 for analysis.
130
128
  if (xPortGetFreeHeapSize() < 2048)
131
129
  return;
132
130
  #endif
133
-
134
131
  this->log_callback_.call(level, tag, msg);
135
132
  }
136
133
 
@@ -141,21 +138,50 @@ Logger::Logger(uint32_t baud_rate, size_t tx_buffer_size) : baud_rate_(baud_rate
141
138
  this->main_task_ = xTaskGetCurrentTaskHandle();
142
139
  #endif
143
140
  }
141
+ #ifdef USE_ESPHOME_TASK_LOG_BUFFER
142
+ void Logger::init_log_buffer(size_t total_buffer_size) {
143
+ this->log_buffer_ = esphome::make_unique<logger::TaskLogBuffer>(total_buffer_size);
144
+ }
145
+ #endif
144
146
 
145
- #ifdef USE_LOGGER_USB_CDC
147
+ #if defined(USE_LOGGER_USB_CDC) || defined(USE_ESP32)
146
148
  void Logger::loop() {
147
- #ifdef USE_ARDUINO
148
- if (this->uart_ != UART_SELECTION_USB_CDC) {
149
- return;
150
- }
151
- static bool opened = false;
152
- if (opened == Serial) {
153
- return;
149
+ #if defined(USE_LOGGER_USB_CDC) && defined(USE_ARDUINO)
150
+ if (this->uart_ == UART_SELECTION_USB_CDC) {
151
+ static bool opened = false;
152
+ if (opened == Serial) {
153
+ return;
154
+ }
155
+ if (false == opened) {
156
+ App.schedule_dump_config();
157
+ }
158
+ opened = !opened;
154
159
  }
155
- if (false == opened) {
156
- App.schedule_dump_config();
160
+ #endif
161
+
162
+ #ifdef USE_ESPHOME_TASK_LOG_BUFFER
163
+ // Process any buffered messages when available
164
+ if (this->log_buffer_->has_messages()) {
165
+ logger::TaskLogBuffer::LogMessage *message;
166
+ const char *text;
167
+ void *received_token;
168
+
169
+ // Process messages from the buffer
170
+ while (this->log_buffer_->borrow_message_main_loop(&message, &text, &received_token)) {
171
+ this->tx_buffer_at_ = 0;
172
+ // Use the thread name that was stored when the message was created
173
+ // This avoids potential crashes if the task no longer exists
174
+ const char *thread_name = message->thread_name[0] != '\0' ? message->thread_name : nullptr;
175
+ this->write_header_to_buffer_(message->level, message->tag, message->line, thread_name, this->tx_buffer_,
176
+ &this->tx_buffer_at_, this->tx_buffer_size_);
177
+ this->write_body_to_buffer_(text, message->text_length, this->tx_buffer_, &this->tx_buffer_at_,
178
+ this->tx_buffer_size_);
179
+ this->write_footer_to_buffer_(this->tx_buffer_, &this->tx_buffer_at_, this->tx_buffer_size_);
180
+ this->tx_buffer_[this->tx_buffer_at_] = '\0';
181
+ this->call_log_callbacks_(message->level, message->tag, this->tx_buffer_);
182
+ this->log_buffer_->release_message_main_loop(received_token);
183
+ }
157
184
  }
158
- opened = !opened;
159
185
  #endif
160
186
  }
161
187
  #endif
@@ -171,7 +197,7 @@ void Logger::add_on_log_callback(std::function<void(int, const char *, const cha
171
197
  this->log_callback_.add(std::move(callback));
172
198
  }
173
199
  float Logger::get_setup_priority() const { return setup_priority::BUS + 500.0f; }
174
- const char *const LOG_LEVELS[] = {"NONE", "ERROR", "WARN", "INFO", "CONFIG", "DEBUG", "VERBOSE", "VERY_VERBOSE"};
200
+ static const char *const LOG_LEVELS[] = {"NONE", "ERROR", "WARN", "INFO", "CONFIG", "DEBUG", "VERBOSE", "VERY_VERBOSE"};
175
201
 
176
202
  void Logger::dump_config() {
177
203
  ESP_LOGCONFIG(TAG, "Logger:");
@@ -181,12 +207,16 @@ void Logger::dump_config() {
181
207
  ESP_LOGCONFIG(TAG, " Log Baud Rate: %" PRIu32, this->baud_rate_);
182
208
  ESP_LOGCONFIG(TAG, " Hardware UART: %s", get_uart_selection_());
183
209
  #endif
210
+ #ifdef USE_ESPHOME_TASK_LOG_BUFFER
211
+ if (this->log_buffer_) {
212
+ ESP_LOGCONFIG(TAG, " Task Log Buffer Size: %u", this->log_buffer_->size());
213
+ }
214
+ #endif
184
215
 
185
216
  for (auto &it : this->log_levels_) {
186
217
  ESP_LOGCONFIG(TAG, " Level for '%s': %s", it.first.c_str(), LOG_LEVELS[it.second]);
187
218
  }
188
219
  }
189
- void Logger::write_footer_() { this->write_to_buffer_(ESPHOME_LOG_RESET_COLOR, strlen(ESPHOME_LOG_RESET_COLOR)); }
190
220
 
191
221
  void Logger::set_log_level(int level) {
192
222
  if (level > ESPHOME_LOG_LEVEL) {