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
@@ -114,13 +114,14 @@ void IRAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
114
114
  // fully off, disable output immediately
115
115
  this->gate_pin.digital_write(false);
116
116
  } else {
117
+ auto min_us = this->cycle_time_us * this->min_power / 1000;
117
118
  if (this->method == DIM_METHOD_TRAILING) {
118
119
  this->enable_time_us = 1; // cannot be 0
119
- this->disable_time_us = std::max((uint32_t) 10, this->value * this->cycle_time_us / 65535);
120
+ // calculate time until disable in µs with integer arithmetic and take into account min_power
121
+ this->disable_time_us = std::max((uint32_t) 10, this->value * (this->cycle_time_us - min_us) / 65535 + min_us);
120
122
  } else {
121
123
  // calculate time until enable in µs: (1.0-value)*cycle_time, but with integer arithmetic
122
124
  // also take into account min_power
123
- auto min_us = this->cycle_time_us * this->min_power / 1000;
124
125
  this->enable_time_us = std::max((uint32_t) 1, ((65535 - this->value) * (this->cycle_time_us - min_us)) / 65535);
125
126
 
126
127
  if (this->method == DIM_METHOD_LEADING_PULSE) {
@@ -47,9 +47,10 @@ SAMPLING_MODES = {
47
47
  adc1_channel_t = cg.global_ns.enum("adc1_channel_t")
48
48
  adc2_channel_t = cg.global_ns.enum("adc2_channel_t")
49
49
 
50
- # From https://github.com/espressif/esp-idf/blob/master/components/driver/include/driver/adc_common.h
51
50
  # pin to adc1 channel mapping
51
+ # https://github.com/espressif/esp-idf/blob/v4.4.8/components/driver/include/driver/adc.h
52
52
  ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = {
53
+ # https://github.com/espressif/esp-idf/blob/master/components/soc/esp32/include/soc/adc_channel.h
53
54
  VARIANT_ESP32: {
54
55
  36: adc1_channel_t.ADC1_CHANNEL_0,
55
56
  37: adc1_channel_t.ADC1_CHANNEL_1,
@@ -60,44 +61,23 @@ ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = {
60
61
  34: adc1_channel_t.ADC1_CHANNEL_6,
61
62
  35: adc1_channel_t.ADC1_CHANNEL_7,
62
63
  },
63
- VARIANT_ESP32S2: {
64
- 1: adc1_channel_t.ADC1_CHANNEL_0,
65
- 2: adc1_channel_t.ADC1_CHANNEL_1,
66
- 3: adc1_channel_t.ADC1_CHANNEL_2,
67
- 4: adc1_channel_t.ADC1_CHANNEL_3,
68
- 5: adc1_channel_t.ADC1_CHANNEL_4,
69
- 6: adc1_channel_t.ADC1_CHANNEL_5,
70
- 7: adc1_channel_t.ADC1_CHANNEL_6,
71
- 8: adc1_channel_t.ADC1_CHANNEL_7,
72
- 9: adc1_channel_t.ADC1_CHANNEL_8,
73
- 10: adc1_channel_t.ADC1_CHANNEL_9,
74
- },
75
- VARIANT_ESP32S3: {
76
- 1: adc1_channel_t.ADC1_CHANNEL_0,
77
- 2: adc1_channel_t.ADC1_CHANNEL_1,
78
- 3: adc1_channel_t.ADC1_CHANNEL_2,
79
- 4: adc1_channel_t.ADC1_CHANNEL_3,
80
- 5: adc1_channel_t.ADC1_CHANNEL_4,
81
- 6: adc1_channel_t.ADC1_CHANNEL_5,
82
- 7: adc1_channel_t.ADC1_CHANNEL_6,
83
- 8: adc1_channel_t.ADC1_CHANNEL_7,
84
- 9: adc1_channel_t.ADC1_CHANNEL_8,
85
- 10: adc1_channel_t.ADC1_CHANNEL_9,
86
- },
87
- VARIANT_ESP32C3: {
64
+ # https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c2/include/soc/adc_channel.h
65
+ VARIANT_ESP32C2: {
88
66
  0: adc1_channel_t.ADC1_CHANNEL_0,
89
67
  1: adc1_channel_t.ADC1_CHANNEL_1,
90
68
  2: adc1_channel_t.ADC1_CHANNEL_2,
91
69
  3: adc1_channel_t.ADC1_CHANNEL_3,
92
70
  4: adc1_channel_t.ADC1_CHANNEL_4,
93
71
  },
94
- VARIANT_ESP32C2: {
72
+ # https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c3/include/soc/adc_channel.h
73
+ VARIANT_ESP32C3: {
95
74
  0: adc1_channel_t.ADC1_CHANNEL_0,
96
75
  1: adc1_channel_t.ADC1_CHANNEL_1,
97
76
  2: adc1_channel_t.ADC1_CHANNEL_2,
98
77
  3: adc1_channel_t.ADC1_CHANNEL_3,
99
78
  4: adc1_channel_t.ADC1_CHANNEL_4,
100
79
  },
80
+ # https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c6/include/soc/adc_channel.h
101
81
  VARIANT_ESP32C6: {
102
82
  0: adc1_channel_t.ADC1_CHANNEL_0,
103
83
  1: adc1_channel_t.ADC1_CHANNEL_1,
@@ -107,6 +87,7 @@ ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = {
107
87
  5: adc1_channel_t.ADC1_CHANNEL_5,
108
88
  6: adc1_channel_t.ADC1_CHANNEL_6,
109
89
  },
90
+ # https://github.com/espressif/esp-idf/blob/master/components/soc/esp32h2/include/soc/adc_channel.h
110
91
  VARIANT_ESP32H2: {
111
92
  1: adc1_channel_t.ADC1_CHANNEL_0,
112
93
  2: adc1_channel_t.ADC1_CHANNEL_1,
@@ -114,10 +95,38 @@ ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = {
114
95
  4: adc1_channel_t.ADC1_CHANNEL_3,
115
96
  5: adc1_channel_t.ADC1_CHANNEL_4,
116
97
  },
98
+ # https://github.com/espressif/esp-idf/blob/master/components/soc/esp32s2/include/soc/adc_channel.h
99
+ VARIANT_ESP32S2: {
100
+ 1: adc1_channel_t.ADC1_CHANNEL_0,
101
+ 2: adc1_channel_t.ADC1_CHANNEL_1,
102
+ 3: adc1_channel_t.ADC1_CHANNEL_2,
103
+ 4: adc1_channel_t.ADC1_CHANNEL_3,
104
+ 5: adc1_channel_t.ADC1_CHANNEL_4,
105
+ 6: adc1_channel_t.ADC1_CHANNEL_5,
106
+ 7: adc1_channel_t.ADC1_CHANNEL_6,
107
+ 8: adc1_channel_t.ADC1_CHANNEL_7,
108
+ 9: adc1_channel_t.ADC1_CHANNEL_8,
109
+ 10: adc1_channel_t.ADC1_CHANNEL_9,
110
+ },
111
+ # https://github.com/espressif/esp-idf/blob/master/components/soc/esp32s3/include/soc/adc_channel.h
112
+ VARIANT_ESP32S3: {
113
+ 1: adc1_channel_t.ADC1_CHANNEL_0,
114
+ 2: adc1_channel_t.ADC1_CHANNEL_1,
115
+ 3: adc1_channel_t.ADC1_CHANNEL_2,
116
+ 4: adc1_channel_t.ADC1_CHANNEL_3,
117
+ 5: adc1_channel_t.ADC1_CHANNEL_4,
118
+ 6: adc1_channel_t.ADC1_CHANNEL_5,
119
+ 7: adc1_channel_t.ADC1_CHANNEL_6,
120
+ 8: adc1_channel_t.ADC1_CHANNEL_7,
121
+ 9: adc1_channel_t.ADC1_CHANNEL_8,
122
+ 10: adc1_channel_t.ADC1_CHANNEL_9,
123
+ },
117
124
  }
118
125
 
126
+ # pin to adc2 channel mapping
127
+ # https://github.com/espressif/esp-idf/blob/v4.4.8/components/driver/include/driver/adc.h
119
128
  ESP32_VARIANT_ADC2_PIN_TO_CHANNEL = {
120
- # TODO: add other variants
129
+ # https://github.com/espressif/esp-idf/blob/master/components/soc/esp32/include/soc/adc_channel.h
121
130
  VARIANT_ESP32: {
122
131
  4: adc2_channel_t.ADC2_CHANNEL_0,
123
132
  0: adc2_channel_t.ADC2_CHANNEL_1,
@@ -130,6 +139,19 @@ ESP32_VARIANT_ADC2_PIN_TO_CHANNEL = {
130
139
  25: adc2_channel_t.ADC2_CHANNEL_8,
131
140
  26: adc2_channel_t.ADC2_CHANNEL_9,
132
141
  },
142
+ # https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c2/include/soc/adc_channel.h
143
+ VARIANT_ESP32C2: {
144
+ 5: adc2_channel_t.ADC2_CHANNEL_0,
145
+ },
146
+ # https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c3/include/soc/adc_channel.h
147
+ VARIANT_ESP32C3: {
148
+ 5: adc2_channel_t.ADC2_CHANNEL_0,
149
+ },
150
+ # https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c6/include/soc/adc_channel.h
151
+ VARIANT_ESP32C6: {}, # no ADC2
152
+ # https://github.com/espressif/esp-idf/blob/master/components/soc/esp32h2/include/soc/adc_channel.h
153
+ VARIANT_ESP32H2: {}, # no ADC2
154
+ # https://github.com/espressif/esp-idf/blob/master/components/soc/esp32s2/include/soc/adc_channel.h
133
155
  VARIANT_ESP32S2: {
134
156
  11: adc2_channel_t.ADC2_CHANNEL_0,
135
157
  12: adc2_channel_t.ADC2_CHANNEL_1,
@@ -142,6 +164,7 @@ ESP32_VARIANT_ADC2_PIN_TO_CHANNEL = {
142
164
  19: adc2_channel_t.ADC2_CHANNEL_8,
143
165
  20: adc2_channel_t.ADC2_CHANNEL_9,
144
166
  },
167
+ # https://github.com/espressif/esp-idf/blob/master/components/soc/esp32s3/include/soc/adc_channel.h
145
168
  VARIANT_ESP32S3: {
146
169
  11: adc2_channel_t.ADC2_CHANNEL_0,
147
170
  12: adc2_channel_t.ADC2_CHANNEL_1,
@@ -154,12 +177,6 @@ ESP32_VARIANT_ADC2_PIN_TO_CHANNEL = {
154
177
  19: adc2_channel_t.ADC2_CHANNEL_8,
155
178
  20: adc2_channel_t.ADC2_CHANNEL_9,
156
179
  },
157
- VARIANT_ESP32C3: {
158
- 5: adc2_channel_t.ADC2_CHANNEL_0,
159
- },
160
- VARIANT_ESP32C2: {},
161
- VARIANT_ESP32C6: {},
162
- VARIANT_ESP32H2: {},
163
180
  }
164
181
 
165
182
 
@@ -34,7 +34,7 @@ AirthingsWaveBase = airthings_wave_base_ns.class_(
34
34
 
35
35
 
36
36
  BASE_SCHEMA = (
37
- sensor.SENSOR_SCHEMA.extend(
37
+ cv.Schema(
38
38
  {
39
39
  cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
40
40
  unit_of_measurement=UNIT_PERCENT,
@@ -5,6 +5,8 @@ from esphome.components import mqtt, web_server
5
5
  import esphome.config_validation as cv
6
6
  from esphome.const import (
7
7
  CONF_CODE,
8
+ CONF_ENTITY_CATEGORY,
9
+ CONF_ICON,
8
10
  CONF_ID,
9
11
  CONF_MQTT_ID,
10
12
  CONF_ON_STATE,
@@ -12,6 +14,7 @@ from esphome.const import (
12
14
  CONF_WEB_SERVER,
13
15
  )
14
16
  from esphome.core import CORE, coroutine_with_priority
17
+ from esphome.cpp_generator import MockObjClass
15
18
  from esphome.cpp_helpers import setup_entity
16
19
 
17
20
  CODEOWNERS = ["@grahambrown11", "@hwstar"]
@@ -78,12 +81,11 @@ AlarmControlPanelCondition = alarm_control_panel_ns.class_(
78
81
  "AlarmControlPanelCondition", automation.Condition
79
82
  )
80
83
 
81
- ALARM_CONTROL_PANEL_SCHEMA = (
84
+ _ALARM_CONTROL_PANEL_SCHEMA = (
82
85
  cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
83
86
  .extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
84
87
  .extend(
85
88
  {
86
- cv.GenerateID(): cv.declare_id(AlarmControlPanel),
87
89
  cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
88
90
  mqtt.MQTTAlarmControlPanelComponent
89
91
  ),
@@ -146,6 +148,33 @@ ALARM_CONTROL_PANEL_SCHEMA = (
146
148
  )
147
149
  )
148
150
 
151
+
152
+ def alarm_control_panel_schema(
153
+ class_: MockObjClass,
154
+ *,
155
+ entity_category: str = cv.UNDEFINED,
156
+ icon: str = cv.UNDEFINED,
157
+ ) -> cv.Schema:
158
+ schema = {
159
+ cv.GenerateID(): cv.declare_id(class_),
160
+ }
161
+
162
+ for key, default, validator in [
163
+ (CONF_ENTITY_CATEGORY, entity_category, cv.entity_category),
164
+ (CONF_ICON, icon, cv.icon),
165
+ ]:
166
+ if default is not cv.UNDEFINED:
167
+ schema[cv.Optional(key, default=default)] = validator
168
+
169
+ return _ALARM_CONTROL_PANEL_SCHEMA.extend(schema)
170
+
171
+
172
+ # Remove before 2025.11.0
173
+ ALARM_CONTROL_PANEL_SCHEMA = alarm_control_panel_schema(AlarmControlPanel)
174
+ ALARM_CONTROL_PANEL_SCHEMA.add_extra(
175
+ cv.deprecated_schema_constant("alarm_control_panel")
176
+ )
177
+
149
178
  ALARM_CONTROL_PANEL_ACTION_SCHEMA = maybe_simple_id(
150
179
  {
151
180
  cv.GenerateID(): cv.use_id(AlarmControlPanel),
@@ -209,6 +238,12 @@ async def register_alarm_control_panel(var, config):
209
238
  await setup_alarm_control_panel_core_(var, config)
210
239
 
211
240
 
241
+ async def new_alarm_control_panel(config, *args):
242
+ var = cg.new_Pvariable(config[CONF_ID], *args)
243
+ await register_alarm_control_panel(var, config)
244
+ return var
245
+
246
+
212
247
  @automation.register_action(
213
248
  "alarm_control_panel.arm_away", ArmAwayAction, ALARM_CONTROL_PANEL_ACTION_SCHEMA
214
249
  )
@@ -1,7 +1,7 @@
1
1
  import esphome.codegen as cg
2
2
  from esphome.components import ble_client, cover
3
3
  import esphome.config_validation as cv
4
- from esphome.const import CONF_ID, CONF_PIN
4
+ from esphome.const import CONF_PIN
5
5
 
6
6
  CODEOWNERS = ["@buxtronix"]
7
7
  DEPENDENCIES = ["ble_client"]
@@ -15,9 +15,9 @@ Am43Component = am43_ns.class_(
15
15
  )
16
16
 
17
17
  CONFIG_SCHEMA = (
18
- cover.COVER_SCHEMA.extend(
18
+ cover.cover_schema(Am43Component)
19
+ .extend(
19
20
  {
20
- cv.GenerateID(): cv.declare_id(Am43Component),
21
21
  cv.Optional(CONF_PIN, default=8888): cv.int_range(min=0, max=0xFFFF),
22
22
  cv.Optional(CONF_INVERT_POSITION, default=False): cv.boolean,
23
23
  }
@@ -28,9 +28,8 @@ CONFIG_SCHEMA = (
28
28
 
29
29
 
30
30
  async def to_code(config):
31
- var = cg.new_Pvariable(config[CONF_ID])
31
+ var = await cover.new_cover(config)
32
32
  cg.add(var.set_pin(config[CONF_PIN]))
33
33
  cg.add(var.set_invert_position(config[CONF_INVERT_POSITION]))
34
34
  await cg.register_component(var, config)
35
- await cover.register_cover(var, config)
36
35
  await ble_client.register_ble_node(var, config)
@@ -14,7 +14,8 @@ void AnalogThresholdBinarySensor::setup() {
14
14
  if (std::isnan(sensor_value)) {
15
15
  this->publish_initial_state(false);
16
16
  } else {
17
- this->publish_initial_state(sensor_value >= (this->lower_threshold_ + this->upper_threshold_) / 2.0f);
17
+ this->publish_initial_state(sensor_value >=
18
+ (this->lower_threshold_.value() + this->upper_threshold_.value()) / 2.0f);
18
19
  }
19
20
  }
20
21
 
@@ -24,7 +25,8 @@ void AnalogThresholdBinarySensor::set_sensor(sensor::Sensor *analog_sensor) {
24
25
  this->sensor_->add_on_state_callback([this](float sensor_value) {
25
26
  // if there is an invalid sensor reading, ignore the change and keep the current state
26
27
  if (!std::isnan(sensor_value)) {
27
- this->publish_state(sensor_value >= (this->state ? this->lower_threshold_ : this->upper_threshold_));
28
+ this->publish_state(sensor_value >=
29
+ (this->state ? this->lower_threshold_.value() : this->upper_threshold_.value()));
28
30
  }
29
31
  });
30
32
  }
@@ -32,8 +34,8 @@ void AnalogThresholdBinarySensor::set_sensor(sensor::Sensor *analog_sensor) {
32
34
  void AnalogThresholdBinarySensor::dump_config() {
33
35
  LOG_BINARY_SENSOR("", "Analog Threshold Binary Sensor", this);
34
36
  LOG_SENSOR(" ", "Sensor", this->sensor_);
35
- ESP_LOGCONFIG(TAG, " Upper threshold: %.11f", this->upper_threshold_);
36
- ESP_LOGCONFIG(TAG, " Lower threshold: %.11f", this->lower_threshold_);
37
+ ESP_LOGCONFIG(TAG, " Upper threshold: %.11f", this->upper_threshold_.value());
38
+ ESP_LOGCONFIG(TAG, " Lower threshold: %.11f", this->lower_threshold_.value());
37
39
  }
38
40
 
39
41
  } // namespace analog_threshold
@@ -15,14 +15,13 @@ class AnalogThresholdBinarySensor : public Component, public binary_sensor::Bina
15
15
  float get_setup_priority() const override { return setup_priority::DATA; }
16
16
 
17
17
  void set_sensor(sensor::Sensor *analog_sensor);
18
- void set_upper_threshold(float threshold) { this->upper_threshold_ = threshold; }
19
- void set_lower_threshold(float threshold) { this->lower_threshold_ = threshold; }
18
+ template<typename T> void set_upper_threshold(T upper_threshold) { this->upper_threshold_ = upper_threshold; }
19
+ template<typename T> void set_lower_threshold(T lower_threshold) { this->lower_threshold_ = lower_threshold; }
20
20
 
21
21
  protected:
22
22
  sensor::Sensor *sensor_{nullptr};
23
-
24
- float upper_threshold_;
25
- float lower_threshold_;
23
+ TemplatableValue<float> upper_threshold_{};
24
+ TemplatableValue<float> lower_threshold_{};
26
25
  };
27
26
 
28
27
  } // namespace analog_threshold
@@ -18,11 +18,11 @@ CONFIG_SCHEMA = (
18
18
  {
19
19
  cv.Required(CONF_SENSOR_ID): cv.use_id(sensor.Sensor),
20
20
  cv.Required(CONF_THRESHOLD): cv.Any(
21
- cv.float_,
21
+ cv.templatable(cv.float_),
22
22
  cv.Schema(
23
23
  {
24
- cv.Required(CONF_UPPER): cv.float_,
25
- cv.Required(CONF_LOWER): cv.float_,
24
+ cv.Required(CONF_UPPER): cv.templatable(cv.float_),
25
+ cv.Required(CONF_LOWER): cv.templatable(cv.float_),
26
26
  }
27
27
  ),
28
28
  ),
@@ -39,9 +39,11 @@ async def to_code(config):
39
39
  sens = await cg.get_variable(config[CONF_SENSOR_ID])
40
40
  cg.add(var.set_sensor(sens))
41
41
 
42
- if isinstance(config[CONF_THRESHOLD], float):
43
- cg.add(var.set_upper_threshold(config[CONF_THRESHOLD]))
44
- cg.add(var.set_lower_threshold(config[CONF_THRESHOLD]))
42
+ if isinstance(config[CONF_THRESHOLD], dict):
43
+ lower = await cg.templatable(config[CONF_THRESHOLD][CONF_LOWER], [], float)
44
+ upper = await cg.templatable(config[CONF_THRESHOLD][CONF_UPPER], [], float)
45
45
  else:
46
- cg.add(var.set_upper_threshold(config[CONF_THRESHOLD][CONF_UPPER]))
47
- cg.add(var.set_lower_threshold(config[CONF_THRESHOLD][CONF_LOWER]))
46
+ lower = await cg.templatable(config[CONF_THRESHOLD], [], float)
47
+ upper = lower
48
+ cg.add(var.set_upper_threshold(upper))
49
+ cg.add(var.set_lower_threshold(lower))
@@ -1,7 +1,7 @@
1
1
  import esphome.codegen as cg
2
2
  from esphome.components import ble_client, climate
3
3
  import esphome.config_validation as cv
4
- from esphome.const import CONF_ID, CONF_UNIT_OF_MEASUREMENT
4
+ from esphome.const import CONF_UNIT_OF_MEASUREMENT
5
5
 
6
6
  UNITS = {
7
7
  "f": "f",
@@ -17,9 +17,9 @@ Anova = anova_ns.class_(
17
17
  )
18
18
 
19
19
  CONFIG_SCHEMA = (
20
- climate.CLIMATE_SCHEMA.extend(
20
+ climate.climate_schema(Anova)
21
+ .extend(
21
22
  {
22
- cv.GenerateID(): cv.declare_id(Anova),
23
23
  cv.Required(CONF_UNIT_OF_MEASUREMENT): cv.enum(UNITS),
24
24
  }
25
25
  )
@@ -29,8 +29,7 @@ CONFIG_SCHEMA = (
29
29
 
30
30
 
31
31
  async def to_code(config):
32
- var = cg.new_Pvariable(config[CONF_ID])
32
+ var = await climate.new_climate(config)
33
33
  await cg.register_component(var, config)
34
- await climate.register_climate(var, config)
35
34
  await ble_client.register_ble_node(var, config)
36
35
  cg.add(var.set_unit_of_measurement(config[CONF_UNIT_OF_MEASUREMENT]))
@@ -82,6 +82,19 @@ ACTIONS_SCHEMA = automation.validate_automation(
82
82
  ),
83
83
  )
84
84
 
85
+ ENCRYPTION_SCHEMA = cv.Schema(
86
+ {
87
+ cv.Optional(CONF_KEY): validate_encryption_key,
88
+ }
89
+ )
90
+
91
+
92
+ def _encryption_schema(config):
93
+ if config is None:
94
+ config = {}
95
+ return ENCRYPTION_SCHEMA(config)
96
+
97
+
85
98
  CONFIG_SCHEMA = cv.All(
86
99
  cv.Schema(
87
100
  {
@@ -95,11 +108,7 @@ CONFIG_SCHEMA = cv.All(
95
108
  CONF_SERVICES, group_of_exclusion=CONF_ACTIONS
96
109
  ): ACTIONS_SCHEMA,
97
110
  cv.Exclusive(CONF_ACTIONS, group_of_exclusion=CONF_ACTIONS): ACTIONS_SCHEMA,
98
- cv.Optional(CONF_ENCRYPTION): cv.Schema(
99
- {
100
- cv.Required(CONF_KEY): validate_encryption_key,
101
- }
102
- ),
111
+ cv.Optional(CONF_ENCRYPTION): _encryption_schema,
103
112
  cv.Optional(CONF_ON_CLIENT_CONNECTED): automation.validate_automation(
104
113
  single=True
105
114
  ),
@@ -151,9 +160,17 @@ async def to_code(config):
151
160
  config[CONF_ON_CLIENT_DISCONNECTED],
152
161
  )
153
162
 
154
- if encryption_config := config.get(CONF_ENCRYPTION):
155
- decoded = base64.b64decode(encryption_config[CONF_KEY])
156
- cg.add(var.set_noise_psk(list(decoded)))
163
+ if (encryption_config := config.get(CONF_ENCRYPTION, None)) is not None:
164
+ if key := encryption_config.get(CONF_KEY):
165
+ decoded = base64.b64decode(key)
166
+ cg.add(var.set_noise_psk(list(decoded)))
167
+ else:
168
+ # No key provided, but encryption desired
169
+ # This will allow a plaintext client to provide a noise key,
170
+ # send it to the device, and then switch to noise.
171
+ # The key will be saved in flash and used for future connections
172
+ # and plaintext disabled. Only a factory reset can remove it.
173
+ cg.add_define("USE_API_PLAINTEXT")
157
174
  cg.add_define("USE_API_NOISE")
158
175
  cg.add_library("esphome/noise-c", "0.1.6")
159
176
  else:
@@ -62,7 +62,14 @@ APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *pa
62
62
  : parent_(parent), deferred_message_queue_(this), initial_state_iterator_(this), list_entities_iterator_(this) {
63
63
  this->proto_write_buffer_.reserve(64);
64
64
 
65
- #if defined(USE_API_PLAINTEXT)
65
+ #if defined(USE_API_PLAINTEXT) && defined(USE_API_NOISE)
66
+ auto noise_ctx = parent->get_noise_ctx();
67
+ if (noise_ctx->has_psk()) {
68
+ this->helper_ = std::unique_ptr<APIFrameHelper>{new APINoiseFrameHelper(std::move(sock), noise_ctx)};
69
+ } else {
70
+ this->helper_ = std::unique_ptr<APIFrameHelper>{new APIPlaintextFrameHelper(std::move(sock))};
71
+ }
72
+ #elif defined(USE_API_PLAINTEXT)
66
73
  this->helper_ = std::unique_ptr<APIFrameHelper>{new APIPlaintextFrameHelper(std::move(sock))};
67
74
  #elif defined(USE_API_NOISE)
68
75
  this->helper_ = std::unique_ptr<APIFrameHelper>{new APINoiseFrameHelper(std::move(sock), parent->get_noise_ctx())};
@@ -185,15 +192,34 @@ void APIConnection::loop() {
185
192
 
186
193
  #ifdef USE_ESP32_CAMERA
187
194
  if (this->image_reader_.available() && this->helper_->can_write_without_blocking()) {
188
- uint32_t to_send = std::min((size_t) 1024, this->image_reader_.available());
189
- auto buffer = this->create_buffer();
195
+ // Message will use 8 more bytes than the minimum size, and typical
196
+ // MTU is 1500. Sometimes users will see as low as 1460 MTU.
197
+ // If its IPv6 the header is 40 bytes, and if its IPv4
198
+ // the header is 20 bytes. So we have 1460 - 40 = 1420 bytes
199
+ // available for the payload. But we also need to add the size of
200
+ // the protobuf overhead, which is 8 bytes.
201
+ //
202
+ // To be safe we pick 1390 bytes as the maximum size
203
+ // to send in one go. This is the maximum size of a single packet
204
+ // that can be sent over the network.
205
+ // This is to avoid fragmentation of the packet.
206
+ uint32_t to_send = std::min((size_t) 1390, this->image_reader_.available());
207
+ bool done = this->image_reader_.available() == to_send;
208
+ uint32_t msg_size = 0;
209
+ ProtoSize::add_fixed_field<4>(msg_size, 1, true);
210
+ // partial message size calculated manually since its a special case
211
+ // 1 for the data field, varint for the data size, and the data itself
212
+ msg_size += 1 + ProtoSize::varint(to_send) + to_send;
213
+ ProtoSize::add_bool_field(msg_size, 1, done);
214
+
215
+ auto buffer = this->create_buffer(msg_size);
190
216
  // fixed32 key = 1;
191
217
  buffer.encode_fixed32(1, esp32_camera::global_esp32_camera->get_object_id_hash());
192
218
  // bytes data = 2;
193
219
  buffer.encode_bytes(2, this->image_reader_.peek_data_buffer(), to_send);
194
220
  // bool done = 3;
195
- bool done = this->image_reader_.available() == to_send;
196
221
  buffer.encode_bool(3, done);
222
+
197
223
  bool success = this->send_buffer(buffer, 44);
198
224
 
199
225
  if (success) {
@@ -1468,6 +1494,11 @@ BluetoothConnectionsFreeResponse APIConnection::subscribe_bluetooth_connections_
1468
1494
  resp.limit = bluetooth_proxy::global_bluetooth_proxy->get_bluetooth_connections_limit();
1469
1495
  return resp;
1470
1496
  }
1497
+
1498
+ void APIConnection::bluetooth_scanner_set_mode(const BluetoothScannerSetModeRequest &msg) {
1499
+ bluetooth_proxy::global_bluetooth_proxy->bluetooth_scanner_set_mode(
1500
+ msg.mode == enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_ACTIVE);
1501
+ }
1471
1502
  #endif
1472
1503
 
1473
1504
  #ifdef USE_VOICE_ASSISTANT
@@ -1762,12 +1793,25 @@ bool APIConnection::try_send_log_message(int level, const char *tag, const char
1762
1793
  if (this->log_subscription_ < level)
1763
1794
  return false;
1764
1795
 
1765
- // Send raw so that we don't copy too much
1766
- auto buffer = this->create_buffer();
1767
- // LogLevel level = 1;
1768
- buffer.encode_uint32(1, static_cast<uint32_t>(level));
1769
- // string message = 3;
1770
- buffer.encode_string(3, line, strlen(line));
1796
+ // Pre-calculate message size to avoid reallocations
1797
+ const size_t line_length = strlen(line);
1798
+ uint32_t msg_size = 0;
1799
+
1800
+ // Add size for level field (field ID 1, varint type)
1801
+ // 1 byte for field tag + size of the level varint
1802
+ msg_size += 1 + api::ProtoSize::varint(static_cast<uint32_t>(level));
1803
+
1804
+ // Add size for string field (field ID 3, string type)
1805
+ // 1 byte for field tag + size of length varint + string length
1806
+ msg_size += 1 + api::ProtoSize::varint(static_cast<uint32_t>(line_length)) + line_length;
1807
+
1808
+ // Create a pre-sized buffer
1809
+ auto buffer = this->create_buffer(msg_size);
1810
+
1811
+ // Encode the message (SubscribeLogsResponse)
1812
+ buffer.encode_uint32(1, static_cast<uint32_t>(level)); // LogLevel level = 1
1813
+ buffer.encode_string(3, line, line_length); // string message = 3
1814
+
1771
1815
  // SubscribeLogsResponse - 29
1772
1816
  return this->send_buffer(buffer, 29);
1773
1817
  }
@@ -1848,6 +1892,9 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
1848
1892
  #ifdef USE_VOICE_ASSISTANT
1849
1893
  resp.legacy_voice_assistant_version = voice_assistant::global_voice_assistant->get_legacy_version();
1850
1894
  resp.voice_assistant_feature_flags = voice_assistant::global_voice_assistant->get_feature_flags();
1895
+ #endif
1896
+ #ifdef USE_API_NOISE
1897
+ resp.api_encryption_supported = true;
1851
1898
  #endif
1852
1899
  return resp;
1853
1900
  }
@@ -1869,6 +1916,26 @@ void APIConnection::execute_service(const ExecuteServiceRequest &msg) {
1869
1916
  ESP_LOGV(TAG, "Could not find matching service!");
1870
1917
  }
1871
1918
  }
1919
+ #ifdef USE_API_NOISE
1920
+ NoiseEncryptionSetKeyResponse APIConnection::noise_encryption_set_key(const NoiseEncryptionSetKeyRequest &msg) {
1921
+ psk_t psk{};
1922
+ NoiseEncryptionSetKeyResponse resp;
1923
+ if (base64_decode(msg.key, psk.data(), msg.key.size()) != psk.size()) {
1924
+ ESP_LOGW(TAG, "Invalid encryption key length");
1925
+ resp.success = false;
1926
+ return resp;
1927
+ }
1928
+
1929
+ if (!this->parent_->save_noise_psk(psk, true)) {
1930
+ ESP_LOGW(TAG, "Failed to save encryption key");
1931
+ resp.success = false;
1932
+ return resp;
1933
+ }
1934
+
1935
+ resp.success = true;
1936
+ return resp;
1937
+ }
1938
+ #endif
1872
1939
  void APIConnection::subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) {
1873
1940
  state_subs_at_ = 0;
1874
1941
  }
@@ -221,6 +221,7 @@ class APIConnection : public APIServerConnection {
221
221
  void bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) override;
222
222
  BluetoothConnectionsFreeResponse subscribe_bluetooth_connections_free(
223
223
  const SubscribeBluetoothConnectionsFreeRequest &msg) override;
224
+ void bluetooth_scanner_set_mode(const BluetoothScannerSetModeRequest &msg) override;
224
225
 
225
226
  #endif
226
227
  #ifdef USE_HOMEASSISTANT_TIME
@@ -300,6 +301,9 @@ class APIConnection : public APIServerConnection {
300
301
  return {};
301
302
  }
302
303
  void execute_service(const ExecuteServiceRequest &msg) override;
304
+ #ifdef USE_API_NOISE
305
+ NoiseEncryptionSetKeyResponse noise_encryption_set_key(const NoiseEncryptionSetKeyRequest &msg) override;
306
+ #endif
303
307
 
304
308
  bool is_authenticated() override { return this->connection_state_ == ConnectionState::AUTHENTICATED; }
305
309
  bool is_connection_setup() override {
@@ -308,9 +312,10 @@ class APIConnection : public APIServerConnection {
308
312
  void on_fatal_error() override;
309
313
  void on_unauthenticated_access() override;
310
314
  void on_no_setup_connection() override;
311
- ProtoWriteBuffer create_buffer() override {
315
+ ProtoWriteBuffer create_buffer(uint32_t reserve_size) override {
312
316
  // FIXME: ensure no recursive writes can happen
313
317
  this->proto_write_buffer_.clear();
318
+ this->proto_write_buffer_.reserve(reserve_size);
314
319
  return {&this->proto_write_buffer_};
315
320
  }
316
321
  bool send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) override;