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
@@ -5,6 +5,8 @@ import esphome.codegen as cg
5
5
  from esphome.components import mqtt, web_server
6
6
  import esphome.config_validation as cv
7
7
  from esphome.const import (
8
+ CONF_ENTITY_CATEGORY,
9
+ CONF_ICON,
8
10
  CONF_ID,
9
11
  CONF_MODE,
10
12
  CONF_MQTT_ID,
@@ -14,6 +16,7 @@ from esphome.const import (
14
16
  CONF_WEB_SERVER,
15
17
  )
16
18
  from esphome.core import CORE, coroutine_with_priority
19
+ from esphome.cpp_generator import MockObjClass
17
20
  from esphome.cpp_helpers import setup_entity
18
21
 
19
22
  CODEOWNERS = ["@mauritskorse"]
@@ -39,7 +42,7 @@ TEXT_MODES = {
39
42
  "PASSWORD": TextMode.TEXT_MODE_PASSWORD, # to be implemented for keys, passwords, etc.
40
43
  }
41
44
 
42
- TEXT_SCHEMA = (
45
+ _TEXT_SCHEMA = (
43
46
  cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
44
47
  .extend(cv.MQTT_COMPONENT_SCHEMA)
45
48
  .extend(
@@ -57,6 +60,34 @@ TEXT_SCHEMA = (
57
60
  )
58
61
 
59
62
 
63
+ def text_schema(
64
+ class_: MockObjClass = cv.UNDEFINED,
65
+ *,
66
+ icon: str = cv.UNDEFINED,
67
+ entity_category: str = cv.UNDEFINED,
68
+ mode: str = cv.UNDEFINED,
69
+ ) -> cv.Schema:
70
+ schema = {}
71
+
72
+ if class_ is not cv.UNDEFINED:
73
+ schema[cv.GenerateID()] = cv.declare_id(class_)
74
+
75
+ for key, default, validator in [
76
+ (CONF_ICON, icon, cv.icon),
77
+ (CONF_ENTITY_CATEGORY, entity_category, cv.entity_category),
78
+ (CONF_MODE, mode, cv.enum(TEXT_MODES, upper=True)),
79
+ ]:
80
+ if default is not cv.UNDEFINED:
81
+ schema[cv.Optional(key, default=default)] = validator
82
+
83
+ return _TEXT_SCHEMA.extend(schema)
84
+
85
+
86
+ # Remove before 2025.11.0
87
+ TEXT_SCHEMA = text_schema()
88
+ TEXT_SCHEMA.add_extra(cv.deprecated_schema_constant("text"))
89
+
90
+
60
91
  async def setup_text_core_(
61
92
  var,
62
93
  config,
@@ -125,7 +125,7 @@ async def map_filter_to_code(config, filter_id):
125
125
 
126
126
  validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
127
127
 
128
- TEXT_SENSOR_SCHEMA = (
128
+ _TEXT_SENSOR_SCHEMA = (
129
129
  cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
130
130
  .extend(cv.MQTT_COMPONENT_SCHEMA)
131
131
  .extend(
@@ -152,38 +152,33 @@ TEXT_SENSOR_SCHEMA = (
152
152
  )
153
153
  )
154
154
 
155
- _UNDEF = object()
156
-
157
155
 
158
156
  def text_sensor_schema(
159
- class_: MockObjClass = _UNDEF,
157
+ class_: MockObjClass = cv.UNDEFINED,
160
158
  *,
161
- icon: str = _UNDEF,
162
- entity_category: str = _UNDEF,
163
- device_class: str = _UNDEF,
159
+ device_class: str = cv.UNDEFINED,
160
+ entity_category: str = cv.UNDEFINED,
161
+ icon: str = cv.UNDEFINED,
164
162
  ) -> cv.Schema:
165
- schema = TEXT_SENSOR_SCHEMA
166
- if class_ is not _UNDEF:
167
- schema = schema.extend({cv.GenerateID(): cv.declare_id(class_)})
168
- if icon is not _UNDEF:
169
- schema = schema.extend({cv.Optional(CONF_ICON, default=icon): cv.icon})
170
- if device_class is not _UNDEF:
171
- schema = schema.extend(
172
- {
173
- cv.Optional(
174
- CONF_DEVICE_CLASS, default=device_class
175
- ): validate_device_class
176
- }
177
- )
178
- if entity_category is not _UNDEF:
179
- schema = schema.extend(
180
- {
181
- cv.Optional(
182
- CONF_ENTITY_CATEGORY, default=entity_category
183
- ): cv.entity_category
184
- }
185
- )
186
- return schema
163
+ schema = {}
164
+
165
+ if class_ is not cv.UNDEFINED:
166
+ schema[cv.GenerateID()] = cv.declare_id(class_)
167
+
168
+ for key, default, validator in [
169
+ (CONF_ICON, icon, cv.icon),
170
+ (CONF_DEVICE_CLASS, device_class, validate_device_class),
171
+ (CONF_ENTITY_CATEGORY, entity_category, cv.entity_category),
172
+ ]:
173
+ if default is not cv.UNDEFINED:
174
+ schema[cv.Optional(key, default=default)] = validator
175
+
176
+ return _TEXT_SENSOR_SCHEMA.extend(schema)
177
+
178
+
179
+ # Remove before 2025.11.0
180
+ TEXT_SENSOR_SCHEMA = text_sensor_schema()
181
+ TEXT_SENSOR_SCHEMA.add_extra(cv.deprecated_schema_constant("text_sensor"))
187
182
 
188
183
 
189
184
  async def build_filters(config):
@@ -516,9 +516,9 @@ def validate_thermostat(config):
516
516
 
517
517
 
518
518
  CONFIG_SCHEMA = cv.All(
519
- climate.CLIMATE_SCHEMA.extend(
519
+ climate.climate_schema(ThermostatClimate)
520
+ .extend(
520
521
  {
521
- cv.GenerateID(): cv.declare_id(ThermostatClimate),
522
522
  cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor),
523
523
  cv.Optional(CONF_HUMIDITY_SENSOR): cv.use_id(sensor.Sensor),
524
524
  cv.Required(CONF_IDLE_ACTION): automation.validate_automation(single=True),
@@ -631,7 +631,8 @@ CONFIG_SCHEMA = cv.All(
631
631
  single=True
632
632
  ),
633
633
  }
634
- ).extend(cv.COMPONENT_SCHEMA),
634
+ )
635
+ .extend(cv.COMPONENT_SCHEMA),
635
636
  cv.has_at_least_one_key(
636
637
  CONF_COOL_ACTION, CONF_DRY_ACTION, CONF_FAN_ONLY_ACTION, CONF_HEAT_ACTION
637
638
  ),
@@ -640,9 +641,8 @@ CONFIG_SCHEMA = cv.All(
640
641
 
641
642
 
642
643
  async def to_code(config):
643
- var = cg.new_Pvariable(config[CONF_ID])
644
+ var = await climate.new_climate(config)
644
645
  await cg.register_component(var, config)
645
- await climate.register_climate(var, config)
646
646
 
647
647
  heat_cool_mode_available = CONF_HEAT_ACTION in config and CONF_COOL_ACTION in config
648
648
  two_points_available = CONF_HEAT_ACTION in config and (
@@ -6,7 +6,6 @@ from esphome.const import (
6
6
  CONF_ASSUMED_STATE,
7
7
  CONF_CLOSE_ACTION,
8
8
  CONF_CLOSE_DURATION,
9
- CONF_ID,
10
9
  CONF_OPEN_ACTION,
11
10
  CONF_OPEN_DURATION,
12
11
  CONF_STOP_ACTION,
@@ -18,25 +17,27 @@ TimeBasedCover = time_based_ns.class_("TimeBasedCover", cover.Cover, cg.Componen
18
17
  CONF_HAS_BUILT_IN_ENDSTOP = "has_built_in_endstop"
19
18
  CONF_MANUAL_CONTROL = "manual_control"
20
19
 
21
- CONFIG_SCHEMA = cover.COVER_SCHEMA.extend(
22
- {
23
- cv.GenerateID(): cv.declare_id(TimeBasedCover),
24
- cv.Required(CONF_STOP_ACTION): automation.validate_automation(single=True),
25
- cv.Required(CONF_OPEN_ACTION): automation.validate_automation(single=True),
26
- cv.Required(CONF_OPEN_DURATION): cv.positive_time_period_milliseconds,
27
- cv.Required(CONF_CLOSE_ACTION): automation.validate_automation(single=True),
28
- cv.Required(CONF_CLOSE_DURATION): cv.positive_time_period_milliseconds,
29
- cv.Optional(CONF_HAS_BUILT_IN_ENDSTOP, default=False): cv.boolean,
30
- cv.Optional(CONF_MANUAL_CONTROL, default=False): cv.boolean,
31
- cv.Optional(CONF_ASSUMED_STATE, default=True): cv.boolean,
32
- }
33
- ).extend(cv.COMPONENT_SCHEMA)
20
+ CONFIG_SCHEMA = (
21
+ cover.cover_schema(TimeBasedCover)
22
+ .extend(
23
+ {
24
+ cv.Required(CONF_STOP_ACTION): automation.validate_automation(single=True),
25
+ cv.Required(CONF_OPEN_ACTION): automation.validate_automation(single=True),
26
+ cv.Required(CONF_OPEN_DURATION): cv.positive_time_period_milliseconds,
27
+ cv.Required(CONF_CLOSE_ACTION): automation.validate_automation(single=True),
28
+ cv.Required(CONF_CLOSE_DURATION): cv.positive_time_period_milliseconds,
29
+ cv.Optional(CONF_HAS_BUILT_IN_ENDSTOP, default=False): cv.boolean,
30
+ cv.Optional(CONF_MANUAL_CONTROL, default=False): cv.boolean,
31
+ cv.Optional(CONF_ASSUMED_STATE, default=True): cv.boolean,
32
+ }
33
+ )
34
+ .extend(cv.COMPONENT_SCHEMA)
35
+ )
34
36
 
35
37
 
36
38
  async def to_code(config):
37
- var = cg.new_Pvariable(config[CONF_ID])
39
+ var = await cover.new_cover(config)
38
40
  await cg.register_component(var, config)
39
- await cover.register_cover(var, config)
40
41
 
41
42
  await automation.build_automation(
42
43
  var.get_stop_trigger(), [], config[CONF_STOP_ACTION]
@@ -8,13 +8,16 @@ from ..display import CONF_TM1638_ID, TM1638Component, tm1638_ns
8
8
  TM1638SwitchLed = tm1638_ns.class_("TM1638SwitchLed", switch.Switch, cg.Component)
9
9
 
10
10
 
11
- CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend(
12
- {
13
- cv.GenerateID(): cv.declare_id(TM1638SwitchLed),
14
- cv.GenerateID(CONF_TM1638_ID): cv.use_id(TM1638Component),
15
- cv.Required(CONF_LED): cv.int_range(min=0, max=7),
16
- }
17
- ).extend(cv.COMPONENT_SCHEMA)
11
+ CONFIG_SCHEMA = (
12
+ switch.switch_schema(TM1638SwitchLed)
13
+ .extend(
14
+ {
15
+ cv.GenerateID(CONF_TM1638_ID): cv.use_id(TM1638Component),
16
+ cv.Required(CONF_LED): cv.int_range(min=0, max=7),
17
+ }
18
+ )
19
+ .extend(cv.COMPONENT_SCHEMA)
20
+ )
18
21
 
19
22
 
20
23
  async def to_code(config):
@@ -1,17 +1,17 @@
1
1
  import esphome.codegen as cg
2
2
  from esphome.components import cover, uart
3
3
  import esphome.config_validation as cv
4
- from esphome.const import CONF_CLOSE_DURATION, CONF_ID, CONF_OPEN_DURATION
4
+ from esphome.const import CONF_CLOSE_DURATION, CONF_OPEN_DURATION
5
5
 
6
6
  tormatic_ns = cg.esphome_ns.namespace("tormatic")
7
7
  Tormatic = tormatic_ns.class_("Tormatic", cover.Cover, cg.PollingComponent)
8
8
 
9
9
  CONFIG_SCHEMA = (
10
- cover.COVER_SCHEMA.extend(uart.UART_DEVICE_SCHEMA)
10
+ cover.cover_schema(Tormatic)
11
+ .extend(uart.UART_DEVICE_SCHEMA)
11
12
  .extend(cv.polling_component_schema("300ms"))
12
13
  .extend(
13
14
  {
14
- cv.GenerateID(): cv.declare_id(Tormatic),
15
15
  cv.Optional(
16
16
  CONF_OPEN_DURATION, default="15s"
17
17
  ): cv.positive_time_period_milliseconds,
@@ -34,9 +34,8 @@ FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema(
34
34
 
35
35
 
36
36
  async def to_code(config):
37
- var = cg.new_Pvariable(config[CONF_ID])
37
+ var = await cover.new_cover(config)
38
38
  await cg.register_component(var, config)
39
- await cover.register_cover(var, config)
40
39
  await uart.register_uart_device(var, config)
41
40
 
42
41
  cg.add(var.set_close_duration(config[CONF_CLOSE_DURATION]))
@@ -1,7 +1,7 @@
1
1
  import esphome.codegen as cg
2
2
  from esphome.components import climate_ir
3
3
  import esphome.config_validation as cv
4
- from esphome.const import CONF_ID, CONF_MODEL
4
+ from esphome.const import CONF_MODEL
5
5
 
6
6
  AUTO_LOAD = ["climate_ir"]
7
7
  CODEOWNERS = ["@kbx81"]
@@ -16,15 +16,13 @@ MODELS = {
16
16
  "RAC-PT1411HWRU-F": Model.MODEL_RAC_PT1411HWRU_F,
17
17
  }
18
18
 
19
- CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
19
+ CONFIG_SCHEMA = climate_ir.climare_ir_with_receiver_schema(ToshibaClimate).extend(
20
20
  {
21
- cv.GenerateID(): cv.declare_id(ToshibaClimate),
22
21
  cv.Optional(CONF_MODEL, default="generic"): cv.enum(MODELS, upper=True),
23
22
  }
24
23
  )
25
24
 
26
25
 
27
26
  async def to_code(config):
28
- var = cg.new_Pvariable(config[CONF_ID])
29
- await climate_ir.register_climate_ir(var, config)
27
+ var = await climate_ir.new_climate_ir(config)
30
28
  cg.add(var.set_model(config[CONF_MODEL]))
@@ -50,13 +50,15 @@ void Touchscreen::loop() {
50
50
  tp.second.x_prev = tp.second.x;
51
51
  tp.second.y_prev = tp.second.y;
52
52
  }
53
+ // The interrupt flag must be reset BEFORE calling update_touches, otherwise we might miss an interrupt that was
54
+ // triggered while we were reading touch data.
55
+ this->store_.touched = false;
53
56
  this->update_touches();
54
57
  if (this->skip_update_) {
55
58
  for (auto &tp : this->touches_) {
56
59
  tp.second.state &= ~STATE_RELEASING;
57
60
  }
58
61
  } else {
59
- this->store_.touched = false;
60
62
  this->defer([this]() { this->send_touches_(); });
61
63
  if (this->touch_timeout_ > 0) {
62
64
  // Simulate a touch after <this->touch_timeout_> ms. This will reset any existing timeout operation.
@@ -68,7 +68,7 @@ void TT21100Touchscreen::setup() {
68
68
  this->x_raw_max_ = this->display_->get_native_width();
69
69
  }
70
70
  if (this->y_raw_max_ == this->y_raw_min_) {
71
- this->x_raw_max_ = this->display_->get_native_height();
71
+ this->y_raw_max_ = this->display_->get_native_height();
72
72
  }
73
73
  }
74
74
 
@@ -4,7 +4,6 @@ from esphome.components import climate
4
4
  import esphome.config_validation as cv
5
5
  from esphome.const import (
6
6
  CONF_FAN_MODE,
7
- CONF_ID,
8
7
  CONF_PRESET,
9
8
  CONF_SUPPORTS_COOL,
10
9
  CONF_SUPPORTS_HEAT,
@@ -151,9 +150,9 @@ SWING_MODES = cv.Schema(
151
150
  )
152
151
 
153
152
  CONFIG_SCHEMA = cv.All(
154
- climate.CLIMATE_SCHEMA.extend(
153
+ climate.climate_schema(TuyaClimate)
154
+ .extend(
155
155
  {
156
- cv.GenerateID(): cv.declare_id(TuyaClimate),
157
156
  cv.GenerateID(CONF_TUYA_ID): cv.use_id(Tuya),
158
157
  cv.Optional(CONF_SUPPORTS_HEAT, default=True): cv.boolean,
159
158
  cv.Optional(CONF_SUPPORTS_COOL, default=False): cv.boolean,
@@ -186,7 +185,8 @@ CONFIG_SCHEMA = cv.All(
186
185
  "'eco_temperature' has been moved inside of the 'eco' config block under 'preset' as 'temperature'"
187
186
  ),
188
187
  }
189
- ).extend(cv.COMPONENT_SCHEMA),
188
+ )
189
+ .extend(cv.COMPONENT_SCHEMA),
190
190
  cv.has_at_least_one_key(CONF_TARGET_TEMPERATURE_DATAPOINT, CONF_SWITCH_DATAPOINT),
191
191
  validate_temperature_multipliers,
192
192
  validate_cooling_values,
@@ -194,9 +194,8 @@ CONFIG_SCHEMA = cv.All(
194
194
 
195
195
 
196
196
  async def to_code(config):
197
- var = cg.new_Pvariable(config[CONF_ID])
197
+ var = await climate.new_climate(config)
198
198
  await cg.register_component(var, config)
199
- await climate.register_climate(var, config)
200
199
 
201
200
  paren = await cg.get_variable(config[CONF_TUYA_ID])
202
201
  cg.add(var.set_tuya_parent(paren))
@@ -1,12 +1,7 @@
1
1
  import esphome.codegen as cg
2
2
  from esphome.components import cover
3
3
  import esphome.config_validation as cv
4
- from esphome.const import (
5
- CONF_MAX_VALUE,
6
- CONF_MIN_VALUE,
7
- CONF_OUTPUT_ID,
8
- CONF_RESTORE_MODE,
9
- )
4
+ from esphome.const import CONF_MAX_VALUE, CONF_MIN_VALUE, CONF_RESTORE_MODE
10
5
 
11
6
  from .. import CONF_TUYA_ID, Tuya, tuya_ns
12
7
 
@@ -38,9 +33,9 @@ def validate_range(config):
38
33
 
39
34
 
40
35
  CONFIG_SCHEMA = cv.All(
41
- cover.COVER_SCHEMA.extend(
36
+ cover.cover_schema(TuyaCover)
37
+ .extend(
42
38
  {
43
- cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(TuyaCover),
44
39
  cv.GenerateID(CONF_TUYA_ID): cv.use_id(Tuya),
45
40
  cv.Optional(CONF_CONTROL_DATAPOINT): cv.uint8_t,
46
41
  cv.Optional(CONF_DIRECTION_DATAPOINT): cv.uint8_t,
@@ -54,15 +49,15 @@ CONFIG_SCHEMA = cv.All(
54
49
  RESTORE_MODES, upper=True
55
50
  ),
56
51
  },
57
- ).extend(cv.COMPONENT_SCHEMA),
52
+ )
53
+ .extend(cv.COMPONENT_SCHEMA),
58
54
  validate_range,
59
55
  )
60
56
 
61
57
 
62
58
  async def to_code(config):
63
- var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
59
+ var = await cover.new_cover(config)
64
60
  await cg.register_component(var, config)
65
- await cover.register_cover(var, config)
66
61
 
67
62
  if CONF_CONTROL_DATAPOINT in config:
68
63
  cg.add(var.set_control_id(config[CONF_CONTROL_DATAPOINT]))
@@ -1,7 +1,12 @@
1
1
  import esphome.codegen as cg
2
2
  from esphome.components import select
3
3
  import esphome.config_validation as cv
4
- from esphome.const import CONF_ENUM_DATAPOINT, CONF_OPTIMISTIC, CONF_OPTIONS
4
+ from esphome.const import (
5
+ CONF_ENUM_DATAPOINT,
6
+ CONF_INT_DATAPOINT,
7
+ CONF_OPTIMISTIC,
8
+ CONF_OPTIONS,
9
+ )
5
10
 
6
11
  from .. import CONF_TUYA_ID, Tuya, tuya_ns
7
12
 
@@ -26,17 +31,19 @@ def ensure_option_map(value):
26
31
  return value
27
32
 
28
33
 
29
- CONFIG_SCHEMA = (
34
+ CONFIG_SCHEMA = cv.All(
30
35
  select.select_schema(TuyaSelect)
31
36
  .extend(
32
37
  {
33
38
  cv.GenerateID(CONF_TUYA_ID): cv.use_id(Tuya),
34
- cv.Required(CONF_ENUM_DATAPOINT): cv.uint8_t,
39
+ cv.Optional(CONF_ENUM_DATAPOINT): cv.uint8_t,
40
+ cv.Optional(CONF_INT_DATAPOINT): cv.uint8_t,
35
41
  cv.Required(CONF_OPTIONS): ensure_option_map,
36
42
  cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean,
37
43
  }
38
44
  )
39
- .extend(cv.COMPONENT_SCHEMA)
45
+ .extend(cv.COMPONENT_SCHEMA),
46
+ cv.has_exactly_one_key(CONF_ENUM_DATAPOINT, CONF_INT_DATAPOINT),
40
47
  )
41
48
 
42
49
 
@@ -47,5 +54,8 @@ async def to_code(config):
47
54
  cg.add(var.set_select_mappings(list(options_map.keys())))
48
55
  parent = await cg.get_variable(config[CONF_TUYA_ID])
49
56
  cg.add(var.set_tuya_parent(parent))
50
- cg.add(var.set_select_id(config[CONF_ENUM_DATAPOINT]))
57
+ if enum_datapoint := config.get(CONF_ENUM_DATAPOINT, None) is not None:
58
+ cg.add(var.set_select_id(enum_datapoint, False))
59
+ if int_datapoint := config.get(CONF_INT_DATAPOINT, None) is not None:
60
+ cg.add(var.set_select_id(int_datapoint, True))
51
61
  cg.add(var.set_optimistic(config[CONF_OPTIMISTIC]))
@@ -31,7 +31,11 @@ void TuyaSelect::control(const std::string &value) {
31
31
  if (idx.has_value()) {
32
32
  uint8_t mapping = this->mappings_.at(idx.value());
33
33
  ESP_LOGV(TAG, "Setting %u datapoint value to %u:%s", this->select_id_, mapping, value.c_str());
34
- this->parent_->set_enum_datapoint_value(this->select_id_, mapping);
34
+ if (this->is_int_) {
35
+ this->parent_->set_integer_datapoint_value(this->select_id_, mapping);
36
+ } else {
37
+ this->parent_->set_enum_datapoint_value(this->select_id_, mapping);
38
+ }
35
39
  return;
36
40
  }
37
41
 
@@ -41,6 +45,7 @@ void TuyaSelect::control(const std::string &value) {
41
45
  void TuyaSelect::dump_config() {
42
46
  LOG_SELECT("", "Tuya Select", this);
43
47
  ESP_LOGCONFIG(TAG, " Select has datapoint ID %u", this->select_id_);
48
+ ESP_LOGCONFIG(TAG, " Data type: %s", this->is_int_ ? "int" : "enum");
44
49
  ESP_LOGCONFIG(TAG, " Options are:");
45
50
  auto options = this->traits.get_options();
46
51
  for (auto i = 0; i < this->mappings_.size(); i++) {
@@ -16,7 +16,10 @@ class TuyaSelect : public select::Select, public Component {
16
16
 
17
17
  void set_tuya_parent(Tuya *parent) { this->parent_ = parent; }
18
18
  void set_optimistic(bool optimistic) { this->optimistic_ = optimistic; }
19
- void set_select_id(uint8_t select_id) { this->select_id_ = select_id; }
19
+ void set_select_id(uint8_t select_id, bool is_int) {
20
+ this->select_id_ = select_id;
21
+ this->is_int_ = is_int;
22
+ }
20
23
  void set_select_mappings(std::vector<uint8_t> mappings) { this->mappings_ = std::move(mappings); }
21
24
 
22
25
  protected:
@@ -26,6 +29,7 @@ class TuyaSelect : public select::Select, public Component {
26
29
  bool optimistic_ = false;
27
30
  uint8_t select_id_;
28
31
  std::vector<uint8_t> mappings_;
32
+ bool is_int_ = false;
29
33
  };
30
34
 
31
35
  } // namespace tuya
@@ -0,0 +1,20 @@
1
+ from esphome.components.packet_transport import (
2
+ PacketTransport,
3
+ new_packet_transport,
4
+ transport_schema,
5
+ )
6
+ from esphome.cpp_types import PollingComponent
7
+
8
+ from .. import UART_DEVICE_SCHEMA, register_uart_device, uart_ns
9
+
10
+ CODEOWNERS = ["@clydebarrow"]
11
+ DEPENDENCIES = ["uart"]
12
+
13
+ UARTTransport = uart_ns.class_("UARTTransport", PacketTransport, PollingComponent)
14
+
15
+ CONFIG_SCHEMA = transport_schema(UARTTransport).extend(UART_DEVICE_SCHEMA)
16
+
17
+
18
+ async def to_code(config):
19
+ var, _ = await new_packet_transport(config)
20
+ await register_uart_device(var, config)
@@ -0,0 +1,88 @@
1
+ #include "esphome/core/log.h"
2
+ #include "esphome/core/application.h"
3
+ #include "uart_transport.h"
4
+
5
+ namespace esphome {
6
+ namespace uart {
7
+
8
+ static const char *const TAG = "uart_transport";
9
+
10
+ void UARTTransport::loop() {
11
+ PacketTransport::loop();
12
+
13
+ while (this->parent_->available()) {
14
+ uint8_t byte;
15
+ if (!this->parent_->read_byte(&byte)) {
16
+ ESP_LOGW(TAG, "Failed to read byte from UART");
17
+ return;
18
+ }
19
+ if (byte == FLAG_BYTE) {
20
+ if (this->rx_started_ && this->receive_buffer_.size() > 6) {
21
+ auto len = this->receive_buffer_.size();
22
+ auto crc = crc16(this->receive_buffer_.data(), len - 2);
23
+ if (crc != (this->receive_buffer_[len - 2] | (this->receive_buffer_[len - 1] << 8))) {
24
+ ESP_LOGD(TAG, "CRC mismatch, discarding packet");
25
+ this->rx_started_ = false;
26
+ this->receive_buffer_.clear();
27
+ continue;
28
+ }
29
+ this->receive_buffer_.resize(len - 2);
30
+ this->process_(this->receive_buffer_);
31
+ this->rx_started_ = false;
32
+ } else {
33
+ this->rx_started_ = true;
34
+ }
35
+ this->receive_buffer_.clear();
36
+ this->rx_control_ = false;
37
+ continue;
38
+ }
39
+ if (!this->rx_started_)
40
+ continue;
41
+ if (byte == CONTROL_BYTE) {
42
+ this->rx_control_ = true;
43
+ continue;
44
+ }
45
+ if (this->rx_control_) {
46
+ byte ^= 0x20;
47
+ this->rx_control_ = false;
48
+ }
49
+ if (this->receive_buffer_.size() == MAX_PACKET_SIZE) {
50
+ ESP_LOGD(TAG, "Packet too large, discarding");
51
+ this->rx_started_ = false;
52
+ this->receive_buffer_.clear();
53
+ continue;
54
+ }
55
+ this->receive_buffer_.push_back(byte);
56
+ }
57
+ }
58
+
59
+ void UARTTransport::update() {
60
+ this->updated_ = true;
61
+ this->resend_data_ = true;
62
+ PacketTransport::update();
63
+ }
64
+
65
+ /**
66
+ * Write a byte to the UART bus. If the byte is a flag or control byte, it will be escaped.
67
+ * @param byte The byte to write.
68
+ */
69
+ void UARTTransport::write_byte_(uint8_t byte) const {
70
+ if (byte == FLAG_BYTE || byte == CONTROL_BYTE) {
71
+ this->parent_->write_byte(CONTROL_BYTE);
72
+ byte ^= 0x20;
73
+ }
74
+ this->parent_->write_byte(byte);
75
+ }
76
+
77
+ void UARTTransport::send_packet(const std::vector<uint8_t> &buf) const {
78
+ this->parent_->write_byte(FLAG_BYTE);
79
+ for (uint8_t byte : buf) {
80
+ this->write_byte_(byte);
81
+ }
82
+ auto crc = crc16(buf.data(), buf.size());
83
+ this->write_byte_(crc & 0xFF);
84
+ this->write_byte_(crc >> 8);
85
+ this->parent_->write_byte(FLAG_BYTE);
86
+ }
87
+ } // namespace uart
88
+ } // namespace esphome
@@ -0,0 +1,41 @@
1
+ #pragma once
2
+
3
+ #include "esphome/core/component.h"
4
+ #include "esphome/components/packet_transport/packet_transport.h"
5
+ #include <vector>
6
+ #include "../uart.h"
7
+
8
+ namespace esphome {
9
+ namespace uart {
10
+
11
+ /**
12
+ * A transport protocol for sending and receiving packets over a UART connection.
13
+ * The protocol is based on Asynchronous HDLC framing. (https://en.wikipedia.org/wiki/High-Level_Data_Link_Control)
14
+ * There are two special bytes: FLAG_BYTE and CONTROL_BYTE.
15
+ * A 16-bit CRC is appended to the packet, then
16
+ * the protocol wraps the resulting data between FLAG_BYTEs.
17
+ * Any occurrence of FLAG_BYTE or CONTROL_BYTE in the data is escaped by emitting CONTROL_BYTE followed by the byte
18
+ * XORed with 0x20.
19
+ */
20
+ static const uint16_t MAX_PACKET_SIZE = 508;
21
+ static const uint8_t FLAG_BYTE = 0x7E;
22
+ static const uint8_t CONTROL_BYTE = 0x7D;
23
+
24
+ class UARTTransport : public packet_transport::PacketTransport, public UARTDevice {
25
+ public:
26
+ void loop() override;
27
+ void update() override;
28
+ float get_setup_priority() const override { return setup_priority::PROCESSOR; }
29
+
30
+ protected:
31
+ void write_byte_(uint8_t byte) const;
32
+ void send_packet(const std::vector<uint8_t> &buf) const override;
33
+ bool should_send() override { return true; };
34
+ size_t get_max_packet_size() override { return MAX_PACKET_SIZE; }
35
+ std::vector<uint8_t> receive_buffer_{};
36
+ bool rx_started_{};
37
+ bool rx_control_{};
38
+ };
39
+
40
+ } // namespace uart
41
+ } // namespace esphome