esphome 2025.4.2__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 (444) 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/as7341/as7341.h +1 -1
  26. esphome/components/at581x/at581x.h +4 -4
  27. esphome/components/atm90e32/__init__.py +1 -0
  28. esphome/components/atm90e32/atm90e32.cpp +576 -199
  29. esphome/components/atm90e32/atm90e32.h +128 -31
  30. esphome/components/atm90e32/atm90e32_reg.h +4 -2
  31. esphome/components/atm90e32/button/__init__.py +62 -10
  32. esphome/components/atm90e32/button/atm90e32_button.cpp +63 -4
  33. esphome/components/atm90e32/button/atm90e32_button.h +36 -4
  34. esphome/components/atm90e32/number/__init__.py +130 -0
  35. esphome/components/atm90e32/number/atm90e32_number.h +16 -0
  36. esphome/components/atm90e32/sensor.py +21 -4
  37. esphome/components/atm90e32/text_sensor/__init__.py +48 -0
  38. esphome/components/audio/__init__.py +96 -49
  39. esphome/components/audio/audio.h +48 -0
  40. esphome/components/audio/audio_decoder.cpp +1 -1
  41. esphome/components/audio/audio_resampler.cpp +2 -0
  42. esphome/components/audio/audio_resampler.h +1 -0
  43. esphome/components/ballu/climate.py +2 -9
  44. esphome/components/bang_bang/climate.py +5 -6
  45. esphome/components/bedjet/bedjet_hub.cpp +1 -0
  46. esphome/components/bedjet/climate/__init__.py +3 -8
  47. esphome/components/bedjet/fan/__init__.py +2 -11
  48. esphome/components/binary/fan/__init__.py +13 -16
  49. esphome/components/binary_sensor/__init__.py +13 -10
  50. esphome/components/bl0906/constants.h +16 -16
  51. esphome/components/ble_client/text_sensor/__init__.py +3 -5
  52. esphome/components/bluetooth_proxy/bluetooth_connection.cpp +4 -6
  53. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +136 -21
  54. esphome/components/bluetooth_proxy/bluetooth_proxy.h +7 -0
  55. esphome/components/button/__init__.py +11 -8
  56. esphome/components/canbus/canbus.cpp +3 -0
  57. esphome/components/canbus/canbus.h +16 -0
  58. esphome/components/ccs811/sensor.py +9 -6
  59. esphome/components/climate/__init__.py +35 -2
  60. esphome/components/climate/climate_mode.h +1 -1
  61. esphome/components/climate/climate_traits.h +63 -57
  62. esphome/components/climate_ir/__init__.py +57 -17
  63. esphome/components/climate_ir_lg/climate.py +2 -5
  64. esphome/components/climate_ir_lg/climate_ir_lg.cpp +7 -7
  65. esphome/components/climate_ir_lg/climate_ir_lg.h +1 -1
  66. esphome/components/color/__init__.py +2 -0
  67. esphome/components/const/__init__.py +5 -0
  68. esphome/components/coolix/climate.py +2 -9
  69. esphome/components/copy/cover/__init__.py +10 -9
  70. esphome/components/copy/fan/__init__.py +11 -9
  71. esphome/components/copy/lock/__init__.py +11 -9
  72. esphome/components/copy/text/__init__.py +9 -6
  73. esphome/components/cover/__init__.py +37 -2
  74. esphome/components/cse7766/cse7766.cpp +2 -1
  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/current_based/current_based_cover.cpp +2 -1
  82. esphome/components/daikin/climate.py +2 -9
  83. esphome/components/daikin/daikin.cpp +15 -9
  84. esphome/components/daikin/daikin.h +5 -5
  85. esphome/components/daikin_arc/climate.py +2 -7
  86. esphome/components/daikin_brc/climate.py +3 -5
  87. esphome/components/dallas_temp/dallas_temp.cpp +17 -24
  88. esphome/components/dallas_temp/dallas_temp.h +0 -1
  89. esphome/components/daly_bms/daly_bms.cpp +2 -1
  90. esphome/components/debug/debug_component.cpp +6 -1
  91. esphome/components/debug/debug_component.h +8 -0
  92. esphome/components/debug/debug_esp32.cpp +109 -254
  93. esphome/components/debug/sensor.py +14 -0
  94. esphome/components/deep_sleep/deep_sleep_esp32.cpp +13 -1
  95. esphome/components/delonghi/climate.py +2 -9
  96. esphome/components/demo/__init__.py +18 -20
  97. esphome/components/dfrobot_sen0395/switch/__init__.py +21 -22
  98. esphome/components/dps310/sensor.py +6 -6
  99. esphome/components/ee895/sensor.py +9 -9
  100. esphome/components/emmeti/climate.py +2 -9
  101. esphome/components/endstop/cover.py +17 -16
  102. esphome/components/endstop/endstop_cover.cpp +2 -1
  103. esphome/components/ens160_base/__init__.py +12 -9
  104. esphome/components/esp32/__init__.py +60 -3
  105. esphome/components/esp32/core.cpp +11 -5
  106. esphome/components/esp32/gpio.cpp +86 -24
  107. esphome/components/esp32/gpio.py +15 -16
  108. esphome/components/esp32/gpio_esp32.py +1 -2
  109. esphome/components/esp32/gpio_esp32_c2.py +1 -1
  110. esphome/components/esp32/gpio_esp32_c3.py +1 -1
  111. esphome/components/esp32/gpio_esp32_c6.py +1 -1
  112. esphome/components/esp32/gpio_esp32_h2.py +1 -1
  113. esphome/components/esp32_ble/ble.cpp +1 -0
  114. esphome/components/esp32_ble/ble.h +5 -3
  115. esphome/components/esp32_ble/ble_advertising.cpp +2 -1
  116. esphome/components/esp32_ble/ble_advertising.h +1 -0
  117. esphome/components/esp32_ble_server/__init__.py +3 -0
  118. esphome/components/esp32_ble_tracker/__init__.py +7 -1
  119. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +192 -118
  120. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +29 -3
  121. esphome/components/esp32_camera/__init__.py +1 -1
  122. esphome/components/esp32_camera/esp32_camera.cpp +2 -10
  123. esphome/components/esp32_camera/esp32_camera.h +1 -1
  124. esphome/components/esp32_can/esp32_can.cpp +1 -1
  125. esphome/components/esp32_improv/esp32_improv_component.cpp +1 -1
  126. esphome/components/esp32_rmt_led_strip/led_strip.cpp +1 -1
  127. esphome/components/esp32_rmt_led_strip/led_strip.h +7 -5
  128. esphome/components/esp32_rmt_led_strip/light.py +9 -1
  129. esphome/components/esp32_touch/esp32_touch.cpp +1 -1
  130. esphome/components/esp8266/gpio.cpp +69 -8
  131. esphome/components/ethernet/ethernet_component.cpp +1 -1
  132. esphome/components/event/__init__.py +13 -10
  133. esphome/components/factory_reset/switch/__init__.py +7 -21
  134. esphome/components/fan/__init__.py +52 -5
  135. esphome/components/fastled_base/__init__.py +1 -4
  136. esphome/components/fastled_base/fastled_light.cpp +1 -1
  137. esphome/components/feedback/cover.py +38 -33
  138. esphome/components/feedback/feedback_cover.cpp +2 -1
  139. esphome/components/fujitsu_general/climate.py +2 -9
  140. esphome/components/gcja5/gcja5.cpp +2 -1
  141. esphome/components/gpio/one_wire/gpio_one_wire.cpp +45 -43
  142. esphome/components/gpio/one_wire/gpio_one_wire.h +2 -1
  143. esphome/components/gpio_expander/cached_gpio.h +22 -7
  144. esphome/components/gps/__init__.py +47 -17
  145. esphome/components/gps/gps.cpp +42 -23
  146. esphome/components/gps/gps.h +17 -13
  147. esphome/components/graph/__init__.py +1 -2
  148. esphome/components/gree/climate.py +4 -6
  149. esphome/components/gree/gree.cpp +16 -2
  150. esphome/components/gree/gree.h +2 -2
  151. esphome/components/growatt_solar/growatt_solar.cpp +2 -1
  152. esphome/components/haier/climate.py +37 -34
  153. esphome/components/hbridge/fan/__init__.py +19 -17
  154. esphome/components/he60r/cover.py +4 -5
  155. esphome/components/heatpumpir/climate.py +3 -6
  156. esphome/components/hitachi_ac344/climate.py +2 -9
  157. esphome/components/hitachi_ac424/climate.py +2 -9
  158. esphome/components/hm3301/hm3301.h +1 -1
  159. esphome/components/hte501/sensor.py +6 -6
  160. esphome/components/http_request/__init__.py +39 -6
  161. esphome/components/http_request/http_request.cpp +20 -0
  162. esphome/components/http_request/http_request.h +57 -15
  163. esphome/components/http_request/http_request_arduino.cpp +22 -6
  164. esphome/components/http_request/http_request_arduino.h +4 -3
  165. esphome/components/http_request/http_request_host.cpp +141 -0
  166. esphome/components/http_request/http_request_host.h +37 -0
  167. esphome/components/http_request/http_request_idf.cpp +35 -3
  168. esphome/components/http_request/http_request_idf.h +10 -3
  169. esphome/components/http_request/httplib.h +9691 -0
  170. esphome/components/http_request/update/__init__.py +11 -8
  171. esphome/components/hyt271/sensor.py +6 -6
  172. esphome/components/i2c/i2c.h +4 -0
  173. esphome/components/i2c/i2c_bus_esp_idf.cpp +1 -1
  174. esphome/components/i2s_audio/__init__.py +131 -22
  175. esphome/components/i2s_audio/i2s_audio.h +44 -4
  176. esphome/components/i2s_audio/media_player/__init__.py +19 -9
  177. esphome/components/i2s_audio/microphone/__init__.py +63 -5
  178. esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +351 -61
  179. esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +40 -6
  180. esphome/components/i2s_audio/speaker/__init__.py +31 -5
  181. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +155 -19
  182. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +17 -4
  183. esphome/components/ili9xxx/ili9xxx_init.h +1 -1
  184. esphome/components/image/__init__.py +37 -17
  185. esphome/components/image/image.cpp +25 -8
  186. esphome/components/internal_temperature/internal_temperature.cpp +6 -4
  187. esphome/components/key_collector/__init__.py +35 -0
  188. esphome/components/key_collector/key_collector.cpp +8 -0
  189. esphome/components/key_collector/key_collector.h +10 -0
  190. esphome/components/kuntze/kuntze.cpp +2 -1
  191. esphome/components/ld2410/ld2410.h +1 -1
  192. esphome/components/ld2450/ld2450.h +1 -1
  193. esphome/components/light/__init__.py +57 -0
  194. esphome/components/lock/__init__.py +51 -4
  195. esphome/components/lock/automation.h +2 -13
  196. esphome/components/logger/__init__.py +22 -0
  197. esphome/components/logger/logger.cpp +154 -103
  198. esphome/components/logger/logger.h +211 -36
  199. esphome/components/logger/task_log_buffer.cpp +138 -0
  200. esphome/components/logger/task_log_buffer.h +69 -0
  201. esphome/components/lvgl/__init__.py +13 -5
  202. esphome/components/lvgl/automation.py +50 -1
  203. esphome/components/lvgl/defines.py +0 -1
  204. esphome/components/lvgl/lvgl_esphome.cpp +5 -1
  205. esphome/components/lvgl/text/__init__.py +1 -2
  206. esphome/components/mapping/__init__.py +134 -0
  207. esphome/components/matrix_keypad/matrix_keypad.cpp +2 -1
  208. esphome/components/max7219digit/max7219digit.cpp +28 -27
  209. esphome/components/mdns/__init__.py +11 -5
  210. esphome/components/mdns/mdns_component.cpp +11 -5
  211. esphome/components/mdns/mdns_component.h +3 -2
  212. esphome/components/mdns/mdns_esp32.cpp +4 -3
  213. esphome/components/mdns/mdns_esp8266.cpp +4 -2
  214. esphome/components/mdns/mdns_libretiny.cpp +4 -2
  215. esphome/components/mdns/mdns_rp2040.cpp +4 -2
  216. esphome/components/media_player/__init__.py +33 -1
  217. esphome/components/mhz19/sensor.py +11 -7
  218. esphome/components/micro_wake_word/__init__.py +99 -31
  219. esphome/components/micro_wake_word/automation.h +54 -0
  220. esphome/components/micro_wake_word/micro_wake_word.cpp +331 -319
  221. esphome/components/micro_wake_word/micro_wake_word.h +58 -105
  222. esphome/components/micro_wake_word/preprocessor_settings.h +19 -2
  223. esphome/components/micro_wake_word/streaming_model.cpp +158 -41
  224. esphome/components/micro_wake_word/streaming_model.h +85 -13
  225. esphome/components/microphone/__init__.py +139 -9
  226. esphome/components/microphone/automation.h +14 -2
  227. esphome/components/microphone/microphone.cpp +21 -0
  228. esphome/components/microphone/microphone.h +14 -5
  229. esphome/components/microphone/microphone_source.cpp +95 -0
  230. esphome/components/microphone/microphone_source.h +80 -0
  231. esphome/components/mics_4514/sensor.py +25 -14
  232. esphome/components/midea/climate.py +3 -4
  233. esphome/components/midea_ir/climate.py +3 -5
  234. esphome/components/mipi_spi/__init__.py +15 -0
  235. esphome/components/mipi_spi/display.py +474 -0
  236. esphome/components/mipi_spi/mipi_spi.cpp +481 -0
  237. esphome/components/mipi_spi/mipi_spi.h +171 -0
  238. esphome/components/mipi_spi/models/__init__.py +65 -0
  239. esphome/components/mipi_spi/models/amoled.py +72 -0
  240. esphome/components/mipi_spi/models/commands.py +82 -0
  241. esphome/components/mipi_spi/models/cyd.py +10 -0
  242. esphome/components/mipi_spi/models/ili.py +749 -0
  243. esphome/components/mipi_spi/models/jc.py +260 -0
  244. esphome/components/mipi_spi/models/lanbon.py +15 -0
  245. esphome/components/mipi_spi/models/lilygo.py +60 -0
  246. esphome/components/mipi_spi/models/waveshare.py +139 -0
  247. esphome/components/mitsubishi/climate.py +2 -5
  248. esphome/components/mitsubishi/mitsubishi.cpp +9 -9
  249. esphome/components/mixer/speaker/mixer_speaker.cpp +12 -22
  250. esphome/components/mixer/speaker/mixer_speaker.h +1 -3
  251. esphome/components/mlx90393/sensor.py +5 -0
  252. esphome/components/mlx90393/sensor_mlx90393.cpp +195 -13
  253. esphome/components/mlx90393/sensor_mlx90393.h +21 -4
  254. esphome/components/modbus/modbus.cpp +2 -1
  255. esphome/components/mqtt/__init__.py +1 -1
  256. esphome/components/mqtt/mqtt_client.cpp +6 -2
  257. esphome/components/mqtt/mqtt_const.h +4 -0
  258. esphome/components/mqtt/mqtt_fan.cpp +39 -0
  259. esphome/components/mqtt/mqtt_fan.h +2 -0
  260. esphome/components/ms5611/sensor.py +6 -6
  261. esphome/components/ms8607/sensor.py +3 -3
  262. esphome/components/network/__init__.py +1 -1
  263. esphome/components/nextion/base_component.py +17 -16
  264. esphome/components/nextion/display.py +11 -2
  265. esphome/components/nextion/nextion.cpp +39 -1
  266. esphome/components/nextion/nextion.h +50 -0
  267. esphome/components/noblex/climate.py +2 -9
  268. esphome/components/number/__init__.py +12 -9
  269. esphome/components/one_wire/one_wire_bus.cpp +14 -10
  270. esphome/components/one_wire/one_wire_bus.h +14 -8
  271. esphome/components/online_image/bmp_image.cpp +48 -11
  272. esphome/components/online_image/bmp_image.h +2 -0
  273. esphome/components/opentherm/binary_sensor/__init__.py +2 -4
  274. esphome/components/opentherm/number/__init__.py +11 -20
  275. esphome/components/opentherm/sensor/__init__.py +3 -3
  276. esphome/components/opentherm/switch/__init__.py +3 -5
  277. esphome/components/output/lock/__init__.py +11 -9
  278. esphome/components/packages/__init__.py +33 -31
  279. esphome/components/packet_transport/__init__.py +201 -0
  280. esphome/components/packet_transport/binary_sensor.py +19 -0
  281. esphome/components/packet_transport/packet_transport.cpp +534 -0
  282. esphome/components/packet_transport/packet_transport.h +154 -0
  283. esphome/components/packet_transport/sensor.py +19 -0
  284. esphome/components/pca9685/pca9685_output.cpp +2 -1
  285. esphome/components/pid/climate.py +2 -4
  286. esphome/components/pm2005/__init__.py +1 -0
  287. esphome/components/pm2005/pm2005.cpp +123 -0
  288. esphome/components/pm2005/pm2005.h +46 -0
  289. esphome/components/pm2005/sensor.py +86 -0
  290. esphome/components/pmsa003i/pmsa003i.cpp +43 -16
  291. esphome/components/pmsa003i/pmsa003i.h +25 -25
  292. esphome/components/pmsx003/pmsx003.cpp +195 -230
  293. esphome/components/pmsx003/pmsx003.h +51 -33
  294. esphome/components/pmsx003/sensor.py +21 -11
  295. esphome/components/pn7150/pn7150.h +2 -2
  296. esphome/components/pn7160/pn7160.h +2 -2
  297. esphome/components/prometheus/prometheus_handler.cpp +174 -0
  298. esphome/components/prometheus/prometheus_handler.h +17 -0
  299. esphome/components/psram/__init__.py +7 -5
  300. esphome/components/pulse_meter/pulse_meter_sensor.cpp +32 -12
  301. esphome/components/pulse_meter/pulse_meter_sensor.h +5 -5
  302. esphome/components/pzem004t/pzem004t.cpp +2 -1
  303. esphome/components/qspi_dbi/__init__.py +0 -1
  304. esphome/components/qspi_dbi/display.py +2 -1
  305. esphome/components/qspi_dbi/models.py +1 -2
  306. esphome/components/remote_base/__init__.py +91 -0
  307. esphome/components/remote_base/beo4_protocol.cpp +153 -0
  308. esphome/components/remote_base/beo4_protocol.h +43 -0
  309. esphome/components/remote_base/gobox_protocol.cpp +131 -0
  310. esphome/components/remote_base/gobox_protocol.h +54 -0
  311. esphome/components/remote_receiver/remote_receiver_esp32.cpp +16 -9
  312. esphome/components/resampler/speaker/resampler_speaker.cpp +12 -10
  313. esphome/components/resampler/speaker/resampler_speaker.h +1 -1
  314. esphome/components/rf_bridge/rf_bridge.cpp +2 -1
  315. esphome/components/scd30/sensor.py +2 -3
  316. esphome/components/scd4x/sensor.py +4 -5
  317. esphome/components/sdp3x/sensor.py +2 -1
  318. esphome/components/sds011/sds011.cpp +2 -1
  319. esphome/components/select/__init__.py +19 -20
  320. esphome/components/sen5x/sen5x.cpp +55 -36
  321. esphome/components/sen5x/sensor.py +1 -1
  322. esphome/components/senseair/sensor.py +3 -3
  323. esphome/components/sensor/__init__.py +158 -14
  324. esphome/components/sensor/filter.cpp +23 -0
  325. esphome/components/sensor/filter.h +22 -0
  326. esphome/components/sgp30/sensor.py +14 -16
  327. esphome/components/sgp4x/sensor.py +1 -1
  328. esphome/components/sht4x/sht4x.cpp +43 -22
  329. esphome/components/sht4x/sht4x.h +1 -1
  330. esphome/components/shtcx/sensor.py +6 -6
  331. esphome/components/slow_pwm/slow_pwm_output.cpp +2 -1
  332. esphome/components/sml/text_sensor/__init__.py +4 -6
  333. esphome/components/sound_level/__init__.py +0 -0
  334. esphome/components/sound_level/sensor.py +97 -0
  335. esphome/components/sound_level/sound_level.cpp +194 -0
  336. esphome/components/sound_level/sound_level.h +73 -0
  337. esphome/components/speaker/media_player/__init__.py +4 -8
  338. esphome/components/speaker/media_player/speaker_media_player.cpp +0 -18
  339. esphome/components/speaker/media_player/speaker_media_player.h +0 -11
  340. esphome/components/speaker/speaker.h +4 -7
  341. esphome/components/speed/fan/__init__.py +17 -16
  342. esphome/components/spi/spi.h +11 -1
  343. esphome/components/sprinkler/__init__.py +18 -19
  344. esphome/components/sprinkler/sprinkler.cpp +6 -5
  345. esphome/components/switch/__init__.py +32 -42
  346. esphome/components/syslog/__init__.py +41 -0
  347. esphome/components/syslog/esphome_syslog.cpp +49 -0
  348. esphome/components/syslog/esphome_syslog.h +27 -0
  349. esphome/components/t6615/sensor.py +3 -3
  350. esphome/components/t6615/t6615.cpp +2 -1
  351. esphome/components/tca9555/tca9555.cpp +11 -6
  352. esphome/components/tcl112/climate.py +2 -9
  353. esphome/components/template/alarm_control_panel/__init__.py +7 -6
  354. esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +21 -17
  355. esphome/components/template/alarm_control_panel/template_alarm_control_panel.h +2 -1
  356. esphome/components/template/cover/__init__.py +27 -21
  357. esphome/components/template/fan/__init__.py +14 -12
  358. esphome/components/template/lock/__init__.py +20 -25
  359. esphome/components/template/lock/automation.h +18 -0
  360. esphome/components/template/text/__init__.py +4 -3
  361. esphome/components/template/valve/__init__.py +32 -21
  362. esphome/components/template/valve/automation.h +24 -0
  363. esphome/components/text/__init__.py +32 -1
  364. esphome/components/text_sensor/__init__.py +24 -29
  365. esphome/components/thermostat/climate.py +5 -5
  366. esphome/components/time_based/cover.py +17 -16
  367. esphome/components/time_based/time_based_cover.cpp +2 -1
  368. esphome/components/tm1638/switch/__init__.py +10 -7
  369. esphome/components/tormatic/cover.py +4 -5
  370. esphome/components/toshiba/climate.py +3 -5
  371. esphome/components/touchscreen/touchscreen.cpp +3 -1
  372. esphome/components/tuya/climate/__init__.py +5 -6
  373. esphome/components/tuya/cover/__init__.py +6 -11
  374. esphome/components/tuya/select/__init__.py +15 -5
  375. esphome/components/tuya/select/tuya_select.cpp +6 -1
  376. esphome/components/tuya/select/tuya_select.h +5 -1
  377. esphome/components/uart/packet_transport/__init__.py +20 -0
  378. esphome/components/uart/packet_transport/uart_transport.cpp +88 -0
  379. esphome/components/uart/packet_transport/uart_transport.h +41 -0
  380. esphome/components/uart/switch/uart_switch.cpp +2 -1
  381. esphome/components/udp/__init__.py +126 -128
  382. esphome/components/udp/automation.h +40 -0
  383. esphome/components/udp/binary_sensor.py +3 -25
  384. esphome/components/udp/packet_transport/__init__.py +29 -0
  385. esphome/components/udp/packet_transport/udp_transport.cpp +36 -0
  386. esphome/components/udp/packet_transport/udp_transport.h +28 -0
  387. esphome/components/udp/sensor.py +3 -25
  388. esphome/components/udp/udp_component.cpp +26 -470
  389. esphome/components/udp/udp_component.h +21 -128
  390. esphome/components/update/__init__.py +31 -1
  391. esphome/components/uponor_smatrix/climate/__init__.py +4 -9
  392. esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp +2 -1
  393. esphome/components/uponor_smatrix/uponor_smatrix.cpp +2 -1
  394. esphome/components/uptime/text_sensor/__init__.py +47 -7
  395. esphome/components/uptime/text_sensor/uptime_text_sensor.cpp +12 -7
  396. esphome/components/uptime/text_sensor/uptime_text_sensor.h +19 -0
  397. esphome/components/valve/__init__.py +34 -3
  398. esphome/components/valve/automation.h +1 -19
  399. esphome/components/vl53l0x/sensor.py +11 -0
  400. esphome/components/vl53l0x/vl53l0x_sensor.cpp +5 -1
  401. esphome/components/vl53l0x/vl53l0x_sensor.h +2 -1
  402. esphome/components/voice_assistant/__init__.py +36 -10
  403. esphome/components/voice_assistant/voice_assistant.cpp +170 -144
  404. esphome/components/voice_assistant/voice_assistant.h +26 -25
  405. esphome/components/waveshare_epaper/display.py +6 -0
  406. esphome/components/waveshare_epaper/waveshare_epaper.cpp +439 -37
  407. esphome/components/waveshare_epaper/waveshare_epaper.h +60 -11
  408. esphome/components/weikai/weikai.cpp +0 -52
  409. esphome/components/whirlpool/climate.py +3 -5
  410. esphome/components/whynter/climate.py +3 -5
  411. esphome/components/xpt2046/touchscreen/xpt2046.cpp +1 -1
  412. esphome/components/yashima/climate.py +6 -6
  413. esphome/components/zhlt01/climate.py +2 -7
  414. esphome/config.py +13 -13
  415. esphome/config_validation.py +38 -58
  416. esphome/const.py +15 -1
  417. esphome/core/__init__.py +2 -0
  418. esphome/core/application.cpp +27 -10
  419. esphome/core/application.h +9 -1
  420. esphome/core/automation.h +4 -3
  421. esphome/core/component.cpp +28 -7
  422. esphome/core/component.h +10 -1
  423. esphome/core/defines.h +23 -17
  424. esphome/core/doxygen.h +13 -0
  425. esphome/core/macros.h +4 -0
  426. esphome/core/scheduler.cpp +7 -1
  427. esphome/cpp_generator.py +6 -2
  428. esphome/dashboard/web_server.py +3 -3
  429. esphome/helpers.py +39 -0
  430. esphome/loader.py +4 -0
  431. esphome/log.py +15 -19
  432. esphome/mqtt.py +23 -10
  433. esphome/platformio_api.py +1 -1
  434. esphome/schema_extractors.py +0 -1
  435. esphome/voluptuous_schema.py +3 -1
  436. esphome/vscode.py +15 -0
  437. esphome/wizard.py +47 -37
  438. esphome/zeroconf.py +7 -3
  439. {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/METADATA +10 -11
  440. {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/RECORD +444 -383
  441. {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/WHEEL +1 -1
  442. {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/entry_points.txt +0 -0
  443. {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/licenses/LICENSE +0 -0
  444. {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,534 @@
1
+ #include "esphome/core/log.h"
2
+ #include "esphome/core/application.h"
3
+ #include "packet_transport.h"
4
+
5
+ #include "esphome/components/xxtea/xxtea.h"
6
+
7
+ namespace esphome {
8
+ namespace packet_transport {
9
+ /**
10
+ * Structure of a data packet; everything is little-endian
11
+ *
12
+ * --- In clear text ---
13
+ * MAGIC_NUMBER: 16 bits
14
+ * host name length: 1 byte
15
+ * host name: (length) bytes
16
+ * padding: 0 or more null bytes to a 4 byte boundary
17
+ *
18
+ * --- Encrypted (if key set) ----
19
+ * DATA_KEY: 1 byte: OR ROLLING_CODE_KEY:
20
+ * Rolling code (if enabled): 8 bytes
21
+ * Ping keys: if any
22
+ * repeat:
23
+ * PING_KEY: 1 byte
24
+ * ping code: 4 bytes
25
+ * Sensors:
26
+ * repeat:
27
+ * SENSOR_KEY: 1 byte
28
+ * float value: 4 bytes
29
+ * name length: 1 byte
30
+ * name
31
+ * Binary Sensors:
32
+ * repeat:
33
+ * BINARY_SENSOR_KEY: 1 byte
34
+ * bool value: 1 bytes
35
+ * name length: 1 byte
36
+ * name
37
+ *
38
+ * Padded to a 4 byte boundary with nulls
39
+ *
40
+ * Structure of a ping request packet:
41
+ * --- In clear text ---
42
+ * MAGIC_PING: 16 bits
43
+ * host name length: 1 byte
44
+ * host name: (length) bytes
45
+ * Ping key (4 bytes)
46
+ *
47
+ */
48
+ static const char *const TAG = "packet_transport";
49
+
50
+ static size_t round4(size_t value) { return (value + 3) & ~3; }
51
+
52
+ union FuData {
53
+ uint32_t u32;
54
+ float f32;
55
+ };
56
+
57
+ static const uint16_t MAGIC_NUMBER = 0x4553;
58
+ static const uint16_t MAGIC_PING = 0x5048;
59
+ static const uint32_t PREF_HASH = 0x45535043;
60
+ enum DataKey {
61
+ ZERO_FILL_KEY,
62
+ DATA_KEY,
63
+ SENSOR_KEY,
64
+ BINARY_SENSOR_KEY,
65
+ PING_KEY,
66
+ ROLLING_CODE_KEY,
67
+ };
68
+
69
+ enum DecodeResult {
70
+ DECODE_OK,
71
+ DECODE_UNMATCHED,
72
+ DECODE_ERROR,
73
+ DECODE_EMPTY,
74
+ };
75
+
76
+ static const size_t MAX_PING_KEYS = 4;
77
+
78
+ static inline void add(std::vector<uint8_t> &vec, uint32_t data) {
79
+ vec.push_back(data & 0xFF);
80
+ vec.push_back((data >> 8) & 0xFF);
81
+ vec.push_back((data >> 16) & 0xFF);
82
+ vec.push_back((data >> 24) & 0xFF);
83
+ }
84
+
85
+ class PacketDecoder {
86
+ public:
87
+ PacketDecoder(const uint8_t *buffer, size_t len) : buffer_(buffer), len_(len) {}
88
+
89
+ DecodeResult decode_string(char *data, size_t maxlen) {
90
+ if (this->position_ == this->len_)
91
+ return DECODE_EMPTY;
92
+ auto len = this->buffer_[this->position_];
93
+ if (len == 0 || this->position_ + 1 + len > this->len_ || len >= maxlen)
94
+ return DECODE_ERROR;
95
+ this->position_++;
96
+ memcpy(data, this->buffer_ + this->position_, len);
97
+ data[len] = 0;
98
+ this->position_ += len;
99
+ return DECODE_OK;
100
+ }
101
+
102
+ template<typename T> DecodeResult get(T &data) {
103
+ if (this->position_ + sizeof(T) > this->len_)
104
+ return DECODE_ERROR;
105
+ T value = 0;
106
+ for (size_t i = 0; i != sizeof(T); ++i) {
107
+ value += this->buffer_[this->position_++] << (i * 8);
108
+ }
109
+ data = value;
110
+ return DECODE_OK;
111
+ }
112
+
113
+ template<typename T> DecodeResult decode(uint8_t key, T &data) {
114
+ if (this->position_ == this->len_)
115
+ return DECODE_EMPTY;
116
+ if (this->buffer_[this->position_] != key)
117
+ return DECODE_UNMATCHED;
118
+ if (this->position_ + 1 + sizeof(T) > this->len_)
119
+ return DECODE_ERROR;
120
+ this->position_++;
121
+ T value = 0;
122
+ for (size_t i = 0; i != sizeof(T); ++i) {
123
+ value += this->buffer_[this->position_++] << (i * 8);
124
+ }
125
+ data = value;
126
+ return DECODE_OK;
127
+ }
128
+
129
+ template<typename T> DecodeResult decode(uint8_t key, char *buf, size_t buflen, T &data) {
130
+ if (this->position_ == this->len_)
131
+ return DECODE_EMPTY;
132
+ if (this->buffer_[this->position_] != key)
133
+ return DECODE_UNMATCHED;
134
+ this->position_++;
135
+ T value = 0;
136
+ for (size_t i = 0; i != sizeof(T); ++i) {
137
+ value += this->buffer_[this->position_++] << (i * 8);
138
+ }
139
+ data = value;
140
+ return this->decode_string(buf, buflen);
141
+ }
142
+
143
+ DecodeResult decode(uint8_t key) {
144
+ if (this->position_ == this->len_)
145
+ return DECODE_EMPTY;
146
+ if (this->buffer_[this->position_] != key)
147
+ return DECODE_UNMATCHED;
148
+ this->position_++;
149
+ return DECODE_OK;
150
+ }
151
+
152
+ size_t get_remaining_size() const { return this->len_ - this->position_; }
153
+
154
+ // align the pointer to the given byte boundary
155
+ bool bump_to(size_t boundary) {
156
+ auto newpos = this->position_;
157
+ auto offset = this->position_ % boundary;
158
+ if (offset != 0) {
159
+ newpos += boundary - offset;
160
+ }
161
+ if (newpos >= this->len_)
162
+ return false;
163
+ this->position_ = newpos;
164
+ return true;
165
+ }
166
+
167
+ bool decrypt(const uint32_t *key) {
168
+ if (this->get_remaining_size() % 4 != 0) {
169
+ return false;
170
+ }
171
+ xxtea::decrypt((uint32_t *) (this->buffer_ + this->position_), this->get_remaining_size() / 4, key);
172
+ return true;
173
+ }
174
+
175
+ protected:
176
+ const uint8_t *buffer_;
177
+ size_t len_;
178
+ size_t position_{};
179
+ };
180
+
181
+ static inline void add(std::vector<uint8_t> &vec, uint8_t data) { vec.push_back(data); }
182
+ static inline void add(std::vector<uint8_t> &vec, uint16_t data) {
183
+ vec.push_back((uint8_t) data);
184
+ vec.push_back((uint8_t) (data >> 8));
185
+ }
186
+ static inline void add(std::vector<uint8_t> &vec, DataKey data) { vec.push_back(data); }
187
+ static void add(std::vector<uint8_t> &vec, const char *str) {
188
+ auto len = strlen(str);
189
+ vec.push_back(len);
190
+ for (size_t i = 0; i != len; i++) {
191
+ vec.push_back(*str++);
192
+ }
193
+ }
194
+
195
+ void PacketTransport::setup() {
196
+ this->name_ = App.get_name().c_str();
197
+ if (strlen(this->name_) > 255) {
198
+ this->mark_failed();
199
+ this->status_set_error("Device name exceeds 255 chars");
200
+ return;
201
+ }
202
+ this->resend_ping_key_ = this->ping_pong_enable_;
203
+ this->pref_ = global_preferences->make_preference<uint32_t>(PREF_HASH, true);
204
+ if (this->rolling_code_enable_) {
205
+ // restore the upper 32 bits of the rolling code, increment and save.
206
+ this->pref_.load(&this->rolling_code_[1]);
207
+ this->rolling_code_[1]++;
208
+ this->pref_.save(&this->rolling_code_[1]);
209
+ // must make sure it's saved immediately
210
+ global_preferences->sync();
211
+ this->ping_key_ = random_uint32();
212
+ ESP_LOGV(TAG, "Rolling code incremented, upper part now %u", (unsigned) this->rolling_code_[1]);
213
+ }
214
+ #ifdef USE_SENSOR
215
+ for (auto &sensor : this->sensors_) {
216
+ sensor.sensor->add_on_state_callback([this, &sensor](float x) {
217
+ this->updated_ = true;
218
+ sensor.updated = true;
219
+ });
220
+ }
221
+ #endif
222
+ #ifdef USE_BINARY_SENSOR
223
+ for (auto &sensor : this->binary_sensors_) {
224
+ sensor.sensor->add_on_state_callback([this, &sensor](bool value) {
225
+ this->updated_ = true;
226
+ sensor.updated = true;
227
+ });
228
+ }
229
+ #endif
230
+ // initialise the header. This is invariant.
231
+ add(this->header_, MAGIC_NUMBER);
232
+ add(this->header_, this->name_);
233
+ // pad to a multiple of 4 bytes
234
+ while (this->header_.size() & 0x3)
235
+ this->header_.push_back(0);
236
+ }
237
+
238
+ void PacketTransport::init_data_() {
239
+ this->data_.clear();
240
+ if (this->rolling_code_enable_) {
241
+ add(this->data_, ROLLING_CODE_KEY);
242
+ add(this->data_, this->rolling_code_[0]);
243
+ add(this->data_, this->rolling_code_[1]);
244
+ this->increment_code_();
245
+ } else {
246
+ add(this->data_, DATA_KEY);
247
+ }
248
+ for (auto pkey : this->ping_keys_) {
249
+ add(this->data_, PING_KEY);
250
+ add(this->data_, pkey.second);
251
+ }
252
+ }
253
+
254
+ void PacketTransport::flush_() {
255
+ if (!this->should_send() || this->data_.empty())
256
+ return;
257
+ auto header_len = round4(this->header_.size());
258
+ auto len = round4(data_.size());
259
+ auto encode_buffer = std::vector<uint8_t>(round4(header_len + len));
260
+ memcpy(encode_buffer.data(), this->header_.data(), this->header_.size());
261
+ memcpy(encode_buffer.data() + header_len, this->data_.data(), this->data_.size());
262
+ if (this->is_encrypted_()) {
263
+ xxtea::encrypt((uint32_t *) (encode_buffer.data() + header_len), len / 4,
264
+ (uint32_t *) this->encryption_key_.data());
265
+ }
266
+ this->send_packet(encode_buffer);
267
+ }
268
+
269
+ void PacketTransport::add_binary_data_(uint8_t key, const char *id, bool data) {
270
+ auto len = 1 + 1 + 1 + strlen(id);
271
+ if (len + this->header_.size() + this->data_.size() > this->get_max_packet_size()) {
272
+ this->flush_();
273
+ }
274
+ add(this->data_, key);
275
+ add(this->data_, (uint8_t) data);
276
+ add(this->data_, id);
277
+ }
278
+ void PacketTransport::add_data_(uint8_t key, const char *id, float data) {
279
+ FuData udata{.f32 = data};
280
+ this->add_data_(key, id, udata.u32);
281
+ }
282
+
283
+ void PacketTransport::add_data_(uint8_t key, const char *id, uint32_t data) {
284
+ auto len = 4 + 1 + 1 + strlen(id);
285
+ if (len + this->header_.size() + this->data_.size() > this->get_max_packet_size()) {
286
+ this->flush_();
287
+ }
288
+ add(this->data_, key);
289
+ add(this->data_, data);
290
+ add(this->data_, id);
291
+ }
292
+ void PacketTransport::send_data_(bool all) {
293
+ if (!this->should_send())
294
+ return;
295
+ this->init_data_();
296
+ #ifdef USE_SENSOR
297
+ for (auto &sensor : this->sensors_) {
298
+ if (all || sensor.updated) {
299
+ sensor.updated = false;
300
+ this->add_data_(SENSOR_KEY, sensor.id, sensor.sensor->get_state());
301
+ }
302
+ }
303
+ #endif
304
+ #ifdef USE_BINARY_SENSOR
305
+ for (auto &sensor : this->binary_sensors_) {
306
+ if (all || sensor.updated) {
307
+ sensor.updated = false;
308
+ this->add_binary_data_(BINARY_SENSOR_KEY, sensor.id, sensor.sensor->state);
309
+ }
310
+ }
311
+ #endif
312
+ this->flush_();
313
+ this->updated_ = false;
314
+ }
315
+
316
+ void PacketTransport::update() {
317
+ auto now = millis() / 1000;
318
+ if (this->last_key_time_ + this->ping_pong_recyle_time_ < now) {
319
+ this->resend_ping_key_ = this->ping_pong_enable_;
320
+ this->last_key_time_ = now;
321
+ }
322
+ }
323
+
324
+ void PacketTransport::add_key_(const char *name, uint32_t key) {
325
+ if (!this->is_encrypted_())
326
+ return;
327
+ if (this->ping_keys_.count(name) == 0 && this->ping_keys_.size() == MAX_PING_KEYS) {
328
+ ESP_LOGW(TAG, "Ping key from %s discarded", name);
329
+ return;
330
+ }
331
+ this->ping_keys_[name] = key;
332
+ this->updated_ = true;
333
+ ESP_LOGV(TAG, "Ping key from %s now %X", name, (unsigned) key);
334
+ }
335
+
336
+ static bool process_rolling_code(Provider &provider, PacketDecoder &decoder) {
337
+ uint32_t code0, code1;
338
+ if (decoder.get(code0) != DECODE_OK || decoder.get(code1) != DECODE_OK) {
339
+ ESP_LOGW(TAG, "Rolling code requires 8 bytes");
340
+ return false;
341
+ }
342
+ if (code1 < provider.last_code[1] || (code1 == provider.last_code[1] && code0 <= provider.last_code[0])) {
343
+ ESP_LOGW(TAG, "Rolling code for %s %08lX:%08lX is old", provider.name, (unsigned long) code1,
344
+ (unsigned long) code0);
345
+ return false;
346
+ }
347
+ provider.last_code[0] = code0;
348
+ provider.last_code[1] = code1;
349
+ ESP_LOGV(TAG, "Saw new rolling code for %s %08lX:%08lX", provider.name, (unsigned long) code1, (unsigned long) code0);
350
+ return true;
351
+ }
352
+
353
+ /**
354
+ * Process a received packet
355
+ */
356
+ void PacketTransport::process_(const std::vector<uint8_t> &data) {
357
+ auto ping_key_seen = !this->ping_pong_enable_;
358
+ PacketDecoder decoder((data.data()), data.size());
359
+ char namebuf[256]{};
360
+ uint8_t byte;
361
+ FuData rdata{};
362
+ uint16_t magic;
363
+ if (decoder.get(magic) != DECODE_OK) {
364
+ ESP_LOGD(TAG, "Short buffer");
365
+ return;
366
+ }
367
+ if (magic != MAGIC_NUMBER && magic != MAGIC_PING) {
368
+ ESP_LOGV(TAG, "Bad magic %X", magic);
369
+ return;
370
+ }
371
+
372
+ if (decoder.decode_string(namebuf, sizeof namebuf) != DECODE_OK) {
373
+ ESP_LOGV(TAG, "Bad hostname length");
374
+ return;
375
+ }
376
+ if (strcmp(this->name_, namebuf) == 0) {
377
+ ESP_LOGVV(TAG, "Ignoring our own data");
378
+ return;
379
+ }
380
+ if (magic == MAGIC_PING) {
381
+ uint32_t key;
382
+ if (decoder.get(key) != DECODE_OK) {
383
+ ESP_LOGW(TAG, "Bad ping request");
384
+ return;
385
+ }
386
+ this->add_key_(namebuf, key);
387
+ ESP_LOGV(TAG, "Updated ping key for %s to %08X", namebuf, (unsigned) key);
388
+ return;
389
+ }
390
+
391
+ if (this->providers_.count(namebuf) == 0) {
392
+ ESP_LOGVV(TAG, "Unknown hostname %s", namebuf);
393
+ return;
394
+ }
395
+ ESP_LOGV(TAG, "Found hostname %s", namebuf);
396
+
397
+ #ifdef USE_SENSOR
398
+ auto &sensors = this->remote_sensors_[namebuf];
399
+ #endif
400
+ #ifdef USE_BINARY_SENSOR
401
+ auto &binary_sensors = this->remote_binary_sensors_[namebuf];
402
+ #endif
403
+
404
+ if (!decoder.bump_to(4)) {
405
+ ESP_LOGW(TAG, "Bad packet length %zu", data.size());
406
+ }
407
+ auto len = decoder.get_remaining_size();
408
+ if (round4(len) != len) {
409
+ ESP_LOGW(TAG, "Bad payload length %zu", len);
410
+ return;
411
+ }
412
+
413
+ auto &provider = this->providers_[namebuf];
414
+ // if encryption not used with this host, ping check is pointless since it would be easily spoofed.
415
+ if (provider.encryption_key.empty())
416
+ ping_key_seen = true;
417
+
418
+ if (!provider.encryption_key.empty()) {
419
+ decoder.decrypt((const uint32_t *) provider.encryption_key.data());
420
+ }
421
+ if (decoder.get(byte) != DECODE_OK) {
422
+ ESP_LOGV(TAG, "No key byte");
423
+ return;
424
+ }
425
+
426
+ if (byte == ROLLING_CODE_KEY) {
427
+ if (!process_rolling_code(provider, decoder))
428
+ return;
429
+ } else if (byte != DATA_KEY) {
430
+ ESP_LOGV(TAG, "Expected rolling_key or data_key, got %X", byte);
431
+ return;
432
+ }
433
+ uint32_t key;
434
+ while (decoder.get_remaining_size() != 0) {
435
+ if (decoder.decode(ZERO_FILL_KEY) == DECODE_OK)
436
+ continue;
437
+ if (decoder.decode(PING_KEY, key) == DECODE_OK) {
438
+ if (key == this->ping_key_) {
439
+ ping_key_seen = true;
440
+ ESP_LOGV(TAG, "Found good ping key %X", (unsigned) key);
441
+ } else {
442
+ ESP_LOGV(TAG, "Unknown ping key %X", (unsigned) key);
443
+ }
444
+ continue;
445
+ }
446
+ if (!ping_key_seen) {
447
+ ESP_LOGW(TAG, "Ping key not seen");
448
+ this->resend_ping_key_ = true;
449
+ break;
450
+ }
451
+ if (decoder.decode(BINARY_SENSOR_KEY, namebuf, sizeof(namebuf), byte) == DECODE_OK) {
452
+ ESP_LOGV(TAG, "Got binary sensor %s %d", namebuf, byte);
453
+ #ifdef USE_BINARY_SENSOR
454
+ if (binary_sensors.count(namebuf) != 0)
455
+ binary_sensors[namebuf]->publish_state(byte != 0);
456
+ #endif
457
+ continue;
458
+ }
459
+ if (decoder.decode(SENSOR_KEY, namebuf, sizeof(namebuf), rdata.u32) == DECODE_OK) {
460
+ ESP_LOGV(TAG, "Got sensor %s %f", namebuf, rdata.f32);
461
+ #ifdef USE_SENSOR
462
+ if (sensors.count(namebuf) != 0)
463
+ sensors[namebuf]->publish_state(rdata.f32);
464
+ #endif
465
+ continue;
466
+ }
467
+ if (decoder.get(byte) == DECODE_OK) {
468
+ ESP_LOGW(TAG, "Unknown key %X", byte);
469
+ ESP_LOGD(TAG, "Buffer pos: %zu contents: %s", data.size() - decoder.get_remaining_size(),
470
+ format_hex_pretty(data).c_str());
471
+ }
472
+ break;
473
+ }
474
+ }
475
+
476
+ void PacketTransport::dump_config() {
477
+ ESP_LOGCONFIG(TAG, "Packet Transport:");
478
+ ESP_LOGCONFIG(TAG, " Platform: %s", this->platform_name_);
479
+ ESP_LOGCONFIG(TAG, " Encrypted: %s", YESNO(this->is_encrypted_()));
480
+ ESP_LOGCONFIG(TAG, " Ping-pong: %s", YESNO(this->ping_pong_enable_));
481
+ #ifdef USE_SENSOR
482
+ for (auto sensor : this->sensors_)
483
+ ESP_LOGCONFIG(TAG, " Sensor: %s", sensor.id);
484
+ #endif
485
+ #ifdef USE_BINARY_SENSOR
486
+ for (auto sensor : this->binary_sensors_)
487
+ ESP_LOGCONFIG(TAG, " Binary Sensor: %s", sensor.id);
488
+ #endif
489
+ for (const auto &host : this->providers_) {
490
+ ESP_LOGCONFIG(TAG, " Remote host: %s", host.first.c_str());
491
+ ESP_LOGCONFIG(TAG, " Encrypted: %s", YESNO(!host.second.encryption_key.empty()));
492
+ #ifdef USE_SENSOR
493
+ for (const auto &sensor : this->remote_sensors_[host.first.c_str()])
494
+ ESP_LOGCONFIG(TAG, " Sensor: %s", sensor.first.c_str());
495
+ #endif
496
+ #ifdef USE_BINARY_SENSOR
497
+ for (const auto &sensor : this->remote_binary_sensors_[host.first.c_str()])
498
+ ESP_LOGCONFIG(TAG, " Binary Sensor: %s", sensor.first.c_str());
499
+ #endif
500
+ }
501
+ }
502
+ void PacketTransport::increment_code_() {
503
+ if (this->rolling_code_enable_) {
504
+ if (++this->rolling_code_[0] == 0) {
505
+ this->rolling_code_[1]++;
506
+ this->pref_.save(&this->rolling_code_[1]);
507
+ // must make sure it's saved immediately
508
+ global_preferences->sync();
509
+ }
510
+ }
511
+ }
512
+
513
+ void PacketTransport::loop() {
514
+ if (this->resend_ping_key_)
515
+ this->send_ping_pong_request_();
516
+ if (this->updated_) {
517
+ this->send_data_(this->resend_data_);
518
+ }
519
+ }
520
+
521
+ void PacketTransport::send_ping_pong_request_() {
522
+ if (!this->ping_pong_enable_ || !this->should_send())
523
+ return;
524
+ this->ping_key_ = random_uint32();
525
+ this->ping_header_.clear();
526
+ add(this->ping_header_, MAGIC_PING);
527
+ add(this->ping_header_, this->name_);
528
+ add(this->ping_header_, this->ping_key_);
529
+ this->send_packet(this->ping_header_);
530
+ this->resend_ping_key_ = false;
531
+ ESP_LOGV(TAG, "Sent new ping request %08X", (unsigned) this->ping_key_);
532
+ }
533
+ } // namespace packet_transport
534
+ } // namespace esphome
@@ -0,0 +1,154 @@
1
+ #pragma once
2
+
3
+ #include "esphome/core/component.h"
4
+ #include "esphome/core/preferences.h"
5
+ #ifdef USE_SENSOR
6
+ #include "esphome/components/sensor/sensor.h"
7
+ #endif
8
+ #ifdef USE_BINARY_SENSOR
9
+ #include "esphome/components/binary_sensor/binary_sensor.h"
10
+ #endif
11
+ #
12
+ #include <vector>
13
+ #include <map>
14
+
15
+ /**
16
+ * Providing packet encoding functions for exchanging data with a remote host.
17
+ *
18
+ * A transport is required to send the data; this is provided by a child class.
19
+ * The child class should implement the virtual functions send_packet_ and get_max_packet_size_.
20
+ * On receipt of a data packet, it should call `this->process_()` with the data.
21
+ */
22
+
23
+ namespace esphome {
24
+ namespace packet_transport {
25
+
26
+ struct Provider {
27
+ std::vector<uint8_t> encryption_key;
28
+ const char *name;
29
+ uint32_t last_code[2];
30
+ };
31
+
32
+ #ifdef USE_SENSOR
33
+ struct Sensor {
34
+ sensor::Sensor *sensor;
35
+ const char *id;
36
+ bool updated;
37
+ };
38
+ #endif
39
+ #ifdef USE_BINARY_SENSOR
40
+ struct BinarySensor {
41
+ binary_sensor::BinarySensor *sensor;
42
+ const char *id;
43
+ bool updated;
44
+ };
45
+ #endif
46
+
47
+ class PacketTransport : public PollingComponent {
48
+ public:
49
+ void setup() override;
50
+ void loop() override;
51
+ void update() override;
52
+ void dump_config() override;
53
+
54
+ #ifdef USE_SENSOR
55
+ void add_sensor(const char *id, sensor::Sensor *sensor) {
56
+ Sensor st{sensor, id, true};
57
+ this->sensors_.push_back(st);
58
+ }
59
+ void add_remote_sensor(const char *hostname, const char *remote_id, sensor::Sensor *sensor) {
60
+ this->add_provider(hostname);
61
+ this->remote_sensors_[hostname][remote_id] = sensor;
62
+ }
63
+ #endif
64
+ #ifdef USE_BINARY_SENSOR
65
+ void add_binary_sensor(const char *id, binary_sensor::BinarySensor *sensor) {
66
+ BinarySensor st{sensor, id, true};
67
+ this->binary_sensors_.push_back(st);
68
+ }
69
+
70
+ void add_remote_binary_sensor(const char *hostname, const char *remote_id, binary_sensor::BinarySensor *sensor) {
71
+ this->add_provider(hostname);
72
+ this->remote_binary_sensors_[hostname][remote_id] = sensor;
73
+ }
74
+ #endif
75
+
76
+ void add_provider(const char *hostname) {
77
+ if (this->providers_.count(hostname) == 0) {
78
+ Provider provider;
79
+ provider.encryption_key = std::vector<uint8_t>{};
80
+ provider.last_code[0] = 0;
81
+ provider.last_code[1] = 0;
82
+ provider.name = hostname;
83
+ this->providers_[hostname] = provider;
84
+ #ifdef USE_SENSOR
85
+ this->remote_sensors_[hostname] = std::map<std::string, sensor::Sensor *>();
86
+ #endif
87
+ #ifdef USE_BINARY_SENSOR
88
+ this->remote_binary_sensors_[hostname] = std::map<std::string, binary_sensor::BinarySensor *>();
89
+ #endif
90
+ }
91
+ }
92
+
93
+ void set_encryption_key(std::vector<uint8_t> key) { this->encryption_key_ = std::move(key); }
94
+ void set_rolling_code_enable(bool enable) { this->rolling_code_enable_ = enable; }
95
+ void set_ping_pong_enable(bool enable) { this->ping_pong_enable_ = enable; }
96
+ void set_ping_pong_recycle_time(uint32_t recycle_time) { this->ping_pong_recyle_time_ = recycle_time; }
97
+ void set_provider_encryption(const char *name, std::vector<uint8_t> key) {
98
+ this->providers_[name].encryption_key = std::move(key);
99
+ }
100
+ void set_platform_name(const char *name) { this->platform_name_ = name; }
101
+
102
+ protected:
103
+ // child classes must implement this
104
+ virtual void send_packet(const std::vector<uint8_t> &buf) const = 0;
105
+ virtual size_t get_max_packet_size() = 0;
106
+ virtual bool should_send() { return true; }
107
+
108
+ // to be called by child classes when a data packet is received.
109
+ void process_(const std::vector<uint8_t> &data);
110
+ void send_data_(bool all);
111
+ void flush_();
112
+ void add_data_(uint8_t key, const char *id, float data);
113
+ void add_data_(uint8_t key, const char *id, uint32_t data);
114
+ void increment_code_();
115
+ void add_binary_data_(uint8_t key, const char *id, bool data);
116
+ void init_data_();
117
+
118
+ bool updated_{};
119
+ uint32_t ping_key_{};
120
+ uint32_t rolling_code_[2]{};
121
+ bool rolling_code_enable_{};
122
+ bool ping_pong_enable_{};
123
+ uint32_t ping_pong_recyle_time_{};
124
+ uint32_t last_key_time_{};
125
+ bool resend_ping_key_{};
126
+ bool resend_data_{};
127
+ const char *name_{};
128
+ ESPPreferenceObject pref_{};
129
+
130
+ std::vector<uint8_t> encryption_key_{};
131
+
132
+ #ifdef USE_SENSOR
133
+ std::vector<Sensor> sensors_{};
134
+ std::map<std::string, std::map<std::string, sensor::Sensor *>> remote_sensors_{};
135
+ #endif
136
+ #ifdef USE_BINARY_SENSOR
137
+ std::vector<BinarySensor> binary_sensors_{};
138
+ std::map<std::string, std::map<std::string, binary_sensor::BinarySensor *>> remote_binary_sensors_{};
139
+ #endif
140
+
141
+ std::map<std::string, Provider> providers_{};
142
+ std::vector<uint8_t> ping_header_{};
143
+ std::vector<uint8_t> header_{};
144
+ std::vector<uint8_t> data_{};
145
+ std::map<const char *, uint32_t> ping_keys_{};
146
+ const char *platform_name_{""};
147
+ void add_key_(const char *name, uint32_t key);
148
+ void send_ping_pong_request_();
149
+
150
+ inline bool is_encrypted_() { return !this->encryption_key_.empty(); }
151
+ };
152
+
153
+ } // namespace packet_transport
154
+ } // namespace esphome
@@ -0,0 +1,19 @@
1
+ import esphome.codegen as cg
2
+ from esphome.components.sensor import new_sensor, sensor_schema
3
+ from esphome.const import CONF_ID
4
+
5
+ from . import (
6
+ CONF_PROVIDER,
7
+ CONF_REMOTE_ID,
8
+ CONF_TRANSPORT_ID,
9
+ packet_transport_sensor_schema,
10
+ )
11
+
12
+ CONFIG_SCHEMA = packet_transport_sensor_schema(sensor_schema())
13
+
14
+
15
+ async def to_code(config):
16
+ var = await new_sensor(config)
17
+ comp = await cg.get_variable(config[CONF_TRANSPORT_ID])
18
+ remote_id = str(config.get(CONF_REMOTE_ID) or config.get(CONF_ID))
19
+ cg.add(comp.add_remote_sensor(config[CONF_PROVIDER], remote_id, var))