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
@@ -6,13 +6,41 @@ namespace mlx90393 {
6
6
 
7
7
  static const char *const TAG = "mlx90393";
8
8
 
9
+ const LogString *settings_to_string(MLX90393Setting setting) {
10
+ switch (setting) {
11
+ case MLX90393_GAIN_SEL:
12
+ return LOG_STR("gain");
13
+ case MLX90393_RESOLUTION:
14
+ return LOG_STR("resolution");
15
+ case MLX90393_OVER_SAMPLING:
16
+ return LOG_STR("oversampling");
17
+ case MLX90393_DIGITAL_FILTERING:
18
+ return LOG_STR("digital filtering");
19
+ case MLX90393_TEMPERATURE_OVER_SAMPLING:
20
+ return LOG_STR("temperature oversampling");
21
+ case MLX90393_TEMPERATURE_COMPENSATION:
22
+ return LOG_STR("temperature compensation");
23
+ case MLX90393_HALLCONF:
24
+ return LOG_STR("hallconf");
25
+ case MLX90393_LAST:
26
+ return LOG_STR("error");
27
+ default:
28
+ return LOG_STR("unknown");
29
+ }
30
+ };
31
+
9
32
  bool MLX90393Cls::transceive(const uint8_t *request, size_t request_size, uint8_t *response, size_t response_size) {
10
33
  i2c::ErrorCode e = this->write(request, request_size);
11
34
  if (e != i2c::ErrorCode::ERROR_OK) {
35
+ ESP_LOGV(TAG, "i2c failed to write %u", e);
12
36
  return false;
13
37
  }
14
38
  e = this->read(response, response_size);
15
- return e == i2c::ErrorCode::ERROR_OK;
39
+ if (e != i2c::ErrorCode::ERROR_OK) {
40
+ ESP_LOGV(TAG, "i2c failed to read %u", e);
41
+ return false;
42
+ }
43
+ return true;
16
44
  }
17
45
 
18
46
  bool MLX90393Cls::has_drdy_pin() { return this->drdy_pin_ != nullptr; }
@@ -27,6 +55,53 @@ bool MLX90393Cls::read_drdy_pin() {
27
55
  void MLX90393Cls::sleep_millis(uint32_t millis) { delay(millis); }
28
56
  void MLX90393Cls::sleep_micros(uint32_t micros) { delayMicroseconds(micros); }
29
57
 
58
+ uint8_t MLX90393Cls::apply_setting_(MLX90393Setting which) {
59
+ uint8_t ret = -1;
60
+ switch (which) {
61
+ case MLX90393_GAIN_SEL:
62
+ ret = this->mlx_.setGainSel(this->gain_);
63
+ break;
64
+ case MLX90393_RESOLUTION:
65
+ ret = this->mlx_.setResolution(this->resolutions_[0], this->resolutions_[1], this->resolutions_[2]);
66
+ break;
67
+ case MLX90393_OVER_SAMPLING:
68
+ ret = this->mlx_.setOverSampling(this->oversampling_);
69
+ break;
70
+ case MLX90393_DIGITAL_FILTERING:
71
+ ret = this->mlx_.setDigitalFiltering(this->filter_);
72
+ break;
73
+ case MLX90393_TEMPERATURE_OVER_SAMPLING:
74
+ ret = this->mlx_.setTemperatureOverSampling(this->temperature_oversampling_);
75
+ break;
76
+ case MLX90393_TEMPERATURE_COMPENSATION:
77
+ ret = this->mlx_.setTemperatureCompensation(this->temperature_compensation_);
78
+ break;
79
+ case MLX90393_HALLCONF:
80
+ ret = this->mlx_.setHallConf(this->hallconf_);
81
+ break;
82
+ default:
83
+ break;
84
+ }
85
+ if (ret != MLX90393::STATUS_OK) {
86
+ ESP_LOGE(TAG, "failed to apply %s", LOG_STR_ARG(settings_to_string(which)));
87
+ }
88
+ return ret;
89
+ }
90
+
91
+ bool MLX90393Cls::apply_all_settings_() {
92
+ // perform dummy read after reset
93
+ // first one always gets NAK even tough everything is fine
94
+ uint8_t ignore = 0;
95
+ this->mlx_.getGainSel(ignore);
96
+
97
+ uint8_t result = MLX90393::STATUS_OK;
98
+ for (int i = MLX90393_GAIN_SEL; i != MLX90393_LAST; i++) {
99
+ MLX90393Setting stage = static_cast<MLX90393Setting>(i);
100
+ result |= this->apply_setting_(stage);
101
+ }
102
+ return result == MLX90393::STATUS_OK;
103
+ }
104
+
30
105
  void MLX90393Cls::setup() {
31
106
  ESP_LOGCONFIG(TAG, "Setting up MLX90393...");
32
107
  // note the two arguments A0 and A1 which are used to construct an i2c address
@@ -34,19 +109,12 @@ void MLX90393Cls::setup() {
34
109
  // see the transceive function above, which uses the address from I2CComponent
35
110
  this->mlx_.begin_with_hal(this, 0, 0);
36
111
 
37
- this->mlx_.setGainSel(this->gain_);
38
-
39
- this->mlx_.setResolution(this->resolutions_[0], this->resolutions_[1], this->resolutions_[2]);
40
-
41
- this->mlx_.setOverSampling(this->oversampling_);
42
-
43
- this->mlx_.setDigitalFiltering(this->filter_);
44
-
45
- this->mlx_.setTemperatureOverSampling(this->temperature_oversampling_);
46
-
47
- this->mlx_.setTemperatureCompensation(this->temperature_compensation_);
112
+ if (!this->apply_all_settings_()) {
113
+ this->mark_failed();
114
+ }
48
115
 
49
- this->mlx_.setHallConf(this->hallconf_);
116
+ // start verify settings process
117
+ this->set_timeout("verify settings", 3000, [this]() { this->verify_settings_timeout_(MLX90393_GAIN_SEL); });
50
118
  }
51
119
 
52
120
  void MLX90393Cls::dump_config() {
@@ -91,5 +159,119 @@ void MLX90393Cls::update() {
91
159
  }
92
160
  }
93
161
 
162
+ bool MLX90393Cls::verify_setting_(MLX90393Setting which) {
163
+ uint8_t read_value = 0xFF;
164
+ uint8_t expected_value = 0xFF;
165
+ uint8_t read_status = -1;
166
+ char read_back_str[25] = {0};
167
+
168
+ switch (which) {
169
+ case MLX90393_GAIN_SEL: {
170
+ read_status = this->mlx_.getGainSel(read_value);
171
+ expected_value = this->gain_;
172
+ break;
173
+ }
174
+
175
+ case MLX90393_RESOLUTION: {
176
+ uint8_t read_resolutions[3] = {0xFF};
177
+ read_status = this->mlx_.getResolution(read_resolutions[0], read_resolutions[1], read_resolutions[2]);
178
+ snprintf(read_back_str, sizeof(read_back_str), "%u %u %u expected %u %u %u", read_resolutions[0],
179
+ read_resolutions[1], read_resolutions[2], this->resolutions_[0], this->resolutions_[1],
180
+ this->resolutions_[2]);
181
+ bool is_correct = true;
182
+ for (int i = 0; i < 3; i++) {
183
+ is_correct &= read_resolutions[i] == this->resolutions_[i];
184
+ }
185
+ if (is_correct) {
186
+ // set read_value and expected_value to same number, so the code blow recognizes it is correct
187
+ read_value = 0;
188
+ expected_value = 0;
189
+ } else {
190
+ // set to different numbers, to show incorrect
191
+ read_value = 1;
192
+ expected_value = 0;
193
+ }
194
+ break;
195
+ }
196
+ case MLX90393_OVER_SAMPLING: {
197
+ read_status = this->mlx_.getOverSampling(read_value);
198
+ expected_value = this->oversampling_;
199
+ break;
200
+ }
201
+ case MLX90393_DIGITAL_FILTERING: {
202
+ read_status = this->mlx_.getDigitalFiltering(read_value);
203
+ expected_value = this->filter_;
204
+ break;
205
+ }
206
+ case MLX90393_TEMPERATURE_OVER_SAMPLING: {
207
+ read_status = this->mlx_.getTemperatureOverSampling(read_value);
208
+ expected_value = this->temperature_oversampling_;
209
+ break;
210
+ }
211
+ case MLX90393_TEMPERATURE_COMPENSATION: {
212
+ read_status = this->mlx_.getTemperatureCompensation(read_value);
213
+ expected_value = (bool) this->temperature_compensation_;
214
+ break;
215
+ }
216
+ case MLX90393_HALLCONF: {
217
+ read_status = this->mlx_.getHallConf(read_value);
218
+ expected_value = this->hallconf_;
219
+ break;
220
+ }
221
+ default: {
222
+ return false;
223
+ }
224
+ }
225
+ if (read_status != MLX90393::STATUS_OK) {
226
+ ESP_LOGE(TAG, "verify error: failed to read %s", LOG_STR_ARG(settings_to_string(which)));
227
+ return false;
228
+ }
229
+ if (read_back_str[0] == 0x0) {
230
+ snprintf(read_back_str, sizeof(read_back_str), "%u expected %u", read_value, expected_value);
231
+ }
232
+ bool is_correct = read_value == expected_value;
233
+ if (!is_correct) {
234
+ ESP_LOGW(TAG, "verify failed: read back wrong %s: got %s", LOG_STR_ARG(settings_to_string(which)), read_back_str);
235
+ return false;
236
+ }
237
+ ESP_LOGD(TAG, "verify succeeded for %s. got %s", LOG_STR_ARG(settings_to_string(which)), read_back_str);
238
+ return true;
239
+ }
240
+
241
+ /**
242
+ * Regularly checks that our settings are still applied.
243
+ * Used to catch spurious chip resets.
244
+ *
245
+ * returns true if everything is fine.
246
+ * false if not
247
+ */
248
+ void MLX90393Cls::verify_settings_timeout_(MLX90393Setting stage) {
249
+ bool is_setting_ok = this->verify_setting_(stage);
250
+
251
+ if (!is_setting_ok) {
252
+ if (this->mlx_.checkStatus(this->mlx_.reset()) != MLX90393::STATUS_OK) {
253
+ ESP_LOGE(TAG, "failed to reset device");
254
+ this->status_set_error();
255
+ this->mark_failed();
256
+ return;
257
+ }
258
+
259
+ if (!this->apply_all_settings_()) {
260
+ ESP_LOGE(TAG, "failed to re-apply settings");
261
+ this->status_set_error();
262
+ this->mark_failed();
263
+ } else {
264
+ ESP_LOGI(TAG, "reset and re-apply settings completed");
265
+ }
266
+ }
267
+
268
+ MLX90393Setting next_stage = static_cast<MLX90393Setting>(static_cast<int>(stage) + 1);
269
+ if (next_stage == MLX90393_LAST) {
270
+ next_stage = static_cast<MLX90393Setting>(0);
271
+ }
272
+
273
+ this->set_timeout("verify settings", 3000, [this, next_stage]() { this->verify_settings_timeout_(next_stage); });
274
+ }
275
+
94
276
  } // namespace mlx90393
95
277
  } // namespace esphome
@@ -1,15 +1,26 @@
1
1
  #pragma once
2
2
 
3
- #include "esphome/core/component.h"
4
- #include "esphome/components/sensor/sensor.h"
5
- #include "esphome/components/i2c/i2c.h"
6
- #include "esphome/core/hal.h"
7
3
  #include <MLX90393.h>
8
4
  #include <MLX90393Hal.h>
5
+ #include "esphome/components/i2c/i2c.h"
6
+ #include "esphome/components/sensor/sensor.h"
7
+ #include "esphome/core/component.h"
8
+ #include "esphome/core/hal.h"
9
9
 
10
10
  namespace esphome {
11
11
  namespace mlx90393 {
12
12
 
13
+ enum MLX90393Setting {
14
+ MLX90393_GAIN_SEL = 0,
15
+ MLX90393_RESOLUTION,
16
+ MLX90393_OVER_SAMPLING,
17
+ MLX90393_DIGITAL_FILTERING,
18
+ MLX90393_TEMPERATURE_OVER_SAMPLING,
19
+ MLX90393_TEMPERATURE_COMPENSATION,
20
+ MLX90393_HALLCONF,
21
+ MLX90393_LAST,
22
+ };
23
+
13
24
  class MLX90393Cls : public PollingComponent, public i2c::I2CDevice, public MLX90393Hal {
14
25
  public:
15
26
  void setup() override;
@@ -58,6 +69,12 @@ class MLX90393Cls : public PollingComponent, public i2c::I2CDevice, public MLX90
58
69
  bool temperature_compensation_{false};
59
70
  uint8_t hallconf_{0xC};
60
71
  GPIOPin *drdy_pin_{nullptr};
72
+
73
+ bool apply_all_settings_();
74
+ uint8_t apply_setting_(MLX90393Setting which);
75
+
76
+ bool verify_setting_(MLX90393Setting which);
77
+ void verify_settings_timeout_(MLX90393Setting stage);
61
78
  };
62
79
 
63
80
  } // namespace mlx90393
@@ -41,6 +41,7 @@ from esphome.const import (
41
41
  CONF_REBOOT_TIMEOUT,
42
42
  CONF_RETAIN,
43
43
  CONF_SHUTDOWN_MESSAGE,
44
+ CONF_SKIP_CERT_CN_CHECK,
44
45
  CONF_SSL_FINGERPRINTS,
45
46
  CONF_STATE_TOPIC,
46
47
  CONF_SUBSCRIBE_QOS,
@@ -67,7 +68,6 @@ def AUTO_LOAD():
67
68
 
68
69
  CONF_DISCOVER_IP = "discover_ip"
69
70
  CONF_IDF_SEND_ASYNC = "idf_send_async"
70
- CONF_SKIP_CERT_CN_CHECK = "skip_cert_cn_check"
71
71
 
72
72
 
73
73
  def validate_message_just_topic(value):
@@ -138,7 +138,11 @@ void MQTTClientComponent::send_device_info_() {
138
138
  #endif
139
139
 
140
140
  #ifdef USE_API_NOISE
141
- root["api_encryption"] = "Noise_NNpsk0_25519_ChaChaPoly_SHA256";
141
+ if (api::global_api_server->get_noise_ctx()->has_psk()) {
142
+ root["api_encryption"] = "Noise_NNpsk0_25519_ChaChaPoly_SHA256";
143
+ } else {
144
+ root["api_encryption_supported"] = "Noise_NNpsk0_25519_ChaChaPoly_SHA256";
145
+ }
142
146
  #endif
143
147
  },
144
148
  2, this->discovery_info_.retain);
@@ -64,6 +64,8 @@ constexpr const char *const MQTT_DEVICE_NAME = "name";
64
64
  constexpr const char *const MQTT_DEVICE_SUGGESTED_AREA = "sa";
65
65
  constexpr const char *const MQTT_DEVICE_SW_VERSION = "sw";
66
66
  constexpr const char *const MQTT_DEVICE_HW_VERSION = "hw";
67
+ constexpr const char *const MQTT_DIRECTION_COMMAND_TOPIC = "dir_cmd_t";
68
+ constexpr const char *const MQTT_DIRECTION_STATE_TOPIC = "dir_stat_t";
67
69
  constexpr const char *const MQTT_DOCKED_TEMPLATE = "dock_tpl";
68
70
  constexpr const char *const MQTT_DOCKED_TOPIC = "dock_t";
69
71
  constexpr const char *const MQTT_EFFECT_COMMAND_TOPIC = "fx_cmd_t";
@@ -328,6 +330,8 @@ constexpr const char *const MQTT_DEVICE_NAME = "name";
328
330
  constexpr const char *const MQTT_DEVICE_SUGGESTED_AREA = "suggested_area";
329
331
  constexpr const char *const MQTT_DEVICE_SW_VERSION = "sw_version";
330
332
  constexpr const char *const MQTT_DEVICE_HW_VERSION = "hw_version";
333
+ constexpr const char *const MQTT_DIRECTION_COMMAND_TOPIC = "direction_command_topic";
334
+ constexpr const char *const MQTT_DIRECTION_STATE_TOPIC = "direction_state_topic";
331
335
  constexpr const char *const MQTT_DOCKED_TEMPLATE = "docked_template";
332
336
  constexpr const char *const MQTT_DOCKED_TOPIC = "docked_topic";
333
337
  constexpr const char *const MQTT_EFFECT_COMMAND_TOPIC = "effect_command_topic";
@@ -43,6 +43,32 @@ void MQTTFanComponent::setup() {
43
43
  }
44
44
  });
45
45
 
46
+ if (this->state_->get_traits().supports_direction()) {
47
+ this->subscribe(this->get_direction_command_topic(), [this](const std::string &topic, const std::string &payload) {
48
+ auto val = parse_on_off(payload.c_str(), "forward", "reverse");
49
+ switch (val) {
50
+ case PARSE_ON:
51
+ ESP_LOGD(TAG, "'%s': Setting direction FORWARD", this->friendly_name().c_str());
52
+ this->state_->make_call().set_direction(fan::FanDirection::FORWARD).perform();
53
+ break;
54
+ case PARSE_OFF:
55
+ ESP_LOGD(TAG, "'%s': Setting direction REVERSE", this->friendly_name().c_str());
56
+ this->state_->make_call().set_direction(fan::FanDirection::REVERSE).perform();
57
+ break;
58
+ case PARSE_TOGGLE:
59
+ this->state_->make_call()
60
+ .set_direction(this->state_->direction == fan::FanDirection::FORWARD ? fan::FanDirection::REVERSE
61
+ : fan::FanDirection::FORWARD)
62
+ .perform();
63
+ break;
64
+ case PARSE_NONE:
65
+ ESP_LOGW(TAG, "Unknown direction Payload %s", payload.c_str());
66
+ this->status_momentary_warning("direction", 5000);
67
+ break;
68
+ }
69
+ });
70
+ }
71
+
46
72
  if (this->state_->get_traits().supports_oscillation()) {
47
73
  this->subscribe(this->get_oscillation_command_topic(),
48
74
  [this](const std::string &topic, const std::string &payload) {
@@ -94,6 +120,10 @@ void MQTTFanComponent::setup() {
94
120
  void MQTTFanComponent::dump_config() {
95
121
  ESP_LOGCONFIG(TAG, "MQTT Fan '%s': ", this->state_->get_name().c_str());
96
122
  LOG_MQTT_COMPONENT(true, true);
123
+ if (this->state_->get_traits().supports_direction()) {
124
+ ESP_LOGCONFIG(TAG, " Direction State Topic: '%s'", this->get_direction_state_topic().c_str());
125
+ ESP_LOGCONFIG(TAG, " Direction Command Topic: '%s'", this->get_direction_command_topic().c_str());
126
+ }
97
127
  if (this->state_->get_traits().supports_oscillation()) {
98
128
  ESP_LOGCONFIG(TAG, " Oscillation State Topic: '%s'", this->get_oscillation_state_topic().c_str());
99
129
  ESP_LOGCONFIG(TAG, " Oscillation Command Topic: '%s'", this->get_oscillation_command_topic().c_str());
@@ -107,6 +137,10 @@ void MQTTFanComponent::dump_config() {
107
137
  bool MQTTFanComponent::send_initial_state() { return this->publish_state(); }
108
138
 
109
139
  void MQTTFanComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
140
+ if (this->state_->get_traits().supports_direction()) {
141
+ root[MQTT_DIRECTION_COMMAND_TOPIC] = this->get_direction_command_topic();
142
+ root[MQTT_DIRECTION_STATE_TOPIC] = this->get_direction_state_topic();
143
+ }
110
144
  if (this->state_->get_traits().supports_oscillation()) {
111
145
  root[MQTT_OSCILLATION_COMMAND_TOPIC] = this->get_oscillation_command_topic();
112
146
  root[MQTT_OSCILLATION_STATE_TOPIC] = this->get_oscillation_state_topic();
@@ -122,6 +156,11 @@ bool MQTTFanComponent::publish_state() {
122
156
  ESP_LOGD(TAG, "'%s' Sending state %s.", this->state_->get_name().c_str(), state_s);
123
157
  this->publish(this->get_state_topic_(), state_s);
124
158
  bool failed = false;
159
+ if (this->state_->get_traits().supports_direction()) {
160
+ bool success = this->publish(this->get_direction_state_topic(),
161
+ this->state_->direction == fan::FanDirection::FORWARD ? "forward" : "reverse");
162
+ failed = failed || !success;
163
+ }
125
164
  if (this->state_->get_traits().supports_oscillation()) {
126
165
  bool success = this->publish(this->get_oscillation_state_topic(),
127
166
  this->state_->oscillating ? "oscillate_on" : "oscillate_off");
@@ -15,6 +15,8 @@ class MQTTFanComponent : public mqtt::MQTTComponent {
15
15
  public:
16
16
  explicit MQTTFanComponent(fan::Fan *state);
17
17
 
18
+ MQTT_COMPONENT_CUSTOM_TOPIC(direction, command)
19
+ MQTT_COMPONENT_CUSTOM_TOPIC(direction, state)
18
20
  MQTT_COMPONENT_CUSTOM_TOPIC(oscillation, command)
19
21
  MQTT_COMPONENT_CUSTOM_TOPIC(oscillation, state)
20
22
  MQTT_COMPONENT_CUSTOM_TOPIC(speed_level, command)
@@ -26,7 +26,7 @@ CONFIG_SCHEMA = cv.Schema(
26
26
  esp32_arduino=cv.Version(0, 0, 0),
27
27
  esp8266_arduino=cv.Version(0, 0, 0),
28
28
  rp2040_arduino=cv.Version(0, 0, 0),
29
- bk72xx_libretiny=cv.Version(1, 7, 0),
29
+ bk72xx_arduino=cv.Version(1, 7, 0),
30
30
  ),
31
31
  cv.boolean_false,
32
32
  ),
@@ -7,28 +7,29 @@ from esphome.const import CONF_BACKGROUND_COLOR, CONF_FOREGROUND_COLOR, CONF_VIS
7
7
 
8
8
  from . import CONF_NEXTION_ID, Nextion
9
9
 
10
- CONF_VARIABLE_NAME = "variable_name"
10
+ CONF_AUTO_WAKE_ON_TOUCH = "auto_wake_on_touch"
11
+ CONF_BACKGROUND_PRESSED_COLOR = "background_pressed_color"
12
+ CONF_COMMAND_SPACING = "command_spacing"
11
13
  CONF_COMPONENT_NAME = "component_name"
12
- CONF_WAVE_CHANNEL_ID = "wave_channel_id"
13
- CONF_WAVE_MAX_VALUE = "wave_max_value"
14
- CONF_PRECISION = "precision"
15
- CONF_WAVEFORM_SEND_LAST_VALUE = "waveform_send_last_value"
16
- CONF_TFT_URL = "tft_url"
14
+ CONF_EXIT_REPARSE_ON_START = "exit_reparse_on_start"
15
+ CONF_FONT_ID = "font_id"
16
+ CONF_FOREGROUND_PRESSED_COLOR = "foreground_pressed_color"
17
+ CONF_ON_BUFFER_OVERFLOW = "on_buffer_overflow"
18
+ CONF_ON_PAGE = "on_page"
19
+ CONF_ON_SETUP = "on_setup"
17
20
  CONF_ON_SLEEP = "on_sleep"
18
21
  CONF_ON_WAKE = "on_wake"
19
- CONF_ON_SETUP = "on_setup"
20
- CONF_ON_PAGE = "on_page"
21
- CONF_ON_BUFFER_OVERFLOW = "on_buffer_overflow"
22
+ CONF_PRECISION = "precision"
23
+ CONF_SKIP_CONNECTION_HANDSHAKE = "skip_connection_handshake"
24
+ CONF_START_UP_PAGE = "start_up_page"
25
+ CONF_TFT_URL = "tft_url"
22
26
  CONF_TOUCH_SLEEP_TIMEOUT = "touch_sleep_timeout"
27
+ CONF_VARIABLE_NAME = "variable_name"
23
28
  CONF_WAKE_UP_PAGE = "wake_up_page"
24
- CONF_START_UP_PAGE = "start_up_page"
25
- CONF_AUTO_WAKE_ON_TOUCH = "auto_wake_on_touch"
29
+ CONF_WAVE_CHANNEL_ID = "wave_channel_id"
26
30
  CONF_WAVE_MAX_LENGTH = "wave_max_length"
27
- CONF_BACKGROUND_PRESSED_COLOR = "background_pressed_color"
28
- CONF_FOREGROUND_PRESSED_COLOR = "foreground_pressed_color"
29
- CONF_FONT_ID = "font_id"
30
- CONF_EXIT_REPARSE_ON_START = "exit_reparse_on_start"
31
- CONF_SKIP_CONNECTION_HANDSHAKE = "skip_connection_handshake"
31
+ CONF_WAVE_MAX_VALUE = "wave_max_value"
32
+ CONF_WAVEFORM_SEND_LAST_VALUE = "waveform_send_last_value"
32
33
 
33
34
 
34
35
  def NextionName(value):
@@ -9,16 +9,17 @@ from esphome.const import (
9
9
  CONF_ON_TOUCH,
10
10
  CONF_TRIGGER_ID,
11
11
  )
12
- from esphome.core import CORE
12
+ from esphome.core import CORE, TimePeriod
13
13
 
14
14
  from . import Nextion, nextion_ns, nextion_ref
15
15
  from .base_component import (
16
16
  CONF_AUTO_WAKE_ON_TOUCH,
17
+ CONF_COMMAND_SPACING,
17
18
  CONF_EXIT_REPARSE_ON_START,
18
19
  CONF_ON_BUFFER_OVERFLOW,
19
- CONF_ON_PAGE,
20
20
  CONF_ON_SETUP,
21
21
  CONF_ON_SLEEP,
22
+ CONF_ON_PAGE,
22
23
  CONF_ON_WAKE,
23
24
  CONF_SKIP_CONNECTION_HANDSHAKE,
24
25
  CONF_START_UP_PAGE,
@@ -88,6 +89,10 @@ CONFIG_SCHEMA = (
88
89
  cv.Optional(CONF_AUTO_WAKE_ON_TOUCH, default=True): cv.boolean,
89
90
  cv.Optional(CONF_EXIT_REPARSE_ON_START, default=False): cv.boolean,
90
91
  cv.Optional(CONF_SKIP_CONNECTION_HANDSHAKE, default=False): cv.boolean,
92
+ cv.Optional(CONF_COMMAND_SPACING): cv.All(
93
+ cv.positive_time_period_milliseconds,
94
+ cv.Range(max=TimePeriod(milliseconds=255)),
95
+ ),
91
96
  }
92
97
  )
93
98
  .extend(cv.polling_component_schema("5s"))
@@ -120,6 +125,10 @@ async def to_code(config):
120
125
  var = cg.new_Pvariable(config[CONF_ID])
121
126
  await uart.register_uart_device(var, config)
122
127
 
128
+ if command_spacing := config.get(CONF_COMMAND_SPACING):
129
+ cg.add_define("USE_NEXTION_COMMAND_SPACING")
130
+ cg.add(var.set_command_spacing(command_spacing.total_milliseconds))
131
+
123
132
  if CONF_BRIGHTNESS in config:
124
133
  cg.add(var.set_brightness(config[CONF_BRIGHTNESS]))
125
134
 
@@ -31,11 +31,22 @@ bool Nextion::send_command_(const std::string &command) {
31
31
  return false;
32
32
  }
33
33
 
34
+ #ifdef USE_NEXTION_COMMAND_SPACING
35
+ if (!this->ignore_is_setup_ && !this->command_pacer_.can_send()) {
36
+ return false;
37
+ }
38
+ #endif // USE_NEXTION_COMMAND_SPACING
39
+
34
40
  ESP_LOGN(TAG, "send_command %s", command.c_str());
35
41
 
36
42
  this->write_str(command.c_str());
37
43
  const uint8_t to_send[3] = {0xFF, 0xFF, 0xFF};
38
44
  this->write_array(to_send, sizeof(to_send));
45
+
46
+ #ifdef USE_NEXTION_COMMAND_SPACING
47
+ this->command_pacer_.mark_sent();
48
+ #endif // USE_NEXTION_COMMAND_SPACING
49
+
39
50
  return true;
40
51
  }
41
52
 
@@ -158,6 +169,10 @@ void Nextion::dump_config() {
158
169
  if (this->start_up_page_ != -1) {
159
170
  ESP_LOGCONFIG(TAG, " Start Up Page: %" PRId16, this->start_up_page_);
160
171
  }
172
+
173
+ #ifdef USE_NEXTION_COMMAND_SPACING
174
+ ESP_LOGCONFIG(TAG, " Command spacing: %" PRIu8 "ms", this->command_pacer_.get_spacing());
175
+ #endif // USE_NEXTION_COMMAND_SPACING
161
176
  }
162
177
 
163
178
  float Nextion::get_setup_priority() const { return setup_priority::DATA; }
@@ -312,6 +327,11 @@ bool Nextion::remove_from_q_(bool report_empty) {
312
327
  }
313
328
 
314
329
  NextionQueue *nb = this->nextion_queue_.front();
330
+ if (!nb || !nb->component) {
331
+ ESP_LOGE(TAG, "Invalid queue entry!");
332
+ this->nextion_queue_.pop_front();
333
+ return false;
334
+ }
315
335
  NextionComponentBase *component = nb->component;
316
336
 
317
337
  ESP_LOGN(TAG, "Removing %s from the queue", component->get_variable_name().c_str());
@@ -341,6 +361,12 @@ void Nextion::process_nextion_commands_() {
341
361
  return;
342
362
  }
343
363
 
364
+ #ifdef USE_NEXTION_COMMAND_SPACING
365
+ if (!this->command_pacer_.can_send()) {
366
+ return; // Will try again in next loop iteration
367
+ }
368
+ #endif
369
+
344
370
  size_t to_process_length = 0;
345
371
  std::string to_process;
346
372
 
@@ -380,7 +406,9 @@ void Nextion::process_nextion_commands_() {
380
406
  this->setup_callback_.call();
381
407
  }
382
408
  }
383
-
409
+ #ifdef USE_NEXTION_COMMAND_SPACING
410
+ this->command_pacer_.mark_sent(); // Here is where we should mark the command as sent
411
+ #endif
384
412
  break;
385
413
  case 0x02: // invalid Component ID or name was used
386
414
  ESP_LOGW(TAG, "Nextion reported component ID or name invalid!");
@@ -524,6 +552,11 @@ void Nextion::process_nextion_commands_() {
524
552
  }
525
553
 
526
554
  NextionQueue *nb = this->nextion_queue_.front();
555
+ if (!nb || !nb->component) {
556
+ ESP_LOGE(TAG, "Invalid queue entry!");
557
+ this->nextion_queue_.pop_front();
558
+ return;
559
+ }
527
560
  NextionComponentBase *component = nb->component;
528
561
 
529
562
  if (component->get_queue_type() != NextionQueueType::TEXT_SENSOR) {
@@ -564,6 +597,11 @@ void Nextion::process_nextion_commands_() {
564
597
  }
565
598
 
566
599
  NextionQueue *nb = this->nextion_queue_.front();
600
+ if (!nb || !nb->component) {
601
+ ESP_LOGE(TAG, "Invalid queue entry!");
602
+ this->nextion_queue_.pop_front();
603
+ return;
604
+ }
567
605
  NextionComponentBase *component = nb->component;
568
606
 
569
607
  if (component->get_queue_type() != NextionQueueType::SENSOR &&
@@ -35,8 +35,54 @@ using nextion_writer_t = std::function<void(Nextion &)>;
35
35
 
36
36
  static const std::string COMMAND_DELIMITER{static_cast<char>(255), static_cast<char>(255), static_cast<char>(255)};
37
37
 
38
+ #ifdef USE_NEXTION_COMMAND_SPACING
39
+ class NextionCommandPacer {
40
+ public:
41
+ /**
42
+ * @brief Creates command pacer with initial spacing
43
+ * @param initial_spacing Initial time between commands in milliseconds
44
+ */
45
+ explicit NextionCommandPacer(uint8_t initial_spacing = 0) : spacing_ms_(initial_spacing) {}
46
+
47
+ /**
48
+ * @brief Set the minimum time between commands
49
+ * @param spacing_ms Spacing in milliseconds
50
+ */
51
+ void set_spacing(uint8_t spacing_ms) { spacing_ms_ = spacing_ms; }
52
+
53
+ /**
54
+ * @brief Get current command spacing
55
+ * @return Current spacing in milliseconds
56
+ */
57
+ uint8_t get_spacing() const { return spacing_ms_; }
58
+
59
+ /**
60
+ * @brief Check if enough time has passed to send next command
61
+ * @return true if enough time has passed since last command
62
+ */
63
+ bool can_send() const { return (millis() - last_command_time_) >= spacing_ms_; }
64
+
65
+ /**
66
+ * @brief Mark a command as sent, updating the timing
67
+ */
68
+ void mark_sent() { last_command_time_ = millis(); }
69
+
70
+ private:
71
+ uint8_t spacing_ms_;
72
+ uint32_t last_command_time_{0};
73
+ };
74
+ #endif // USE_NEXTION_COMMAND_SPACING
75
+
38
76
  class Nextion : public NextionBase, public PollingComponent, public uart::UARTDevice {
39
77
  public:
78
+ #ifdef USE_NEXTION_COMMAND_SPACING
79
+ /**
80
+ * @brief Set the command spacing for the display
81
+ * @param spacing_ms Time in milliseconds between commands
82
+ */
83
+ void set_command_spacing(uint32_t spacing_ms) { this->command_pacer_.set_spacing(spacing_ms); }
84
+ #endif // USE_NEXTION_COMMAND_SPACING
85
+
40
86
  /**
41
87
  * Set the text of a component to a static string.
42
88
  * @param component The component name.
@@ -1227,6 +1273,9 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
1227
1273
  bool is_connected() { return this->is_connected_; }
1228
1274
 
1229
1275
  protected:
1276
+ #ifdef USE_NEXTION_COMMAND_SPACING
1277
+ NextionCommandPacer command_pacer_{0};
1278
+ #endif // USE_NEXTION_COMMAND_SPACING
1230
1279
  std::deque<NextionQueue *> nextion_queue_;
1231
1280
  std::deque<NextionQueue *> waveform_queue_;
1232
1281
  uint16_t recv_ret_string_(std::string &response, uint32_t timeout, bool recv_flag);
@@ -1360,5 +1409,6 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
1360
1409
  uint32_t started_ms_ = 0;
1361
1410
  bool sent_setup_commands_ = false;
1362
1411
  };
1412
+
1363
1413
  } // namespace nextion
1364
1414
  } // namespace esphome