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
@@ -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
@@ -1,5 +1,6 @@
1
1
  #include "uart_switch.h"
2
2
  #include "esphome/core/log.h"
3
+ #include "esphome/core/application.h"
3
4
 
4
5
  namespace esphome {
5
6
  namespace uart {
@@ -8,7 +9,7 @@ static const char *const TAG = "uart.switch";
8
9
 
9
10
  void UARTSwitch::loop() {
10
11
  if (this->send_every_) {
11
- const uint32_t now = millis();
12
+ const uint32_t now = App.get_loop_component_start_time();
12
13
  if (now - this->last_transmission_ > this->send_every_) {
13
14
  this->write_command_(this->state);
14
15
  this->last_transmission_ = now;
@@ -1,164 +1,162 @@
1
- import hashlib
2
-
1
+ from esphome import automation
2
+ from esphome.automation import Trigger
3
3
  import esphome.codegen as cg
4
- from esphome.components.api import CONF_ENCRYPTION
5
- from esphome.components.binary_sensor import BinarySensor
6
- from esphome.components.sensor import Sensor
7
- import esphome.config_validation as cv
8
- from esphome.const import (
4
+ from esphome.components.packet_transport import (
9
5
  CONF_BINARY_SENSORS,
10
- CONF_ID,
11
- CONF_INTERNAL,
12
- CONF_KEY,
13
- CONF_NAME,
14
- CONF_PORT,
6
+ CONF_ENCRYPTION,
7
+ CONF_PING_PONG_ENABLE,
8
+ CONF_PROVIDERS,
9
+ CONF_ROLLING_CODE_ENABLE,
15
10
  CONF_SENSORS,
16
11
  )
17
- from esphome.cpp_generator import MockObjClass
12
+ import esphome.config_validation as cv
13
+ from esphome.const import CONF_DATA, CONF_ID, CONF_PORT, CONF_TRIGGER_ID
14
+ from esphome.core import Lambda
15
+ from esphome.cpp_generator import ExpressionStatement, MockObj
18
16
 
19
17
  CODEOWNERS = ["@clydebarrow"]
20
18
  DEPENDENCIES = ["network"]
21
- AUTO_LOAD = ["socket", "xxtea"]
22
- MULTI_CONF = True
19
+ AUTO_LOAD = ["socket"]
23
20
 
21
+ MULTI_CONF = True
24
22
  udp_ns = cg.esphome_ns.namespace("udp")
25
- UDPComponent = udp_ns.class_("UDPComponent", cg.PollingComponent)
23
+ UDPComponent = udp_ns.class_("UDPComponent", cg.Component)
24
+ UDPWriteAction = udp_ns.class_("UDPWriteAction", automation.Action)
25
+ trigger_args = cg.std_vector.template(cg.uint8)
26
26
 
27
- CONF_BROADCAST = "broadcast"
28
- CONF_BROADCAST_ID = "broadcast_id"
29
27
  CONF_ADDRESSES = "addresses"
30
28
  CONF_LISTEN_ADDRESS = "listen_address"
31
- CONF_PROVIDER = "provider"
32
- CONF_PROVIDERS = "providers"
33
- CONF_REMOTE_ID = "remote_id"
34
29
  CONF_UDP_ID = "udp_id"
35
- CONF_PING_PONG_ENABLE = "ping_pong_enable"
36
- CONF_PING_PONG_RECYCLE_TIME = "ping_pong_recycle_time"
37
- CONF_ROLLING_CODE_ENABLE = "rolling_code_enable"
38
-
39
-
40
- def sensor_validation(cls: MockObjClass):
41
- return cv.maybe_simple_value(
42
- cv.Schema(
43
- {
44
- cv.Required(CONF_ID): cv.use_id(cls),
45
- cv.Optional(CONF_BROADCAST_ID): cv.validate_id_name,
46
- }
47
- ),
48
- key=CONF_ID,
49
- )
50
-
30
+ CONF_ON_RECEIVE = "on_receive"
31
+ CONF_LISTEN_PORT = "listen_port"
32
+ CONF_BROADCAST_PORT = "broadcast_port"
51
33
 
52
- ENCRYPTION_SCHEMA = {
53
- cv.Optional(CONF_ENCRYPTION): cv.maybe_simple_value(
54
- cv.Schema(
55
- {
56
- cv.Required(CONF_KEY): cv.string,
57
- }
58
- ),
59
- key=CONF_KEY,
60
- )
61
- }
62
-
63
- PROVIDER_SCHEMA = cv.Schema(
34
+ UDP_SCHEMA = cv.Schema(
64
35
  {
65
- cv.Required(CONF_NAME): cv.valid_name,
36
+ cv.GenerateID(CONF_UDP_ID): cv.use_id(UDPComponent),
66
37
  }
67
- ).extend(ENCRYPTION_SCHEMA)
38
+ )
68
39
 
69
40
 
70
- def validate_(config):
71
- if CONF_ENCRYPTION in config:
72
- if CONF_SENSORS not in config and CONF_BINARY_SENSORS not in config:
73
- raise cv.Invalid("No sensors or binary sensors to encrypt")
74
- elif config[CONF_ROLLING_CODE_ENABLE]:
75
- raise cv.Invalid("Rolling code requires an encryption key")
76
- if config[CONF_PING_PONG_ENABLE]:
77
- if not any(CONF_ENCRYPTION in p for p in config.get(CONF_PROVIDERS) or ()):
78
- raise cv.Invalid("Ping-pong requires at least one encrypted provider")
79
- return config
41
+ def is_relocated(option):
42
+ def validator(value):
43
+ raise cv.Invalid(
44
+ f"The '{option}' option should now be configured in the 'packet_transport' component"
45
+ )
80
46
 
47
+ return validator
81
48
 
82
- CONFIG_SCHEMA = cv.All(
83
- cv.polling_component_schema("15s")
84
- .extend(
85
- {
86
- cv.GenerateID(): cv.declare_id(UDPComponent),
87
- cv.Optional(CONF_PORT, default=18511): cv.port,
88
- cv.Optional(
89
- CONF_LISTEN_ADDRESS, default="255.255.255.255"
90
- ): cv.ipv4address_multi_broadcast,
91
- cv.Optional(CONF_ADDRESSES, default=["255.255.255.255"]): cv.ensure_list(
92
- cv.ipv4address,
93
- ),
94
- cv.Optional(CONF_ROLLING_CODE_ENABLE, default=False): cv.boolean,
95
- cv.Optional(CONF_PING_PONG_ENABLE, default=False): cv.boolean,
96
- cv.Optional(
97
- CONF_PING_PONG_RECYCLE_TIME, default="600s"
98
- ): cv.positive_time_period_seconds,
99
- cv.Optional(CONF_SENSORS): cv.ensure_list(sensor_validation(Sensor)),
100
- cv.Optional(CONF_BINARY_SENSORS): cv.ensure_list(
101
- sensor_validation(BinarySensor)
102
- ),
103
- cv.Optional(CONF_PROVIDERS): cv.ensure_list(PROVIDER_SCHEMA),
104
- },
49
+
50
+ RELOCATED = {
51
+ cv.Optional(x): is_relocated(x)
52
+ for x in (
53
+ CONF_PROVIDERS,
54
+ CONF_ENCRYPTION,
55
+ CONF_PING_PONG_ENABLE,
56
+ CONF_ROLLING_CODE_ENABLE,
57
+ CONF_SENSORS,
58
+ CONF_BINARY_SENSORS,
105
59
  )
106
- .extend(ENCRYPTION_SCHEMA),
107
- validate_,
108
- )
60
+ }
109
61
 
110
- SENSOR_SCHEMA = cv.Schema(
62
+ CONFIG_SCHEMA = cv.COMPONENT_SCHEMA.extend(
111
63
  {
112
- cv.Optional(CONF_REMOTE_ID): cv.string_strict,
113
- cv.Required(CONF_PROVIDER): cv.valid_name,
114
- cv.GenerateID(CONF_UDP_ID): cv.use_id(UDPComponent),
64
+ cv.GenerateID(): cv.declare_id(UDPComponent),
65
+ cv.Optional(CONF_PORT, default=18511): cv.Any(
66
+ cv.port,
67
+ cv.Schema(
68
+ {
69
+ cv.Required(CONF_LISTEN_PORT): cv.port,
70
+ cv.Required(CONF_BROADCAST_PORT): cv.port,
71
+ }
72
+ ),
73
+ ),
74
+ cv.Optional(
75
+ CONF_LISTEN_ADDRESS, default="255.255.255.255"
76
+ ): cv.ipv4address_multi_broadcast,
77
+ cv.Optional(CONF_ADDRESSES, default=["255.255.255.255"]): cv.ensure_list(
78
+ cv.ipv4address,
79
+ ),
80
+ cv.Optional(CONF_ON_RECEIVE): automation.validate_automation(
81
+ {
82
+ cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
83
+ Trigger.template(trigger_args)
84
+ ),
85
+ }
86
+ ),
115
87
  }
116
- )
117
-
88
+ ).extend(RELOCATED)
118
89
 
119
- def require_internal_with_name(config):
120
- if CONF_NAME in config and CONF_INTERNAL not in config:
121
- raise cv.Invalid("Must provide internal: config when using name:")
122
- return config
123
90
 
124
-
125
- def hash_encryption_key(config: dict):
126
- return list(hashlib.sha256(config[CONF_KEY].encode()).digest())
91
+ async def register_udp_client(var, config):
92
+ udp_var = await cg.get_variable(config[CONF_UDP_ID])
93
+ cg.add(var.set_parent(udp_var))
94
+ return udp_var
127
95
 
128
96
 
129
97
  async def to_code(config):
130
98
  cg.add_define("USE_UDP")
131
99
  cg.add_global(udp_ns.using)
132
100
  var = cg.new_Pvariable(config[CONF_ID])
133
- await cg.register_component(var, config)
134
- cg.add(var.set_port(config[CONF_PORT]))
135
- cg.add(var.set_rolling_code_enable(config[CONF_ROLLING_CODE_ENABLE]))
136
- cg.add(var.set_ping_pong_enable(config[CONF_PING_PONG_ENABLE]))
137
- cg.add(
138
- var.set_ping_pong_recycle_time(
139
- config[CONF_PING_PONG_RECYCLE_TIME].total_seconds
140
- )
141
- )
142
- for sens_conf in config.get(CONF_SENSORS, ()):
143
- sens_id = sens_conf[CONF_ID]
144
- sensor = await cg.get_variable(sens_id)
145
- bcst_id = sens_conf.get(CONF_BROADCAST_ID, sens_id.id)
146
- cg.add(var.add_sensor(bcst_id, sensor))
147
- for sens_conf in config.get(CONF_BINARY_SENSORS, ()):
148
- sens_id = sens_conf[CONF_ID]
149
- sensor = await cg.get_variable(sens_id)
150
- bcst_id = sens_conf.get(CONF_BROADCAST_ID, sens_id.id)
151
- cg.add(var.add_binary_sensor(bcst_id, sensor))
101
+ var = await cg.register_component(var, config)
102
+ conf_port = config[CONF_PORT]
103
+ if isinstance(conf_port, int):
104
+ cg.add(var.set_listen_port(conf_port))
105
+ cg.add(var.set_broadcast_port(conf_port))
106
+ else:
107
+ cg.add(var.set_listen_port(conf_port[CONF_LISTEN_PORT]))
108
+ cg.add(var.set_broadcast_port(conf_port[CONF_BROADCAST_PORT]))
109
+ if (listen_address := str(config[CONF_LISTEN_ADDRESS])) != "255.255.255.255":
110
+ cg.add(var.set_listen_address(listen_address))
152
111
  for address in config[CONF_ADDRESSES]:
153
112
  cg.add(var.add_address(str(address)))
113
+ if on_receive := config.get(CONF_ON_RECEIVE):
114
+ on_receive = on_receive[0]
115
+ trigger = cg.new_Pvariable(on_receive[CONF_TRIGGER_ID])
116
+ trigger = await automation.build_automation(
117
+ trigger, [(trigger_args, "data")], on_receive
118
+ )
119
+ trigger = Lambda(str(ExpressionStatement(trigger.trigger(MockObj("data")))))
120
+ trigger = await cg.process_lambda(trigger, [(trigger_args, "data")])
121
+ cg.add(var.add_listener(trigger))
122
+ cg.add(var.set_should_listen())
123
+
124
+
125
+ def validate_raw_data(value):
126
+ if isinstance(value, str):
127
+ return value.encode("utf-8")
128
+ if isinstance(value, str):
129
+ return value
130
+ if isinstance(value, list):
131
+ return cv.Schema([cv.hex_uint8_t])(value)
132
+ raise cv.Invalid(
133
+ "data must either be a string wrapped in quotes or a list of bytes"
134
+ )
154
135
 
155
- if encryption := config.get(CONF_ENCRYPTION):
156
- cg.add(var.set_encryption_key(hash_encryption_key(encryption)))
157
136
 
158
- for provider in config.get(CONF_PROVIDERS, ()):
159
- name = provider[CONF_NAME]
160
- cg.add(var.add_provider(name))
161
- if (listen_address := str(config[CONF_LISTEN_ADDRESS])) != "255.255.255.255":
162
- cg.add(var.set_listen_address(listen_address))
163
- if encryption := provider.get(CONF_ENCRYPTION):
164
- cg.add(var.set_provider_encryption(name, hash_encryption_key(encryption)))
137
+ @automation.register_action(
138
+ "udp.write",
139
+ UDPWriteAction,
140
+ cv.maybe_simple_value(
141
+ {
142
+ cv.GenerateID(): cv.use_id(UDPComponent),
143
+ cv.Required(CONF_DATA): cv.templatable(validate_raw_data),
144
+ },
145
+ key=CONF_DATA,
146
+ ),
147
+ )
148
+ async def udp_write_to_code(config, action_id, template_arg, args):
149
+ var = cg.new_Pvariable(action_id, template_arg)
150
+ udp_var = await cg.get_variable(config[CONF_ID])
151
+ await cg.register_parented(var, udp_var)
152
+ cg.add(udp_var.set_should_broadcast())
153
+ data = config[CONF_DATA]
154
+ if isinstance(data, bytes):
155
+ data = list(data)
156
+
157
+ if cg.is_template(data):
158
+ templ = await cg.templatable(data, args, cg.std_vector.template(cg.uint8))
159
+ cg.add(var.set_data_template(templ))
160
+ else:
161
+ cg.add(var.set_data_static(data))
162
+ return var
@@ -0,0 +1,40 @@
1
+ #pragma once
2
+
3
+ #include "udp_component.h"
4
+ #ifdef USE_NETWORK
5
+ #include "esphome/core/automation.h"
6
+
7
+ #include <vector>
8
+
9
+ namespace esphome {
10
+ namespace udp {
11
+
12
+ template<typename... Ts> class UDPWriteAction : public Action<Ts...>, public Parented<UDPComponent> {
13
+ public:
14
+ void set_data_template(std::function<std::vector<uint8_t>(Ts...)> func) {
15
+ this->data_func_ = func;
16
+ this->static_ = false;
17
+ }
18
+ void set_data_static(const std::vector<uint8_t> &data) {
19
+ this->data_static_ = data;
20
+ this->static_ = true;
21
+ }
22
+
23
+ void play(Ts... x) override {
24
+ if (this->static_) {
25
+ this->parent_->send_packet(this->data_static_);
26
+ } else {
27
+ auto val = this->data_func_(x...);
28
+ this->parent_->send_packet(val);
29
+ }
30
+ }
31
+
32
+ protected:
33
+ bool static_{false};
34
+ std::function<std::vector<uint8_t>(Ts...)> data_func_{};
35
+ std::vector<uint8_t> data_static_{};
36
+ };
37
+
38
+ } // namespace udp
39
+ } // namespace esphome
40
+ #endif
@@ -1,27 +1,5 @@
1
- import esphome.codegen as cg
2
- from esphome.components import binary_sensor
3
- from esphome.config_validation import All, has_at_least_one_key
4
- from esphome.const import CONF_ID
1
+ import esphome.config_validation as cv
5
2
 
6
- from . import (
7
- CONF_PROVIDER,
8
- CONF_REMOTE_ID,
9
- CONF_UDP_ID,
10
- SENSOR_SCHEMA,
11
- require_internal_with_name,
3
+ CONFIG_SCHEMA = cv.invalid(
4
+ "The 'udp.binary_sensor' component has been migrated to the 'packet_transport.binary_sensor' component."
12
5
  )
13
-
14
- DEPENDENCIES = ["udp"]
15
-
16
- CONFIG_SCHEMA = All(
17
- binary_sensor.binary_sensor_schema().extend(SENSOR_SCHEMA),
18
- has_at_least_one_key(CONF_ID, CONF_REMOTE_ID),
19
- require_internal_with_name,
20
- )
21
-
22
-
23
- async def to_code(config):
24
- var = await binary_sensor.new_binary_sensor(config)
25
- comp = await cg.get_variable(config[CONF_UDP_ID])
26
- remote_id = str(config.get(CONF_REMOTE_ID) or config.get(CONF_ID))
27
- cg.add(comp.add_remote_binary_sensor(config[CONF_PROVIDER], remote_id, var))
@@ -0,0 +1,29 @@
1
+ import esphome.codegen as cg
2
+ from esphome.components.api import CONF_ENCRYPTION
3
+ from esphome.components.packet_transport import (
4
+ CONF_PING_PONG_ENABLE,
5
+ PacketTransport,
6
+ new_packet_transport,
7
+ transport_schema,
8
+ )
9
+ from esphome.const import CONF_BINARY_SENSORS, CONF_SENSORS
10
+ from esphome.cpp_types import PollingComponent
11
+
12
+ from .. import UDP_SCHEMA, register_udp_client, udp_ns
13
+
14
+ UDPTransport = udp_ns.class_("UDPTransport", PacketTransport, PollingComponent)
15
+
16
+ CONFIG_SCHEMA = transport_schema(UDPTransport).extend(UDP_SCHEMA)
17
+
18
+
19
+ async def to_code(config):
20
+ var, providers = await new_packet_transport(config)
21
+ udp_var = await register_udp_client(var, config)
22
+ if CONF_ENCRYPTION in config or providers:
23
+ cg.add(udp_var.set_should_listen())
24
+ if (
25
+ config[CONF_PING_PONG_ENABLE]
26
+ or config.get(CONF_SENSORS, ())
27
+ or config.get(CONF_BINARY_SENSORS, ())
28
+ ):
29
+ cg.add(udp_var.set_should_broadcast())