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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (412) hide show
  1. esphome/components/ac_dimmer/ac_dimmer.cpp +3 -2
  2. esphome/components/adc/__init__.py +51 -34
  3. esphome/components/airthings_wave_base/__init__.py +1 -1
  4. esphome/components/alarm_control_panel/__init__.py +37 -2
  5. esphome/components/am43/cover/__init__.py +4 -5
  6. esphome/components/analog_threshold/analog_threshold_binary_sensor.cpp +6 -4
  7. esphome/components/analog_threshold/analog_threshold_binary_sensor.h +4 -5
  8. esphome/components/analog_threshold/binary_sensor.py +10 -8
  9. esphome/components/anova/climate.py +4 -5
  10. esphome/components/api/__init__.py +25 -8
  11. esphome/components/api/api_connection.cpp +77 -10
  12. esphome/components/api/api_connection.h +6 -1
  13. esphome/components/api/api_frame_helper.cpp +98 -130
  14. esphome/components/api/api_frame_helper.h +12 -2
  15. esphome/components/api/api_noise_context.h +13 -4
  16. esphome/components/api/api_pb2.cpp +1422 -1
  17. esphome/components/api/api_pb2.h +255 -1
  18. esphome/components/api/api_pb2_service.cpp +162 -49
  19. esphome/components/api/api_pb2_service.h +90 -51
  20. esphome/components/api/api_pb2_size.h +361 -0
  21. esphome/components/api/api_server.cpp +110 -34
  22. esphome/components/api/api_server.h +8 -0
  23. esphome/components/api/proto.h +38 -9
  24. esphome/components/as3935_i2c/as3935_i2c.h +0 -3
  25. esphome/components/as7341/as7341.h +1 -1
  26. esphome/components/atm90e32/__init__.py +1 -0
  27. esphome/components/atm90e32/atm90e32.cpp +576 -199
  28. esphome/components/atm90e32/atm90e32.h +128 -31
  29. esphome/components/atm90e32/atm90e32_reg.h +4 -2
  30. esphome/components/atm90e32/button/__init__.py +62 -10
  31. esphome/components/atm90e32/button/atm90e32_button.cpp +63 -4
  32. esphome/components/atm90e32/button/atm90e32_button.h +36 -4
  33. esphome/components/atm90e32/number/__init__.py +130 -0
  34. esphome/components/atm90e32/number/atm90e32_number.h +16 -0
  35. esphome/components/atm90e32/sensor.py +21 -4
  36. esphome/components/atm90e32/text_sensor/__init__.py +48 -0
  37. esphome/components/audio/__init__.py +96 -49
  38. esphome/components/audio/audio.h +48 -0
  39. esphome/components/audio/audio_decoder.cpp +1 -1
  40. esphome/components/audio/audio_resampler.cpp +2 -0
  41. esphome/components/audio/audio_resampler.h +1 -0
  42. esphome/components/ballu/climate.py +2 -9
  43. esphome/components/bang_bang/climate.py +5 -6
  44. esphome/components/bedjet/climate/__init__.py +3 -8
  45. esphome/components/bedjet/fan/__init__.py +2 -11
  46. esphome/components/binary/fan/__init__.py +13 -16
  47. esphome/components/binary_sensor/__init__.py +13 -10
  48. esphome/components/binary_sensor/binary_sensor.cpp +6 -10
  49. esphome/components/binary_sensor/binary_sensor.h +1 -1
  50. esphome/components/binary_sensor/filter.cpp +21 -21
  51. esphome/components/binary_sensor/filter.h +10 -10
  52. esphome/components/bl0906/constants.h +16 -16
  53. esphome/components/ble_client/text_sensor/__init__.py +3 -5
  54. esphome/components/bluetooth_proxy/bluetooth_connection.cpp +4 -6
  55. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +135 -21
  56. esphome/components/bluetooth_proxy/bluetooth_proxy.h +7 -0
  57. esphome/components/button/__init__.py +11 -8
  58. esphome/components/canbus/canbus.cpp +3 -0
  59. esphome/components/canbus/canbus.h +16 -0
  60. esphome/components/climate/__init__.py +35 -2
  61. esphome/components/climate/climate_mode.h +1 -1
  62. esphome/components/climate/climate_traits.h +63 -57
  63. esphome/components/climate_ir/__init__.py +57 -17
  64. esphome/components/climate_ir_lg/climate.py +2 -5
  65. esphome/components/climate_ir_lg/climate_ir_lg.cpp +7 -7
  66. esphome/components/climate_ir_lg/climate_ir_lg.h +1 -1
  67. esphome/components/color/__init__.py +2 -0
  68. esphome/components/const/__init__.py +5 -0
  69. esphome/components/coolix/climate.py +2 -9
  70. esphome/components/copy/cover/__init__.py +10 -9
  71. esphome/components/copy/fan/__init__.py +11 -9
  72. esphome/components/copy/lock/__init__.py +11 -9
  73. esphome/components/copy/text/__init__.py +9 -6
  74. esphome/components/cover/__init__.py +37 -2
  75. esphome/components/cst226/binary_sensor/__init__.py +28 -0
  76. esphome/components/cst226/binary_sensor/cs226_button.h +22 -0
  77. esphome/components/cst226/binary_sensor/cstt6_button.cpp +19 -0
  78. esphome/components/cst226/touchscreen/cst226_touchscreen.cpp +27 -5
  79. esphome/components/cst226/touchscreen/cst226_touchscreen.h +10 -10
  80. esphome/components/current_based/cover.py +37 -36
  81. esphome/components/daikin/climate.py +2 -9
  82. esphome/components/daikin/daikin.cpp +15 -9
  83. esphome/components/daikin/daikin.h +5 -5
  84. esphome/components/daikin_arc/climate.py +2 -7
  85. esphome/components/daikin_brc/climate.py +3 -5
  86. esphome/components/dallas_temp/dallas_temp.cpp +17 -24
  87. esphome/components/dallas_temp/dallas_temp.h +0 -1
  88. esphome/components/debug/debug_component.cpp +5 -0
  89. esphome/components/debug/debug_component.h +6 -0
  90. esphome/components/debug/debug_esp32.cpp +109 -254
  91. esphome/components/debug/sensor.py +14 -0
  92. esphome/components/deep_sleep/deep_sleep_esp32.cpp +13 -1
  93. esphome/components/delonghi/climate.py +2 -9
  94. esphome/components/demo/__init__.py +18 -20
  95. esphome/components/dfrobot_sen0395/switch/__init__.py +21 -22
  96. esphome/components/display/rect.cpp +4 -9
  97. esphome/components/display/rect.h +1 -1
  98. esphome/components/emmeti/climate.py +2 -9
  99. esphome/components/endstop/cover.py +17 -16
  100. esphome/components/esp32/__init__.py +60 -3
  101. esphome/components/esp32/core.cpp +11 -5
  102. esphome/components/esp32/gpio.cpp +86 -24
  103. esphome/components/esp32/gpio.py +15 -16
  104. esphome/components/esp32/gpio_esp32.py +1 -2
  105. esphome/components/esp32/gpio_esp32_c2.py +1 -1
  106. esphome/components/esp32/gpio_esp32_c3.py +1 -1
  107. esphome/components/esp32/gpio_esp32_c6.py +1 -1
  108. esphome/components/esp32/gpio_esp32_h2.py +1 -1
  109. esphome/components/esp32_ble/ble.cpp +1 -8
  110. esphome/components/esp32_ble/ble.h +5 -3
  111. esphome/components/esp32_ble/ble_advertising.h +1 -0
  112. esphome/components/esp32_ble_server/__init__.py +3 -0
  113. esphome/components/esp32_ble_tracker/__init__.py +7 -1
  114. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +192 -118
  115. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +29 -3
  116. esphome/components/esp32_can/esp32_can.cpp +1 -1
  117. esphome/components/esp32_rmt_led_strip/led_strip.cpp +1 -1
  118. esphome/components/esp32_rmt_led_strip/led_strip.h +7 -5
  119. esphome/components/esp32_rmt_led_strip/light.py +9 -1
  120. esphome/components/esp8266/gpio.cpp +69 -8
  121. esphome/components/event/__init__.py +13 -10
  122. esphome/components/factory_reset/switch/__init__.py +7 -21
  123. esphome/components/fan/__init__.py +52 -5
  124. esphome/components/fastled_base/__init__.py +1 -4
  125. esphome/components/fastled_base/fastled_light.cpp +1 -1
  126. esphome/components/feedback/cover.py +38 -33
  127. esphome/components/fujitsu_general/climate.py +2 -9
  128. esphome/components/gpio/one_wire/gpio_one_wire.cpp +45 -43
  129. esphome/components/gpio/one_wire/gpio_one_wire.h +2 -1
  130. esphome/components/gpio_expander/cached_gpio.h +22 -7
  131. esphome/components/gps/__init__.py +11 -2
  132. esphome/components/gps/gps.cpp +11 -8
  133. esphome/components/gps/gps.h +9 -6
  134. esphome/components/graph/__init__.py +1 -2
  135. esphome/components/gree/climate.py +4 -6
  136. esphome/components/gree/gree.cpp +16 -2
  137. esphome/components/gree/gree.h +2 -2
  138. esphome/components/haier/climate.py +37 -34
  139. esphome/components/hbridge/fan/__init__.py +19 -17
  140. esphome/components/he60r/cover.py +4 -5
  141. esphome/components/heatpumpir/climate.py +3 -6
  142. esphome/components/hitachi_ac344/climate.py +2 -9
  143. esphome/components/hitachi_ac424/climate.py +2 -9
  144. esphome/components/hlw8012/hlw8012.cpp +1 -1
  145. esphome/components/hm3301/hm3301.h +1 -1
  146. esphome/components/http_request/__init__.py +39 -6
  147. esphome/components/http_request/http_request.cpp +20 -0
  148. esphome/components/http_request/http_request.h +57 -15
  149. esphome/components/http_request/http_request_arduino.cpp +22 -6
  150. esphome/components/http_request/http_request_arduino.h +4 -3
  151. esphome/components/http_request/http_request_host.cpp +141 -0
  152. esphome/components/http_request/http_request_host.h +37 -0
  153. esphome/components/http_request/http_request_idf.cpp +35 -3
  154. esphome/components/http_request/http_request_idf.h +10 -3
  155. esphome/components/http_request/httplib.h +9691 -0
  156. esphome/components/http_request/update/__init__.py +11 -8
  157. esphome/components/i2c/i2c.h +4 -0
  158. esphome/components/i2c/i2c_bus_esp_idf.cpp +1 -1
  159. esphome/components/i2s_audio/__init__.py +131 -22
  160. esphome/components/i2s_audio/i2s_audio.h +44 -4
  161. esphome/components/i2s_audio/media_player/__init__.py +19 -9
  162. esphome/components/i2s_audio/microphone/__init__.py +63 -5
  163. esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +351 -61
  164. esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +40 -6
  165. esphome/components/i2s_audio/speaker/__init__.py +31 -5
  166. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +155 -19
  167. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +17 -4
  168. esphome/components/ili9xxx/ili9xxx_init.h +1 -1
  169. esphome/components/image/__init__.py +37 -17
  170. esphome/components/image/image.cpp +25 -8
  171. esphome/components/internal_temperature/internal_temperature.cpp +6 -4
  172. esphome/components/key_collector/__init__.py +35 -0
  173. esphome/components/key_collector/key_collector.cpp +8 -0
  174. esphome/components/key_collector/key_collector.h +10 -0
  175. esphome/components/ld2410/ld2410.h +1 -1
  176. esphome/components/ld2450/ld2450.h +1 -1
  177. esphome/components/light/__init__.py +57 -0
  178. esphome/components/lock/__init__.py +51 -4
  179. esphome/components/lock/automation.h +2 -13
  180. esphome/components/logger/__init__.py +21 -0
  181. esphome/components/logger/logger.cpp +125 -95
  182. esphome/components/logger/logger.h +160 -35
  183. esphome/components/logger/task_log_buffer.cpp +138 -0
  184. esphome/components/logger/task_log_buffer.h +69 -0
  185. esphome/components/lvgl/__init__.py +13 -5
  186. esphome/components/lvgl/automation.py +50 -1
  187. esphome/components/lvgl/defines.py +0 -1
  188. esphome/components/lvgl/lv_validation.py +10 -1
  189. esphome/components/lvgl/lvgl_esphome.cpp +5 -1
  190. esphome/components/lvgl/schemas.py +14 -14
  191. esphome/components/lvgl/text/__init__.py +1 -2
  192. esphome/components/lvgl/widgets/arc.py +7 -6
  193. esphome/components/lvgl/widgets/buttonmatrix.py +3 -3
  194. esphome/components/lvgl/widgets/checkbox.py +2 -2
  195. esphome/components/lvgl/widgets/dropdown.py +2 -1
  196. esphome/components/lvgl/widgets/img.py +15 -12
  197. esphome/components/mapping/__init__.py +134 -0
  198. esphome/components/max7219digit/max7219digit.cpp +27 -27
  199. esphome/components/mdns/__init__.py +11 -5
  200. esphome/components/mdns/mdns_component.cpp +11 -5
  201. esphome/components/mdns/mdns_component.h +3 -2
  202. esphome/components/mdns/mdns_esp32.cpp +4 -3
  203. esphome/components/mdns/mdns_esp8266.cpp +4 -2
  204. esphome/components/mdns/mdns_libretiny.cpp +4 -2
  205. esphome/components/mdns/mdns_rp2040.cpp +4 -2
  206. esphome/components/media_player/__init__.py +40 -6
  207. esphome/components/micro_wake_word/__init__.py +99 -31
  208. esphome/components/micro_wake_word/automation.h +54 -0
  209. esphome/components/micro_wake_word/micro_wake_word.cpp +331 -319
  210. esphome/components/micro_wake_word/micro_wake_word.h +58 -105
  211. esphome/components/micro_wake_word/preprocessor_settings.h +19 -2
  212. esphome/components/micro_wake_word/streaming_model.cpp +158 -41
  213. esphome/components/micro_wake_word/streaming_model.h +85 -13
  214. esphome/components/microphone/__init__.py +139 -9
  215. esphome/components/microphone/automation.h +14 -2
  216. esphome/components/microphone/microphone.cpp +21 -0
  217. esphome/components/microphone/microphone.h +14 -5
  218. esphome/components/microphone/microphone_source.cpp +95 -0
  219. esphome/components/microphone/microphone_source.h +80 -0
  220. esphome/components/mics_4514/sensor.py +25 -14
  221. esphome/components/midea/climate.py +3 -4
  222. esphome/components/midea_ir/climate.py +3 -5
  223. esphome/components/mipi_spi/__init__.py +15 -0
  224. esphome/components/mipi_spi/display.py +474 -0
  225. esphome/components/mipi_spi/mipi_spi.cpp +481 -0
  226. esphome/components/mipi_spi/mipi_spi.h +171 -0
  227. esphome/components/mipi_spi/models/__init__.py +65 -0
  228. esphome/components/mipi_spi/models/amoled.py +72 -0
  229. esphome/components/mipi_spi/models/commands.py +82 -0
  230. esphome/components/mipi_spi/models/cyd.py +10 -0
  231. esphome/components/mipi_spi/models/ili.py +749 -0
  232. esphome/components/mipi_spi/models/jc.py +260 -0
  233. esphome/components/mipi_spi/models/lanbon.py +15 -0
  234. esphome/components/mipi_spi/models/lilygo.py +60 -0
  235. esphome/components/mipi_spi/models/waveshare.py +139 -0
  236. esphome/components/mitsubishi/climate.py +2 -5
  237. esphome/components/mitsubishi/mitsubishi.cpp +9 -9
  238. esphome/components/mixer/speaker/mixer_speaker.cpp +12 -22
  239. esphome/components/mixer/speaker/mixer_speaker.h +1 -3
  240. esphome/components/mlx90393/sensor.py +5 -0
  241. esphome/components/mlx90393/sensor_mlx90393.cpp +195 -13
  242. esphome/components/mlx90393/sensor_mlx90393.h +21 -4
  243. esphome/components/mqtt/__init__.py +1 -1
  244. esphome/components/mqtt/mqtt_client.cpp +5 -1
  245. esphome/components/mqtt/mqtt_const.h +4 -0
  246. esphome/components/mqtt/mqtt_fan.cpp +39 -0
  247. esphome/components/mqtt/mqtt_fan.h +2 -0
  248. esphome/components/network/__init__.py +1 -1
  249. esphome/components/nextion/base_component.py +17 -16
  250. esphome/components/nextion/display.py +11 -2
  251. esphome/components/nextion/nextion.cpp +39 -1
  252. esphome/components/nextion/nextion.h +50 -0
  253. esphome/components/noblex/climate.py +2 -9
  254. esphome/components/number/__init__.py +12 -9
  255. esphome/components/one_wire/one_wire_bus.cpp +14 -10
  256. esphome/components/one_wire/one_wire_bus.h +14 -8
  257. esphome/components/online_image/bmp_image.cpp +48 -11
  258. esphome/components/online_image/bmp_image.h +2 -0
  259. esphome/components/opentherm/binary_sensor/__init__.py +2 -4
  260. esphome/components/opentherm/number/__init__.py +11 -20
  261. esphome/components/opentherm/sensor/__init__.py +3 -3
  262. esphome/components/opentherm/switch/__init__.py +3 -5
  263. esphome/components/output/lock/__init__.py +11 -9
  264. esphome/components/packages/__init__.py +33 -31
  265. esphome/components/packet_transport/__init__.py +201 -0
  266. esphome/components/packet_transport/binary_sensor.py +19 -0
  267. esphome/components/packet_transport/packet_transport.cpp +534 -0
  268. esphome/components/packet_transport/packet_transport.h +154 -0
  269. esphome/components/packet_transport/sensor.py +19 -0
  270. esphome/components/pca9685/pca9685_output.cpp +2 -1
  271. esphome/components/pid/climate.py +2 -4
  272. esphome/components/pm2005/__init__.py +1 -0
  273. esphome/components/pm2005/pm2005.cpp +123 -0
  274. esphome/components/pm2005/pm2005.h +46 -0
  275. esphome/components/pm2005/sensor.py +86 -0
  276. esphome/components/pmsa003i/pmsa003i.cpp +43 -16
  277. esphome/components/pmsa003i/pmsa003i.h +25 -25
  278. esphome/components/pmsx003/pmsx003.cpp +193 -229
  279. esphome/components/pmsx003/pmsx003.h +51 -33
  280. esphome/components/pmsx003/sensor.py +21 -11
  281. esphome/components/pn7150/pn7150.h +2 -2
  282. esphome/components/pn7160/pn7160.h +2 -2
  283. esphome/components/prometheus/prometheus_handler.cpp +174 -0
  284. esphome/components/prometheus/prometheus_handler.h +17 -0
  285. esphome/components/psram/__init__.py +7 -5
  286. esphome/components/pulse_meter/pulse_meter_sensor.cpp +32 -12
  287. esphome/components/pulse_meter/pulse_meter_sensor.h +5 -5
  288. esphome/components/qspi_dbi/__init__.py +0 -1
  289. esphome/components/qspi_dbi/display.py +2 -1
  290. esphome/components/qspi_dbi/models.py +1 -2
  291. esphome/components/remote_base/__init__.py +91 -0
  292. esphome/components/remote_base/beo4_protocol.cpp +153 -0
  293. esphome/components/remote_base/beo4_protocol.h +43 -0
  294. esphome/components/remote_base/gobox_protocol.cpp +131 -0
  295. esphome/components/remote_base/gobox_protocol.h +54 -0
  296. esphome/components/remote_receiver/remote_receiver_esp32.cpp +16 -9
  297. esphome/components/resampler/speaker/resampler_speaker.cpp +12 -10
  298. esphome/components/resampler/speaker/resampler_speaker.h +1 -1
  299. esphome/components/scd30/sensor.py +2 -3
  300. esphome/components/scd4x/sensor.py +4 -5
  301. esphome/components/sdp3x/sensor.py +2 -1
  302. esphome/components/select/__init__.py +19 -20
  303. esphome/components/sen5x/sensor.py +1 -1
  304. esphome/components/sensor/__init__.py +158 -14
  305. esphome/components/sensor/filter.cpp +23 -0
  306. esphome/components/sensor/filter.h +22 -0
  307. esphome/components/sgp4x/sensor.py +1 -1
  308. esphome/components/sht4x/sht4x.cpp +43 -22
  309. esphome/components/sht4x/sht4x.h +1 -1
  310. esphome/components/sml/text_sensor/__init__.py +4 -6
  311. esphome/components/sound_level/__init__.py +0 -0
  312. esphome/components/sound_level/sensor.py +97 -0
  313. esphome/components/sound_level/sound_level.cpp +194 -0
  314. esphome/components/sound_level/sound_level.h +73 -0
  315. esphome/components/speaker/media_player/__init__.py +4 -8
  316. esphome/components/speaker/media_player/speaker_media_player.cpp +0 -18
  317. esphome/components/speaker/media_player/speaker_media_player.h +0 -11
  318. esphome/components/speaker/speaker.h +4 -7
  319. esphome/components/speed/fan/__init__.py +17 -16
  320. esphome/components/spi/spi.h +11 -1
  321. esphome/components/sprinkler/__init__.py +18 -19
  322. esphome/components/switch/__init__.py +32 -42
  323. esphome/components/syslog/__init__.py +41 -0
  324. esphome/components/syslog/esphome_syslog.cpp +49 -0
  325. esphome/components/syslog/esphome_syslog.h +27 -0
  326. esphome/components/tca9555/tca9555.cpp +11 -6
  327. esphome/components/tcl112/climate.py +2 -9
  328. esphome/components/template/alarm_control_panel/__init__.py +7 -6
  329. esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +21 -17
  330. esphome/components/template/alarm_control_panel/template_alarm_control_panel.h +2 -1
  331. esphome/components/template/cover/__init__.py +27 -21
  332. esphome/components/template/fan/__init__.py +14 -12
  333. esphome/components/template/lock/__init__.py +20 -25
  334. esphome/components/template/lock/automation.h +18 -0
  335. esphome/components/template/text/__init__.py +4 -3
  336. esphome/components/template/valve/__init__.py +32 -21
  337. esphome/components/template/valve/automation.h +24 -0
  338. esphome/components/text/__init__.py +32 -1
  339. esphome/components/text_sensor/__init__.py +24 -29
  340. esphome/components/thermostat/climate.py +5 -5
  341. esphome/components/time_based/cover.py +17 -16
  342. esphome/components/tm1638/switch/__init__.py +10 -7
  343. esphome/components/tormatic/cover.py +4 -5
  344. esphome/components/toshiba/climate.py +3 -5
  345. esphome/components/touchscreen/touchscreen.cpp +3 -1
  346. esphome/components/tt21100/touchscreen/tt21100.cpp +1 -1
  347. esphome/components/tuya/climate/__init__.py +5 -6
  348. esphome/components/tuya/cover/__init__.py +6 -11
  349. esphome/components/tuya/select/__init__.py +15 -5
  350. esphome/components/tuya/select/tuya_select.cpp +6 -1
  351. esphome/components/tuya/select/tuya_select.h +5 -1
  352. esphome/components/uart/packet_transport/__init__.py +20 -0
  353. esphome/components/uart/packet_transport/uart_transport.cpp +88 -0
  354. esphome/components/uart/packet_transport/uart_transport.h +41 -0
  355. esphome/components/udp/__init__.py +126 -128
  356. esphome/components/udp/automation.h +40 -0
  357. esphome/components/udp/binary_sensor.py +3 -25
  358. esphome/components/udp/packet_transport/__init__.py +29 -0
  359. esphome/components/udp/packet_transport/udp_transport.cpp +36 -0
  360. esphome/components/udp/packet_transport/udp_transport.h +28 -0
  361. esphome/components/udp/sensor.py +3 -25
  362. esphome/components/udp/udp_component.cpp +26 -470
  363. esphome/components/udp/udp_component.h +21 -128
  364. esphome/components/update/__init__.py +31 -1
  365. esphome/components/uponor_smatrix/climate/__init__.py +4 -9
  366. esphome/components/uptime/text_sensor/__init__.py +47 -7
  367. esphome/components/uptime/text_sensor/uptime_text_sensor.cpp +12 -7
  368. esphome/components/uptime/text_sensor/uptime_text_sensor.h +19 -0
  369. esphome/components/valve/__init__.py +34 -3
  370. esphome/components/valve/automation.h +1 -19
  371. esphome/components/vl53l0x/sensor.py +11 -0
  372. esphome/components/vl53l0x/vl53l0x_sensor.cpp +5 -1
  373. esphome/components/vl53l0x/vl53l0x_sensor.h +2 -1
  374. esphome/components/voice_assistant/__init__.py +36 -10
  375. esphome/components/voice_assistant/voice_assistant.cpp +170 -144
  376. esphome/components/voice_assistant/voice_assistant.h +26 -25
  377. esphome/components/waveshare_epaper/display.py +6 -0
  378. esphome/components/waveshare_epaper/waveshare_epaper.cpp +439 -37
  379. esphome/components/waveshare_epaper/waveshare_epaper.h +60 -11
  380. esphome/components/whirlpool/climate.py +3 -5
  381. esphome/components/whynter/climate.py +3 -5
  382. esphome/components/xpt2046/touchscreen/xpt2046.cpp +1 -1
  383. esphome/components/yashima/climate.py +6 -6
  384. esphome/components/zhlt01/climate.py +2 -7
  385. esphome/config_validation.py +38 -58
  386. esphome/const.py +15 -1
  387. esphome/core/__init__.py +2 -0
  388. esphome/core/application.cpp +1 -0
  389. esphome/core/application.h +4 -0
  390. esphome/core/automation.h +4 -3
  391. esphome/core/component.cpp +19 -3
  392. esphome/core/component.h +5 -0
  393. esphome/core/defines.h +23 -17
  394. esphome/core/macros.h +4 -0
  395. esphome/core/scheduler.cpp +3 -0
  396. esphome/cpp_generator.py +6 -2
  397. esphome/dashboard/web_server.py +3 -3
  398. esphome/helpers.py +39 -0
  399. esphome/loader.py +4 -0
  400. esphome/mqtt.py +21 -8
  401. esphome/platformio_api.py +1 -1
  402. esphome/schema_extractors.py +0 -1
  403. esphome/vscode.py +15 -0
  404. esphome/wizard.py +2 -2
  405. esphome/zeroconf.py +7 -3
  406. {esphome-2025.4.1.dist-info → esphome-2025.5.0b2.dist-info}/METADATA +10 -11
  407. {esphome-2025.4.1.dist-info → esphome-2025.5.0b2.dist-info}/RECORD +411 -352
  408. {esphome-2025.4.1.dist-info → esphome-2025.5.0b2.dist-info}/WHEEL +1 -1
  409. esphome/components/esp32_ble/const_esp32c6.h +0 -74
  410. {esphome-2025.4.1.dist-info → esphome-2025.5.0b2.dist-info}/entry_points.txt +0 -0
  411. {esphome-2025.4.1.dist-info → esphome-2025.5.0b2.dist-info}/licenses/LICENSE +0 -0
  412. {esphome-2025.4.1.dist-info → esphome-2025.5.0b2.dist-info}/top_level.txt +0 -0
@@ -1,17 +1,17 @@
1
1
  import esphome.codegen as cg
2
2
  from esphome.components import cover, uart
3
3
  import esphome.config_validation as cv
4
- from esphome.const import CONF_CLOSE_DURATION, CONF_ID, CONF_OPEN_DURATION
4
+ from esphome.const import CONF_CLOSE_DURATION, CONF_OPEN_DURATION
5
5
 
6
6
  he60r_ns = cg.esphome_ns.namespace("he60r")
7
7
  HE60rCover = he60r_ns.class_("HE60rCover", cover.Cover, cg.Component)
8
8
 
9
9
  CONFIG_SCHEMA = (
10
- cover.COVER_SCHEMA.extend(uart.UART_DEVICE_SCHEMA)
10
+ cover.cover_schema(HE60rCover)
11
+ .extend(uart.UART_DEVICE_SCHEMA)
11
12
  .extend(cv.COMPONENT_SCHEMA)
12
13
  .extend(
13
14
  {
14
- cv.GenerateID(): cv.declare_id(HE60rCover),
15
15
  cv.Optional(
16
16
  CONF_OPEN_DURATION, default="15s"
17
17
  ): cv.positive_time_period_milliseconds,
@@ -34,9 +34,8 @@ FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema(
34
34
 
35
35
 
36
36
  async def to_code(config):
37
- var = cg.new_Pvariable(config[CONF_ID])
37
+ var = await cover.new_cover(config)
38
38
  await cg.register_component(var, config)
39
- await cover.register_cover(var, config)
40
39
  await uart.register_uart_device(var, config)
41
40
 
42
41
  cg.add(var.set_close_duration(config[CONF_CLOSE_DURATION]))
@@ -2,7 +2,6 @@ import esphome.codegen as cg
2
2
  from esphome.components import climate_ir
3
3
  import esphome.config_validation as cv
4
4
  from esphome.const import (
5
- CONF_ID,
6
5
  CONF_MAX_TEMPERATURE,
7
6
  CONF_MIN_TEMPERATURE,
8
7
  CONF_PROTOCOL,
@@ -98,9 +97,8 @@ VERTICAL_DIRECTIONS = {
98
97
  }
99
98
 
100
99
  CONFIG_SCHEMA = cv.All(
101
- climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
100
+ climate_ir.climare_ir_with_receiver_schema(HeatpumpIRClimate).extend(
102
101
  {
103
- cv.GenerateID(): cv.declare_id(HeatpumpIRClimate),
104
102
  cv.Required(CONF_PROTOCOL): cv.enum(PROTOCOLS),
105
103
  cv.Required(CONF_HORIZONTAL_DEFAULT): cv.enum(HORIZONTAL_DIRECTIONS),
106
104
  cv.Required(CONF_VERTICAL_DEFAULT): cv.enum(VERTICAL_DIRECTIONS),
@@ -112,8 +110,8 @@ CONFIG_SCHEMA = cv.All(
112
110
  )
113
111
 
114
112
 
115
- def to_code(config):
116
- var = cg.new_Pvariable(config[CONF_ID])
113
+ async def to_code(config):
114
+ var = await climate_ir.new_climate_ir(config)
117
115
  if CONF_VISUAL not in config:
118
116
  config[CONF_VISUAL] = {}
119
117
  visual = config[CONF_VISUAL]
@@ -121,7 +119,6 @@ def to_code(config):
121
119
  visual[CONF_MAX_TEMPERATURE] = config[CONF_MAX_TEMPERATURE]
122
120
  if CONF_MIN_TEMPERATURE not in visual:
123
121
  visual[CONF_MIN_TEMPERATURE] = config[CONF_MIN_TEMPERATURE]
124
- yield climate_ir.register_climate_ir(var, config)
125
122
  cg.add(var.set_protocol(config[CONF_PROTOCOL]))
126
123
  cg.add(var.set_horizontal_default(config[CONF_HORIZONTAL_DEFAULT]))
127
124
  cg.add(var.set_vertical_default(config[CONF_VERTICAL_DEFAULT]))
@@ -1,20 +1,13 @@
1
1
  import esphome.codegen as cg
2
2
  from esphome.components import climate_ir
3
- import esphome.config_validation as cv
4
- from esphome.const import CONF_ID
5
3
 
6
4
  AUTO_LOAD = ["climate_ir"]
7
5
 
8
6
  hitachi_ac344_ns = cg.esphome_ns.namespace("hitachi_ac344")
9
7
  HitachiClimate = hitachi_ac344_ns.class_("HitachiClimate", climate_ir.ClimateIR)
10
8
 
11
- CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
12
- {
13
- cv.GenerateID(): cv.declare_id(HitachiClimate),
14
- }
15
- )
9
+ CONFIG_SCHEMA = climate_ir.climare_ir_with_receiver_schema(HitachiClimate)
16
10
 
17
11
 
18
12
  async def to_code(config):
19
- var = cg.new_Pvariable(config[CONF_ID])
20
- await climate_ir.register_climate_ir(var, config)
13
+ await climate_ir.new_climate_ir(config)
@@ -1,20 +1,13 @@
1
1
  import esphome.codegen as cg
2
2
  from esphome.components import climate_ir
3
- import esphome.config_validation as cv
4
- from esphome.const import CONF_ID
5
3
 
6
4
  AUTO_LOAD = ["climate_ir"]
7
5
 
8
6
  hitachi_ac424_ns = cg.esphome_ns.namespace("hitachi_ac424")
9
7
  HitachiClimate = hitachi_ac424_ns.class_("HitachiClimate", climate_ir.ClimateIR)
10
8
 
11
- CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
12
- {
13
- cv.GenerateID(): cv.declare_id(HitachiClimate),
14
- }
15
- )
9
+ CONFIG_SCHEMA = climate_ir.climare_ir_with_receiver_schema(HitachiClimate)
16
10
 
17
11
 
18
12
  async def to_code(config):
19
- var = cg.new_Pvariable(config[CONF_ID])
20
- await climate_ir.register_climate_ir(var, config)
13
+ await climate_ir.new_climate_ir(config)
@@ -69,7 +69,7 @@ void HLW8012Component::update() {
69
69
 
70
70
  float power = cf_hz * this->power_multiplier_;
71
71
 
72
- if (this->change_mode_at_ != 0) {
72
+ if (this->change_mode_at_ != 0 || this->change_mode_every_ == 0) {
73
73
  // Only read cf1 after one cycle. Apparently it's quite unstable after being changed.
74
74
  if (this->current_mode_) {
75
75
  float current = cf1_hz * this->current_multiplier_;
@@ -8,7 +8,7 @@
8
8
  namespace esphome {
9
9
  namespace hm3301 {
10
10
 
11
- static const uint8_t SELECT_COMM_CMD = 0X88;
11
+ static const uint8_t SELECT_COMM_CMD = 0x88;
12
12
 
13
13
  class HM3301Component : public PollingComponent, public i2c::I2CDevice {
14
14
  public:
@@ -10,9 +10,11 @@ from esphome.const import (
10
10
  CONF_TIMEOUT,
11
11
  CONF_TRIGGER_ID,
12
12
  CONF_URL,
13
+ PLATFORM_HOST,
13
14
  __version__,
14
15
  )
15
16
  from esphome.core import CORE, Lambda
17
+ from esphome.helpers import IS_MACOS
16
18
 
17
19
  DEPENDENCIES = ["network"]
18
20
  AUTO_LOAD = ["json", "watchdog"]
@@ -21,6 +23,7 @@ http_request_ns = cg.esphome_ns.namespace("http_request")
21
23
  HttpRequestComponent = http_request_ns.class_("HttpRequestComponent", cg.Component)
22
24
  HttpRequestArduino = http_request_ns.class_("HttpRequestArduino", HttpRequestComponent)
23
25
  HttpRequestIDF = http_request_ns.class_("HttpRequestIDF", HttpRequestComponent)
26
+ HttpRequestHost = http_request_ns.class_("HttpRequestHost", HttpRequestComponent)
24
27
 
25
28
  HttpContainer = http_request_ns.class_("HttpContainer")
26
29
 
@@ -43,10 +46,13 @@ CONF_REDIRECT_LIMIT = "redirect_limit"
43
46
  CONF_WATCHDOG_TIMEOUT = "watchdog_timeout"
44
47
  CONF_BUFFER_SIZE_RX = "buffer_size_rx"
45
48
  CONF_BUFFER_SIZE_TX = "buffer_size_tx"
49
+ CONF_CA_CERTIFICATE_PATH = "ca_certificate_path"
46
50
 
47
51
  CONF_MAX_RESPONSE_BUFFER_SIZE = "max_response_buffer_size"
48
52
  CONF_ON_RESPONSE = "on_response"
49
53
  CONF_HEADERS = "headers"
54
+ CONF_REQUEST_HEADERS = "request_headers"
55
+ CONF_COLLECT_HEADERS = "collect_headers"
50
56
  CONF_BODY = "body"
51
57
  CONF_JSON = "json"
52
58
  CONF_CAPTURE_RESPONSE = "capture_response"
@@ -85,6 +91,8 @@ def validate_ssl_verification(config):
85
91
 
86
92
 
87
93
  def _declare_request_class(value):
94
+ if CORE.is_host:
95
+ return cv.declare_id(HttpRequestHost)(value)
88
96
  if CORE.using_esp_idf:
89
97
  return cv.declare_id(HttpRequestIDF)(value)
90
98
  if CORE.is_esp8266 or CORE.is_esp32 or CORE.is_rp2040:
@@ -119,6 +127,10 @@ CONFIG_SCHEMA = cv.All(
119
127
  cv.SplitDefault(CONF_BUFFER_SIZE_TX, esp32_idf=512): cv.All(
120
128
  cv.uint16_t, cv.only_with_esp_idf
121
129
  ),
130
+ cv.Optional(CONF_CA_CERTIFICATE_PATH): cv.All(
131
+ cv.file_,
132
+ cv.only_on(PLATFORM_HOST),
133
+ ),
122
134
  }
123
135
  ).extend(cv.COMPONENT_SCHEMA),
124
136
  cv.require_framework_version(
@@ -126,6 +138,7 @@ CONFIG_SCHEMA = cv.All(
126
138
  esp32_arduino=cv.Version(0, 0, 0),
127
139
  esp_idf=cv.Version(0, 0, 0),
128
140
  rp2040_arduino=cv.Version(0, 0, 0),
141
+ host=cv.Version(0, 0, 0),
129
142
  ),
130
143
  validate_ssl_verification,
131
144
  )
@@ -168,6 +181,21 @@ async def to_code(config):
168
181
  cg.add_library("ESP8266HTTPClient", None)
169
182
  if CORE.is_rp2040 and CORE.using_arduino:
170
183
  cg.add_library("HTTPClient", None)
184
+ if CORE.is_host:
185
+ if IS_MACOS:
186
+ cg.add_build_flag("-I/opt/homebrew/opt/openssl/include")
187
+ cg.add_build_flag("-L/opt/homebrew/opt/openssl/lib")
188
+ cg.add_build_flag("-lssl")
189
+ cg.add_build_flag("-lcrypto")
190
+ cg.add_build_flag("-Wl,-framework,CoreFoundation")
191
+ cg.add_build_flag("-Wl,-framework,Security")
192
+ cg.add_define("CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN")
193
+ cg.add_define("CPPHTTPLIB_OPENSSL_SUPPORT")
194
+ elif path := config.get(CONF_CA_CERTIFICATE_PATH):
195
+ cg.add_define("CPPHTTPLIB_OPENSSL_SUPPORT")
196
+ cg.add(var.set_ca_path(path))
197
+ cg.add_build_flag("-lssl")
198
+ cg.add_build_flag("-lcrypto")
171
199
 
172
200
  await cg.register_component(var, config)
173
201
 
@@ -176,9 +204,13 @@ HTTP_REQUEST_ACTION_SCHEMA = cv.Schema(
176
204
  {
177
205
  cv.GenerateID(): cv.use_id(HttpRequestComponent),
178
206
  cv.Required(CONF_URL): cv.templatable(validate_url),
179
- cv.Optional(CONF_HEADERS): cv.All(
207
+ cv.Optional(CONF_HEADERS): cv.invalid(
208
+ "The 'headers' options has been renamed to 'request_headers'"
209
+ ),
210
+ cv.Optional(CONF_REQUEST_HEADERS): cv.All(
180
211
  cv.Schema({cv.string: cv.templatable(cv.string)})
181
212
  ),
213
+ cv.Optional(CONF_COLLECT_HEADERS): cv.ensure_list(cv.string),
182
214
  cv.Optional(CONF_VERIFY_SSL): cv.invalid(
183
215
  f"{CONF_VERIFY_SSL} has moved to the base component configuration."
184
216
  ),
@@ -263,11 +295,12 @@ async def http_request_action_to_code(config, action_id, template_arg, args):
263
295
  for key in json_:
264
296
  template_ = await cg.templatable(json_[key], args, cg.std_string)
265
297
  cg.add(var.add_json(key, template_))
266
- for key in config.get(CONF_HEADERS, []):
267
- template_ = await cg.templatable(
268
- config[CONF_HEADERS][key], args, cg.const_char_ptr
269
- )
270
- cg.add(var.add_header(key, template_))
298
+ for key, value in config.get(CONF_REQUEST_HEADERS, {}).items():
299
+ template_ = await cg.templatable(value, args, cg.const_char_ptr)
300
+ cg.add(var.add_request_header(key, template_))
301
+
302
+ for value in config.get(CONF_COLLECT_HEADERS, []):
303
+ cg.add(var.add_collect_header(value))
271
304
 
272
305
  for conf in config.get(CONF_ON_RESPONSE, []):
273
306
  trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID])
@@ -20,5 +20,25 @@ void HttpRequestComponent::dump_config() {
20
20
  }
21
21
  }
22
22
 
23
+ std::string HttpContainer::get_response_header(const std::string &header_name) {
24
+ auto response_headers = this->get_response_headers();
25
+ auto header_name_lower_case = str_lower_case(header_name);
26
+ if (response_headers.count(header_name_lower_case) == 0) {
27
+ ESP_LOGW(TAG, "No header with name %s found", header_name_lower_case.c_str());
28
+ return "";
29
+ } else {
30
+ auto values = response_headers[header_name_lower_case];
31
+ if (values.empty()) {
32
+ ESP_LOGE(TAG, "header with name %s returned an empty list, this shouldn't happen",
33
+ header_name_lower_case.c_str());
34
+ return "";
35
+ } else {
36
+ auto header_value = values.front();
37
+ ESP_LOGD(TAG, "Header with name %s found with value %s", header_name_lower_case.c_str(), header_value.c_str());
38
+ return header_value;
39
+ }
40
+ }
41
+ }
42
+
23
43
  } // namespace http_request
24
44
  } // namespace esphome
@@ -3,6 +3,7 @@
3
3
  #include <list>
4
4
  #include <map>
5
5
  #include <memory>
6
+ #include <set>
6
7
  #include <utility>
7
8
  #include <vector>
8
9
 
@@ -95,9 +96,19 @@ class HttpContainer : public Parented<HttpRequestComponent> {
95
96
 
96
97
  size_t get_bytes_read() const { return this->bytes_read_; }
97
98
 
99
+ /**
100
+ * @brief Get response headers.
101
+ *
102
+ * @return The key is the lower case response header name, the value is the header value.
103
+ */
104
+ std::map<std::string, std::list<std::string>> get_response_headers() { return this->response_headers_; }
105
+
106
+ std::string get_response_header(const std::string &header_name);
107
+
98
108
  protected:
99
109
  size_t bytes_read_{0};
100
110
  bool secure_{false};
111
+ std::map<std::string, std::list<std::string>> response_headers_{};
101
112
  };
102
113
 
103
114
  class HttpRequestResponseTrigger : public Trigger<std::shared_ptr<HttpContainer>, std::string &> {
@@ -119,21 +130,46 @@ class HttpRequestComponent : public Component {
119
130
  void set_follow_redirects(bool follow_redirects) { this->follow_redirects_ = follow_redirects; }
120
131
  void set_redirect_limit(uint16_t limit) { this->redirect_limit_ = limit; }
121
132
 
122
- std::shared_ptr<HttpContainer> get(std::string url) { return this->start(std::move(url), "GET", "", {}); }
123
- std::shared_ptr<HttpContainer> get(std::string url, std::list<Header> headers) {
124
- return this->start(std::move(url), "GET", "", std::move(headers));
133
+ std::shared_ptr<HttpContainer> get(const std::string &url) { return this->start(url, "GET", "", {}); }
134
+ std::shared_ptr<HttpContainer> get(const std::string &url, const std::list<Header> &request_headers) {
135
+ return this->start(url, "GET", "", request_headers);
136
+ }
137
+ std::shared_ptr<HttpContainer> get(const std::string &url, const std::list<Header> &request_headers,
138
+ const std::set<std::string> &collect_headers) {
139
+ return this->start(url, "GET", "", request_headers, collect_headers);
140
+ }
141
+ std::shared_ptr<HttpContainer> post(const std::string &url, const std::string &body) {
142
+ return this->start(url, "POST", body, {});
143
+ }
144
+ std::shared_ptr<HttpContainer> post(const std::string &url, const std::string &body,
145
+ const std::list<Header> &request_headers) {
146
+ return this->start(url, "POST", body, request_headers);
125
147
  }
126
- std::shared_ptr<HttpContainer> post(std::string url, std::string body) {
127
- return this->start(std::move(url), "POST", std::move(body), {});
148
+ std::shared_ptr<HttpContainer> post(const std::string &url, const std::string &body,
149
+ const std::list<Header> &request_headers,
150
+ const std::set<std::string> &collect_headers) {
151
+ return this->start(url, "POST", body, request_headers, collect_headers);
128
152
  }
129
- std::shared_ptr<HttpContainer> post(std::string url, std::string body, std::list<Header> headers) {
130
- return this->start(std::move(url), "POST", std::move(body), std::move(headers));
153
+
154
+ std::shared_ptr<HttpContainer> start(const std::string &url, const std::string &method, const std::string &body,
155
+ const std::list<Header> &request_headers) {
156
+ return this->start(url, method, body, request_headers, {});
131
157
  }
132
158
 
133
- virtual std::shared_ptr<HttpContainer> start(std::string url, std::string method, std::string body,
134
- std::list<Header> headers) = 0;
159
+ std::shared_ptr<HttpContainer> start(const std::string &url, const std::string &method, const std::string &body,
160
+ const std::list<Header> &request_headers,
161
+ const std::set<std::string> &collect_headers) {
162
+ std::set<std::string> lower_case_collect_headers;
163
+ for (const std::string &collect_header : collect_headers) {
164
+ lower_case_collect_headers.insert(str_lower_case(collect_header));
165
+ }
166
+ return this->perform(url, method, body, request_headers, lower_case_collect_headers);
167
+ }
135
168
 
136
169
  protected:
170
+ virtual std::shared_ptr<HttpContainer> perform(std::string url, std::string method, std::string body,
171
+ std::list<Header> request_headers,
172
+ std::set<std::string> collect_headers) = 0;
137
173
  const char *useragent_{nullptr};
138
174
  bool follow_redirects_{};
139
175
  uint16_t redirect_limit_{};
@@ -149,7 +185,11 @@ template<typename... Ts> class HttpRequestSendAction : public Action<Ts...> {
149
185
  TEMPLATABLE_VALUE(std::string, body)
150
186
  TEMPLATABLE_VALUE(bool, capture_response)
151
187
 
152
- void add_header(const char *key, TemplatableValue<const char *, Ts...> value) { this->headers_.insert({key, value}); }
188
+ void add_request_header(const char *key, TemplatableValue<const char *, Ts...> value) {
189
+ this->request_headers_.insert({key, value});
190
+ }
191
+
192
+ void add_collect_header(const char *value) { this->collect_headers_.insert(value); }
153
193
 
154
194
  void add_json(const char *key, TemplatableValue<std::string, Ts...> value) { this->json_.insert({key, value}); }
155
195
 
@@ -176,16 +216,17 @@ template<typename... Ts> class HttpRequestSendAction : public Action<Ts...> {
176
216
  auto f = std::bind(&HttpRequestSendAction<Ts...>::encode_json_func_, this, x..., std::placeholders::_1);
177
217
  body = json::build_json(f);
178
218
  }
179
- std::list<Header> headers;
180
- for (const auto &item : this->headers_) {
219
+ std::list<Header> request_headers;
220
+ for (const auto &item : this->request_headers_) {
181
221
  auto val = item.second;
182
222
  Header header;
183
223
  header.name = item.first;
184
224
  header.value = val.value(x...);
185
- headers.push_back(header);
225
+ request_headers.push_back(header);
186
226
  }
187
227
 
188
- auto container = this->parent_->start(this->url_.value(x...), this->method_.value(x...), body, headers);
228
+ auto container = this->parent_->start(this->url_.value(x...), this->method_.value(x...), body, request_headers,
229
+ this->collect_headers_);
189
230
 
190
231
  if (container == nullptr) {
191
232
  for (auto *trigger : this->error_triggers_)
@@ -238,7 +279,8 @@ template<typename... Ts> class HttpRequestSendAction : public Action<Ts...> {
238
279
  }
239
280
  void encode_json_func_(Ts... x, JsonObject root) { this->json_func_(x..., root); }
240
281
  HttpRequestComponent *parent_;
241
- std::map<const char *, TemplatableValue<const char *, Ts...>> headers_{};
282
+ std::map<const char *, TemplatableValue<const char *, Ts...>> request_headers_{};
283
+ std::set<std::string> collect_headers_{"content-type", "content-length"};
242
284
  std::map<const char *, TemplatableValue<std::string, Ts...>> json_{};
243
285
  std::function<void(Ts..., JsonObject)> json_func_{nullptr};
244
286
  std::vector<HttpRequestResponseTrigger *> response_triggers_{};
@@ -14,8 +14,9 @@ namespace http_request {
14
14
 
15
15
  static const char *const TAG = "http_request.arduino";
16
16
 
17
- std::shared_ptr<HttpContainer> HttpRequestArduino::start(std::string url, std::string method, std::string body,
18
- std::list<Header> headers) {
17
+ std::shared_ptr<HttpContainer> HttpRequestArduino::perform(std::string url, std::string method, std::string body,
18
+ std::list<Header> request_headers,
19
+ std::set<std::string> collect_headers) {
19
20
  if (!network::is_connected()) {
20
21
  this->status_momentary_error("failed", 1000);
21
22
  ESP_LOGW(TAG, "HTTP Request failed; Not connected to network");
@@ -95,14 +96,17 @@ std::shared_ptr<HttpContainer> HttpRequestArduino::start(std::string url, std::s
95
96
  if (this->useragent_ != nullptr) {
96
97
  container->client_.setUserAgent(this->useragent_);
97
98
  }
98
- for (const auto &header : headers) {
99
+ for (const auto &header : request_headers) {
99
100
  container->client_.addHeader(header.name.c_str(), header.value.c_str(), false, true);
100
101
  }
101
102
 
102
103
  // returned needed headers must be collected before the requests
103
- static const char *header_keys[] = {"Content-Length", "Content-Type"};
104
- static const size_t HEADER_COUNT = sizeof(header_keys) / sizeof(header_keys[0]);
105
- container->client_.collectHeaders(header_keys, HEADER_COUNT);
104
+ const char *header_keys[collect_headers.size()];
105
+ int index = 0;
106
+ for (auto const &header_name : collect_headers) {
107
+ header_keys[index++] = header_name.c_str();
108
+ }
109
+ container->client_.collectHeaders(header_keys, index);
106
110
 
107
111
  App.feed_wdt();
108
112
  container->status_code = container->client_.sendRequest(method.c_str(), body.c_str());
@@ -121,6 +125,18 @@ std::shared_ptr<HttpContainer> HttpRequestArduino::start(std::string url, std::s
121
125
  // Still return the container, so it can be used to get the status code and error message
122
126
  }
123
127
 
128
+ container->response_headers_ = {};
129
+ auto header_count = container->client_.headers();
130
+ for (int i = 0; i < header_count; i++) {
131
+ const std::string header_name = str_lower_case(container->client_.headerName(i).c_str());
132
+ if (collect_headers.count(header_name) > 0) {
133
+ std::string header_value = container->client_.header(i).c_str();
134
+ ESP_LOGD(TAG, "Received response header, name: %s, value: %s", header_name.c_str(), header_value.c_str());
135
+ container->response_headers_[header_name].push_back(header_value);
136
+ break;
137
+ }
138
+ }
139
+
124
140
  int content_length = container->client_.getSize();
125
141
  ESP_LOGD(TAG, "Content-Length: %d", content_length);
126
142
  container->content_length = (size_t) content_length;
@@ -29,9 +29,10 @@ class HttpContainerArduino : public HttpContainer {
29
29
  };
30
30
 
31
31
  class HttpRequestArduino : public HttpRequestComponent {
32
- public:
33
- std::shared_ptr<HttpContainer> start(std::string url, std::string method, std::string body,
34
- std::list<Header> headers) override;
32
+ protected:
33
+ std::shared_ptr<HttpContainer> perform(std::string url, std::string method, std::string body,
34
+ std::list<Header> request_headers,
35
+ std::set<std::string> collect_headers) override;
35
36
  };
36
37
 
37
38
  } // namespace http_request
@@ -0,0 +1,141 @@
1
+ #include "http_request_host.h"
2
+
3
+ #ifdef USE_HOST
4
+
5
+ #include <regex>
6
+ #include "esphome/components/network/util.h"
7
+ #include "esphome/components/watchdog/watchdog.h"
8
+
9
+ #include "esphome/core/application.h"
10
+ #include "esphome/core/log.h"
11
+
12
+ namespace esphome {
13
+ namespace http_request {
14
+
15
+ static const char *const TAG = "http_request.host";
16
+
17
+ std::shared_ptr<HttpContainer> HttpRequestHost::perform(std::string url, std::string method, std::string body,
18
+ std::list<Header> request_headers,
19
+ std::set<std::string> response_headers) {
20
+ if (!network::is_connected()) {
21
+ this->status_momentary_error("failed", 1000);
22
+ ESP_LOGW(TAG, "HTTP Request failed; Not connected to network");
23
+ return nullptr;
24
+ }
25
+
26
+ std::regex url_regex(R"(^(([^:\/?#]+):)?(//([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?)", std::regex::extended);
27
+ std::smatch url_match_result;
28
+
29
+ if (!std::regex_match(url, url_match_result, url_regex) || url_match_result.length() < 7) {
30
+ ESP_LOGE(TAG, "HTTP Request failed; Malformed URL: %s", url.c_str());
31
+ return nullptr;
32
+ }
33
+ auto host = url_match_result[4].str();
34
+ auto scheme_host = url_match_result[1].str() + url_match_result[3].str();
35
+ auto path = url_match_result[5].str() + url_match_result[6].str();
36
+ if (path.empty())
37
+ path = "/";
38
+
39
+ std::shared_ptr<HttpContainerHost> container = std::make_shared<HttpContainerHost>();
40
+ container->set_parent(this);
41
+
42
+ const uint32_t start = millis();
43
+
44
+ watchdog::WatchdogManager wdm(this->get_watchdog_timeout());
45
+
46
+ httplib::Headers h_headers;
47
+ h_headers.emplace("Host", host.c_str());
48
+ h_headers.emplace("User-Agent", this->useragent_);
49
+ for (const auto &[name, value] : request_headers) {
50
+ h_headers.emplace(name, value);
51
+ }
52
+ httplib::Client client(scheme_host.c_str());
53
+ if (!client.is_valid()) {
54
+ ESP_LOGE(TAG, "HTTP Request failed; Invalid URL: %s", url.c_str());
55
+ return nullptr;
56
+ }
57
+ client.set_follow_location(this->follow_redirects_);
58
+ #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
59
+ if (this->ca_path_ != nullptr)
60
+ client.set_ca_cert_path(this->ca_path_);
61
+ #endif
62
+
63
+ httplib::Result result;
64
+ if (method == "GET") {
65
+ result = client.Get(path, h_headers, [&](const char *data, size_t data_length) {
66
+ ESP_LOGV(TAG, "Got data length: %zu", data_length);
67
+ container->response_body_.insert(container->response_body_.end(), (const uint8_t *) data,
68
+ (const uint8_t *) data + data_length);
69
+ return true;
70
+ });
71
+ } else if (method == "HEAD") {
72
+ result = client.Head(path, h_headers);
73
+ } else if (method == "PUT") {
74
+ result = client.Put(path, h_headers, body, "");
75
+ if (result) {
76
+ auto data = std::vector<uint8_t>(result->body.begin(), result->body.end());
77
+ container->response_body_.insert(container->response_body_.end(), data.begin(), data.end());
78
+ }
79
+ } else if (method == "PATCH") {
80
+ result = client.Patch(path, h_headers, body, "");
81
+ if (result) {
82
+ auto data = std::vector<uint8_t>(result->body.begin(), result->body.end());
83
+ container->response_body_.insert(container->response_body_.end(), data.begin(), data.end());
84
+ }
85
+ } else if (method == "POST") {
86
+ result = client.Post(path, h_headers, body, "");
87
+ if (result) {
88
+ auto data = std::vector<uint8_t>(result->body.begin(), result->body.end());
89
+ container->response_body_.insert(container->response_body_.end(), data.begin(), data.end());
90
+ }
91
+ } else {
92
+ ESP_LOGW(TAG, "HTTP Request failed - unsupported method %s; URL: %s", method.c_str(), url.c_str());
93
+ container->end();
94
+ return nullptr;
95
+ }
96
+ App.feed_wdt();
97
+ if (!result) {
98
+ ESP_LOGW(TAG, "HTTP Request failed; URL: %s, error code: %u", url.c_str(), (unsigned) result.error());
99
+ container->end();
100
+ this->status_momentary_error("failed", 1000);
101
+ return nullptr;
102
+ }
103
+ App.feed_wdt();
104
+ auto response = *result;
105
+ container->status_code = response.status;
106
+ if (!is_success(response.status)) {
107
+ ESP_LOGE(TAG, "HTTP Request failed; URL: %s; Code: %d", url.c_str(), response.status);
108
+ this->status_momentary_error("failed", 1000);
109
+ // Still return the container, so it can be used to get the status code and error message
110
+ }
111
+
112
+ container->content_length = container->response_body_.size();
113
+ for (auto header : response.headers) {
114
+ ESP_LOGD(TAG, "Header: %s: %s", header.first.c_str(), header.second.c_str());
115
+ auto lower_name = str_lower_case(header.first);
116
+ if (response_headers.find(lower_name) != response_headers.end()) {
117
+ container->response_headers_[lower_name].emplace_back(header.second);
118
+ }
119
+ }
120
+ container->duration_ms = millis() - start;
121
+ return container;
122
+ }
123
+
124
+ int HttpContainerHost::read(uint8_t *buf, size_t max_len) {
125
+ auto bytes_remaining = this->response_body_.size() - this->bytes_read_;
126
+ auto read_len = std::min(max_len, bytes_remaining);
127
+ memcpy(buf, this->response_body_.data() + this->bytes_read_, read_len);
128
+ this->bytes_read_ += read_len;
129
+ return read_len;
130
+ }
131
+
132
+ void HttpContainerHost::end() {
133
+ watchdog::WatchdogManager wdm(this->parent_->get_watchdog_timeout());
134
+ this->response_body_ = std::vector<uint8_t>();
135
+ this->bytes_read_ = 0;
136
+ }
137
+
138
+ } // namespace http_request
139
+ } // namespace esphome
140
+
141
+ #endif // USE_HOST
@@ -0,0 +1,37 @@
1
+ #pragma once
2
+
3
+ #include "http_request.h"
4
+
5
+ #ifdef USE_HOST
6
+
7
+ #define CPPHTTPLIB_NO_EXCEPTIONS
8
+ #include "httplib.h"
9
+ namespace esphome {
10
+ namespace http_request {
11
+
12
+ class HttpRequestHost;
13
+ class HttpContainerHost : public HttpContainer {
14
+ public:
15
+ int read(uint8_t *buf, size_t max_len) override;
16
+ void end() override;
17
+
18
+ protected:
19
+ friend class HttpRequestHost;
20
+ std::vector<uint8_t> response_body_{};
21
+ };
22
+
23
+ class HttpRequestHost : public HttpRequestComponent {
24
+ public:
25
+ std::shared_ptr<HttpContainer> perform(std::string url, std::string method, std::string body,
26
+ std::list<Header> request_headers,
27
+ std::set<std::string> response_headers) override;
28
+ void set_ca_path(const char *ca_path) { this->ca_path_ = ca_path; }
29
+
30
+ protected:
31
+ const char *ca_path_{};
32
+ };
33
+
34
+ } // namespace http_request
35
+ } // namespace esphome
36
+
37
+ #endif // USE_HOST