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
@@ -2,12 +2,19 @@
2
2
 
3
3
  #include <cstdarg>
4
4
  #include <map>
5
+ #ifdef USE_ESP32
6
+ #include <atomic>
7
+ #endif
5
8
  #include "esphome/core/automation.h"
6
9
  #include "esphome/core/component.h"
7
10
  #include "esphome/core/defines.h"
8
11
  #include "esphome/core/helpers.h"
9
12
  #include "esphome/core/log.h"
10
13
 
14
+ #ifdef USE_ESPHOME_TASK_LOG_BUFFER
15
+ #include "task_log_buffer.h"
16
+ #endif
17
+
11
18
  #ifdef USE_ARDUINO
12
19
  #if defined(USE_ESP8266) || defined(USE_ESP32)
13
20
  #include <HardwareSerial.h>
@@ -26,6 +33,29 @@ namespace esphome {
26
33
 
27
34
  namespace logger {
28
35
 
36
+ // Color and letter constants for log levels
37
+ static const char *const LOG_LEVEL_COLORS[] = {
38
+ "", // NONE
39
+ ESPHOME_LOG_BOLD(ESPHOME_LOG_COLOR_RED), // ERROR
40
+ ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_YELLOW), // WARNING
41
+ ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_GREEN), // INFO
42
+ ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_MAGENTA), // CONFIG
43
+ ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_CYAN), // DEBUG
44
+ ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_GRAY), // VERBOSE
45
+ ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_WHITE), // VERY_VERBOSE
46
+ };
47
+
48
+ static const char *const LOG_LEVEL_LETTERS[] = {
49
+ "", // NONE
50
+ "E", // ERROR
51
+ "W", // WARNING
52
+ "I", // INFO
53
+ "C", // CONFIG
54
+ "D", // DEBUG
55
+ "V", // VERBOSE
56
+ "VV", // VERY_VERBOSE
57
+ };
58
+
29
59
  #if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY)
30
60
  /** Enum for logging UART selection
31
61
  *
@@ -57,7 +87,10 @@ enum UARTSelection {
57
87
  class Logger : public Component {
58
88
  public:
59
89
  explicit Logger(uint32_t baud_rate, size_t tx_buffer_size);
60
- #ifdef USE_LOGGER_USB_CDC
90
+ #ifdef USE_ESPHOME_TASK_LOG_BUFFER
91
+ void init_log_buffer(size_t total_buffer_size);
92
+ #endif
93
+ #if defined(USE_LOGGER_USB_CDC) || defined(USE_ESP32)
61
94
  void loop() override;
62
95
  #endif
63
96
  /// Manually set the baud rate for serial, set to 0 to disable.
@@ -87,7 +120,7 @@ class Logger : public Component {
87
120
  void pre_setup();
88
121
  void dump_config() override;
89
122
 
90
- int level_for(const char *tag);
123
+ inline int level_for(const char *tag);
91
124
 
92
125
  /// Register a callback that will be called for every log message sent
93
126
  void add_on_log_callback(std::function<void(int, const char *, const char *)> &&callback);
@@ -103,46 +136,66 @@ class Logger : public Component {
103
136
  #endif
104
137
 
105
138
  protected:
106
- void write_header_(int level, const char *tag, int line);
107
- void write_footer_();
108
- void log_message_(int level, const char *tag, int offset = 0);
139
+ void call_log_callbacks_(int level, const char *tag, const char *msg);
109
140
  void write_msg_(const char *msg);
110
141
 
111
- inline bool is_buffer_full_() const { return this->tx_buffer_at_ >= this->tx_buffer_size_; }
112
- inline int buffer_remaining_capacity_() const { return this->tx_buffer_size_ - this->tx_buffer_at_; }
113
- inline void reset_buffer_() { this->tx_buffer_at_ = 0; }
114
- inline void set_null_terminator_() {
115
- // does not increment buffer_at
116
- this->tx_buffer_[this->tx_buffer_at_] = '\0';
117
- }
118
- inline void write_to_buffer_(char value) {
119
- if (!this->is_buffer_full_())
120
- this->tx_buffer_[this->tx_buffer_at_++] = value;
142
+ // Format a log message with printf-style arguments and write it to a buffer with header, footer, and null terminator
143
+ // It's the caller's responsibility to initialize buffer_at (typically to 0)
144
+ inline void HOT format_log_to_buffer_with_terminator_(int level, const char *tag, int line, const char *format,
145
+ va_list args, char *buffer, int *buffer_at, int buffer_size) {
146
+ #if defined(USE_ESP32) || defined(USE_LIBRETINY)
147
+ this->write_header_to_buffer_(level, tag, line, this->get_thread_name_(), buffer, buffer_at, buffer_size);
148
+ #else
149
+ this->write_header_to_buffer_(level, tag, line, nullptr, buffer, buffer_at, buffer_size);
150
+ #endif
151
+ this->format_body_to_buffer_(buffer, buffer_at, buffer_size, format, args);
152
+ this->write_footer_to_buffer_(buffer, buffer_at, buffer_size);
153
+
154
+ // Always ensure the buffer has a null terminator, even if we need to
155
+ // overwrite the last character of the actual content
156
+ if (*buffer_at >= buffer_size) {
157
+ buffer[buffer_size - 1] = '\0'; // Truncate and ensure null termination
158
+ } else {
159
+ buffer[*buffer_at] = '\0'; // Normal case, append null terminator
160
+ }
121
161
  }
122
- inline void write_to_buffer_(const char *value, int length) {
123
- for (int i = 0; i < length && !this->is_buffer_full_(); i++) {
124
- this->tx_buffer_[this->tx_buffer_at_++] = value[i];
162
+
163
+ // Helper to format and send a log message to both console and callbacks
164
+ inline void HOT log_message_to_buffer_and_send_(int level, const char *tag, int line, const char *format,
165
+ va_list args) {
166
+ // Format to tx_buffer and prepare for output
167
+ this->tx_buffer_at_ = 0; // Initialize buffer position
168
+ this->format_log_to_buffer_with_terminator_(level, tag, line, format, args, this->tx_buffer_, &this->tx_buffer_at_,
169
+ this->tx_buffer_size_);
170
+
171
+ if (this->baud_rate_ > 0) {
172
+ this->write_msg_(this->tx_buffer_); // If logging is enabled, write to console
125
173
  }
174
+ this->call_log_callbacks_(level, tag, this->tx_buffer_);
126
175
  }
127
- inline void vprintf_to_buffer_(const char *format, va_list args) {
128
- if (this->is_buffer_full_())
129
- return;
130
- int remaining = this->buffer_remaining_capacity_();
131
- int ret = vsnprintf(this->tx_buffer_ + this->tx_buffer_at_, remaining, format, args);
132
- if (ret < 0) {
133
- // Encoding error, do not increment buffer_at
176
+
177
+ // Write the body of the log message to the buffer
178
+ inline void write_body_to_buffer_(const char *value, size_t length, char *buffer, int *buffer_at, int buffer_size) {
179
+ // Calculate available space
180
+ const int available = buffer_size - *buffer_at;
181
+ if (available <= 0)
134
182
  return;
183
+
184
+ // Determine copy length (minimum of remaining capacity and string length)
185
+ const size_t copy_len = (length < static_cast<size_t>(available)) ? length : available;
186
+
187
+ // Copy the data
188
+ if (copy_len > 0) {
189
+ memcpy(buffer + *buffer_at, value, copy_len);
190
+ *buffer_at += copy_len;
135
191
  }
136
- if (ret >= remaining) {
137
- // output was too long, truncated
138
- ret = remaining;
139
- }
140
- this->tx_buffer_at_ += ret;
141
192
  }
142
- inline void printf_to_buffer_(const char *format, ...) {
193
+
194
+ // Format string to explicit buffer with varargs
195
+ inline void printf_to_buffer_(const char *format, char *buffer, int *buffer_at, int buffer_size, ...) {
143
196
  va_list arg;
144
- va_start(arg, format);
145
- this->vprintf_to_buffer_(format, arg);
197
+ va_start(arg, buffer_size);
198
+ this->format_body_to_buffer_(buffer, buffer_at, buffer_size, format, arg);
146
199
  va_end(arg);
147
200
  }
148
201
 
@@ -169,10 +222,82 @@ class Logger : public Component {
169
222
  std::map<std::string, int> log_levels_{};
170
223
  CallbackManager<void(int, const char *, const char *)> log_callback_{};
171
224
  int current_level_{ESPHOME_LOG_LEVEL_VERY_VERBOSE};
172
- /// Prevents recursive log calls, if true a log message is already being processed.
173
- bool recursion_guard_ = false;
225
+ #ifdef USE_ESP32
226
+ std::atomic<bool> recursion_guard_{false};
227
+ #ifdef USE_ESPHOME_TASK_LOG_BUFFER
228
+ std::unique_ptr<logger::TaskLogBuffer> log_buffer_; // Will be initialized with init_log_buffer
229
+ #endif
230
+ #else
231
+ bool recursion_guard_{false};
232
+ #endif
174
233
  void *main_task_ = nullptr;
175
234
  CallbackManager<void(int)> level_callback_{};
235
+
236
+ #if defined(USE_ESP32) || defined(USE_LIBRETINY)
237
+ const char *HOT get_thread_name_() {
238
+ TaskHandle_t current_task = xTaskGetCurrentTaskHandle();
239
+ if (current_task == main_task_) {
240
+ return nullptr; // Main task
241
+ } else {
242
+ #if defined(USE_ESP32)
243
+ return pcTaskGetName(current_task);
244
+ #elif defined(USE_LIBRETINY)
245
+ return pcTaskGetTaskName(current_task);
246
+ #endif
247
+ }
248
+ }
249
+ #endif
250
+
251
+ inline void HOT write_header_to_buffer_(int level, const char *tag, int line, const char *thread_name, char *buffer,
252
+ int *buffer_at, int buffer_size) {
253
+ // Format header
254
+ if (level < 0)
255
+ level = 0;
256
+ if (level > 7)
257
+ level = 7;
258
+
259
+ const char *color = esphome::logger::LOG_LEVEL_COLORS[level];
260
+ const char *letter = esphome::logger::LOG_LEVEL_LETTERS[level];
261
+
262
+ #if defined(USE_ESP32) || defined(USE_LIBRETINY)
263
+ if (thread_name != nullptr) {
264
+ // Non-main task with thread name
265
+ this->printf_to_buffer_("%s[%s][%s:%03u]%s[%s]%s: ", buffer, buffer_at, buffer_size, color, letter, tag, line,
266
+ ESPHOME_LOG_BOLD(ESPHOME_LOG_COLOR_RED), thread_name, color);
267
+ return;
268
+ }
269
+ #endif
270
+ // Main task or non ESP32/LibreTiny platform
271
+ this->printf_to_buffer_("%s[%s][%s:%03u]: ", buffer, buffer_at, buffer_size, color, letter, tag, line);
272
+ }
273
+
274
+ inline void HOT format_body_to_buffer_(char *buffer, int *buffer_at, int buffer_size, const char *format,
275
+ va_list args) {
276
+ // Get remaining capacity in the buffer
277
+ const int remaining = buffer_size - *buffer_at;
278
+ if (remaining <= 0)
279
+ return;
280
+
281
+ const int ret = vsnprintf(buffer + *buffer_at, remaining, format, args);
282
+
283
+ if (ret < 0) {
284
+ return; // Encoding error, do not increment buffer_at
285
+ }
286
+
287
+ // Update buffer_at with the formatted length (handle truncation)
288
+ int formatted_len = (ret >= remaining) ? remaining : ret;
289
+ *buffer_at += formatted_len;
290
+
291
+ // Remove all trailing newlines right after formatting
292
+ while (*buffer_at > 0 && buffer[*buffer_at - 1] == '\n') {
293
+ (*buffer_at)--;
294
+ }
295
+ }
296
+
297
+ inline void HOT write_footer_to_buffer_(char *buffer, int *buffer_at, int buffer_size) {
298
+ static const int RESET_COLOR_LEN = strlen(ESPHOME_LOG_RESET_COLOR);
299
+ this->write_body_to_buffer_(ESPHOME_LOG_RESET_COLOR, RESET_COLOR_LEN, buffer, buffer_at, buffer_size);
300
+ }
176
301
  };
177
302
  extern Logger *global_logger; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
178
303
 
@@ -0,0 +1,138 @@
1
+
2
+ #include "task_log_buffer.h"
3
+ #include "esphome/core/helpers.h"
4
+ #include "esphome/core/log.h"
5
+
6
+ #ifdef USE_ESPHOME_TASK_LOG_BUFFER
7
+
8
+ namespace esphome {
9
+ namespace logger {
10
+
11
+ TaskLogBuffer::TaskLogBuffer(size_t total_buffer_size) {
12
+ // Store the buffer size
13
+ this->size_ = total_buffer_size;
14
+ // Allocate memory for the ring buffer using ESPHome's RAM allocator
15
+ RAMAllocator<uint8_t> allocator;
16
+ this->storage_ = allocator.allocate(this->size_);
17
+ // Create a static ring buffer with RINGBUF_TYPE_NOSPLIT for message integrity
18
+ this->ring_buffer_ = xRingbufferCreateStatic(this->size_, RINGBUF_TYPE_NOSPLIT, this->storage_, &this->structure_);
19
+ }
20
+
21
+ TaskLogBuffer::~TaskLogBuffer() {
22
+ if (this->ring_buffer_ != nullptr) {
23
+ // Delete the ring buffer
24
+ vRingbufferDelete(this->ring_buffer_);
25
+ this->ring_buffer_ = nullptr;
26
+
27
+ // Free the allocated memory
28
+ RAMAllocator<uint8_t> allocator;
29
+ allocator.deallocate(this->storage_, this->size_);
30
+ this->storage_ = nullptr;
31
+ }
32
+ }
33
+
34
+ bool TaskLogBuffer::borrow_message_main_loop(LogMessage **message, const char **text, void **received_token) {
35
+ if (message == nullptr || text == nullptr || received_token == nullptr) {
36
+ return false;
37
+ }
38
+
39
+ size_t item_size = 0;
40
+ void *received_item = xRingbufferReceive(ring_buffer_, &item_size, 0);
41
+ if (received_item == nullptr) {
42
+ return false;
43
+ }
44
+
45
+ LogMessage *msg = static_cast<LogMessage *>(received_item);
46
+ *message = msg;
47
+ *text = msg->text_data();
48
+ *received_token = received_item;
49
+
50
+ return true;
51
+ }
52
+
53
+ void TaskLogBuffer::release_message_main_loop(void *token) {
54
+ if (token == nullptr) {
55
+ return;
56
+ }
57
+ vRingbufferReturnItem(ring_buffer_, token);
58
+ // Update counter to mark all messages as processed
59
+ last_processed_counter_ = message_counter_.load(std::memory_order_relaxed);
60
+ }
61
+
62
+ bool TaskLogBuffer::send_message_thread_safe(uint8_t level, const char *tag, uint16_t line, TaskHandle_t task_handle,
63
+ const char *format, va_list args) {
64
+ // First, calculate the exact length needed using a null buffer (no actual writing)
65
+ va_list args_copy;
66
+ va_copy(args_copy, args);
67
+ int ret = vsnprintf(nullptr, 0, format, args_copy);
68
+ va_end(args_copy);
69
+
70
+ if (ret <= 0) {
71
+ return false; // Formatting error or empty message
72
+ }
73
+
74
+ // Calculate actual text length (capped to maximum size)
75
+ static constexpr size_t MAX_TEXT_SIZE = 255;
76
+ size_t text_length = (static_cast<size_t>(ret) > MAX_TEXT_SIZE) ? MAX_TEXT_SIZE : ret;
77
+
78
+ // Calculate total size needed (header + text length + null terminator)
79
+ size_t total_size = sizeof(LogMessage) + text_length + 1;
80
+
81
+ // Acquire memory directly from the ring buffer
82
+ void *acquired_memory = nullptr;
83
+ BaseType_t result = xRingbufferSendAcquire(ring_buffer_, &acquired_memory, total_size, 0);
84
+
85
+ if (result != pdTRUE || acquired_memory == nullptr) {
86
+ return false; // Failed to acquire memory
87
+ }
88
+
89
+ // Set up the message header in the acquired memory
90
+ LogMessage *msg = static_cast<LogMessage *>(acquired_memory);
91
+ msg->level = level;
92
+ msg->tag = tag;
93
+ msg->line = line;
94
+
95
+ // Store the thread name now instead of waiting until main loop processing
96
+ // This avoids crashes if the task completes or is deleted between when this message
97
+ // is enqueued and when it's processed by the main loop
98
+ const char *thread_name = pcTaskGetName(task_handle);
99
+ if (thread_name != nullptr) {
100
+ strncpy(msg->thread_name, thread_name, sizeof(msg->thread_name) - 1);
101
+ msg->thread_name[sizeof(msg->thread_name) - 1] = '\0'; // Ensure null termination
102
+ } else {
103
+ msg->thread_name[0] = '\0'; // Empty string if no thread name
104
+ }
105
+
106
+ // Format the message text directly into the acquired memory
107
+ // We add 1 to text_length to ensure space for null terminator during formatting
108
+ char *text_area = msg->text_data();
109
+ ret = vsnprintf(text_area, text_length + 1, format, args);
110
+
111
+ // Handle unexpected formatting error
112
+ if (ret <= 0) {
113
+ vRingbufferReturnItem(ring_buffer_, acquired_memory);
114
+ return false;
115
+ }
116
+
117
+ // Remove trailing newlines
118
+ while (text_length > 0 && text_area[text_length - 1] == '\n') {
119
+ text_length--;
120
+ }
121
+
122
+ msg->text_length = text_length;
123
+ // Complete the send operation with the acquired memory
124
+ result = xRingbufferSendComplete(ring_buffer_, acquired_memory);
125
+
126
+ if (result != pdTRUE) {
127
+ return false; // Failed to complete the message send
128
+ }
129
+
130
+ // Message sent successfully, increment the counter
131
+ message_counter_.fetch_add(1, std::memory_order_relaxed);
132
+ return true;
133
+ }
134
+
135
+ } // namespace logger
136
+ } // namespace esphome
137
+
138
+ #endif // USE_ESPHOME_TASK_LOG_BUFFER
@@ -0,0 +1,69 @@
1
+ #pragma once
2
+
3
+ #include "esphome/core/defines.h"
4
+ #include "esphome/core/helpers.h"
5
+
6
+ #ifdef USE_ESPHOME_TASK_LOG_BUFFER
7
+ #include <cstddef>
8
+ #include <cstring>
9
+ #include <memory>
10
+ #include <atomic>
11
+ #include <freertos/FreeRTOS.h>
12
+ #include <freertos/ringbuf.h>
13
+
14
+ namespace esphome {
15
+ namespace logger {
16
+
17
+ class TaskLogBuffer {
18
+ public:
19
+ // Structure for a log message header (text data follows immediately after)
20
+ struct LogMessage {
21
+ const char *tag; // We store the pointer, assuming tags are static
22
+ char thread_name[16]; // Store thread name directly (only used for non-main threads)
23
+ uint16_t text_length; // Length of the message text (up to ~64KB)
24
+ uint16_t line; // Source code line number
25
+ uint8_t level; // Log level (0-7)
26
+
27
+ // Methods for accessing message contents
28
+ inline char *text_data() { return reinterpret_cast<char *>(this) + sizeof(LogMessage); }
29
+
30
+ inline const char *text_data() const { return reinterpret_cast<const char *>(this) + sizeof(LogMessage); }
31
+ };
32
+
33
+ // Constructor that takes a total buffer size
34
+ explicit TaskLogBuffer(size_t total_buffer_size);
35
+ ~TaskLogBuffer();
36
+
37
+ // NOT thread-safe - borrow a message from the ring buffer, only call from main loop
38
+ bool borrow_message_main_loop(LogMessage **message, const char **text, void **received_token);
39
+
40
+ // NOT thread-safe - release a message buffer and update the counter, only call from main loop
41
+ void release_message_main_loop(void *token);
42
+
43
+ // Thread-safe - send a message to the ring buffer from any thread
44
+ bool send_message_thread_safe(uint8_t level, const char *tag, uint16_t line, TaskHandle_t task_handle,
45
+ const char *format, va_list args);
46
+
47
+ // Check if there are messages ready to be processed using an atomic counter for performance
48
+ inline bool HOT has_messages() const {
49
+ return message_counter_.load(std::memory_order_relaxed) != last_processed_counter_;
50
+ }
51
+
52
+ // Get the total buffer size in bytes
53
+ inline size_t size() const { return size_; }
54
+
55
+ private:
56
+ RingbufHandle_t ring_buffer_{nullptr}; // FreeRTOS ring buffer handle
57
+ StaticRingbuffer_t structure_; // Static structure for the ring buffer
58
+ uint8_t *storage_{nullptr}; // Pointer to allocated memory
59
+ size_t size_{0}; // Size of allocated memory
60
+
61
+ // Atomic counter for message tracking (only differences matter)
62
+ std::atomic<uint16_t> message_counter_{0}; // Incremented when messages are committed
63
+ mutable uint16_t last_processed_counter_{0}; // Tracks last processed message
64
+ };
65
+
66
+ } // namespace logger
67
+ } // namespace esphome
68
+
69
+ #endif // USE_ESPHOME_TASK_LOG_BUFFER
@@ -2,6 +2,7 @@ import logging
2
2
 
3
3
  from esphome.automation import build_automation, register_action, validate_automation
4
4
  import esphome.codegen as cg
5
+ from esphome.components.const import CONF_DRAW_ROUNDING
5
6
  from esphome.components.display import Display
6
7
  import esphome.config_validation as cv
7
8
  from esphome.const import (
@@ -17,14 +18,14 @@ from esphome.const import (
17
18
  CONF_TRIGGER_ID,
18
19
  CONF_TYPE,
19
20
  )
20
- from esphome.core import CORE, ID
21
+ from esphome.core import CORE, ID, Lambda
21
22
  from esphome.cpp_generator import MockObj
22
23
  from esphome.final_validate import full_config
23
24
  from esphome.helpers import write_file_if_changed
24
25
 
25
26
  from . import defines as df, helpers, lv_validation as lvalid
26
- from .automation import disp_update, focused_widgets, update_to_code
27
- from .defines import CONF_DRAW_ROUNDING, add_define
27
+ from .automation import disp_update, focused_widgets, refreshed_widgets, update_to_code
28
+ from .defines import add_define
28
29
  from .encoders import (
29
30
  ENCODERS_CONFIG,
30
31
  encoders_to_code,
@@ -239,6 +240,13 @@ def final_validation(configs):
239
240
  "A non adjustable arc may not be focused",
240
241
  path,
241
242
  )
243
+ for w in refreshed_widgets:
244
+ path = global_config.get_path_for_id(w)
245
+ widget_conf = global_config.get_config_for_path(path[:-1])
246
+ if not any(isinstance(v, Lambda) for v in widget_conf.values()):
247
+ raise cv.Invalid(
248
+ f"Widget '{w}' does not have any templated properties to refresh",
249
+ )
242
250
 
243
251
 
244
252
  async def to_code(configs):
@@ -323,7 +331,7 @@ async def to_code(configs):
323
331
  displays,
324
332
  frac,
325
333
  config[df.CONF_FULL_REFRESH],
326
- config[df.CONF_DRAW_ROUNDING],
334
+ config[CONF_DRAW_ROUNDING],
327
335
  config[df.CONF_RESUME_ON_INPUT],
328
336
  )
329
337
  await cg.register_component(lv_component, config)
@@ -413,7 +421,7 @@ LVGL_SCHEMA = cv.All(
413
421
  df.CONF_DEFAULT_FONT, default="montserrat_14"
414
422
  ): lvalid.lv_font,
415
423
  cv.Optional(df.CONF_FULL_REFRESH, default=False): cv.boolean,
416
- cv.Optional(df.CONF_DRAW_ROUNDING, default=2): cv.positive_int,
424
+ cv.Optional(CONF_DRAW_ROUNDING, default=2): cv.positive_int,
417
425
  cv.Optional(CONF_BUFFER_SIZE, default="100%"): cv.percentage,
418
426
  cv.Optional(df.CONF_LOG_LEVEL, default="WARN"): cv.one_of(
419
427
  *df.LV_LOG_LEVELS, upper=True
@@ -35,7 +35,13 @@ from .lvcode import (
35
35
  lv_obj,
36
36
  lvgl_comp,
37
37
  )
38
- from .schemas import DISP_BG_SCHEMA, LIST_ACTION_SCHEMA, LVGL_SCHEMA, base_update_schema
38
+ from .schemas import (
39
+ ALL_STYLES,
40
+ DISP_BG_SCHEMA,
41
+ LIST_ACTION_SCHEMA,
42
+ LVGL_SCHEMA,
43
+ base_update_schema,
44
+ )
39
45
  from .types import (
40
46
  LV_STATE,
41
47
  LvglAction,
@@ -57,6 +63,7 @@ from .widgets import (
57
63
 
58
64
  # Record widgets that are used in a focused action here
59
65
  focused_widgets = set()
66
+ refreshed_widgets = set()
60
67
 
61
68
 
62
69
  async def action_to_code(
@@ -361,3 +368,45 @@ async def obj_update_to_code(config, action_id, template_arg, args):
361
368
  return await action_to_code(
362
369
  widgets, do_update, action_id, template_arg, args, config
363
370
  )
371
+
372
+
373
+ def validate_refresh_config(config):
374
+ for w in config:
375
+ refreshed_widgets.add(w[CONF_ID])
376
+ return config
377
+
378
+
379
+ @automation.register_action(
380
+ "lvgl.widget.refresh",
381
+ ObjUpdateAction,
382
+ cv.All(
383
+ cv.ensure_list(
384
+ cv.maybe_simple_value(
385
+ {
386
+ cv.Required(CONF_ID): cv.use_id(lv_obj_t),
387
+ },
388
+ key=CONF_ID,
389
+ )
390
+ ),
391
+ validate_refresh_config,
392
+ ),
393
+ )
394
+ async def obj_refresh_to_code(config, action_id, template_arg, args):
395
+ widget = await get_widgets(config)
396
+
397
+ async def do_refresh(widget: Widget):
398
+ # only update style properties that might have changed, i.e. are templated
399
+ config = {k: v for k, v in widget.config.items() if isinstance(v, Lambda)}
400
+ await set_obj_properties(widget, config)
401
+ # must pass all widget-specific options here, even if not templated, but only do so if at least one is
402
+ # templated. First filter out common style properties.
403
+ config = {k: v for k, v in widget.config.items() if k not in ALL_STYLES}
404
+ if any(isinstance(v, Lambda) for v in config.values()):
405
+ await widget.type.to_code(widget, config)
406
+ if (
407
+ widget.type.w_type.value_property is not None
408
+ and widget.type.w_type.value_property in config
409
+ ):
410
+ lv.event_send(widget.obj, UPDATE_EVENT, nullptr)
411
+
412
+ return await action_to_code(widget, do_refresh, action_id, template_arg, args)
@@ -424,7 +424,6 @@ CONF_DEFAULT_FONT = "default_font"
424
424
  CONF_DEFAULT_GROUP = "default_group"
425
425
  CONF_DIR = "dir"
426
426
  CONF_DISPLAYS = "displays"
427
- CONF_DRAW_ROUNDING = "draw_rounding"
428
427
  CONF_EDITING = "editing"
429
428
  CONF_ENCODERS = "encoders"
430
429
  CONF_END_ANGLE = "end_angle"
@@ -16,7 +16,7 @@ from esphome.const import (
16
16
  )
17
17
  from esphome.core import CORE, ID, Lambda
18
18
  from esphome.cpp_generator import MockObj
19
- from esphome.cpp_types import ESPTime, uint32
19
+ from esphome.cpp_types import ESPTime, int32, uint32
20
20
  from esphome.helpers import cpp_string_escape
21
21
  from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
22
22
 
@@ -263,6 +263,15 @@ def pixels_validator(value):
263
263
  pixels = LValidator(pixels_validator, uint32, retmapper=literal)
264
264
 
265
265
 
266
+ def padding_validator(value):
267
+ if isinstance(value, str) and value.lower().endswith("px"):
268
+ value = value[:-2]
269
+ return cv.int_(value)
270
+
271
+
272
+ padding = LValidator(padding_validator, int32, retmapper=literal)
273
+
274
+
266
275
  def zoom_validator(value):
267
276
  value = cv.float_range(0.1, 10.0)(value)
268
277
  return value
@@ -434,7 +434,11 @@ void LvglComponent::setup() {
434
434
  auto height = display->get_height();
435
435
  size_t buffer_pixels = width * height / this->buffer_frac_;
436
436
  auto buf_bytes = buffer_pixels * LV_COLOR_DEPTH / 8;
437
- auto *buffer = lv_custom_mem_alloc(buf_bytes); // NOLINT
437
+ void *buffer = nullptr;
438
+ if (this->buffer_frac_ >= 4)
439
+ buffer = malloc(buf_bytes); // NOLINT
440
+ if (buffer == nullptr)
441
+ buffer = lv_custom_mem_alloc(buf_bytes); // NOLINT
438
442
  if (buffer == nullptr) {
439
443
  this->mark_failed();
440
444
  this->status_set_error("Memory allocation failure");