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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (457) hide show
  1. esphome/__main__.py +16 -14
  2. esphome/components/ac_dimmer/ac_dimmer.cpp +3 -2
  3. esphome/components/adc/__init__.py +51 -34
  4. esphome/components/airthings_wave_base/__init__.py +1 -1
  5. esphome/components/alarm_control_panel/__init__.py +37 -2
  6. esphome/components/am43/cover/__init__.py +4 -5
  7. esphome/components/analog_threshold/analog_threshold_binary_sensor.cpp +6 -4
  8. esphome/components/analog_threshold/analog_threshold_binary_sensor.h +4 -5
  9. esphome/components/analog_threshold/binary_sensor.py +10 -8
  10. esphome/components/anova/climate.py +4 -5
  11. esphome/components/api/__init__.py +25 -8
  12. esphome/components/api/api_connection.cpp +416 -662
  13. esphome/components/api/api_connection.h +256 -57
  14. esphome/components/api/api_frame_helper.cpp +232 -177
  15. esphome/components/api/api_frame_helper.h +61 -8
  16. esphome/components/api/api_noise_context.h +13 -4
  17. esphome/components/api/api_pb2.cpp +1422 -1
  18. esphome/components/api/api_pb2.h +255 -1
  19. esphome/components/api/api_pb2_service.cpp +162 -49
  20. esphome/components/api/api_pb2_service.h +90 -51
  21. esphome/components/api/api_pb2_size.h +361 -0
  22. esphome/components/api/api_server.cpp +110 -34
  23. esphome/components/api/api_server.h +8 -0
  24. esphome/components/api/proto.h +86 -17
  25. esphome/components/as3935_i2c/as3935_i2c.h +0 -3
  26. esphome/components/as7341/as7341.h +1 -1
  27. esphome/components/at581x/at581x.h +4 -4
  28. esphome/components/atm90e32/__init__.py +1 -0
  29. esphome/components/atm90e32/atm90e32.cpp +576 -199
  30. esphome/components/atm90e32/atm90e32.h +128 -31
  31. esphome/components/atm90e32/atm90e32_reg.h +4 -2
  32. esphome/components/atm90e32/button/__init__.py +62 -10
  33. esphome/components/atm90e32/button/atm90e32_button.cpp +63 -4
  34. esphome/components/atm90e32/button/atm90e32_button.h +36 -4
  35. esphome/components/atm90e32/number/__init__.py +130 -0
  36. esphome/components/atm90e32/number/atm90e32_number.h +16 -0
  37. esphome/components/atm90e32/sensor.py +21 -4
  38. esphome/components/atm90e32/text_sensor/__init__.py +48 -0
  39. esphome/components/audio/__init__.py +96 -49
  40. esphome/components/audio/audio.h +48 -0
  41. esphome/components/audio/audio_decoder.cpp +1 -1
  42. esphome/components/audio/audio_resampler.cpp +2 -0
  43. esphome/components/audio/audio_resampler.h +1 -0
  44. esphome/components/ballu/climate.py +2 -9
  45. esphome/components/bang_bang/climate.py +5 -6
  46. esphome/components/bedjet/bedjet_hub.cpp +1 -0
  47. esphome/components/bedjet/climate/__init__.py +3 -8
  48. esphome/components/bedjet/fan/__init__.py +2 -11
  49. esphome/components/binary/fan/__init__.py +13 -16
  50. esphome/components/binary_sensor/__init__.py +13 -10
  51. esphome/components/bl0906/constants.h +16 -16
  52. esphome/components/ble_client/text_sensor/__init__.py +3 -5
  53. esphome/components/bluetooth_proxy/bluetooth_connection.cpp +4 -6
  54. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +136 -21
  55. esphome/components/bluetooth_proxy/bluetooth_proxy.h +7 -0
  56. esphome/components/button/__init__.py +11 -8
  57. esphome/components/canbus/canbus.cpp +3 -0
  58. esphome/components/canbus/canbus.h +16 -0
  59. esphome/components/ccs811/sensor.py +9 -6
  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/cse7766/cse7766.cpp +2 -1
  76. esphome/components/cst226/binary_sensor/__init__.py +28 -0
  77. esphome/components/cst226/binary_sensor/cs226_button.h +22 -0
  78. esphome/components/cst226/binary_sensor/cstt6_button.cpp +19 -0
  79. esphome/components/cst226/touchscreen/cst226_touchscreen.cpp +27 -5
  80. esphome/components/cst226/touchscreen/cst226_touchscreen.h +10 -10
  81. esphome/components/current_based/cover.py +37 -36
  82. esphome/components/current_based/current_based_cover.cpp +2 -1
  83. esphome/components/daikin/climate.py +2 -9
  84. esphome/components/daikin/daikin.cpp +15 -9
  85. esphome/components/daikin/daikin.h +5 -5
  86. esphome/components/daikin_arc/climate.py +2 -7
  87. esphome/components/daikin_brc/climate.py +3 -5
  88. esphome/components/dallas_temp/dallas_temp.cpp +17 -24
  89. esphome/components/dallas_temp/dallas_temp.h +0 -1
  90. esphome/components/daly_bms/daly_bms.cpp +2 -1
  91. esphome/components/debug/debug_component.cpp +6 -1
  92. esphome/components/debug/debug_component.h +8 -0
  93. esphome/components/debug/debug_esp32.cpp +109 -254
  94. esphome/components/debug/sensor.py +14 -0
  95. esphome/components/deep_sleep/deep_sleep_esp32.cpp +13 -1
  96. esphome/components/delonghi/climate.py +2 -9
  97. esphome/components/demo/__init__.py +18 -20
  98. esphome/components/dfrobot_sen0395/switch/__init__.py +21 -22
  99. esphome/components/display/rect.cpp +4 -9
  100. esphome/components/display/rect.h +1 -1
  101. esphome/components/dps310/sensor.py +6 -6
  102. esphome/components/ee895/sensor.py +9 -9
  103. esphome/components/emmeti/climate.py +2 -9
  104. esphome/components/endstop/cover.py +17 -16
  105. esphome/components/endstop/endstop_cover.cpp +2 -1
  106. esphome/components/ens160_base/__init__.py +12 -9
  107. esphome/components/esp32/__init__.py +60 -3
  108. esphome/components/esp32/core.cpp +11 -5
  109. esphome/components/esp32/gpio.cpp +86 -24
  110. esphome/components/esp32/gpio.py +15 -16
  111. esphome/components/esp32/gpio_esp32.py +1 -2
  112. esphome/components/esp32/gpio_esp32_c2.py +1 -1
  113. esphome/components/esp32/gpio_esp32_c3.py +1 -1
  114. esphome/components/esp32/gpio_esp32_c6.py +1 -1
  115. esphome/components/esp32/gpio_esp32_h2.py +1 -1
  116. esphome/components/esp32_ble/ble.cpp +1 -8
  117. esphome/components/esp32_ble/ble.h +5 -3
  118. esphome/components/esp32_ble/ble_advertising.cpp +2 -1
  119. esphome/components/esp32_ble/ble_advertising.h +1 -0
  120. esphome/components/esp32_ble_server/__init__.py +3 -0
  121. esphome/components/esp32_ble_tracker/__init__.py +7 -1
  122. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +192 -118
  123. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +29 -3
  124. esphome/components/esp32_camera/__init__.py +1 -1
  125. esphome/components/esp32_camera/esp32_camera.cpp +2 -10
  126. esphome/components/esp32_camera/esp32_camera.h +1 -1
  127. esphome/components/esp32_can/esp32_can.cpp +1 -1
  128. esphome/components/esp32_improv/esp32_improv_component.cpp +1 -1
  129. esphome/components/esp32_rmt_led_strip/led_strip.cpp +1 -1
  130. esphome/components/esp32_rmt_led_strip/led_strip.h +7 -5
  131. esphome/components/esp32_rmt_led_strip/light.py +9 -1
  132. esphome/components/esp32_touch/esp32_touch.cpp +1 -1
  133. esphome/components/esp8266/gpio.cpp +69 -8
  134. esphome/components/ethernet/ethernet_component.cpp +1 -1
  135. esphome/components/event/__init__.py +13 -10
  136. esphome/components/factory_reset/switch/__init__.py +7 -21
  137. esphome/components/fan/__init__.py +52 -5
  138. esphome/components/fastled_base/__init__.py +1 -4
  139. esphome/components/fastled_base/fastled_light.cpp +1 -1
  140. esphome/components/feedback/cover.py +38 -33
  141. esphome/components/feedback/feedback_cover.cpp +2 -1
  142. esphome/components/fujitsu_general/climate.py +2 -9
  143. esphome/components/gcja5/gcja5.cpp +2 -1
  144. esphome/components/gpio/one_wire/gpio_one_wire.cpp +45 -43
  145. esphome/components/gpio/one_wire/gpio_one_wire.h +2 -1
  146. esphome/components/gpio_expander/cached_gpio.h +22 -7
  147. esphome/components/gps/__init__.py +47 -17
  148. esphome/components/gps/gps.cpp +42 -23
  149. esphome/components/gps/gps.h +17 -13
  150. esphome/components/graph/__init__.py +1 -2
  151. esphome/components/gree/climate.py +4 -6
  152. esphome/components/gree/gree.cpp +16 -2
  153. esphome/components/gree/gree.h +2 -2
  154. esphome/components/growatt_solar/growatt_solar.cpp +2 -1
  155. esphome/components/haier/climate.py +37 -34
  156. esphome/components/hbridge/fan/__init__.py +19 -17
  157. esphome/components/he60r/cover.py +4 -5
  158. esphome/components/heatpumpir/climate.py +3 -6
  159. esphome/components/hitachi_ac344/climate.py +2 -9
  160. esphome/components/hitachi_ac424/climate.py +2 -9
  161. esphome/components/hlw8012/hlw8012.cpp +1 -1
  162. esphome/components/hm3301/hm3301.h +1 -1
  163. esphome/components/hte501/sensor.py +6 -6
  164. esphome/components/http_request/__init__.py +39 -6
  165. esphome/components/http_request/http_request.cpp +20 -0
  166. esphome/components/http_request/http_request.h +57 -15
  167. esphome/components/http_request/http_request_arduino.cpp +22 -6
  168. esphome/components/http_request/http_request_arduino.h +4 -3
  169. esphome/components/http_request/http_request_host.cpp +141 -0
  170. esphome/components/http_request/http_request_host.h +37 -0
  171. esphome/components/http_request/http_request_idf.cpp +35 -3
  172. esphome/components/http_request/http_request_idf.h +10 -3
  173. esphome/components/http_request/httplib.h +9691 -0
  174. esphome/components/http_request/update/__init__.py +11 -8
  175. esphome/components/hyt271/sensor.py +6 -6
  176. esphome/components/i2c/i2c.h +4 -0
  177. esphome/components/i2c/i2c_bus_esp_idf.cpp +1 -1
  178. esphome/components/i2s_audio/__init__.py +131 -22
  179. esphome/components/i2s_audio/i2s_audio.h +44 -4
  180. esphome/components/i2s_audio/media_player/__init__.py +19 -9
  181. esphome/components/i2s_audio/microphone/__init__.py +63 -5
  182. esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +351 -61
  183. esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +40 -6
  184. esphome/components/i2s_audio/speaker/__init__.py +31 -5
  185. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +155 -19
  186. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +17 -4
  187. esphome/components/ili9xxx/ili9xxx_init.h +1 -1
  188. esphome/components/image/__init__.py +37 -17
  189. esphome/components/image/image.cpp +25 -8
  190. esphome/components/internal_temperature/internal_temperature.cpp +6 -4
  191. esphome/components/key_collector/__init__.py +35 -0
  192. esphome/components/key_collector/key_collector.cpp +8 -0
  193. esphome/components/key_collector/key_collector.h +10 -0
  194. esphome/components/kuntze/kuntze.cpp +2 -1
  195. esphome/components/ld2410/ld2410.h +1 -1
  196. esphome/components/ld2450/ld2450.h +1 -1
  197. esphome/components/light/__init__.py +57 -0
  198. esphome/components/lock/__init__.py +51 -4
  199. esphome/components/lock/automation.h +2 -13
  200. esphome/components/logger/__init__.py +22 -0
  201. esphome/components/logger/logger.cpp +154 -103
  202. esphome/components/logger/logger.h +211 -36
  203. esphome/components/logger/task_log_buffer.cpp +138 -0
  204. esphome/components/logger/task_log_buffer.h +69 -0
  205. esphome/components/lvgl/__init__.py +13 -5
  206. esphome/components/lvgl/automation.py +50 -1
  207. esphome/components/lvgl/defines.py +0 -1
  208. esphome/components/lvgl/lv_validation.py +10 -1
  209. esphome/components/lvgl/lvgl_esphome.cpp +5 -1
  210. esphome/components/lvgl/schemas.py +14 -14
  211. esphome/components/lvgl/text/__init__.py +1 -2
  212. esphome/components/lvgl/widgets/arc.py +7 -6
  213. esphome/components/lvgl/widgets/buttonmatrix.py +3 -3
  214. esphome/components/lvgl/widgets/checkbox.py +2 -2
  215. esphome/components/lvgl/widgets/dropdown.py +2 -1
  216. esphome/components/lvgl/widgets/img.py +15 -12
  217. esphome/components/mapping/__init__.py +134 -0
  218. esphome/components/matrix_keypad/matrix_keypad.cpp +2 -1
  219. esphome/components/max7219digit/max7219digit.cpp +28 -27
  220. esphome/components/mdns/__init__.py +11 -5
  221. esphome/components/mdns/mdns_component.cpp +11 -5
  222. esphome/components/mdns/mdns_component.h +3 -2
  223. esphome/components/mdns/mdns_esp32.cpp +4 -3
  224. esphome/components/mdns/mdns_esp8266.cpp +4 -2
  225. esphome/components/mdns/mdns_libretiny.cpp +4 -2
  226. esphome/components/mdns/mdns_rp2040.cpp +4 -2
  227. esphome/components/media_player/__init__.py +40 -6
  228. esphome/components/mhz19/sensor.py +11 -7
  229. esphome/components/micro_wake_word/__init__.py +99 -31
  230. esphome/components/micro_wake_word/automation.h +54 -0
  231. esphome/components/micro_wake_word/micro_wake_word.cpp +331 -319
  232. esphome/components/micro_wake_word/micro_wake_word.h +58 -105
  233. esphome/components/micro_wake_word/preprocessor_settings.h +19 -2
  234. esphome/components/micro_wake_word/streaming_model.cpp +158 -41
  235. esphome/components/micro_wake_word/streaming_model.h +85 -13
  236. esphome/components/microphone/__init__.py +139 -9
  237. esphome/components/microphone/automation.h +14 -2
  238. esphome/components/microphone/microphone.cpp +21 -0
  239. esphome/components/microphone/microphone.h +14 -5
  240. esphome/components/microphone/microphone_source.cpp +95 -0
  241. esphome/components/microphone/microphone_source.h +80 -0
  242. esphome/components/mics_4514/sensor.py +25 -14
  243. esphome/components/midea/climate.py +3 -4
  244. esphome/components/midea_ir/climate.py +3 -5
  245. esphome/components/mipi_spi/__init__.py +15 -0
  246. esphome/components/mipi_spi/display.py +474 -0
  247. esphome/components/mipi_spi/mipi_spi.cpp +481 -0
  248. esphome/components/mipi_spi/mipi_spi.h +171 -0
  249. esphome/components/mipi_spi/models/__init__.py +65 -0
  250. esphome/components/mipi_spi/models/amoled.py +72 -0
  251. esphome/components/mipi_spi/models/commands.py +82 -0
  252. esphome/components/mipi_spi/models/cyd.py +10 -0
  253. esphome/components/mipi_spi/models/ili.py +749 -0
  254. esphome/components/mipi_spi/models/jc.py +260 -0
  255. esphome/components/mipi_spi/models/lanbon.py +15 -0
  256. esphome/components/mipi_spi/models/lilygo.py +60 -0
  257. esphome/components/mipi_spi/models/waveshare.py +139 -0
  258. esphome/components/mitsubishi/climate.py +2 -5
  259. esphome/components/mitsubishi/mitsubishi.cpp +9 -9
  260. esphome/components/mixer/speaker/mixer_speaker.cpp +12 -22
  261. esphome/components/mixer/speaker/mixer_speaker.h +1 -3
  262. esphome/components/mlx90393/sensor.py +5 -0
  263. esphome/components/mlx90393/sensor_mlx90393.cpp +195 -13
  264. esphome/components/mlx90393/sensor_mlx90393.h +21 -4
  265. esphome/components/modbus/modbus.cpp +2 -1
  266. esphome/components/mqtt/__init__.py +1 -1
  267. esphome/components/mqtt/mqtt_client.cpp +6 -2
  268. esphome/components/mqtt/mqtt_const.h +4 -0
  269. esphome/components/mqtt/mqtt_fan.cpp +39 -0
  270. esphome/components/mqtt/mqtt_fan.h +2 -0
  271. esphome/components/ms5611/sensor.py +6 -6
  272. esphome/components/ms8607/sensor.py +3 -3
  273. esphome/components/network/__init__.py +1 -1
  274. esphome/components/nextion/base_component.py +17 -16
  275. esphome/components/nextion/display.py +11 -2
  276. esphome/components/nextion/nextion.cpp +39 -1
  277. esphome/components/nextion/nextion.h +50 -0
  278. esphome/components/noblex/climate.py +2 -9
  279. esphome/components/number/__init__.py +12 -9
  280. esphome/components/one_wire/one_wire_bus.cpp +14 -10
  281. esphome/components/one_wire/one_wire_bus.h +14 -8
  282. esphome/components/online_image/bmp_image.cpp +48 -11
  283. esphome/components/online_image/bmp_image.h +2 -0
  284. esphome/components/opentherm/binary_sensor/__init__.py +2 -4
  285. esphome/components/opentherm/number/__init__.py +11 -20
  286. esphome/components/opentherm/sensor/__init__.py +3 -3
  287. esphome/components/opentherm/switch/__init__.py +3 -5
  288. esphome/components/output/lock/__init__.py +11 -9
  289. esphome/components/packages/__init__.py +33 -31
  290. esphome/components/packet_transport/__init__.py +201 -0
  291. esphome/components/packet_transport/binary_sensor.py +19 -0
  292. esphome/components/packet_transport/packet_transport.cpp +534 -0
  293. esphome/components/packet_transport/packet_transport.h +154 -0
  294. esphome/components/packet_transport/sensor.py +19 -0
  295. esphome/components/pca9685/pca9685_output.cpp +2 -1
  296. esphome/components/pid/climate.py +2 -4
  297. esphome/components/pm2005/__init__.py +1 -0
  298. esphome/components/pm2005/pm2005.cpp +123 -0
  299. esphome/components/pm2005/pm2005.h +46 -0
  300. esphome/components/pm2005/sensor.py +86 -0
  301. esphome/components/pmsa003i/pmsa003i.cpp +43 -16
  302. esphome/components/pmsa003i/pmsa003i.h +25 -25
  303. esphome/components/pmsx003/pmsx003.cpp +195 -230
  304. esphome/components/pmsx003/pmsx003.h +51 -33
  305. esphome/components/pmsx003/sensor.py +21 -11
  306. esphome/components/pn7150/pn7150.h +2 -2
  307. esphome/components/pn7160/pn7160.h +2 -2
  308. esphome/components/prometheus/prometheus_handler.cpp +174 -0
  309. esphome/components/prometheus/prometheus_handler.h +17 -0
  310. esphome/components/psram/__init__.py +7 -5
  311. esphome/components/pulse_meter/pulse_meter_sensor.cpp +32 -12
  312. esphome/components/pulse_meter/pulse_meter_sensor.h +5 -5
  313. esphome/components/pzem004t/pzem004t.cpp +2 -1
  314. esphome/components/qspi_dbi/__init__.py +0 -1
  315. esphome/components/qspi_dbi/display.py +2 -1
  316. esphome/components/qspi_dbi/models.py +1 -2
  317. esphome/components/remote_base/__init__.py +91 -0
  318. esphome/components/remote_base/beo4_protocol.cpp +153 -0
  319. esphome/components/remote_base/beo4_protocol.h +43 -0
  320. esphome/components/remote_base/gobox_protocol.cpp +131 -0
  321. esphome/components/remote_base/gobox_protocol.h +54 -0
  322. esphome/components/remote_receiver/remote_receiver_esp32.cpp +16 -9
  323. esphome/components/resampler/speaker/resampler_speaker.cpp +12 -10
  324. esphome/components/resampler/speaker/resampler_speaker.h +1 -1
  325. esphome/components/rf_bridge/rf_bridge.cpp +2 -1
  326. esphome/components/scd30/sensor.py +2 -3
  327. esphome/components/scd4x/sensor.py +4 -5
  328. esphome/components/sdp3x/sensor.py +2 -1
  329. esphome/components/sds011/sds011.cpp +2 -1
  330. esphome/components/select/__init__.py +19 -20
  331. esphome/components/sen5x/sen5x.cpp +55 -36
  332. esphome/components/sen5x/sensor.py +1 -1
  333. esphome/components/senseair/sensor.py +3 -3
  334. esphome/components/sensor/__init__.py +158 -14
  335. esphome/components/sensor/filter.cpp +23 -0
  336. esphome/components/sensor/filter.h +22 -0
  337. esphome/components/sgp30/sensor.py +14 -16
  338. esphome/components/sgp4x/sensor.py +1 -1
  339. esphome/components/sht4x/sht4x.cpp +43 -22
  340. esphome/components/sht4x/sht4x.h +1 -1
  341. esphome/components/shtcx/sensor.py +6 -6
  342. esphome/components/slow_pwm/slow_pwm_output.cpp +2 -1
  343. esphome/components/sml/text_sensor/__init__.py +4 -6
  344. esphome/components/sound_level/__init__.py +0 -0
  345. esphome/components/sound_level/sensor.py +97 -0
  346. esphome/components/sound_level/sound_level.cpp +194 -0
  347. esphome/components/sound_level/sound_level.h +73 -0
  348. esphome/components/speaker/media_player/__init__.py +4 -8
  349. esphome/components/speaker/media_player/speaker_media_player.cpp +0 -18
  350. esphome/components/speaker/media_player/speaker_media_player.h +0 -11
  351. esphome/components/speaker/speaker.h +4 -7
  352. esphome/components/speed/fan/__init__.py +17 -16
  353. esphome/components/spi/spi.h +11 -1
  354. esphome/components/sprinkler/__init__.py +18 -19
  355. esphome/components/sprinkler/sprinkler.cpp +6 -5
  356. esphome/components/switch/__init__.py +32 -42
  357. esphome/components/syslog/__init__.py +41 -0
  358. esphome/components/syslog/esphome_syslog.cpp +49 -0
  359. esphome/components/syslog/esphome_syslog.h +27 -0
  360. esphome/components/t6615/sensor.py +3 -3
  361. esphome/components/t6615/t6615.cpp +2 -1
  362. esphome/components/tca9555/tca9555.cpp +11 -6
  363. esphome/components/tcl112/climate.py +2 -9
  364. esphome/components/template/alarm_control_panel/__init__.py +7 -6
  365. esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +21 -17
  366. esphome/components/template/alarm_control_panel/template_alarm_control_panel.h +2 -1
  367. esphome/components/template/cover/__init__.py +27 -21
  368. esphome/components/template/fan/__init__.py +14 -12
  369. esphome/components/template/lock/__init__.py +20 -25
  370. esphome/components/template/lock/automation.h +18 -0
  371. esphome/components/template/text/__init__.py +4 -3
  372. esphome/components/template/valve/__init__.py +32 -21
  373. esphome/components/template/valve/automation.h +24 -0
  374. esphome/components/text/__init__.py +32 -1
  375. esphome/components/text_sensor/__init__.py +24 -29
  376. esphome/components/thermostat/climate.py +5 -5
  377. esphome/components/time_based/cover.py +17 -16
  378. esphome/components/time_based/time_based_cover.cpp +2 -1
  379. esphome/components/tm1638/switch/__init__.py +10 -7
  380. esphome/components/tormatic/cover.py +4 -5
  381. esphome/components/toshiba/climate.py +3 -5
  382. esphome/components/touchscreen/touchscreen.cpp +3 -1
  383. esphome/components/tt21100/touchscreen/tt21100.cpp +1 -1
  384. esphome/components/tuya/climate/__init__.py +5 -6
  385. esphome/components/tuya/cover/__init__.py +6 -11
  386. esphome/components/tuya/select/__init__.py +15 -5
  387. esphome/components/tuya/select/tuya_select.cpp +6 -1
  388. esphome/components/tuya/select/tuya_select.h +5 -1
  389. esphome/components/uart/packet_transport/__init__.py +20 -0
  390. esphome/components/uart/packet_transport/uart_transport.cpp +88 -0
  391. esphome/components/uart/packet_transport/uart_transport.h +41 -0
  392. esphome/components/uart/switch/uart_switch.cpp +2 -1
  393. esphome/components/udp/__init__.py +126 -128
  394. esphome/components/udp/automation.h +40 -0
  395. esphome/components/udp/binary_sensor.py +3 -25
  396. esphome/components/udp/packet_transport/__init__.py +29 -0
  397. esphome/components/udp/packet_transport/udp_transport.cpp +36 -0
  398. esphome/components/udp/packet_transport/udp_transport.h +28 -0
  399. esphome/components/udp/sensor.py +3 -25
  400. esphome/components/udp/udp_component.cpp +26 -470
  401. esphome/components/udp/udp_component.h +21 -128
  402. esphome/components/update/__init__.py +31 -1
  403. esphome/components/uponor_smatrix/climate/__init__.py +4 -9
  404. esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp +2 -1
  405. esphome/components/uponor_smatrix/uponor_smatrix.cpp +2 -1
  406. esphome/components/uptime/text_sensor/__init__.py +47 -7
  407. esphome/components/uptime/text_sensor/uptime_text_sensor.cpp +12 -7
  408. esphome/components/uptime/text_sensor/uptime_text_sensor.h +19 -0
  409. esphome/components/valve/__init__.py +34 -3
  410. esphome/components/valve/automation.h +1 -19
  411. esphome/components/vl53l0x/sensor.py +11 -0
  412. esphome/components/vl53l0x/vl53l0x_sensor.cpp +5 -1
  413. esphome/components/vl53l0x/vl53l0x_sensor.h +2 -1
  414. esphome/components/voice_assistant/__init__.py +36 -10
  415. esphome/components/voice_assistant/voice_assistant.cpp +170 -144
  416. esphome/components/voice_assistant/voice_assistant.h +26 -25
  417. esphome/components/waveshare_epaper/display.py +6 -0
  418. esphome/components/waveshare_epaper/waveshare_epaper.cpp +439 -37
  419. esphome/components/waveshare_epaper/waveshare_epaper.h +60 -11
  420. esphome/components/weikai/weikai.cpp +0 -52
  421. esphome/components/whirlpool/climate.py +3 -5
  422. esphome/components/whynter/climate.py +3 -5
  423. esphome/components/xpt2046/touchscreen/xpt2046.cpp +1 -1
  424. esphome/components/yashima/climate.py +6 -6
  425. esphome/components/zhlt01/climate.py +2 -7
  426. esphome/config.py +13 -13
  427. esphome/config_validation.py +38 -58
  428. esphome/const.py +15 -1
  429. esphome/core/__init__.py +2 -0
  430. esphome/core/application.cpp +27 -10
  431. esphome/core/application.h +9 -1
  432. esphome/core/automation.h +4 -3
  433. esphome/core/component.cpp +28 -7
  434. esphome/core/component.h +10 -1
  435. esphome/core/defines.h +23 -17
  436. esphome/core/doxygen.h +13 -0
  437. esphome/core/macros.h +4 -0
  438. esphome/core/scheduler.cpp +7 -1
  439. esphome/cpp_generator.py +6 -2
  440. esphome/dashboard/web_server.py +3 -3
  441. esphome/helpers.py +39 -0
  442. esphome/loader.py +4 -0
  443. esphome/log.py +15 -19
  444. esphome/mqtt.py +23 -10
  445. esphome/platformio_api.py +1 -1
  446. esphome/schema_extractors.py +0 -1
  447. esphome/voluptuous_schema.py +3 -1
  448. esphome/vscode.py +15 -0
  449. esphome/wizard.py +47 -37
  450. esphome/zeroconf.py +7 -3
  451. {esphome-2025.4.1.dist-info → esphome-2025.5.0.dist-info}/METADATA +10 -11
  452. {esphome-2025.4.1.dist-info → esphome-2025.5.0.dist-info}/RECORD +456 -396
  453. {esphome-2025.4.1.dist-info → esphome-2025.5.0.dist-info}/WHEEL +1 -1
  454. esphome/components/esp32_ble/const_esp32c6.h +0 -74
  455. {esphome-2025.4.1.dist-info → esphome-2025.5.0.dist-info}/entry_points.txt +0 -0
  456. {esphome-2025.4.1.dist-info → esphome-2025.5.0.dist-info}/licenses/LICENSE +0 -0
  457. {esphome-2025.4.1.dist-info → esphome-2025.5.0.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
@@ -1,6 +1,7 @@
1
1
  #include "modbus.h"
2
2
  #include "esphome/core/log.h"
3
3
  #include "esphome/core/helpers.h"
4
+ #include "esphome/core/application.h"
4
5
 
5
6
  namespace esphome {
6
7
  namespace modbus {
@@ -13,7 +14,7 @@ void Modbus::setup() {
13
14
  }
14
15
  }
15
16
  void Modbus::loop() {
16
- const uint32_t now = millis();
17
+ const uint32_t now = App.get_loop_component_start_time();
17
18
 
18
19
  while (this->available()) {
19
20
  uint8_t byte;
@@ -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);
@@ -341,7 +345,7 @@ void MQTTClientComponent::loop() {
341
345
  this->disconnect_reason_.reset();
342
346
  }
343
347
 
344
- const uint32_t now = millis();
348
+ const uint32_t now = App.get_loop_component_start_time();
345
349
 
346
350
  switch (this->state_) {
347
351
  case MQTT_CLIENT_DISABLED:
@@ -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)
@@ -24,13 +24,13 @@ CONFIG_SCHEMA = (
24
24
  cv.Schema(
25
25
  {
26
26
  cv.GenerateID(): cv.declare_id(MS5611Component),
27
- cv.Required(CONF_TEMPERATURE): sensor.sensor_schema(
27
+ cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
28
28
  unit_of_measurement=UNIT_CELSIUS,
29
29
  accuracy_decimals=1,
30
30
  device_class=DEVICE_CLASS_TEMPERATURE,
31
31
  state_class=STATE_CLASS_MEASUREMENT,
32
32
  ),
33
- cv.Required(CONF_PRESSURE): sensor.sensor_schema(
33
+ cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
34
34
  unit_of_measurement=UNIT_HECTOPASCAL,
35
35
  icon=ICON_GAUGE,
36
36
  accuracy_decimals=1,
@@ -49,10 +49,10 @@ async def to_code(config):
49
49
  await cg.register_component(var, config)
50
50
  await i2c.register_i2c_device(var, config)
51
51
 
52
- if CONF_TEMPERATURE in config:
53
- sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
52
+ if temperature := config.get(CONF_TEMPERATURE):
53
+ sens = await sensor.new_sensor(temperature)
54
54
  cg.add(var.set_temperature_sensor(sens))
55
55
 
56
- if CONF_PRESSURE in config:
57
- sens = await sensor.new_sensor(config[CONF_PRESSURE])
56
+ if pressure := config.get(CONF_PRESSURE):
57
+ sens = await sensor.new_sensor(pressure)
58
58
  cg.add(var.set_pressure_sensor(sens))
@@ -29,19 +29,19 @@ CONFIG_SCHEMA = (
29
29
  cv.Schema(
30
30
  {
31
31
  cv.GenerateID(): cv.declare_id(MS8607Component),
32
- cv.Required(CONF_TEMPERATURE): sensor.sensor_schema(
32
+ cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
33
33
  unit_of_measurement=UNIT_CELSIUS,
34
34
  accuracy_decimals=2, # Resolution: 0.01
35
35
  device_class=DEVICE_CLASS_TEMPERATURE,
36
36
  state_class=STATE_CLASS_MEASUREMENT,
37
37
  ),
38
- cv.Required(CONF_PRESSURE): sensor.sensor_schema(
38
+ cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
39
39
  unit_of_measurement=UNIT_HECTOPASCAL,
40
40
  accuracy_decimals=2, # Resolution: 0.016
41
41
  device_class=DEVICE_CLASS_PRESSURE,
42
42
  state_class=STATE_CLASS_MEASUREMENT,
43
43
  ),
44
- cv.Required(CONF_HUMIDITY): sensor.sensor_schema(
44
+ cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
45
45
  unit_of_measurement=UNIT_PERCENT,
46
46
  accuracy_decimals=2, # Resolution: 0.04
47
47
  device_class=DEVICE_CLASS_HUMIDITY,
@@ -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 &&