esphome 2025.4.2__py3-none-any.whl → 2025.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (444) hide show
  1. esphome/__main__.py +16 -14
  2. esphome/components/ac_dimmer/ac_dimmer.cpp +3 -2
  3. esphome/components/adc/__init__.py +51 -34
  4. esphome/components/airthings_wave_base/__init__.py +1 -1
  5. esphome/components/alarm_control_panel/__init__.py +37 -2
  6. esphome/components/am43/cover/__init__.py +4 -5
  7. esphome/components/analog_threshold/analog_threshold_binary_sensor.cpp +6 -4
  8. esphome/components/analog_threshold/analog_threshold_binary_sensor.h +4 -5
  9. esphome/components/analog_threshold/binary_sensor.py +10 -8
  10. esphome/components/anova/climate.py +4 -5
  11. esphome/components/api/__init__.py +25 -8
  12. esphome/components/api/api_connection.cpp +416 -662
  13. esphome/components/api/api_connection.h +256 -57
  14. esphome/components/api/api_frame_helper.cpp +232 -177
  15. esphome/components/api/api_frame_helper.h +61 -8
  16. esphome/components/api/api_noise_context.h +13 -4
  17. esphome/components/api/api_pb2.cpp +1422 -1
  18. esphome/components/api/api_pb2.h +255 -1
  19. esphome/components/api/api_pb2_service.cpp +162 -49
  20. esphome/components/api/api_pb2_service.h +90 -51
  21. esphome/components/api/api_pb2_size.h +361 -0
  22. esphome/components/api/api_server.cpp +110 -34
  23. esphome/components/api/api_server.h +8 -0
  24. esphome/components/api/proto.h +86 -17
  25. esphome/components/as7341/as7341.h +1 -1
  26. esphome/components/at581x/at581x.h +4 -4
  27. esphome/components/atm90e32/__init__.py +1 -0
  28. esphome/components/atm90e32/atm90e32.cpp +576 -199
  29. esphome/components/atm90e32/atm90e32.h +128 -31
  30. esphome/components/atm90e32/atm90e32_reg.h +4 -2
  31. esphome/components/atm90e32/button/__init__.py +62 -10
  32. esphome/components/atm90e32/button/atm90e32_button.cpp +63 -4
  33. esphome/components/atm90e32/button/atm90e32_button.h +36 -4
  34. esphome/components/atm90e32/number/__init__.py +130 -0
  35. esphome/components/atm90e32/number/atm90e32_number.h +16 -0
  36. esphome/components/atm90e32/sensor.py +21 -4
  37. esphome/components/atm90e32/text_sensor/__init__.py +48 -0
  38. esphome/components/audio/__init__.py +96 -49
  39. esphome/components/audio/audio.h +48 -0
  40. esphome/components/audio/audio_decoder.cpp +1 -1
  41. esphome/components/audio/audio_resampler.cpp +2 -0
  42. esphome/components/audio/audio_resampler.h +1 -0
  43. esphome/components/ballu/climate.py +2 -9
  44. esphome/components/bang_bang/climate.py +5 -6
  45. esphome/components/bedjet/bedjet_hub.cpp +1 -0
  46. esphome/components/bedjet/climate/__init__.py +3 -8
  47. esphome/components/bedjet/fan/__init__.py +2 -11
  48. esphome/components/binary/fan/__init__.py +13 -16
  49. esphome/components/binary_sensor/__init__.py +13 -10
  50. esphome/components/bl0906/constants.h +16 -16
  51. esphome/components/ble_client/text_sensor/__init__.py +3 -5
  52. esphome/components/bluetooth_proxy/bluetooth_connection.cpp +4 -6
  53. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +136 -21
  54. esphome/components/bluetooth_proxy/bluetooth_proxy.h +7 -0
  55. esphome/components/button/__init__.py +11 -8
  56. esphome/components/canbus/canbus.cpp +3 -0
  57. esphome/components/canbus/canbus.h +16 -0
  58. esphome/components/ccs811/sensor.py +9 -6
  59. esphome/components/climate/__init__.py +35 -2
  60. esphome/components/climate/climate_mode.h +1 -1
  61. esphome/components/climate/climate_traits.h +63 -57
  62. esphome/components/climate_ir/__init__.py +57 -17
  63. esphome/components/climate_ir_lg/climate.py +2 -5
  64. esphome/components/climate_ir_lg/climate_ir_lg.cpp +7 -7
  65. esphome/components/climate_ir_lg/climate_ir_lg.h +1 -1
  66. esphome/components/color/__init__.py +2 -0
  67. esphome/components/const/__init__.py +5 -0
  68. esphome/components/coolix/climate.py +2 -9
  69. esphome/components/copy/cover/__init__.py +10 -9
  70. esphome/components/copy/fan/__init__.py +11 -9
  71. esphome/components/copy/lock/__init__.py +11 -9
  72. esphome/components/copy/text/__init__.py +9 -6
  73. esphome/components/cover/__init__.py +37 -2
  74. esphome/components/cse7766/cse7766.cpp +2 -1
  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/current_based/current_based_cover.cpp +2 -1
  82. esphome/components/daikin/climate.py +2 -9
  83. esphome/components/daikin/daikin.cpp +15 -9
  84. esphome/components/daikin/daikin.h +5 -5
  85. esphome/components/daikin_arc/climate.py +2 -7
  86. esphome/components/daikin_brc/climate.py +3 -5
  87. esphome/components/dallas_temp/dallas_temp.cpp +17 -24
  88. esphome/components/dallas_temp/dallas_temp.h +0 -1
  89. esphome/components/daly_bms/daly_bms.cpp +2 -1
  90. esphome/components/debug/debug_component.cpp +6 -1
  91. esphome/components/debug/debug_component.h +8 -0
  92. esphome/components/debug/debug_esp32.cpp +109 -254
  93. esphome/components/debug/sensor.py +14 -0
  94. esphome/components/deep_sleep/deep_sleep_esp32.cpp +13 -1
  95. esphome/components/delonghi/climate.py +2 -9
  96. esphome/components/demo/__init__.py +18 -20
  97. esphome/components/dfrobot_sen0395/switch/__init__.py +21 -22
  98. esphome/components/dps310/sensor.py +6 -6
  99. esphome/components/ee895/sensor.py +9 -9
  100. esphome/components/emmeti/climate.py +2 -9
  101. esphome/components/endstop/cover.py +17 -16
  102. esphome/components/endstop/endstop_cover.cpp +2 -1
  103. esphome/components/ens160_base/__init__.py +12 -9
  104. esphome/components/esp32/__init__.py +60 -3
  105. esphome/components/esp32/core.cpp +11 -5
  106. esphome/components/esp32/gpio.cpp +86 -24
  107. esphome/components/esp32/gpio.py +15 -16
  108. esphome/components/esp32/gpio_esp32.py +1 -2
  109. esphome/components/esp32/gpio_esp32_c2.py +1 -1
  110. esphome/components/esp32/gpio_esp32_c3.py +1 -1
  111. esphome/components/esp32/gpio_esp32_c6.py +1 -1
  112. esphome/components/esp32/gpio_esp32_h2.py +1 -1
  113. esphome/components/esp32_ble/ble.cpp +1 -0
  114. esphome/components/esp32_ble/ble.h +5 -3
  115. esphome/components/esp32_ble/ble_advertising.cpp +2 -1
  116. esphome/components/esp32_ble/ble_advertising.h +1 -0
  117. esphome/components/esp32_ble_server/__init__.py +3 -0
  118. esphome/components/esp32_ble_tracker/__init__.py +7 -1
  119. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +192 -118
  120. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +29 -3
  121. esphome/components/esp32_camera/__init__.py +1 -1
  122. esphome/components/esp32_camera/esp32_camera.cpp +2 -10
  123. esphome/components/esp32_camera/esp32_camera.h +1 -1
  124. esphome/components/esp32_can/esp32_can.cpp +1 -1
  125. esphome/components/esp32_improv/esp32_improv_component.cpp +1 -1
  126. esphome/components/esp32_rmt_led_strip/led_strip.cpp +1 -1
  127. esphome/components/esp32_rmt_led_strip/led_strip.h +7 -5
  128. esphome/components/esp32_rmt_led_strip/light.py +9 -1
  129. esphome/components/esp32_touch/esp32_touch.cpp +1 -1
  130. esphome/components/esp8266/gpio.cpp +69 -8
  131. esphome/components/ethernet/ethernet_component.cpp +1 -1
  132. esphome/components/event/__init__.py +13 -10
  133. esphome/components/factory_reset/switch/__init__.py +7 -21
  134. esphome/components/fan/__init__.py +52 -5
  135. esphome/components/fastled_base/__init__.py +1 -4
  136. esphome/components/fastled_base/fastled_light.cpp +1 -1
  137. esphome/components/feedback/cover.py +38 -33
  138. esphome/components/feedback/feedback_cover.cpp +2 -1
  139. esphome/components/fujitsu_general/climate.py +2 -9
  140. esphome/components/gcja5/gcja5.cpp +2 -1
  141. esphome/components/gpio/one_wire/gpio_one_wire.cpp +45 -43
  142. esphome/components/gpio/one_wire/gpio_one_wire.h +2 -1
  143. esphome/components/gpio_expander/cached_gpio.h +22 -7
  144. esphome/components/gps/__init__.py +47 -17
  145. esphome/components/gps/gps.cpp +42 -23
  146. esphome/components/gps/gps.h +17 -13
  147. esphome/components/graph/__init__.py +1 -2
  148. esphome/components/gree/climate.py +4 -6
  149. esphome/components/gree/gree.cpp +16 -2
  150. esphome/components/gree/gree.h +2 -2
  151. esphome/components/growatt_solar/growatt_solar.cpp +2 -1
  152. esphome/components/haier/climate.py +37 -34
  153. esphome/components/hbridge/fan/__init__.py +19 -17
  154. esphome/components/he60r/cover.py +4 -5
  155. esphome/components/heatpumpir/climate.py +3 -6
  156. esphome/components/hitachi_ac344/climate.py +2 -9
  157. esphome/components/hitachi_ac424/climate.py +2 -9
  158. esphome/components/hm3301/hm3301.h +1 -1
  159. esphome/components/hte501/sensor.py +6 -6
  160. esphome/components/http_request/__init__.py +39 -6
  161. esphome/components/http_request/http_request.cpp +20 -0
  162. esphome/components/http_request/http_request.h +57 -15
  163. esphome/components/http_request/http_request_arduino.cpp +22 -6
  164. esphome/components/http_request/http_request_arduino.h +4 -3
  165. esphome/components/http_request/http_request_host.cpp +141 -0
  166. esphome/components/http_request/http_request_host.h +37 -0
  167. esphome/components/http_request/http_request_idf.cpp +35 -3
  168. esphome/components/http_request/http_request_idf.h +10 -3
  169. esphome/components/http_request/httplib.h +9691 -0
  170. esphome/components/http_request/update/__init__.py +11 -8
  171. esphome/components/hyt271/sensor.py +6 -6
  172. esphome/components/i2c/i2c.h +4 -0
  173. esphome/components/i2c/i2c_bus_esp_idf.cpp +1 -1
  174. esphome/components/i2s_audio/__init__.py +131 -22
  175. esphome/components/i2s_audio/i2s_audio.h +44 -4
  176. esphome/components/i2s_audio/media_player/__init__.py +19 -9
  177. esphome/components/i2s_audio/microphone/__init__.py +63 -5
  178. esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +351 -61
  179. esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +40 -6
  180. esphome/components/i2s_audio/speaker/__init__.py +31 -5
  181. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +155 -19
  182. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +17 -4
  183. esphome/components/ili9xxx/ili9xxx_init.h +1 -1
  184. esphome/components/image/__init__.py +37 -17
  185. esphome/components/image/image.cpp +25 -8
  186. esphome/components/internal_temperature/internal_temperature.cpp +6 -4
  187. esphome/components/key_collector/__init__.py +35 -0
  188. esphome/components/key_collector/key_collector.cpp +8 -0
  189. esphome/components/key_collector/key_collector.h +10 -0
  190. esphome/components/kuntze/kuntze.cpp +2 -1
  191. esphome/components/ld2410/ld2410.h +1 -1
  192. esphome/components/ld2450/ld2450.h +1 -1
  193. esphome/components/light/__init__.py +57 -0
  194. esphome/components/lock/__init__.py +51 -4
  195. esphome/components/lock/automation.h +2 -13
  196. esphome/components/logger/__init__.py +22 -0
  197. esphome/components/logger/logger.cpp +154 -103
  198. esphome/components/logger/logger.h +211 -36
  199. esphome/components/logger/task_log_buffer.cpp +138 -0
  200. esphome/components/logger/task_log_buffer.h +69 -0
  201. esphome/components/lvgl/__init__.py +13 -5
  202. esphome/components/lvgl/automation.py +50 -1
  203. esphome/components/lvgl/defines.py +0 -1
  204. esphome/components/lvgl/lvgl_esphome.cpp +5 -1
  205. esphome/components/lvgl/text/__init__.py +1 -2
  206. esphome/components/mapping/__init__.py +134 -0
  207. esphome/components/matrix_keypad/matrix_keypad.cpp +2 -1
  208. esphome/components/max7219digit/max7219digit.cpp +28 -27
  209. esphome/components/mdns/__init__.py +11 -5
  210. esphome/components/mdns/mdns_component.cpp +11 -5
  211. esphome/components/mdns/mdns_component.h +3 -2
  212. esphome/components/mdns/mdns_esp32.cpp +4 -3
  213. esphome/components/mdns/mdns_esp8266.cpp +4 -2
  214. esphome/components/mdns/mdns_libretiny.cpp +4 -2
  215. esphome/components/mdns/mdns_rp2040.cpp +4 -2
  216. esphome/components/media_player/__init__.py +33 -1
  217. esphome/components/mhz19/sensor.py +11 -7
  218. esphome/components/micro_wake_word/__init__.py +99 -31
  219. esphome/components/micro_wake_word/automation.h +54 -0
  220. esphome/components/micro_wake_word/micro_wake_word.cpp +331 -319
  221. esphome/components/micro_wake_word/micro_wake_word.h +58 -105
  222. esphome/components/micro_wake_word/preprocessor_settings.h +19 -2
  223. esphome/components/micro_wake_word/streaming_model.cpp +158 -41
  224. esphome/components/micro_wake_word/streaming_model.h +85 -13
  225. esphome/components/microphone/__init__.py +139 -9
  226. esphome/components/microphone/automation.h +14 -2
  227. esphome/components/microphone/microphone.cpp +21 -0
  228. esphome/components/microphone/microphone.h +14 -5
  229. esphome/components/microphone/microphone_source.cpp +95 -0
  230. esphome/components/microphone/microphone_source.h +80 -0
  231. esphome/components/mics_4514/sensor.py +25 -14
  232. esphome/components/midea/climate.py +3 -4
  233. esphome/components/midea_ir/climate.py +3 -5
  234. esphome/components/mipi_spi/__init__.py +15 -0
  235. esphome/components/mipi_spi/display.py +474 -0
  236. esphome/components/mipi_spi/mipi_spi.cpp +481 -0
  237. esphome/components/mipi_spi/mipi_spi.h +171 -0
  238. esphome/components/mipi_spi/models/__init__.py +65 -0
  239. esphome/components/mipi_spi/models/amoled.py +72 -0
  240. esphome/components/mipi_spi/models/commands.py +82 -0
  241. esphome/components/mipi_spi/models/cyd.py +10 -0
  242. esphome/components/mipi_spi/models/ili.py +749 -0
  243. esphome/components/mipi_spi/models/jc.py +260 -0
  244. esphome/components/mipi_spi/models/lanbon.py +15 -0
  245. esphome/components/mipi_spi/models/lilygo.py +60 -0
  246. esphome/components/mipi_spi/models/waveshare.py +139 -0
  247. esphome/components/mitsubishi/climate.py +2 -5
  248. esphome/components/mitsubishi/mitsubishi.cpp +9 -9
  249. esphome/components/mixer/speaker/mixer_speaker.cpp +12 -22
  250. esphome/components/mixer/speaker/mixer_speaker.h +1 -3
  251. esphome/components/mlx90393/sensor.py +5 -0
  252. esphome/components/mlx90393/sensor_mlx90393.cpp +195 -13
  253. esphome/components/mlx90393/sensor_mlx90393.h +21 -4
  254. esphome/components/modbus/modbus.cpp +2 -1
  255. esphome/components/mqtt/__init__.py +1 -1
  256. esphome/components/mqtt/mqtt_client.cpp +6 -2
  257. esphome/components/mqtt/mqtt_const.h +4 -0
  258. esphome/components/mqtt/mqtt_fan.cpp +39 -0
  259. esphome/components/mqtt/mqtt_fan.h +2 -0
  260. esphome/components/ms5611/sensor.py +6 -6
  261. esphome/components/ms8607/sensor.py +3 -3
  262. esphome/components/network/__init__.py +1 -1
  263. esphome/components/nextion/base_component.py +17 -16
  264. esphome/components/nextion/display.py +11 -2
  265. esphome/components/nextion/nextion.cpp +39 -1
  266. esphome/components/nextion/nextion.h +50 -0
  267. esphome/components/noblex/climate.py +2 -9
  268. esphome/components/number/__init__.py +12 -9
  269. esphome/components/one_wire/one_wire_bus.cpp +14 -10
  270. esphome/components/one_wire/one_wire_bus.h +14 -8
  271. esphome/components/online_image/bmp_image.cpp +48 -11
  272. esphome/components/online_image/bmp_image.h +2 -0
  273. esphome/components/opentherm/binary_sensor/__init__.py +2 -4
  274. esphome/components/opentherm/number/__init__.py +11 -20
  275. esphome/components/opentherm/sensor/__init__.py +3 -3
  276. esphome/components/opentherm/switch/__init__.py +3 -5
  277. esphome/components/output/lock/__init__.py +11 -9
  278. esphome/components/packages/__init__.py +33 -31
  279. esphome/components/packet_transport/__init__.py +201 -0
  280. esphome/components/packet_transport/binary_sensor.py +19 -0
  281. esphome/components/packet_transport/packet_transport.cpp +534 -0
  282. esphome/components/packet_transport/packet_transport.h +154 -0
  283. esphome/components/packet_transport/sensor.py +19 -0
  284. esphome/components/pca9685/pca9685_output.cpp +2 -1
  285. esphome/components/pid/climate.py +2 -4
  286. esphome/components/pm2005/__init__.py +1 -0
  287. esphome/components/pm2005/pm2005.cpp +123 -0
  288. esphome/components/pm2005/pm2005.h +46 -0
  289. esphome/components/pm2005/sensor.py +86 -0
  290. esphome/components/pmsa003i/pmsa003i.cpp +43 -16
  291. esphome/components/pmsa003i/pmsa003i.h +25 -25
  292. esphome/components/pmsx003/pmsx003.cpp +195 -230
  293. esphome/components/pmsx003/pmsx003.h +51 -33
  294. esphome/components/pmsx003/sensor.py +21 -11
  295. esphome/components/pn7150/pn7150.h +2 -2
  296. esphome/components/pn7160/pn7160.h +2 -2
  297. esphome/components/prometheus/prometheus_handler.cpp +174 -0
  298. esphome/components/prometheus/prometheus_handler.h +17 -0
  299. esphome/components/psram/__init__.py +7 -5
  300. esphome/components/pulse_meter/pulse_meter_sensor.cpp +32 -12
  301. esphome/components/pulse_meter/pulse_meter_sensor.h +5 -5
  302. esphome/components/pzem004t/pzem004t.cpp +2 -1
  303. esphome/components/qspi_dbi/__init__.py +0 -1
  304. esphome/components/qspi_dbi/display.py +2 -1
  305. esphome/components/qspi_dbi/models.py +1 -2
  306. esphome/components/remote_base/__init__.py +91 -0
  307. esphome/components/remote_base/beo4_protocol.cpp +153 -0
  308. esphome/components/remote_base/beo4_protocol.h +43 -0
  309. esphome/components/remote_base/gobox_protocol.cpp +131 -0
  310. esphome/components/remote_base/gobox_protocol.h +54 -0
  311. esphome/components/remote_receiver/remote_receiver_esp32.cpp +16 -9
  312. esphome/components/resampler/speaker/resampler_speaker.cpp +12 -10
  313. esphome/components/resampler/speaker/resampler_speaker.h +1 -1
  314. esphome/components/rf_bridge/rf_bridge.cpp +2 -1
  315. esphome/components/scd30/sensor.py +2 -3
  316. esphome/components/scd4x/sensor.py +4 -5
  317. esphome/components/sdp3x/sensor.py +2 -1
  318. esphome/components/sds011/sds011.cpp +2 -1
  319. esphome/components/select/__init__.py +19 -20
  320. esphome/components/sen5x/sen5x.cpp +55 -36
  321. esphome/components/sen5x/sensor.py +1 -1
  322. esphome/components/senseair/sensor.py +3 -3
  323. esphome/components/sensor/__init__.py +158 -14
  324. esphome/components/sensor/filter.cpp +23 -0
  325. esphome/components/sensor/filter.h +22 -0
  326. esphome/components/sgp30/sensor.py +14 -16
  327. esphome/components/sgp4x/sensor.py +1 -1
  328. esphome/components/sht4x/sht4x.cpp +43 -22
  329. esphome/components/sht4x/sht4x.h +1 -1
  330. esphome/components/shtcx/sensor.py +6 -6
  331. esphome/components/slow_pwm/slow_pwm_output.cpp +2 -1
  332. esphome/components/sml/text_sensor/__init__.py +4 -6
  333. esphome/components/sound_level/__init__.py +0 -0
  334. esphome/components/sound_level/sensor.py +97 -0
  335. esphome/components/sound_level/sound_level.cpp +194 -0
  336. esphome/components/sound_level/sound_level.h +73 -0
  337. esphome/components/speaker/media_player/__init__.py +4 -8
  338. esphome/components/speaker/media_player/speaker_media_player.cpp +0 -18
  339. esphome/components/speaker/media_player/speaker_media_player.h +0 -11
  340. esphome/components/speaker/speaker.h +4 -7
  341. esphome/components/speed/fan/__init__.py +17 -16
  342. esphome/components/spi/spi.h +11 -1
  343. esphome/components/sprinkler/__init__.py +18 -19
  344. esphome/components/sprinkler/sprinkler.cpp +6 -5
  345. esphome/components/switch/__init__.py +32 -42
  346. esphome/components/syslog/__init__.py +41 -0
  347. esphome/components/syslog/esphome_syslog.cpp +49 -0
  348. esphome/components/syslog/esphome_syslog.h +27 -0
  349. esphome/components/t6615/sensor.py +3 -3
  350. esphome/components/t6615/t6615.cpp +2 -1
  351. esphome/components/tca9555/tca9555.cpp +11 -6
  352. esphome/components/tcl112/climate.py +2 -9
  353. esphome/components/template/alarm_control_panel/__init__.py +7 -6
  354. esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +21 -17
  355. esphome/components/template/alarm_control_panel/template_alarm_control_panel.h +2 -1
  356. esphome/components/template/cover/__init__.py +27 -21
  357. esphome/components/template/fan/__init__.py +14 -12
  358. esphome/components/template/lock/__init__.py +20 -25
  359. esphome/components/template/lock/automation.h +18 -0
  360. esphome/components/template/text/__init__.py +4 -3
  361. esphome/components/template/valve/__init__.py +32 -21
  362. esphome/components/template/valve/automation.h +24 -0
  363. esphome/components/text/__init__.py +32 -1
  364. esphome/components/text_sensor/__init__.py +24 -29
  365. esphome/components/thermostat/climate.py +5 -5
  366. esphome/components/time_based/cover.py +17 -16
  367. esphome/components/time_based/time_based_cover.cpp +2 -1
  368. esphome/components/tm1638/switch/__init__.py +10 -7
  369. esphome/components/tormatic/cover.py +4 -5
  370. esphome/components/toshiba/climate.py +3 -5
  371. esphome/components/touchscreen/touchscreen.cpp +3 -1
  372. esphome/components/tuya/climate/__init__.py +5 -6
  373. esphome/components/tuya/cover/__init__.py +6 -11
  374. esphome/components/tuya/select/__init__.py +15 -5
  375. esphome/components/tuya/select/tuya_select.cpp +6 -1
  376. esphome/components/tuya/select/tuya_select.h +5 -1
  377. esphome/components/uart/packet_transport/__init__.py +20 -0
  378. esphome/components/uart/packet_transport/uart_transport.cpp +88 -0
  379. esphome/components/uart/packet_transport/uart_transport.h +41 -0
  380. esphome/components/uart/switch/uart_switch.cpp +2 -1
  381. esphome/components/udp/__init__.py +126 -128
  382. esphome/components/udp/automation.h +40 -0
  383. esphome/components/udp/binary_sensor.py +3 -25
  384. esphome/components/udp/packet_transport/__init__.py +29 -0
  385. esphome/components/udp/packet_transport/udp_transport.cpp +36 -0
  386. esphome/components/udp/packet_transport/udp_transport.h +28 -0
  387. esphome/components/udp/sensor.py +3 -25
  388. esphome/components/udp/udp_component.cpp +26 -470
  389. esphome/components/udp/udp_component.h +21 -128
  390. esphome/components/update/__init__.py +31 -1
  391. esphome/components/uponor_smatrix/climate/__init__.py +4 -9
  392. esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp +2 -1
  393. esphome/components/uponor_smatrix/uponor_smatrix.cpp +2 -1
  394. esphome/components/uptime/text_sensor/__init__.py +47 -7
  395. esphome/components/uptime/text_sensor/uptime_text_sensor.cpp +12 -7
  396. esphome/components/uptime/text_sensor/uptime_text_sensor.h +19 -0
  397. esphome/components/valve/__init__.py +34 -3
  398. esphome/components/valve/automation.h +1 -19
  399. esphome/components/vl53l0x/sensor.py +11 -0
  400. esphome/components/vl53l0x/vl53l0x_sensor.cpp +5 -1
  401. esphome/components/vl53l0x/vl53l0x_sensor.h +2 -1
  402. esphome/components/voice_assistant/__init__.py +36 -10
  403. esphome/components/voice_assistant/voice_assistant.cpp +170 -144
  404. esphome/components/voice_assistant/voice_assistant.h +26 -25
  405. esphome/components/waveshare_epaper/display.py +6 -0
  406. esphome/components/waveshare_epaper/waveshare_epaper.cpp +439 -37
  407. esphome/components/waveshare_epaper/waveshare_epaper.h +60 -11
  408. esphome/components/weikai/weikai.cpp +0 -52
  409. esphome/components/whirlpool/climate.py +3 -5
  410. esphome/components/whynter/climate.py +3 -5
  411. esphome/components/xpt2046/touchscreen/xpt2046.cpp +1 -1
  412. esphome/components/yashima/climate.py +6 -6
  413. esphome/components/zhlt01/climate.py +2 -7
  414. esphome/config.py +13 -13
  415. esphome/config_validation.py +38 -58
  416. esphome/const.py +15 -1
  417. esphome/core/__init__.py +2 -0
  418. esphome/core/application.cpp +27 -10
  419. esphome/core/application.h +9 -1
  420. esphome/core/automation.h +4 -3
  421. esphome/core/component.cpp +28 -7
  422. esphome/core/component.h +10 -1
  423. esphome/core/defines.h +23 -17
  424. esphome/core/doxygen.h +13 -0
  425. esphome/core/macros.h +4 -0
  426. esphome/core/scheduler.cpp +7 -1
  427. esphome/cpp_generator.py +6 -2
  428. esphome/dashboard/web_server.py +3 -3
  429. esphome/helpers.py +39 -0
  430. esphome/loader.py +4 -0
  431. esphome/log.py +15 -19
  432. esphome/mqtt.py +23 -10
  433. esphome/platformio_api.py +1 -1
  434. esphome/schema_extractors.py +0 -1
  435. esphome/voluptuous_schema.py +3 -1
  436. esphome/vscode.py +15 -0
  437. esphome/wizard.py +47 -37
  438. esphome/zeroconf.py +7 -3
  439. {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/METADATA +10 -11
  440. {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/RECORD +444 -383
  441. {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/WHEEL +1 -1
  442. {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/entry_points.txt +0 -0
  443. {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/licenses/LICENSE +0 -0
  444. {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/top_level.txt +0 -0
@@ -129,7 +129,7 @@ enum PeriodicDataStructure : uint8_t {
129
129
  LIGHT_SENSOR = 37,
130
130
  OUT_PIN_SENSOR = 38,
131
131
  };
132
- enum PeriodicDataValue : uint8_t { HEAD = 0XAA, END = 0x55, CHECK = 0x00 };
132
+ enum PeriodicDataValue : uint8_t { HEAD = 0xAA, END = 0x55, CHECK = 0x00 };
133
133
 
134
134
  enum AckDataStructure : uint8_t { COMMAND = 6, COMMAND_STATUS = 7 };
135
135
 
@@ -105,7 +105,7 @@ enum PeriodicDataStructure : uint8_t {
105
105
  TARGET_RESOLUTION = 10,
106
106
  };
107
107
 
108
- enum PeriodicDataValue : uint8_t { HEAD = 0XAA, END = 0x55, CHECK = 0x00 };
108
+ enum PeriodicDataValue : uint8_t { HEAD = 0xAA, END = 0x55, CHECK = 0x00 };
109
109
 
110
110
  enum AckDataStructure : uint8_t { COMMAND = 6, COMMAND_STATUS = 7 };
111
111
 
@@ -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,13 @@ async def to_code(config):
238
253
  baud_rate,
239
254
  config[CONF_TX_BUFFER_SIZE],
240
255
  )
256
+ if CORE.is_esp32:
257
+ cg.add(log.create_pthread_key())
258
+ task_log_buffer_size = config[CONF_TASK_LOG_BUFFER_SIZE]
259
+ if task_log_buffer_size > 0:
260
+ cg.add_define("USE_ESPHOME_TASK_LOG_BUFFER")
261
+ cg.add(log.init_log_buffer(task_log_buffer_size))
262
+
241
263
  cg.add(log.set_log_level(initial_level))
242
264
  if CONF_HARDWARE_UART in config:
243
265
  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,132 @@ 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-task platform with task-specific tracking)
18
+ // Main task always uses direct buffer access for console output and callbacks
19
+ //
20
+ // For non-main tasks:
21
+ // - WITH task log buffer: Prefer sending to ring buffer for async processing
22
+ // - Avoids allocating stack memory for console output in normal operation
23
+ // - Prevents console corruption from concurrent writes by multiple tasks
24
+ // - Messages are serialized through main loop for proper console output
25
+ // - Fallback to emergency console logging only if ring buffer is full
26
+ // - WITHOUT task log buffer: Only emergency console output, no callbacks
27
+ void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *format, va_list args) { // NOLINT
28
+ if (level > this->level_for(tag))
29
+ return;
30
+
43
31
  TaskHandle_t current_task = xTaskGetCurrentTaskHandle();
44
- #else
45
- void *current_task = nullptr;
46
- #endif
47
- 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);
32
+ bool is_main_task = (current_task == main_task_);
33
+
34
+ // Check and set recursion guard - uses pthread TLS for per-task state
35
+ if (this->check_and_set_task_log_recursion_(is_main_task)) {
36
+ return; // Recursion detected
58
37
  }
59
- }
60
38
 
39
+ // Main task uses the shared buffer for efficiency
40
+ if (is_main_task) {
41
+ this->log_message_to_buffer_and_send_(level, tag, line, format, args);
42
+ this->reset_task_log_recursion_(is_main_task);
43
+ return;
44
+ }
45
+
46
+ bool message_sent = false;
47
+ #ifdef USE_ESPHOME_TASK_LOG_BUFFER
48
+ // For non-main tasks, queue the message for callbacks - but only if we have any callbacks registered
49
+ message_sent = this->log_buffer_->send_message_thread_safe(static_cast<uint8_t>(level), tag,
50
+ static_cast<uint16_t>(line), current_task, format, args);
51
+ #endif // USE_ESPHOME_TASK_LOG_BUFFER
52
+
53
+ // Emergency console logging for non-main tasks when ring buffer is full or disabled
54
+ // This is a fallback mechanism to ensure critical log messages are visible
55
+ // Note: This may cause interleaved/corrupted console output if multiple tasks
56
+ // log simultaneously, but it's better than losing important messages entirely
57
+ if (!message_sent && this->baud_rate_ > 0) { // If logging is enabled, write to console
58
+ // Maximum size for console log messages (includes null terminator)
59
+ static const size_t MAX_CONSOLE_LOG_MSG_SIZE = 144;
60
+ char console_buffer[MAX_CONSOLE_LOG_MSG_SIZE]; // MUST be stack allocated for thread safety
61
+ int buffer_at = 0; // Initialize buffer position
62
+ this->format_log_to_buffer_with_terminator_(level, tag, line, format, args, console_buffer, &buffer_at,
63
+ MAX_CONSOLE_LOG_MSG_SIZE);
64
+ this->write_msg_(console_buffer);
65
+ }
66
+
67
+ // Reset the recursion guard for this task
68
+ this->reset_task_log_recursion_(is_main_task);
69
+ }
70
+ #else
71
+ // Implementation for all other platforms
61
72
  void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *format, va_list args) { // NOLINT
62
- if (level > this->level_for(tag) || recursion_guard_)
73
+ if (level > this->level_for(tag) || global_recursion_guard_)
63
74
  return;
64
75
 
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);
71
- recursion_guard_ = false;
76
+ global_recursion_guard_ = true;
77
+
78
+ // Format and send to both console and callbacks
79
+ this->log_message_to_buffer_and_send_(level, tag, line, format, args);
80
+
81
+ global_recursion_guard_ = false;
72
82
  }
83
+ #endif // !USE_ESP32
84
+
73
85
  #ifdef USE_STORE_LOG_STR_IN_FLASH
86
+ // Implementation for ESP8266 with flash string support.
87
+ // Note: USE_STORE_LOG_STR_IN_FLASH is only defined for ESP8266.
74
88
  void Logger::log_vprintf_(int level, const char *tag, int line, const __FlashStringHelper *format,
75
89
  va_list args) { // NOLINT
76
- if (level > this->level_for(tag) || recursion_guard_)
90
+ if (level > this->level_for(tag) || global_recursion_guard_)
77
91
  return;
78
92
 
79
- recursion_guard_ = true;
80
- this->reset_buffer_();
81
- // copy format string
93
+ global_recursion_guard_ = true;
94
+ this->tx_buffer_at_ = 0;
95
+
96
+ // Copy format string from progmem
82
97
  auto *format_pgm_p = reinterpret_cast<const uint8_t *>(format);
83
- size_t len = 0;
84
98
  char ch = '.';
85
- while (!this->is_buffer_full_() && ch != '\0') {
99
+ while (this->tx_buffer_at_ < this->tx_buffer_size_ && ch != '\0') {
86
100
  this->tx_buffer_[this->tx_buffer_at_++] = ch = (char) progmem_read_byte(format_pgm_p++);
87
101
  }
88
- // Buffer full form copying format
89
- if (this->is_buffer_full_())
102
+
103
+ // Buffer full from copying format
104
+ if (this->tx_buffer_at_ >= this->tx_buffer_size_) {
105
+ global_recursion_guard_ = false; // Make sure to reset the recursion guard before returning
90
106
  return;
107
+ }
108
+
109
+ // Save the offset before calling format_log_to_buffer_with_terminator_
110
+ // since it will increment tx_buffer_at_ to the end of the formatted string
111
+ uint32_t msg_start = this->tx_buffer_at_;
112
+ this->format_log_to_buffer_with_terminator_(level, tag, line, this->tx_buffer_, args, this->tx_buffer_,
113
+ &this->tx_buffer_at_, this->tx_buffer_size_);
91
114
 
92
- // length of format string, includes null terminator
93
- uint32_t offset = this->tx_buffer_at_;
115
+ // Write to console and send callback starting at the msg_start
116
+ if (this->baud_rate_ > 0) {
117
+ this->write_msg_(this->tx_buffer_ + msg_start);
118
+ }
119
+ this->call_log_callbacks_(level, tag, this->tx_buffer_ + msg_start);
94
120
 
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
- recursion_guard_ = false;
121
+ global_recursion_guard_ = false;
101
122
  }
102
- #endif
123
+ #endif // USE_STORE_LOG_STR_IN_FLASH
103
124
 
104
- int HOT Logger::level_for(const char *tag) {
105
- if (this->log_levels_.count(tag) != 0)
106
- return this->log_levels_[tag];
125
+ inline int Logger::level_for(const char *tag) {
126
+ auto it = this->log_levels_.find(tag);
127
+ if (it != this->log_levels_.end())
128
+ return it->second;
107
129
  return this->current_level_;
108
130
  }
109
131
 
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
-
132
+ void HOT Logger::call_log_callbacks_(int level, const char *tag, const char *msg) {
124
133
  #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
134
+ // Suppress network-logging if memory constrained
135
+ // In some configurations (eg BLE enabled) there may be some transient
127
136
  // memory exhaustion, and trying to log when OOM can lead to a crash. Skipping
128
137
  // here usually allows the stack to recover instead.
129
138
  // See issue #1234 for analysis.
130
139
  if (xPortGetFreeHeapSize() < 2048)
131
140
  return;
132
141
  #endif
133
-
134
142
  this->log_callback_.call(level, tag, msg);
135
143
  }
136
144
 
@@ -141,21 +149,60 @@ Logger::Logger(uint32_t baud_rate, size_t tx_buffer_size) : baud_rate_(baud_rate
141
149
  this->main_task_ = xTaskGetCurrentTaskHandle();
142
150
  #endif
143
151
  }
152
+ #ifdef USE_ESPHOME_TASK_LOG_BUFFER
153
+ void Logger::init_log_buffer(size_t total_buffer_size) {
154
+ this->log_buffer_ = esphome::make_unique<logger::TaskLogBuffer>(total_buffer_size);
155
+ }
156
+ #endif
144
157
 
145
- #ifdef USE_LOGGER_USB_CDC
158
+ #if defined(USE_LOGGER_USB_CDC) || defined(USE_ESP32)
146
159
  void Logger::loop() {
147
- #ifdef USE_ARDUINO
148
- if (this->uart_ != UART_SELECTION_USB_CDC) {
149
- return;
160
+ #if defined(USE_LOGGER_USB_CDC) && defined(USE_ARDUINO)
161
+ if (this->uart_ == UART_SELECTION_USB_CDC) {
162
+ static bool opened = false;
163
+ if (opened == Serial) {
164
+ return;
165
+ }
166
+ if (false == opened) {
167
+ App.schedule_dump_config();
168
+ }
169
+ opened = !opened;
150
170
  }
151
- static bool opened = false;
152
- if (opened == Serial) {
153
- return;
154
- }
155
- if (false == opened) {
156
- App.schedule_dump_config();
171
+ #endif
172
+
173
+ #ifdef USE_ESPHOME_TASK_LOG_BUFFER
174
+ // Process any buffered messages when available
175
+ if (this->log_buffer_->has_messages()) {
176
+ logger::TaskLogBuffer::LogMessage *message;
177
+ const char *text;
178
+ void *received_token;
179
+
180
+ // Process messages from the buffer
181
+ while (this->log_buffer_->borrow_message_main_loop(&message, &text, &received_token)) {
182
+ this->tx_buffer_at_ = 0;
183
+ // Use the thread name that was stored when the message was created
184
+ // This avoids potential crashes if the task no longer exists
185
+ const char *thread_name = message->thread_name[0] != '\0' ? message->thread_name : nullptr;
186
+ this->write_header_to_buffer_(message->level, message->tag, message->line, thread_name, this->tx_buffer_,
187
+ &this->tx_buffer_at_, this->tx_buffer_size_);
188
+ this->write_body_to_buffer_(text, message->text_length, this->tx_buffer_, &this->tx_buffer_at_,
189
+ this->tx_buffer_size_);
190
+ this->write_footer_to_buffer_(this->tx_buffer_, &this->tx_buffer_at_, this->tx_buffer_size_);
191
+ this->tx_buffer_[this->tx_buffer_at_] = '\0';
192
+ this->call_log_callbacks_(message->level, message->tag, this->tx_buffer_);
193
+ // At this point all the data we need from message has been transferred to the tx_buffer
194
+ // so we can release the message to allow other tasks to use it as soon as possible.
195
+ this->log_buffer_->release_message_main_loop(received_token);
196
+
197
+ // Write to console from the main loop to prevent corruption from concurrent writes
198
+ // This ensures all log messages appear on the console in a clean, serialized manner
199
+ // Note: Messages may appear slightly out of order due to async processing, but
200
+ // this is preferred over corrupted/interleaved console output
201
+ if (this->baud_rate_ > 0) {
202
+ this->write_msg_(this->tx_buffer_);
203
+ }
204
+ }
157
205
  }
158
- opened = !opened;
159
206
  #endif
160
207
  }
161
208
  #endif
@@ -171,7 +218,7 @@ void Logger::add_on_log_callback(std::function<void(int, const char *, const cha
171
218
  this->log_callback_.add(std::move(callback));
172
219
  }
173
220
  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"};
221
+ static const char *const LOG_LEVELS[] = {"NONE", "ERROR", "WARN", "INFO", "CONFIG", "DEBUG", "VERBOSE", "VERY_VERBOSE"};
175
222
 
176
223
  void Logger::dump_config() {
177
224
  ESP_LOGCONFIG(TAG, "Logger:");
@@ -181,12 +228,16 @@ void Logger::dump_config() {
181
228
  ESP_LOGCONFIG(TAG, " Log Baud Rate: %" PRIu32, this->baud_rate_);
182
229
  ESP_LOGCONFIG(TAG, " Hardware UART: %s", get_uart_selection_());
183
230
  #endif
231
+ #ifdef USE_ESPHOME_TASK_LOG_BUFFER
232
+ if (this->log_buffer_) {
233
+ ESP_LOGCONFIG(TAG, " Task Log Buffer Size: %u", this->log_buffer_->size());
234
+ }
235
+ #endif
184
236
 
185
237
  for (auto &it : this->log_levels_) {
186
238
  ESP_LOGCONFIG(TAG, " Level for '%s': %s", it.first.c_str(), LOG_LEVELS[it.second]);
187
239
  }
188
240
  }
189
- void Logger::write_footer_() { this->write_to_buffer_(ESPHOME_LOG_RESET_COLOR, strlen(ESPHOME_LOG_RESET_COLOR)); }
190
241
 
191
242
  void Logger::set_log_level(int level) {
192
243
  if (level > ESPHOME_LOG_LEVEL) {