esphome 2025.4.2__py3-none-any.whl → 2025.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (444) hide show
  1. esphome/__main__.py +16 -14
  2. esphome/components/ac_dimmer/ac_dimmer.cpp +3 -2
  3. esphome/components/adc/__init__.py +51 -34
  4. esphome/components/airthings_wave_base/__init__.py +1 -1
  5. esphome/components/alarm_control_panel/__init__.py +37 -2
  6. esphome/components/am43/cover/__init__.py +4 -5
  7. esphome/components/analog_threshold/analog_threshold_binary_sensor.cpp +6 -4
  8. esphome/components/analog_threshold/analog_threshold_binary_sensor.h +4 -5
  9. esphome/components/analog_threshold/binary_sensor.py +10 -8
  10. esphome/components/anova/climate.py +4 -5
  11. esphome/components/api/__init__.py +25 -8
  12. esphome/components/api/api_connection.cpp +416 -662
  13. esphome/components/api/api_connection.h +256 -57
  14. esphome/components/api/api_frame_helper.cpp +232 -177
  15. esphome/components/api/api_frame_helper.h +61 -8
  16. esphome/components/api/api_noise_context.h +13 -4
  17. esphome/components/api/api_pb2.cpp +1422 -1
  18. esphome/components/api/api_pb2.h +255 -1
  19. esphome/components/api/api_pb2_service.cpp +162 -49
  20. esphome/components/api/api_pb2_service.h +90 -51
  21. esphome/components/api/api_pb2_size.h +361 -0
  22. esphome/components/api/api_server.cpp +110 -34
  23. esphome/components/api/api_server.h +8 -0
  24. esphome/components/api/proto.h +86 -17
  25. esphome/components/as7341/as7341.h +1 -1
  26. esphome/components/at581x/at581x.h +4 -4
  27. esphome/components/atm90e32/__init__.py +1 -0
  28. esphome/components/atm90e32/atm90e32.cpp +576 -199
  29. esphome/components/atm90e32/atm90e32.h +128 -31
  30. esphome/components/atm90e32/atm90e32_reg.h +4 -2
  31. esphome/components/atm90e32/button/__init__.py +62 -10
  32. esphome/components/atm90e32/button/atm90e32_button.cpp +63 -4
  33. esphome/components/atm90e32/button/atm90e32_button.h +36 -4
  34. esphome/components/atm90e32/number/__init__.py +130 -0
  35. esphome/components/atm90e32/number/atm90e32_number.h +16 -0
  36. esphome/components/atm90e32/sensor.py +21 -4
  37. esphome/components/atm90e32/text_sensor/__init__.py +48 -0
  38. esphome/components/audio/__init__.py +96 -49
  39. esphome/components/audio/audio.h +48 -0
  40. esphome/components/audio/audio_decoder.cpp +1 -1
  41. esphome/components/audio/audio_resampler.cpp +2 -0
  42. esphome/components/audio/audio_resampler.h +1 -0
  43. esphome/components/ballu/climate.py +2 -9
  44. esphome/components/bang_bang/climate.py +5 -6
  45. esphome/components/bedjet/bedjet_hub.cpp +1 -0
  46. esphome/components/bedjet/climate/__init__.py +3 -8
  47. esphome/components/bedjet/fan/__init__.py +2 -11
  48. esphome/components/binary/fan/__init__.py +13 -16
  49. esphome/components/binary_sensor/__init__.py +13 -10
  50. esphome/components/bl0906/constants.h +16 -16
  51. esphome/components/ble_client/text_sensor/__init__.py +3 -5
  52. esphome/components/bluetooth_proxy/bluetooth_connection.cpp +4 -6
  53. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +136 -21
  54. esphome/components/bluetooth_proxy/bluetooth_proxy.h +7 -0
  55. esphome/components/button/__init__.py +11 -8
  56. esphome/components/canbus/canbus.cpp +3 -0
  57. esphome/components/canbus/canbus.h +16 -0
  58. esphome/components/ccs811/sensor.py +9 -6
  59. esphome/components/climate/__init__.py +35 -2
  60. esphome/components/climate/climate_mode.h +1 -1
  61. esphome/components/climate/climate_traits.h +63 -57
  62. esphome/components/climate_ir/__init__.py +57 -17
  63. esphome/components/climate_ir_lg/climate.py +2 -5
  64. esphome/components/climate_ir_lg/climate_ir_lg.cpp +7 -7
  65. esphome/components/climate_ir_lg/climate_ir_lg.h +1 -1
  66. esphome/components/color/__init__.py +2 -0
  67. esphome/components/const/__init__.py +5 -0
  68. esphome/components/coolix/climate.py +2 -9
  69. esphome/components/copy/cover/__init__.py +10 -9
  70. esphome/components/copy/fan/__init__.py +11 -9
  71. esphome/components/copy/lock/__init__.py +11 -9
  72. esphome/components/copy/text/__init__.py +9 -6
  73. esphome/components/cover/__init__.py +37 -2
  74. esphome/components/cse7766/cse7766.cpp +2 -1
  75. esphome/components/cst226/binary_sensor/__init__.py +28 -0
  76. esphome/components/cst226/binary_sensor/cs226_button.h +22 -0
  77. esphome/components/cst226/binary_sensor/cstt6_button.cpp +19 -0
  78. esphome/components/cst226/touchscreen/cst226_touchscreen.cpp +27 -5
  79. esphome/components/cst226/touchscreen/cst226_touchscreen.h +10 -10
  80. esphome/components/current_based/cover.py +37 -36
  81. esphome/components/current_based/current_based_cover.cpp +2 -1
  82. esphome/components/daikin/climate.py +2 -9
  83. esphome/components/daikin/daikin.cpp +15 -9
  84. esphome/components/daikin/daikin.h +5 -5
  85. esphome/components/daikin_arc/climate.py +2 -7
  86. esphome/components/daikin_brc/climate.py +3 -5
  87. esphome/components/dallas_temp/dallas_temp.cpp +17 -24
  88. esphome/components/dallas_temp/dallas_temp.h +0 -1
  89. esphome/components/daly_bms/daly_bms.cpp +2 -1
  90. esphome/components/debug/debug_component.cpp +6 -1
  91. esphome/components/debug/debug_component.h +8 -0
  92. esphome/components/debug/debug_esp32.cpp +109 -254
  93. esphome/components/debug/sensor.py +14 -0
  94. esphome/components/deep_sleep/deep_sleep_esp32.cpp +13 -1
  95. esphome/components/delonghi/climate.py +2 -9
  96. esphome/components/demo/__init__.py +18 -20
  97. esphome/components/dfrobot_sen0395/switch/__init__.py +21 -22
  98. esphome/components/dps310/sensor.py +6 -6
  99. esphome/components/ee895/sensor.py +9 -9
  100. esphome/components/emmeti/climate.py +2 -9
  101. esphome/components/endstop/cover.py +17 -16
  102. esphome/components/endstop/endstop_cover.cpp +2 -1
  103. esphome/components/ens160_base/__init__.py +12 -9
  104. esphome/components/esp32/__init__.py +60 -3
  105. esphome/components/esp32/core.cpp +11 -5
  106. esphome/components/esp32/gpio.cpp +86 -24
  107. esphome/components/esp32/gpio.py +15 -16
  108. esphome/components/esp32/gpio_esp32.py +1 -2
  109. esphome/components/esp32/gpio_esp32_c2.py +1 -1
  110. esphome/components/esp32/gpio_esp32_c3.py +1 -1
  111. esphome/components/esp32/gpio_esp32_c6.py +1 -1
  112. esphome/components/esp32/gpio_esp32_h2.py +1 -1
  113. esphome/components/esp32_ble/ble.cpp +1 -0
  114. esphome/components/esp32_ble/ble.h +5 -3
  115. esphome/components/esp32_ble/ble_advertising.cpp +2 -1
  116. esphome/components/esp32_ble/ble_advertising.h +1 -0
  117. esphome/components/esp32_ble_server/__init__.py +3 -0
  118. esphome/components/esp32_ble_tracker/__init__.py +7 -1
  119. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +192 -118
  120. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +29 -3
  121. esphome/components/esp32_camera/__init__.py +1 -1
  122. esphome/components/esp32_camera/esp32_camera.cpp +2 -10
  123. esphome/components/esp32_camera/esp32_camera.h +1 -1
  124. esphome/components/esp32_can/esp32_can.cpp +1 -1
  125. esphome/components/esp32_improv/esp32_improv_component.cpp +1 -1
  126. esphome/components/esp32_rmt_led_strip/led_strip.cpp +1 -1
  127. esphome/components/esp32_rmt_led_strip/led_strip.h +7 -5
  128. esphome/components/esp32_rmt_led_strip/light.py +9 -1
  129. esphome/components/esp32_touch/esp32_touch.cpp +1 -1
  130. esphome/components/esp8266/gpio.cpp +69 -8
  131. esphome/components/ethernet/ethernet_component.cpp +1 -1
  132. esphome/components/event/__init__.py +13 -10
  133. esphome/components/factory_reset/switch/__init__.py +7 -21
  134. esphome/components/fan/__init__.py +52 -5
  135. esphome/components/fastled_base/__init__.py +1 -4
  136. esphome/components/fastled_base/fastled_light.cpp +1 -1
  137. esphome/components/feedback/cover.py +38 -33
  138. esphome/components/feedback/feedback_cover.cpp +2 -1
  139. esphome/components/fujitsu_general/climate.py +2 -9
  140. esphome/components/gcja5/gcja5.cpp +2 -1
  141. esphome/components/gpio/one_wire/gpio_one_wire.cpp +45 -43
  142. esphome/components/gpio/one_wire/gpio_one_wire.h +2 -1
  143. esphome/components/gpio_expander/cached_gpio.h +22 -7
  144. esphome/components/gps/__init__.py +47 -17
  145. esphome/components/gps/gps.cpp +42 -23
  146. esphome/components/gps/gps.h +17 -13
  147. esphome/components/graph/__init__.py +1 -2
  148. esphome/components/gree/climate.py +4 -6
  149. esphome/components/gree/gree.cpp +16 -2
  150. esphome/components/gree/gree.h +2 -2
  151. esphome/components/growatt_solar/growatt_solar.cpp +2 -1
  152. esphome/components/haier/climate.py +37 -34
  153. esphome/components/hbridge/fan/__init__.py +19 -17
  154. esphome/components/he60r/cover.py +4 -5
  155. esphome/components/heatpumpir/climate.py +3 -6
  156. esphome/components/hitachi_ac344/climate.py +2 -9
  157. esphome/components/hitachi_ac424/climate.py +2 -9
  158. esphome/components/hm3301/hm3301.h +1 -1
  159. esphome/components/hte501/sensor.py +6 -6
  160. esphome/components/http_request/__init__.py +39 -6
  161. esphome/components/http_request/http_request.cpp +20 -0
  162. esphome/components/http_request/http_request.h +57 -15
  163. esphome/components/http_request/http_request_arduino.cpp +22 -6
  164. esphome/components/http_request/http_request_arduino.h +4 -3
  165. esphome/components/http_request/http_request_host.cpp +141 -0
  166. esphome/components/http_request/http_request_host.h +37 -0
  167. esphome/components/http_request/http_request_idf.cpp +35 -3
  168. esphome/components/http_request/http_request_idf.h +10 -3
  169. esphome/components/http_request/httplib.h +9691 -0
  170. esphome/components/http_request/update/__init__.py +11 -8
  171. esphome/components/hyt271/sensor.py +6 -6
  172. esphome/components/i2c/i2c.h +4 -0
  173. esphome/components/i2c/i2c_bus_esp_idf.cpp +1 -1
  174. esphome/components/i2s_audio/__init__.py +131 -22
  175. esphome/components/i2s_audio/i2s_audio.h +44 -4
  176. esphome/components/i2s_audio/media_player/__init__.py +19 -9
  177. esphome/components/i2s_audio/microphone/__init__.py +63 -5
  178. esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +351 -61
  179. esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +40 -6
  180. esphome/components/i2s_audio/speaker/__init__.py +31 -5
  181. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +155 -19
  182. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +17 -4
  183. esphome/components/ili9xxx/ili9xxx_init.h +1 -1
  184. esphome/components/image/__init__.py +37 -17
  185. esphome/components/image/image.cpp +25 -8
  186. esphome/components/internal_temperature/internal_temperature.cpp +6 -4
  187. esphome/components/key_collector/__init__.py +35 -0
  188. esphome/components/key_collector/key_collector.cpp +8 -0
  189. esphome/components/key_collector/key_collector.h +10 -0
  190. esphome/components/kuntze/kuntze.cpp +2 -1
  191. esphome/components/ld2410/ld2410.h +1 -1
  192. esphome/components/ld2450/ld2450.h +1 -1
  193. esphome/components/light/__init__.py +57 -0
  194. esphome/components/lock/__init__.py +51 -4
  195. esphome/components/lock/automation.h +2 -13
  196. esphome/components/logger/__init__.py +22 -0
  197. esphome/components/logger/logger.cpp +154 -103
  198. esphome/components/logger/logger.h +211 -36
  199. esphome/components/logger/task_log_buffer.cpp +138 -0
  200. esphome/components/logger/task_log_buffer.h +69 -0
  201. esphome/components/lvgl/__init__.py +13 -5
  202. esphome/components/lvgl/automation.py +50 -1
  203. esphome/components/lvgl/defines.py +0 -1
  204. esphome/components/lvgl/lvgl_esphome.cpp +5 -1
  205. esphome/components/lvgl/text/__init__.py +1 -2
  206. esphome/components/mapping/__init__.py +134 -0
  207. esphome/components/matrix_keypad/matrix_keypad.cpp +2 -1
  208. esphome/components/max7219digit/max7219digit.cpp +28 -27
  209. esphome/components/mdns/__init__.py +11 -5
  210. esphome/components/mdns/mdns_component.cpp +11 -5
  211. esphome/components/mdns/mdns_component.h +3 -2
  212. esphome/components/mdns/mdns_esp32.cpp +4 -3
  213. esphome/components/mdns/mdns_esp8266.cpp +4 -2
  214. esphome/components/mdns/mdns_libretiny.cpp +4 -2
  215. esphome/components/mdns/mdns_rp2040.cpp +4 -2
  216. esphome/components/media_player/__init__.py +33 -1
  217. esphome/components/mhz19/sensor.py +11 -7
  218. esphome/components/micro_wake_word/__init__.py +99 -31
  219. esphome/components/micro_wake_word/automation.h +54 -0
  220. esphome/components/micro_wake_word/micro_wake_word.cpp +331 -319
  221. esphome/components/micro_wake_word/micro_wake_word.h +58 -105
  222. esphome/components/micro_wake_word/preprocessor_settings.h +19 -2
  223. esphome/components/micro_wake_word/streaming_model.cpp +158 -41
  224. esphome/components/micro_wake_word/streaming_model.h +85 -13
  225. esphome/components/microphone/__init__.py +139 -9
  226. esphome/components/microphone/automation.h +14 -2
  227. esphome/components/microphone/microphone.cpp +21 -0
  228. esphome/components/microphone/microphone.h +14 -5
  229. esphome/components/microphone/microphone_source.cpp +95 -0
  230. esphome/components/microphone/microphone_source.h +80 -0
  231. esphome/components/mics_4514/sensor.py +25 -14
  232. esphome/components/midea/climate.py +3 -4
  233. esphome/components/midea_ir/climate.py +3 -5
  234. esphome/components/mipi_spi/__init__.py +15 -0
  235. esphome/components/mipi_spi/display.py +474 -0
  236. esphome/components/mipi_spi/mipi_spi.cpp +481 -0
  237. esphome/components/mipi_spi/mipi_spi.h +171 -0
  238. esphome/components/mipi_spi/models/__init__.py +65 -0
  239. esphome/components/mipi_spi/models/amoled.py +72 -0
  240. esphome/components/mipi_spi/models/commands.py +82 -0
  241. esphome/components/mipi_spi/models/cyd.py +10 -0
  242. esphome/components/mipi_spi/models/ili.py +749 -0
  243. esphome/components/mipi_spi/models/jc.py +260 -0
  244. esphome/components/mipi_spi/models/lanbon.py +15 -0
  245. esphome/components/mipi_spi/models/lilygo.py +60 -0
  246. esphome/components/mipi_spi/models/waveshare.py +139 -0
  247. esphome/components/mitsubishi/climate.py +2 -5
  248. esphome/components/mitsubishi/mitsubishi.cpp +9 -9
  249. esphome/components/mixer/speaker/mixer_speaker.cpp +12 -22
  250. esphome/components/mixer/speaker/mixer_speaker.h +1 -3
  251. esphome/components/mlx90393/sensor.py +5 -0
  252. esphome/components/mlx90393/sensor_mlx90393.cpp +195 -13
  253. esphome/components/mlx90393/sensor_mlx90393.h +21 -4
  254. esphome/components/modbus/modbus.cpp +2 -1
  255. esphome/components/mqtt/__init__.py +1 -1
  256. esphome/components/mqtt/mqtt_client.cpp +6 -2
  257. esphome/components/mqtt/mqtt_const.h +4 -0
  258. esphome/components/mqtt/mqtt_fan.cpp +39 -0
  259. esphome/components/mqtt/mqtt_fan.h +2 -0
  260. esphome/components/ms5611/sensor.py +6 -6
  261. esphome/components/ms8607/sensor.py +3 -3
  262. esphome/components/network/__init__.py +1 -1
  263. esphome/components/nextion/base_component.py +17 -16
  264. esphome/components/nextion/display.py +11 -2
  265. esphome/components/nextion/nextion.cpp +39 -1
  266. esphome/components/nextion/nextion.h +50 -0
  267. esphome/components/noblex/climate.py +2 -9
  268. esphome/components/number/__init__.py +12 -9
  269. esphome/components/one_wire/one_wire_bus.cpp +14 -10
  270. esphome/components/one_wire/one_wire_bus.h +14 -8
  271. esphome/components/online_image/bmp_image.cpp +48 -11
  272. esphome/components/online_image/bmp_image.h +2 -0
  273. esphome/components/opentherm/binary_sensor/__init__.py +2 -4
  274. esphome/components/opentherm/number/__init__.py +11 -20
  275. esphome/components/opentherm/sensor/__init__.py +3 -3
  276. esphome/components/opentherm/switch/__init__.py +3 -5
  277. esphome/components/output/lock/__init__.py +11 -9
  278. esphome/components/packages/__init__.py +33 -31
  279. esphome/components/packet_transport/__init__.py +201 -0
  280. esphome/components/packet_transport/binary_sensor.py +19 -0
  281. esphome/components/packet_transport/packet_transport.cpp +534 -0
  282. esphome/components/packet_transport/packet_transport.h +154 -0
  283. esphome/components/packet_transport/sensor.py +19 -0
  284. esphome/components/pca9685/pca9685_output.cpp +2 -1
  285. esphome/components/pid/climate.py +2 -4
  286. esphome/components/pm2005/__init__.py +1 -0
  287. esphome/components/pm2005/pm2005.cpp +123 -0
  288. esphome/components/pm2005/pm2005.h +46 -0
  289. esphome/components/pm2005/sensor.py +86 -0
  290. esphome/components/pmsa003i/pmsa003i.cpp +43 -16
  291. esphome/components/pmsa003i/pmsa003i.h +25 -25
  292. esphome/components/pmsx003/pmsx003.cpp +195 -230
  293. esphome/components/pmsx003/pmsx003.h +51 -33
  294. esphome/components/pmsx003/sensor.py +21 -11
  295. esphome/components/pn7150/pn7150.h +2 -2
  296. esphome/components/pn7160/pn7160.h +2 -2
  297. esphome/components/prometheus/prometheus_handler.cpp +174 -0
  298. esphome/components/prometheus/prometheus_handler.h +17 -0
  299. esphome/components/psram/__init__.py +7 -5
  300. esphome/components/pulse_meter/pulse_meter_sensor.cpp +32 -12
  301. esphome/components/pulse_meter/pulse_meter_sensor.h +5 -5
  302. esphome/components/pzem004t/pzem004t.cpp +2 -1
  303. esphome/components/qspi_dbi/__init__.py +0 -1
  304. esphome/components/qspi_dbi/display.py +2 -1
  305. esphome/components/qspi_dbi/models.py +1 -2
  306. esphome/components/remote_base/__init__.py +91 -0
  307. esphome/components/remote_base/beo4_protocol.cpp +153 -0
  308. esphome/components/remote_base/beo4_protocol.h +43 -0
  309. esphome/components/remote_base/gobox_protocol.cpp +131 -0
  310. esphome/components/remote_base/gobox_protocol.h +54 -0
  311. esphome/components/remote_receiver/remote_receiver_esp32.cpp +16 -9
  312. esphome/components/resampler/speaker/resampler_speaker.cpp +12 -10
  313. esphome/components/resampler/speaker/resampler_speaker.h +1 -1
  314. esphome/components/rf_bridge/rf_bridge.cpp +2 -1
  315. esphome/components/scd30/sensor.py +2 -3
  316. esphome/components/scd4x/sensor.py +4 -5
  317. esphome/components/sdp3x/sensor.py +2 -1
  318. esphome/components/sds011/sds011.cpp +2 -1
  319. esphome/components/select/__init__.py +19 -20
  320. esphome/components/sen5x/sen5x.cpp +55 -36
  321. esphome/components/sen5x/sensor.py +1 -1
  322. esphome/components/senseair/sensor.py +3 -3
  323. esphome/components/sensor/__init__.py +158 -14
  324. esphome/components/sensor/filter.cpp +23 -0
  325. esphome/components/sensor/filter.h +22 -0
  326. esphome/components/sgp30/sensor.py +14 -16
  327. esphome/components/sgp4x/sensor.py +1 -1
  328. esphome/components/sht4x/sht4x.cpp +43 -22
  329. esphome/components/sht4x/sht4x.h +1 -1
  330. esphome/components/shtcx/sensor.py +6 -6
  331. esphome/components/slow_pwm/slow_pwm_output.cpp +2 -1
  332. esphome/components/sml/text_sensor/__init__.py +4 -6
  333. esphome/components/sound_level/__init__.py +0 -0
  334. esphome/components/sound_level/sensor.py +97 -0
  335. esphome/components/sound_level/sound_level.cpp +194 -0
  336. esphome/components/sound_level/sound_level.h +73 -0
  337. esphome/components/speaker/media_player/__init__.py +4 -8
  338. esphome/components/speaker/media_player/speaker_media_player.cpp +0 -18
  339. esphome/components/speaker/media_player/speaker_media_player.h +0 -11
  340. esphome/components/speaker/speaker.h +4 -7
  341. esphome/components/speed/fan/__init__.py +17 -16
  342. esphome/components/spi/spi.h +11 -1
  343. esphome/components/sprinkler/__init__.py +18 -19
  344. esphome/components/sprinkler/sprinkler.cpp +6 -5
  345. esphome/components/switch/__init__.py +32 -42
  346. esphome/components/syslog/__init__.py +41 -0
  347. esphome/components/syslog/esphome_syslog.cpp +49 -0
  348. esphome/components/syslog/esphome_syslog.h +27 -0
  349. esphome/components/t6615/sensor.py +3 -3
  350. esphome/components/t6615/t6615.cpp +2 -1
  351. esphome/components/tca9555/tca9555.cpp +11 -6
  352. esphome/components/tcl112/climate.py +2 -9
  353. esphome/components/template/alarm_control_panel/__init__.py +7 -6
  354. esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +21 -17
  355. esphome/components/template/alarm_control_panel/template_alarm_control_panel.h +2 -1
  356. esphome/components/template/cover/__init__.py +27 -21
  357. esphome/components/template/fan/__init__.py +14 -12
  358. esphome/components/template/lock/__init__.py +20 -25
  359. esphome/components/template/lock/automation.h +18 -0
  360. esphome/components/template/text/__init__.py +4 -3
  361. esphome/components/template/valve/__init__.py +32 -21
  362. esphome/components/template/valve/automation.h +24 -0
  363. esphome/components/text/__init__.py +32 -1
  364. esphome/components/text_sensor/__init__.py +24 -29
  365. esphome/components/thermostat/climate.py +5 -5
  366. esphome/components/time_based/cover.py +17 -16
  367. esphome/components/time_based/time_based_cover.cpp +2 -1
  368. esphome/components/tm1638/switch/__init__.py +10 -7
  369. esphome/components/tormatic/cover.py +4 -5
  370. esphome/components/toshiba/climate.py +3 -5
  371. esphome/components/touchscreen/touchscreen.cpp +3 -1
  372. esphome/components/tuya/climate/__init__.py +5 -6
  373. esphome/components/tuya/cover/__init__.py +6 -11
  374. esphome/components/tuya/select/__init__.py +15 -5
  375. esphome/components/tuya/select/tuya_select.cpp +6 -1
  376. esphome/components/tuya/select/tuya_select.h +5 -1
  377. esphome/components/uart/packet_transport/__init__.py +20 -0
  378. esphome/components/uart/packet_transport/uart_transport.cpp +88 -0
  379. esphome/components/uart/packet_transport/uart_transport.h +41 -0
  380. esphome/components/uart/switch/uart_switch.cpp +2 -1
  381. esphome/components/udp/__init__.py +126 -128
  382. esphome/components/udp/automation.h +40 -0
  383. esphome/components/udp/binary_sensor.py +3 -25
  384. esphome/components/udp/packet_transport/__init__.py +29 -0
  385. esphome/components/udp/packet_transport/udp_transport.cpp +36 -0
  386. esphome/components/udp/packet_transport/udp_transport.h +28 -0
  387. esphome/components/udp/sensor.py +3 -25
  388. esphome/components/udp/udp_component.cpp +26 -470
  389. esphome/components/udp/udp_component.h +21 -128
  390. esphome/components/update/__init__.py +31 -1
  391. esphome/components/uponor_smatrix/climate/__init__.py +4 -9
  392. esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp +2 -1
  393. esphome/components/uponor_smatrix/uponor_smatrix.cpp +2 -1
  394. esphome/components/uptime/text_sensor/__init__.py +47 -7
  395. esphome/components/uptime/text_sensor/uptime_text_sensor.cpp +12 -7
  396. esphome/components/uptime/text_sensor/uptime_text_sensor.h +19 -0
  397. esphome/components/valve/__init__.py +34 -3
  398. esphome/components/valve/automation.h +1 -19
  399. esphome/components/vl53l0x/sensor.py +11 -0
  400. esphome/components/vl53l0x/vl53l0x_sensor.cpp +5 -1
  401. esphome/components/vl53l0x/vl53l0x_sensor.h +2 -1
  402. esphome/components/voice_assistant/__init__.py +36 -10
  403. esphome/components/voice_assistant/voice_assistant.cpp +170 -144
  404. esphome/components/voice_assistant/voice_assistant.h +26 -25
  405. esphome/components/waveshare_epaper/display.py +6 -0
  406. esphome/components/waveshare_epaper/waveshare_epaper.cpp +439 -37
  407. esphome/components/waveshare_epaper/waveshare_epaper.h +60 -11
  408. esphome/components/weikai/weikai.cpp +0 -52
  409. esphome/components/whirlpool/climate.py +3 -5
  410. esphome/components/whynter/climate.py +3 -5
  411. esphome/components/xpt2046/touchscreen/xpt2046.cpp +1 -1
  412. esphome/components/yashima/climate.py +6 -6
  413. esphome/components/zhlt01/climate.py +2 -7
  414. esphome/config.py +13 -13
  415. esphome/config_validation.py +38 -58
  416. esphome/const.py +15 -1
  417. esphome/core/__init__.py +2 -0
  418. esphome/core/application.cpp +27 -10
  419. esphome/core/application.h +9 -1
  420. esphome/core/automation.h +4 -3
  421. esphome/core/component.cpp +28 -7
  422. esphome/core/component.h +10 -1
  423. esphome/core/defines.h +23 -17
  424. esphome/core/doxygen.h +13 -0
  425. esphome/core/macros.h +4 -0
  426. esphome/core/scheduler.cpp +7 -1
  427. esphome/cpp_generator.py +6 -2
  428. esphome/dashboard/web_server.py +3 -3
  429. esphome/helpers.py +39 -0
  430. esphome/loader.py +4 -0
  431. esphome/log.py +15 -19
  432. esphome/mqtt.py +23 -10
  433. esphome/platformio_api.py +1 -1
  434. esphome/schema_extractors.py +0 -1
  435. esphome/voluptuous_schema.py +3 -1
  436. esphome/vscode.py +15 -0
  437. esphome/wizard.py +47 -37
  438. esphome/zeroconf.py +7 -3
  439. {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/METADATA +10 -11
  440. {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/RECORD +444 -383
  441. {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/WHEEL +1 -1
  442. {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/entry_points.txt +0 -0
  443. {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/licenses/LICENSE +0 -0
  444. {esphome-2025.4.2.dist-info → esphome-2025.5.0.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,7 @@
1
1
  from esphome import automation
2
2
  from esphome.automation import register_action, register_condition
3
3
  import esphome.codegen as cg
4
- from esphome.components import media_player, microphone, speaker
4
+ from esphome.components import media_player, micro_wake_word, microphone, speaker
5
5
  import esphome.config_validation as cv
6
6
  from esphome.const import (
7
7
  CONF_ID,
@@ -41,6 +41,7 @@ CONF_AUTO_GAIN = "auto_gain"
41
41
  CONF_NOISE_SUPPRESSION_LEVEL = "noise_suppression_level"
42
42
  CONF_VOLUME_MULTIPLIER = "volume_multiplier"
43
43
 
44
+ CONF_MICRO_WAKE_WORD = "micro_wake_word"
44
45
  CONF_WAKE_WORD = "wake_word"
45
46
 
46
47
  CONF_CONVERSATION_TIMEOUT = "conversation_timeout"
@@ -88,14 +89,22 @@ CONFIG_SCHEMA = cv.All(
88
89
  cv.Schema(
89
90
  {
90
91
  cv.GenerateID(): cv.declare_id(VoiceAssistant),
91
- cv.GenerateID(CONF_MICROPHONE): cv.use_id(microphone.Microphone),
92
- cv.Exclusive(CONF_SPEAKER, "output"): cv.use_id(speaker.Speaker),
92
+ cv.Optional(
93
+ CONF_MICROPHONE, default={}
94
+ ): microphone.microphone_source_schema(
95
+ min_bits_per_sample=16,
96
+ max_bits_per_sample=16,
97
+ min_channels=1,
98
+ max_channels=1,
99
+ ),
93
100
  cv.Exclusive(CONF_MEDIA_PLAYER, "output"): cv.use_id(
94
101
  media_player.MediaPlayer
95
102
  ),
103
+ cv.Exclusive(CONF_SPEAKER, "output"): cv.use_id(speaker.Speaker),
96
104
  cv.Optional(CONF_USE_WAKE_WORD, default=False): cv.boolean,
97
- cv.Optional(CONF_VAD_THRESHOLD): cv.All(
98
- cv.requires_component("esp_adf"), cv.only_with_esp_idf, cv.uint8_t
105
+ cv.Optional(CONF_MICRO_WAKE_WORD): cv.use_id(micro_wake_word.MicroWakeWord),
106
+ cv.Optional(CONF_VAD_THRESHOLD): cv.invalid(
107
+ "VAD threshold is no longer supported, as it requires the deprecated esp_adf external component. Use an i2s_audio microphone/speaker instead. Additionally, you may need to configure the audio_adc and audio_dac components depending on your hardware."
99
108
  ),
100
109
  cv.Optional(CONF_NOISE_SUPPRESSION_LEVEL, default=0): cv.int_range(0, 4),
101
110
  cv.Optional(CONF_AUTO_GAIN, default="0dBFS"): cv.All(
@@ -163,22 +172,39 @@ CONFIG_SCHEMA = cv.All(
163
172
  tts_stream_validate,
164
173
  )
165
174
 
175
+ FINAL_VALIDATE_SCHEMA = cv.All(
176
+ cv.Schema(
177
+ {
178
+ cv.Optional(
179
+ CONF_MICROPHONE
180
+ ): microphone.final_validate_microphone_source_schema(
181
+ "voice_assistant", sample_rate=16000
182
+ ),
183
+ },
184
+ extra=cv.ALLOW_EXTRA,
185
+ ),
186
+ )
187
+
166
188
 
167
189
  async def to_code(config):
168
190
  var = cg.new_Pvariable(config[CONF_ID])
169
191
  await cg.register_component(var, config)
170
192
 
171
- mic = await cg.get_variable(config[CONF_MICROPHONE])
172
- cg.add(var.set_microphone(mic))
193
+ mic_source = await microphone.microphone_source_to_code(config[CONF_MICROPHONE])
194
+ cg.add(var.set_microphone_source(mic_source))
173
195
 
174
- if CONF_SPEAKER in config:
175
- spkr = await cg.get_variable(config[CONF_SPEAKER])
176
- cg.add(var.set_speaker(spkr))
196
+ if CONF_MICRO_WAKE_WORD in config:
197
+ mww = await cg.get_variable(config[CONF_MICRO_WAKE_WORD])
198
+ cg.add(var.set_micro_wake_word(mww))
177
199
 
178
200
  if CONF_MEDIA_PLAYER in config:
179
201
  mp = await cg.get_variable(config[CONF_MEDIA_PLAYER])
180
202
  cg.add(var.set_media_player(mp))
181
203
 
204
+ if CONF_SPEAKER in config:
205
+ spkr = await cg.get_variable(config[CONF_SPEAKER])
206
+ cg.add(var.set_speaker(spkr))
207
+
182
208
  cg.add(var.set_use_wake_word(config[CONF_USE_WAKE_WORD]))
183
209
 
184
210
  if (vad_threshold := config.get(CONF_VAD_THRESHOLD)) is not None:
@@ -18,14 +18,25 @@ static const char *const TAG = "voice_assistant";
18
18
  #endif
19
19
 
20
20
  static const size_t SAMPLE_RATE_HZ = 16000;
21
- static const size_t INPUT_BUFFER_SIZE = 32 * SAMPLE_RATE_HZ / 1000; // 32ms * 16kHz / 1000ms
22
- static const size_t BUFFER_SIZE = 512 * SAMPLE_RATE_HZ / 1000;
23
- static const size_t SEND_BUFFER_SIZE = INPUT_BUFFER_SIZE * sizeof(int16_t);
21
+
22
+ static const size_t RING_BUFFER_SAMPLES = 512 * SAMPLE_RATE_HZ / 1000; // 512 ms * 16 kHz/ 1000 ms
23
+ static const size_t RING_BUFFER_SIZE = RING_BUFFER_SAMPLES * sizeof(int16_t);
24
+ static const size_t SEND_BUFFER_SAMPLES = 32 * SAMPLE_RATE_HZ / 1000; // 32ms * 16kHz / 1000ms
25
+ static const size_t SEND_BUFFER_SIZE = SEND_BUFFER_SAMPLES * sizeof(int16_t);
24
26
  static const size_t RECEIVE_SIZE = 1024;
25
27
  static const size_t SPEAKER_BUFFER_SIZE = 16 * RECEIVE_SIZE;
26
28
 
27
29
  VoiceAssistant::VoiceAssistant() { global_voice_assistant = this; }
28
30
 
31
+ void VoiceAssistant::setup() {
32
+ this->mic_source_->add_data_callback([this](const std::vector<uint8_t> &data) {
33
+ std::shared_ptr<RingBuffer> temp_ring_buffer = this->ring_buffer_;
34
+ if (this->ring_buffer_.use_count() > 1) {
35
+ temp_ring_buffer->write((void *) data.data(), data.size());
36
+ }
37
+ });
38
+ }
39
+
29
40
  float VoiceAssistant::get_setup_priority() const { return setup_priority::AFTER_CONNECTION; }
30
41
 
31
42
  bool VoiceAssistant::start_udp_socket_() {
@@ -72,12 +83,8 @@ bool VoiceAssistant::start_udp_socket_() {
72
83
  }
73
84
 
74
85
  bool VoiceAssistant::allocate_buffers_() {
75
- if (this->send_buffer_ != nullptr) {
76
- return true; // Already allocated
77
- }
78
-
79
86
  #ifdef USE_SPEAKER
80
- if (this->speaker_ != nullptr) {
87
+ if ((this->speaker_ != nullptr) && (this->speaker_buffer_ == nullptr)) {
81
88
  ExternalRAMAllocator<uint8_t> speaker_allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
82
89
  this->speaker_buffer_ = speaker_allocator.allocate(SPEAKER_BUFFER_SIZE);
83
90
  if (this->speaker_buffer_ == nullptr) {
@@ -87,28 +94,21 @@ bool VoiceAssistant::allocate_buffers_() {
87
94
  }
88
95
  #endif
89
96
 
90
- ExternalRAMAllocator<int16_t> allocator(ExternalRAMAllocator<int16_t>::ALLOW_FAILURE);
91
- this->input_buffer_ = allocator.allocate(INPUT_BUFFER_SIZE);
92
- if (this->input_buffer_ == nullptr) {
93
- ESP_LOGW(TAG, "Could not allocate input buffer");
94
- return false;
95
- }
96
-
97
- #ifdef USE_ESP_ADF
98
- this->vad_instance_ = vad_create(VAD_MODE_4);
99
- #endif
100
-
101
- this->ring_buffer_ = RingBuffer::create(BUFFER_SIZE * sizeof(int16_t));
102
- if (this->ring_buffer_ == nullptr) {
103
- ESP_LOGW(TAG, "Could not allocate ring buffer");
104
- return false;
97
+ if (this->ring_buffer_.use_count() == 0) {
98
+ this->ring_buffer_ = RingBuffer::create(RING_BUFFER_SIZE);
99
+ if (this->ring_buffer_.use_count() == 0) {
100
+ ESP_LOGE(TAG, "Could not allocate ring buffer");
101
+ return false;
102
+ }
105
103
  }
106
104
 
107
- ExternalRAMAllocator<uint8_t> send_allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
108
- this->send_buffer_ = send_allocator.allocate(SEND_BUFFER_SIZE);
109
- if (send_buffer_ == nullptr) {
110
- ESP_LOGW(TAG, "Could not allocate send buffer");
111
- return false;
105
+ if (this->send_buffer_ == nullptr) {
106
+ ExternalRAMAllocator<uint8_t> send_allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
107
+ this->send_buffer_ = send_allocator.allocate(SEND_BUFFER_SIZE);
108
+ if (send_buffer_ == nullptr) {
109
+ ESP_LOGW(TAG, "Could not allocate send buffer");
110
+ return false;
111
+ }
112
112
  }
113
113
 
114
114
  return true;
@@ -119,10 +119,6 @@ void VoiceAssistant::clear_buffers_() {
119
119
  memset(this->send_buffer_, 0, SEND_BUFFER_SIZE);
120
120
  }
121
121
 
122
- if (this->input_buffer_ != nullptr) {
123
- memset(this->input_buffer_, 0, INPUT_BUFFER_SIZE * sizeof(int16_t));
124
- }
125
-
126
122
  if (this->ring_buffer_ != nullptr) {
127
123
  this->ring_buffer_->reset();
128
124
  }
@@ -139,25 +135,15 @@ void VoiceAssistant::clear_buffers_() {
139
135
  }
140
136
 
141
137
  void VoiceAssistant::deallocate_buffers_() {
142
- ExternalRAMAllocator<uint8_t> send_deallocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
143
- send_deallocator.deallocate(this->send_buffer_, SEND_BUFFER_SIZE);
144
- this->send_buffer_ = nullptr;
145
-
146
- if (this->ring_buffer_ != nullptr) {
147
- this->ring_buffer_.reset();
148
- this->ring_buffer_ = nullptr;
138
+ if (this->send_buffer_ != nullptr) {
139
+ ExternalRAMAllocator<uint8_t> send_deallocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
140
+ send_deallocator.deallocate(this->send_buffer_, SEND_BUFFER_SIZE);
141
+ this->send_buffer_ = nullptr;
149
142
  }
150
143
 
151
- #ifdef USE_ESP_ADF
152
- if (this->vad_instance_ != nullptr) {
153
- vad_destroy(this->vad_instance_);
154
- this->vad_instance_ = nullptr;
144
+ if (this->ring_buffer_.use_count() > 0) {
145
+ this->ring_buffer_.reset();
155
146
  }
156
- #endif
157
-
158
- ExternalRAMAllocator<int16_t> input_deallocator(ExternalRAMAllocator<int16_t>::ALLOW_FAILURE);
159
- input_deallocator.deallocate(this->input_buffer_, INPUT_BUFFER_SIZE);
160
- this->input_buffer_ = nullptr;
161
147
 
162
148
  #ifdef USE_SPEAKER
163
149
  if ((this->speaker_ != nullptr) && (this->speaker_buffer_ != nullptr)) {
@@ -173,26 +159,10 @@ void VoiceAssistant::reset_conversation_id() {
173
159
  ESP_LOGD(TAG, "reset conversation ID");
174
160
  }
175
161
 
176
- int VoiceAssistant::read_microphone_() {
177
- size_t bytes_read = 0;
178
- if (this->mic_->is_running()) { // Read audio into input buffer
179
- bytes_read = this->mic_->read(this->input_buffer_, INPUT_BUFFER_SIZE * sizeof(int16_t));
180
- if (bytes_read == 0) {
181
- memset(this->input_buffer_, 0, INPUT_BUFFER_SIZE * sizeof(int16_t));
182
- return 0;
183
- }
184
- // Write audio into ring buffer
185
- this->ring_buffer_->write((void *) this->input_buffer_, bytes_read);
186
- } else {
187
- ESP_LOGD(TAG, "microphone not running");
188
- }
189
- return bytes_read;
190
- }
191
-
192
162
  void VoiceAssistant::loop() {
193
163
  if (this->api_client_ == nullptr && this->state_ != State::IDLE && this->state_ != State::STOP_MICROPHONE &&
194
164
  this->state_ != State::STOPPING_MICROPHONE) {
195
- if (this->mic_->is_running() || this->state_ == State::STARTING_MICROPHONE) {
165
+ if (this->mic_source_->is_running() || this->state_ == State::STARTING_MICROPHONE) {
196
166
  this->set_state_(State::STOP_MICROPHONE, State::IDLE);
197
167
  } else {
198
168
  this->set_state_(State::IDLE, State::IDLE);
@@ -206,16 +176,9 @@ void VoiceAssistant::loop() {
206
176
  case State::IDLE: {
207
177
  if (this->continuous_ && this->desired_state_ == State::IDLE) {
208
178
  this->idle_trigger_->trigger();
209
- #ifdef USE_ESP_ADF
210
- if (this->use_wake_word_) {
211
- this->set_state_(State::START_MICROPHONE, State::WAIT_FOR_VAD);
212
- } else
213
- #endif
214
- {
215
- this->set_state_(State::START_MICROPHONE, State::START_PIPELINE);
216
- }
179
+ this->set_state_(State::START_MICROPHONE, State::START_PIPELINE);
217
180
  } else {
218
- this->high_freq_.stop();
181
+ this->deallocate_buffers_();
219
182
  }
220
183
  break;
221
184
  }
@@ -230,53 +193,20 @@ void VoiceAssistant::loop() {
230
193
  }
231
194
  this->clear_buffers_();
232
195
 
233
- this->mic_->start();
234
- this->high_freq_.start();
196
+ this->mic_source_->start();
235
197
  this->set_state_(State::STARTING_MICROPHONE);
236
198
  break;
237
199
  }
238
200
  case State::STARTING_MICROPHONE: {
239
- if (this->mic_->is_running()) {
201
+ if (this->mic_source_->is_running()) {
240
202
  this->set_state_(this->desired_state_);
241
203
  }
242
204
  break;
243
205
  }
244
- #ifdef USE_ESP_ADF
245
- case State::WAIT_FOR_VAD: {
246
- this->read_microphone_();
247
- ESP_LOGD(TAG, "Waiting for speech...");
248
- this->set_state_(State::WAITING_FOR_VAD);
249
- break;
250
- }
251
- case State::WAITING_FOR_VAD: {
252
- size_t bytes_read = this->read_microphone_();
253
- if (bytes_read > 0) {
254
- vad_state_t vad_state =
255
- vad_process(this->vad_instance_, this->input_buffer_, SAMPLE_RATE_HZ, VAD_FRAME_LENGTH_MS);
256
- if (vad_state == VAD_SPEECH) {
257
- if (this->vad_counter_ < this->vad_threshold_) {
258
- this->vad_counter_++;
259
- } else {
260
- ESP_LOGD(TAG, "VAD detected speech");
261
- this->set_state_(State::START_PIPELINE, State::STREAMING_MICROPHONE);
262
-
263
- // Reset for next time
264
- this->vad_counter_ = 0;
265
- }
266
- } else {
267
- if (this->vad_counter_ > 0) {
268
- this->vad_counter_--;
269
- }
270
- }
271
- }
272
- break;
273
- }
274
- #endif
275
206
  case State::START_PIPELINE: {
276
- this->read_microphone_();
277
207
  ESP_LOGD(TAG, "Requesting start...");
278
208
  uint32_t flags = 0;
279
- if (this->use_wake_word_)
209
+ if (!this->continue_conversation_ && this->use_wake_word_)
280
210
  flags |= api::enums::VOICE_ASSISTANT_REQUEST_USE_WAKE_WORD;
281
211
  if (this->silence_detection_)
282
212
  flags |= api::enums::VOICE_ASSISTANT_REQUEST_USE_VAD;
@@ -306,11 +236,9 @@ void VoiceAssistant::loop() {
306
236
  break;
307
237
  }
308
238
  case State::STARTING_PIPELINE: {
309
- this->read_microphone_();
310
239
  break; // State changed when udp server port received
311
240
  }
312
241
  case State::STREAMING_MICROPHONE: {
313
- this->read_microphone_();
314
242
  size_t available = this->ring_buffer_->available();
315
243
  while (available >= SEND_BUFFER_SIZE) {
316
244
  size_t read_bytes = this->ring_buffer_->read((void *) this->send_buffer_, SEND_BUFFER_SIZE, 0);
@@ -334,8 +262,8 @@ void VoiceAssistant::loop() {
334
262
  break;
335
263
  }
336
264
  case State::STOP_MICROPHONE: {
337
- if (this->mic_->is_running()) {
338
- this->mic_->stop();
265
+ if (this->mic_source_->is_running()) {
266
+ this->mic_source_->stop();
339
267
  this->set_state_(State::STOPPING_MICROPHONE);
340
268
  } else {
341
269
  this->set_state_(this->desired_state_);
@@ -343,7 +271,7 @@ void VoiceAssistant::loop() {
343
271
  break;
344
272
  }
345
273
  case State::STOPPING_MICROPHONE: {
346
- if (this->mic_->is_stopped()) {
274
+ if (this->mic_source_->is_stopped()) {
347
275
  this->set_state_(this->desired_state_);
348
276
  }
349
277
  break;
@@ -387,6 +315,25 @@ void VoiceAssistant::loop() {
387
315
  #ifdef USE_MEDIA_PLAYER
388
316
  if (this->media_player_ != nullptr) {
389
317
  playing = (this->media_player_->state == media_player::MediaPlayerState::MEDIA_PLAYER_STATE_ANNOUNCING);
318
+
319
+ if (playing && this->media_player_wait_for_announcement_start_) {
320
+ // Announcement has started playing, wait for it to finish
321
+ this->media_player_wait_for_announcement_start_ = false;
322
+ this->media_player_wait_for_announcement_end_ = true;
323
+ }
324
+
325
+ if (!playing && this->media_player_wait_for_announcement_end_) {
326
+ // Announcement has finished playing
327
+ this->media_player_wait_for_announcement_end_ = false;
328
+ this->cancel_timeout("playing");
329
+ ESP_LOGD(TAG, "Announcement finished playing");
330
+ this->set_state_(State::RESPONSE_FINISHED, State::RESPONSE_FINISHED);
331
+
332
+ api::VoiceAssistantAnnounceFinished msg;
333
+ msg.success = true;
334
+ this->api_client_->send_voice_assistant_announce_finished(msg);
335
+ break;
336
+ }
390
337
  }
391
338
  #endif
392
339
  if (playing) {
@@ -417,7 +364,11 @@ void VoiceAssistant::loop() {
417
364
  this->tts_stream_end_trigger_->trigger();
418
365
  }
419
366
  #endif
420
- this->set_state_(State::IDLE, State::IDLE);
367
+ if (this->continue_conversation_) {
368
+ this->set_state_(State::START_MICROPHONE, State::START_PIPELINE);
369
+ } else {
370
+ this->set_state_(State::IDLE, State::IDLE);
371
+ }
421
372
  break;
422
373
  }
423
374
  default:
@@ -527,7 +478,7 @@ void VoiceAssistant::start_streaming() {
527
478
  ESP_LOGD(TAG, "Client started, streaming microphone");
528
479
  this->audio_mode_ = AUDIO_MODE_API;
529
480
 
530
- if (this->mic_->is_running()) {
481
+ if (this->mic_source_->is_running()) {
531
482
  this->set_state_(State::STREAMING_MICROPHONE, State::STREAMING_MICROPHONE);
532
483
  } else {
533
484
  this->set_state_(State::START_MICROPHONE, State::STREAMING_MICROPHONE);
@@ -557,7 +508,7 @@ void VoiceAssistant::start_streaming(struct sockaddr_storage *addr, uint16_t por
557
508
  return;
558
509
  }
559
510
 
560
- if (this->mic_->is_running()) {
511
+ if (this->mic_source_->is_running()) {
561
512
  this->set_state_(State::STREAMING_MICROPHONE, State::STREAMING_MICROPHONE);
562
513
  } else {
563
514
  this->set_state_(State::START_MICROPHONE, State::STREAMING_MICROPHONE);
@@ -574,19 +525,14 @@ void VoiceAssistant::request_start(bool continuous, bool silence_detection) {
574
525
  if (this->state_ == State::IDLE) {
575
526
  this->continuous_ = continuous;
576
527
  this->silence_detection_ = silence_detection;
577
- #ifdef USE_ESP_ADF
578
- if (this->use_wake_word_) {
579
- this->set_state_(State::START_MICROPHONE, State::WAIT_FOR_VAD);
580
- } else
581
- #endif
582
- {
583
- this->set_state_(State::START_MICROPHONE, State::START_PIPELINE);
584
- }
528
+
529
+ this->set_state_(State::START_MICROPHONE, State::START_PIPELINE);
585
530
  }
586
531
  }
587
532
 
588
533
  void VoiceAssistant::request_stop() {
589
534
  this->continuous_ = false;
535
+ this->continue_conversation_ = false;
590
536
 
591
537
  switch (this->state_) {
592
538
  case State::IDLE:
@@ -611,6 +557,16 @@ void VoiceAssistant::request_stop() {
611
557
  this->signal_stop_();
612
558
  break;
613
559
  case State::STREAMING_RESPONSE:
560
+ #ifdef USE_MEDIA_PLAYER
561
+ // Stop any ongoing media player announcement
562
+ if (this->media_player_ != nullptr) {
563
+ this->media_player_->make_call()
564
+ .set_command(media_player::MEDIA_PLAYER_COMMAND_STOP)
565
+ .set_announcement(true)
566
+ .perform();
567
+ }
568
+ #endif
569
+ break;
614
570
  case State::RESPONSE_FINISHED:
615
571
  break; // Let the incoming audio stream finish then it will go to idle.
616
572
  }
@@ -628,9 +584,9 @@ void VoiceAssistant::signal_stop_() {
628
584
  }
629
585
 
630
586
  void VoiceAssistant::start_playback_timeout_() {
631
- this->set_timeout("playing", 100, [this]() {
587
+ this->set_timeout("playing", 2000, [this]() {
632
588
  this->cancel_timeout("speaker-timeout");
633
- this->set_state_(State::IDLE, State::IDLE);
589
+ this->set_state_(State::RESPONSE_FINISHED, State::RESPONSE_FINISHED);
634
590
 
635
591
  api::VoiceAssistantAnnounceFinished msg;
636
592
  msg.success = true;
@@ -679,6 +635,8 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
679
635
  for (auto arg : msg.data) {
680
636
  if (arg.name == "conversation_id") {
681
637
  this->conversation_id_ = std::move(arg.value);
638
+ } else if (arg.name == "continue_conversation") {
639
+ this->continue_conversation_ = (arg.value == "1");
682
640
  }
683
641
  }
684
642
  this->defer([this]() { this->intent_end_trigger_->trigger(); });
@@ -722,6 +680,9 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
722
680
  #ifdef USE_MEDIA_PLAYER
723
681
  if (this->media_player_ != nullptr) {
724
682
  this->media_player_->make_call().set_media_url(url).set_announcement(true).perform();
683
+
684
+ this->media_player_wait_for_announcement_start_ = true;
685
+ this->media_player_wait_for_announcement_end_ = false;
725
686
  // Start the playback timeout, as the media player state isn't immediately updated
726
687
  this->start_playback_timeout_();
727
688
  }
@@ -734,21 +695,13 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
734
695
  }
735
696
  case api::enums::VOICE_ASSISTANT_RUN_END: {
736
697
  ESP_LOGD(TAG, "Assist Pipeline ended");
737
- if ((this->state_ == State::STARTING_PIPELINE) || (this->state_ == State::AWAITING_RESPONSE)) {
738
- // Pipeline ended before starting microphone
739
- // Or there wasn't a TTS start event ("nevermind")
698
+ if ((this->state_ == State::START_PIPELINE) || (this->state_ == State::STARTING_PIPELINE) ||
699
+ (this->state_ == State::STREAMING_MICROPHONE)) {
700
+ // Microphone is running, stop it
701
+ this->set_state_(State::STOP_MICROPHONE, State::IDLE);
702
+ } else if (this->state_ == State::AWAITING_RESPONSE) {
703
+ // No TTS start event ("nevermind")
740
704
  this->set_state_(State::IDLE, State::IDLE);
741
- } else if (this->state_ == State::STREAMING_MICROPHONE) {
742
- this->ring_buffer_->reset();
743
- #ifdef USE_ESP_ADF
744
- if (this->use_wake_word_) {
745
- // No need to stop the microphone since we didn't use the speaker
746
- this->set_state_(State::WAIT_FOR_VAD, State::WAITING_FOR_VAD);
747
- } else
748
- #endif
749
- {
750
- this->set_state_(State::IDLE, State::IDLE);
751
- }
752
705
  }
753
706
  this->defer([this]() { this->end_trigger_->trigger(); });
754
707
  break;
@@ -888,14 +841,87 @@ void VoiceAssistant::on_announce(const api::VoiceAssistantAnnounceRequest &msg)
888
841
  #ifdef USE_MEDIA_PLAYER
889
842
  if (this->media_player_ != nullptr) {
890
843
  this->tts_start_trigger_->trigger(msg.text);
891
- this->media_player_->make_call().set_media_url(msg.media_id).set_announcement(true).perform();
892
- this->set_state_(State::STREAMING_RESPONSE, State::STREAMING_RESPONSE);
844
+ if (!msg.preannounce_media_id.empty()) {
845
+ this->media_player_->make_call().set_media_url(msg.preannounce_media_id).set_announcement(true).perform();
846
+ }
847
+ // Enqueueing a URL with an empty playlist will still play the file immediately
848
+ this->media_player_->make_call()
849
+ .set_command(media_player::MEDIA_PLAYER_COMMAND_ENQUEUE)
850
+ .set_media_url(msg.media_id)
851
+ .set_announcement(true)
852
+ .perform();
853
+ this->continue_conversation_ = msg.start_conversation;
854
+
855
+ this->media_player_wait_for_announcement_start_ = true;
856
+ this->media_player_wait_for_announcement_end_ = false;
857
+ // Start the playback timeout, as the media player state isn't immediately updated
858
+ this->start_playback_timeout_();
859
+
860
+ if (this->continuous_) {
861
+ this->set_state_(State::STOP_MICROPHONE, State::STREAMING_RESPONSE);
862
+ } else {
863
+ this->set_state_(State::STREAMING_RESPONSE, State::STREAMING_RESPONSE);
864
+ }
865
+
893
866
  this->tts_end_trigger_->trigger(msg.media_id);
894
867
  this->end_trigger_->trigger();
895
868
  }
896
869
  #endif
897
870
  }
898
871
 
872
+ void VoiceAssistant::on_set_configuration(const std::vector<std::string> &active_wake_words) {
873
+ #ifdef USE_MICRO_WAKE_WORD
874
+ if (this->micro_wake_word_) {
875
+ // Disable all wake words first
876
+ for (auto &model : this->micro_wake_word_->get_wake_words()) {
877
+ model->disable();
878
+ }
879
+
880
+ // Enable only active wake words
881
+ for (auto ww_id : active_wake_words) {
882
+ for (auto &model : this->micro_wake_word_->get_wake_words()) {
883
+ if (model->get_id() == ww_id) {
884
+ model->enable();
885
+ ESP_LOGD(TAG, "Enabled wake word: %s (id=%s)", model->get_wake_word().c_str(), model->get_id().c_str());
886
+ }
887
+ }
888
+ }
889
+ }
890
+ #endif
891
+ };
892
+
893
+ const Configuration &VoiceAssistant::get_configuration() {
894
+ this->config_.available_wake_words.clear();
895
+ this->config_.active_wake_words.clear();
896
+
897
+ #ifdef USE_MICRO_WAKE_WORD
898
+ if (this->micro_wake_word_) {
899
+ this->config_.max_active_wake_words = 1;
900
+
901
+ for (auto &model : this->micro_wake_word_->get_wake_words()) {
902
+ if (model->is_enabled()) {
903
+ this->config_.active_wake_words.push_back(model->get_id());
904
+ }
905
+
906
+ WakeWord wake_word;
907
+ wake_word.id = model->get_id();
908
+ wake_word.wake_word = model->get_wake_word();
909
+ for (const auto &lang : model->get_trained_languages()) {
910
+ wake_word.trained_languages.push_back(lang);
911
+ }
912
+ this->config_.available_wake_words.push_back(std::move(wake_word));
913
+ }
914
+ } else {
915
+ #endif
916
+ // No microWakeWord
917
+ this->config_.max_active_wake_words = 0;
918
+ #ifdef USE_MICRO_WAKE_WORD
919
+ }
920
+ #endif
921
+
922
+ return this->config_;
923
+ };
924
+
899
925
  VoiceAssistant *global_voice_assistant = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
900
926
 
901
927
  } // namespace voice_assistant