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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (457) hide show
  1. esphome/__main__.py +16 -14
  2. esphome/components/ac_dimmer/ac_dimmer.cpp +3 -2
  3. esphome/components/adc/__init__.py +51 -34
  4. esphome/components/airthings_wave_base/__init__.py +1 -1
  5. esphome/components/alarm_control_panel/__init__.py +37 -2
  6. esphome/components/am43/cover/__init__.py +4 -5
  7. esphome/components/analog_threshold/analog_threshold_binary_sensor.cpp +6 -4
  8. esphome/components/analog_threshold/analog_threshold_binary_sensor.h +4 -5
  9. esphome/components/analog_threshold/binary_sensor.py +10 -8
  10. esphome/components/anova/climate.py +4 -5
  11. esphome/components/api/__init__.py +25 -8
  12. esphome/components/api/api_connection.cpp +416 -662
  13. esphome/components/api/api_connection.h +256 -57
  14. esphome/components/api/api_frame_helper.cpp +232 -177
  15. esphome/components/api/api_frame_helper.h +61 -8
  16. esphome/components/api/api_noise_context.h +13 -4
  17. esphome/components/api/api_pb2.cpp +1422 -1
  18. esphome/components/api/api_pb2.h +255 -1
  19. esphome/components/api/api_pb2_service.cpp +162 -49
  20. esphome/components/api/api_pb2_service.h +90 -51
  21. esphome/components/api/api_pb2_size.h +361 -0
  22. esphome/components/api/api_server.cpp +110 -34
  23. esphome/components/api/api_server.h +8 -0
  24. esphome/components/api/proto.h +86 -17
  25. esphome/components/as3935_i2c/as3935_i2c.h +0 -3
  26. esphome/components/as7341/as7341.h +1 -1
  27. esphome/components/at581x/at581x.h +4 -4
  28. esphome/components/atm90e32/__init__.py +1 -0
  29. esphome/components/atm90e32/atm90e32.cpp +576 -199
  30. esphome/components/atm90e32/atm90e32.h +128 -31
  31. esphome/components/atm90e32/atm90e32_reg.h +4 -2
  32. esphome/components/atm90e32/button/__init__.py +62 -10
  33. esphome/components/atm90e32/button/atm90e32_button.cpp +63 -4
  34. esphome/components/atm90e32/button/atm90e32_button.h +36 -4
  35. esphome/components/atm90e32/number/__init__.py +130 -0
  36. esphome/components/atm90e32/number/atm90e32_number.h +16 -0
  37. esphome/components/atm90e32/sensor.py +21 -4
  38. esphome/components/atm90e32/text_sensor/__init__.py +48 -0
  39. esphome/components/audio/__init__.py +96 -49
  40. esphome/components/audio/audio.h +48 -0
  41. esphome/components/audio/audio_decoder.cpp +1 -1
  42. esphome/components/audio/audio_resampler.cpp +2 -0
  43. esphome/components/audio/audio_resampler.h +1 -0
  44. esphome/components/ballu/climate.py +2 -9
  45. esphome/components/bang_bang/climate.py +5 -6
  46. esphome/components/bedjet/bedjet_hub.cpp +1 -0
  47. esphome/components/bedjet/climate/__init__.py +3 -8
  48. esphome/components/bedjet/fan/__init__.py +2 -11
  49. esphome/components/binary/fan/__init__.py +13 -16
  50. esphome/components/binary_sensor/__init__.py +13 -10
  51. esphome/components/bl0906/constants.h +16 -16
  52. esphome/components/ble_client/text_sensor/__init__.py +3 -5
  53. esphome/components/bluetooth_proxy/bluetooth_connection.cpp +4 -6
  54. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +136 -21
  55. esphome/components/bluetooth_proxy/bluetooth_proxy.h +7 -0
  56. esphome/components/button/__init__.py +11 -8
  57. esphome/components/canbus/canbus.cpp +3 -0
  58. esphome/components/canbus/canbus.h +16 -0
  59. esphome/components/ccs811/sensor.py +9 -6
  60. esphome/components/climate/__init__.py +35 -2
  61. esphome/components/climate/climate_mode.h +1 -1
  62. esphome/components/climate/climate_traits.h +63 -57
  63. esphome/components/climate_ir/__init__.py +57 -17
  64. esphome/components/climate_ir_lg/climate.py +2 -5
  65. esphome/components/climate_ir_lg/climate_ir_lg.cpp +7 -7
  66. esphome/components/climate_ir_lg/climate_ir_lg.h +1 -1
  67. esphome/components/color/__init__.py +2 -0
  68. esphome/components/const/__init__.py +5 -0
  69. esphome/components/coolix/climate.py +2 -9
  70. esphome/components/copy/cover/__init__.py +10 -9
  71. esphome/components/copy/fan/__init__.py +11 -9
  72. esphome/components/copy/lock/__init__.py +11 -9
  73. esphome/components/copy/text/__init__.py +9 -6
  74. esphome/components/cover/__init__.py +37 -2
  75. esphome/components/cse7766/cse7766.cpp +2 -1
  76. esphome/components/cst226/binary_sensor/__init__.py +28 -0
  77. esphome/components/cst226/binary_sensor/cs226_button.h +22 -0
  78. esphome/components/cst226/binary_sensor/cstt6_button.cpp +19 -0
  79. esphome/components/cst226/touchscreen/cst226_touchscreen.cpp +27 -5
  80. esphome/components/cst226/touchscreen/cst226_touchscreen.h +10 -10
  81. esphome/components/current_based/cover.py +37 -36
  82. esphome/components/current_based/current_based_cover.cpp +2 -1
  83. esphome/components/daikin/climate.py +2 -9
  84. esphome/components/daikin/daikin.cpp +15 -9
  85. esphome/components/daikin/daikin.h +5 -5
  86. esphome/components/daikin_arc/climate.py +2 -7
  87. esphome/components/daikin_brc/climate.py +3 -5
  88. esphome/components/dallas_temp/dallas_temp.cpp +17 -24
  89. esphome/components/dallas_temp/dallas_temp.h +0 -1
  90. esphome/components/daly_bms/daly_bms.cpp +2 -1
  91. esphome/components/debug/debug_component.cpp +6 -1
  92. esphome/components/debug/debug_component.h +8 -0
  93. esphome/components/debug/debug_esp32.cpp +109 -254
  94. esphome/components/debug/sensor.py +14 -0
  95. esphome/components/deep_sleep/deep_sleep_esp32.cpp +13 -1
  96. esphome/components/delonghi/climate.py +2 -9
  97. esphome/components/demo/__init__.py +18 -20
  98. esphome/components/dfrobot_sen0395/switch/__init__.py +21 -22
  99. esphome/components/display/rect.cpp +4 -9
  100. esphome/components/display/rect.h +1 -1
  101. esphome/components/dps310/sensor.py +6 -6
  102. esphome/components/ee895/sensor.py +9 -9
  103. esphome/components/emmeti/climate.py +2 -9
  104. esphome/components/endstop/cover.py +17 -16
  105. esphome/components/endstop/endstop_cover.cpp +2 -1
  106. esphome/components/ens160_base/__init__.py +12 -9
  107. esphome/components/esp32/__init__.py +60 -3
  108. esphome/components/esp32/core.cpp +11 -5
  109. esphome/components/esp32/gpio.cpp +86 -24
  110. esphome/components/esp32/gpio.py +15 -16
  111. esphome/components/esp32/gpio_esp32.py +1 -2
  112. esphome/components/esp32/gpio_esp32_c2.py +1 -1
  113. esphome/components/esp32/gpio_esp32_c3.py +1 -1
  114. esphome/components/esp32/gpio_esp32_c6.py +1 -1
  115. esphome/components/esp32/gpio_esp32_h2.py +1 -1
  116. esphome/components/esp32_ble/ble.cpp +1 -8
  117. esphome/components/esp32_ble/ble.h +5 -3
  118. esphome/components/esp32_ble/ble_advertising.cpp +2 -1
  119. esphome/components/esp32_ble/ble_advertising.h +1 -0
  120. esphome/components/esp32_ble_server/__init__.py +3 -0
  121. esphome/components/esp32_ble_tracker/__init__.py +7 -1
  122. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +192 -118
  123. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +29 -3
  124. esphome/components/esp32_camera/__init__.py +1 -1
  125. esphome/components/esp32_camera/esp32_camera.cpp +2 -10
  126. esphome/components/esp32_camera/esp32_camera.h +1 -1
  127. esphome/components/esp32_can/esp32_can.cpp +1 -1
  128. esphome/components/esp32_improv/esp32_improv_component.cpp +1 -1
  129. esphome/components/esp32_rmt_led_strip/led_strip.cpp +1 -1
  130. esphome/components/esp32_rmt_led_strip/led_strip.h +7 -5
  131. esphome/components/esp32_rmt_led_strip/light.py +9 -1
  132. esphome/components/esp32_touch/esp32_touch.cpp +1 -1
  133. esphome/components/esp8266/gpio.cpp +69 -8
  134. esphome/components/ethernet/ethernet_component.cpp +1 -1
  135. esphome/components/event/__init__.py +13 -10
  136. esphome/components/factory_reset/switch/__init__.py +7 -21
  137. esphome/components/fan/__init__.py +52 -5
  138. esphome/components/fastled_base/__init__.py +1 -4
  139. esphome/components/fastled_base/fastled_light.cpp +1 -1
  140. esphome/components/feedback/cover.py +38 -33
  141. esphome/components/feedback/feedback_cover.cpp +2 -1
  142. esphome/components/fujitsu_general/climate.py +2 -9
  143. esphome/components/gcja5/gcja5.cpp +2 -1
  144. esphome/components/gpio/one_wire/gpio_one_wire.cpp +45 -43
  145. esphome/components/gpio/one_wire/gpio_one_wire.h +2 -1
  146. esphome/components/gpio_expander/cached_gpio.h +22 -7
  147. esphome/components/gps/__init__.py +47 -17
  148. esphome/components/gps/gps.cpp +42 -23
  149. esphome/components/gps/gps.h +17 -13
  150. esphome/components/graph/__init__.py +1 -2
  151. esphome/components/gree/climate.py +4 -6
  152. esphome/components/gree/gree.cpp +16 -2
  153. esphome/components/gree/gree.h +2 -2
  154. esphome/components/growatt_solar/growatt_solar.cpp +2 -1
  155. esphome/components/haier/climate.py +37 -34
  156. esphome/components/hbridge/fan/__init__.py +19 -17
  157. esphome/components/he60r/cover.py +4 -5
  158. esphome/components/heatpumpir/climate.py +3 -6
  159. esphome/components/hitachi_ac344/climate.py +2 -9
  160. esphome/components/hitachi_ac424/climate.py +2 -9
  161. esphome/components/hlw8012/hlw8012.cpp +1 -1
  162. esphome/components/hm3301/hm3301.h +1 -1
  163. esphome/components/hte501/sensor.py +6 -6
  164. esphome/components/http_request/__init__.py +39 -6
  165. esphome/components/http_request/http_request.cpp +20 -0
  166. esphome/components/http_request/http_request.h +57 -15
  167. esphome/components/http_request/http_request_arduino.cpp +22 -6
  168. esphome/components/http_request/http_request_arduino.h +4 -3
  169. esphome/components/http_request/http_request_host.cpp +141 -0
  170. esphome/components/http_request/http_request_host.h +37 -0
  171. esphome/components/http_request/http_request_idf.cpp +35 -3
  172. esphome/components/http_request/http_request_idf.h +10 -3
  173. esphome/components/http_request/httplib.h +9691 -0
  174. esphome/components/http_request/update/__init__.py +11 -8
  175. esphome/components/hyt271/sensor.py +6 -6
  176. esphome/components/i2c/i2c.h +4 -0
  177. esphome/components/i2c/i2c_bus_esp_idf.cpp +1 -1
  178. esphome/components/i2s_audio/__init__.py +131 -22
  179. esphome/components/i2s_audio/i2s_audio.h +44 -4
  180. esphome/components/i2s_audio/media_player/__init__.py +19 -9
  181. esphome/components/i2s_audio/microphone/__init__.py +63 -5
  182. esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +351 -61
  183. esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +40 -6
  184. esphome/components/i2s_audio/speaker/__init__.py +31 -5
  185. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +155 -19
  186. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +17 -4
  187. esphome/components/ili9xxx/ili9xxx_init.h +1 -1
  188. esphome/components/image/__init__.py +37 -17
  189. esphome/components/image/image.cpp +25 -8
  190. esphome/components/internal_temperature/internal_temperature.cpp +6 -4
  191. esphome/components/key_collector/__init__.py +35 -0
  192. esphome/components/key_collector/key_collector.cpp +8 -0
  193. esphome/components/key_collector/key_collector.h +10 -0
  194. esphome/components/kuntze/kuntze.cpp +2 -1
  195. esphome/components/ld2410/ld2410.h +1 -1
  196. esphome/components/ld2450/ld2450.h +1 -1
  197. esphome/components/light/__init__.py +57 -0
  198. esphome/components/lock/__init__.py +51 -4
  199. esphome/components/lock/automation.h +2 -13
  200. esphome/components/logger/__init__.py +22 -0
  201. esphome/components/logger/logger.cpp +154 -103
  202. esphome/components/logger/logger.h +211 -36
  203. esphome/components/logger/task_log_buffer.cpp +138 -0
  204. esphome/components/logger/task_log_buffer.h +69 -0
  205. esphome/components/lvgl/__init__.py +13 -5
  206. esphome/components/lvgl/automation.py +50 -1
  207. esphome/components/lvgl/defines.py +0 -1
  208. esphome/components/lvgl/lv_validation.py +10 -1
  209. esphome/components/lvgl/lvgl_esphome.cpp +5 -1
  210. esphome/components/lvgl/schemas.py +14 -14
  211. esphome/components/lvgl/text/__init__.py +1 -2
  212. esphome/components/lvgl/widgets/arc.py +7 -6
  213. esphome/components/lvgl/widgets/buttonmatrix.py +3 -3
  214. esphome/components/lvgl/widgets/checkbox.py +2 -2
  215. esphome/components/lvgl/widgets/dropdown.py +2 -1
  216. esphome/components/lvgl/widgets/img.py +15 -12
  217. esphome/components/mapping/__init__.py +134 -0
  218. esphome/components/matrix_keypad/matrix_keypad.cpp +2 -1
  219. esphome/components/max7219digit/max7219digit.cpp +28 -27
  220. esphome/components/mdns/__init__.py +11 -5
  221. esphome/components/mdns/mdns_component.cpp +11 -5
  222. esphome/components/mdns/mdns_component.h +3 -2
  223. esphome/components/mdns/mdns_esp32.cpp +4 -3
  224. esphome/components/mdns/mdns_esp8266.cpp +4 -2
  225. esphome/components/mdns/mdns_libretiny.cpp +4 -2
  226. esphome/components/mdns/mdns_rp2040.cpp +4 -2
  227. esphome/components/media_player/__init__.py +40 -6
  228. esphome/components/mhz19/sensor.py +11 -7
  229. esphome/components/micro_wake_word/__init__.py +99 -31
  230. esphome/components/micro_wake_word/automation.h +54 -0
  231. esphome/components/micro_wake_word/micro_wake_word.cpp +331 -319
  232. esphome/components/micro_wake_word/micro_wake_word.h +58 -105
  233. esphome/components/micro_wake_word/preprocessor_settings.h +19 -2
  234. esphome/components/micro_wake_word/streaming_model.cpp +158 -41
  235. esphome/components/micro_wake_word/streaming_model.h +85 -13
  236. esphome/components/microphone/__init__.py +139 -9
  237. esphome/components/microphone/automation.h +14 -2
  238. esphome/components/microphone/microphone.cpp +21 -0
  239. esphome/components/microphone/microphone.h +14 -5
  240. esphome/components/microphone/microphone_source.cpp +95 -0
  241. esphome/components/microphone/microphone_source.h +80 -0
  242. esphome/components/mics_4514/sensor.py +25 -14
  243. esphome/components/midea/climate.py +3 -4
  244. esphome/components/midea_ir/climate.py +3 -5
  245. esphome/components/mipi_spi/__init__.py +15 -0
  246. esphome/components/mipi_spi/display.py +474 -0
  247. esphome/components/mipi_spi/mipi_spi.cpp +481 -0
  248. esphome/components/mipi_spi/mipi_spi.h +171 -0
  249. esphome/components/mipi_spi/models/__init__.py +65 -0
  250. esphome/components/mipi_spi/models/amoled.py +72 -0
  251. esphome/components/mipi_spi/models/commands.py +82 -0
  252. esphome/components/mipi_spi/models/cyd.py +10 -0
  253. esphome/components/mipi_spi/models/ili.py +749 -0
  254. esphome/components/mipi_spi/models/jc.py +260 -0
  255. esphome/components/mipi_spi/models/lanbon.py +15 -0
  256. esphome/components/mipi_spi/models/lilygo.py +60 -0
  257. esphome/components/mipi_spi/models/waveshare.py +139 -0
  258. esphome/components/mitsubishi/climate.py +2 -5
  259. esphome/components/mitsubishi/mitsubishi.cpp +9 -9
  260. esphome/components/mixer/speaker/mixer_speaker.cpp +12 -22
  261. esphome/components/mixer/speaker/mixer_speaker.h +1 -3
  262. esphome/components/mlx90393/sensor.py +5 -0
  263. esphome/components/mlx90393/sensor_mlx90393.cpp +195 -13
  264. esphome/components/mlx90393/sensor_mlx90393.h +21 -4
  265. esphome/components/modbus/modbus.cpp +2 -1
  266. esphome/components/mqtt/__init__.py +1 -1
  267. esphome/components/mqtt/mqtt_client.cpp +6 -2
  268. esphome/components/mqtt/mqtt_const.h +4 -0
  269. esphome/components/mqtt/mqtt_fan.cpp +39 -0
  270. esphome/components/mqtt/mqtt_fan.h +2 -0
  271. esphome/components/ms5611/sensor.py +6 -6
  272. esphome/components/ms8607/sensor.py +3 -3
  273. esphome/components/network/__init__.py +1 -1
  274. esphome/components/nextion/base_component.py +17 -16
  275. esphome/components/nextion/display.py +11 -2
  276. esphome/components/nextion/nextion.cpp +39 -1
  277. esphome/components/nextion/nextion.h +50 -0
  278. esphome/components/noblex/climate.py +2 -9
  279. esphome/components/number/__init__.py +12 -9
  280. esphome/components/one_wire/one_wire_bus.cpp +14 -10
  281. esphome/components/one_wire/one_wire_bus.h +14 -8
  282. esphome/components/online_image/bmp_image.cpp +48 -11
  283. esphome/components/online_image/bmp_image.h +2 -0
  284. esphome/components/opentherm/binary_sensor/__init__.py +2 -4
  285. esphome/components/opentherm/number/__init__.py +11 -20
  286. esphome/components/opentherm/sensor/__init__.py +3 -3
  287. esphome/components/opentherm/switch/__init__.py +3 -5
  288. esphome/components/output/lock/__init__.py +11 -9
  289. esphome/components/packages/__init__.py +33 -31
  290. esphome/components/packet_transport/__init__.py +201 -0
  291. esphome/components/packet_transport/binary_sensor.py +19 -0
  292. esphome/components/packet_transport/packet_transport.cpp +534 -0
  293. esphome/components/packet_transport/packet_transport.h +154 -0
  294. esphome/components/packet_transport/sensor.py +19 -0
  295. esphome/components/pca9685/pca9685_output.cpp +2 -1
  296. esphome/components/pid/climate.py +2 -4
  297. esphome/components/pm2005/__init__.py +1 -0
  298. esphome/components/pm2005/pm2005.cpp +123 -0
  299. esphome/components/pm2005/pm2005.h +46 -0
  300. esphome/components/pm2005/sensor.py +86 -0
  301. esphome/components/pmsa003i/pmsa003i.cpp +43 -16
  302. esphome/components/pmsa003i/pmsa003i.h +25 -25
  303. esphome/components/pmsx003/pmsx003.cpp +195 -230
  304. esphome/components/pmsx003/pmsx003.h +51 -33
  305. esphome/components/pmsx003/sensor.py +21 -11
  306. esphome/components/pn7150/pn7150.h +2 -2
  307. esphome/components/pn7160/pn7160.h +2 -2
  308. esphome/components/prometheus/prometheus_handler.cpp +174 -0
  309. esphome/components/prometheus/prometheus_handler.h +17 -0
  310. esphome/components/psram/__init__.py +7 -5
  311. esphome/components/pulse_meter/pulse_meter_sensor.cpp +32 -12
  312. esphome/components/pulse_meter/pulse_meter_sensor.h +5 -5
  313. esphome/components/pzem004t/pzem004t.cpp +2 -1
  314. esphome/components/qspi_dbi/__init__.py +0 -1
  315. esphome/components/qspi_dbi/display.py +2 -1
  316. esphome/components/qspi_dbi/models.py +1 -2
  317. esphome/components/remote_base/__init__.py +91 -0
  318. esphome/components/remote_base/beo4_protocol.cpp +153 -0
  319. esphome/components/remote_base/beo4_protocol.h +43 -0
  320. esphome/components/remote_base/gobox_protocol.cpp +131 -0
  321. esphome/components/remote_base/gobox_protocol.h +54 -0
  322. esphome/components/remote_receiver/remote_receiver_esp32.cpp +16 -9
  323. esphome/components/resampler/speaker/resampler_speaker.cpp +12 -10
  324. esphome/components/resampler/speaker/resampler_speaker.h +1 -1
  325. esphome/components/rf_bridge/rf_bridge.cpp +2 -1
  326. esphome/components/scd30/sensor.py +2 -3
  327. esphome/components/scd4x/sensor.py +4 -5
  328. esphome/components/sdp3x/sensor.py +2 -1
  329. esphome/components/sds011/sds011.cpp +2 -1
  330. esphome/components/select/__init__.py +19 -20
  331. esphome/components/sen5x/sen5x.cpp +55 -36
  332. esphome/components/sen5x/sensor.py +1 -1
  333. esphome/components/senseair/sensor.py +3 -3
  334. esphome/components/sensor/__init__.py +158 -14
  335. esphome/components/sensor/filter.cpp +23 -0
  336. esphome/components/sensor/filter.h +22 -0
  337. esphome/components/sgp30/sensor.py +14 -16
  338. esphome/components/sgp4x/sensor.py +1 -1
  339. esphome/components/sht4x/sht4x.cpp +43 -22
  340. esphome/components/sht4x/sht4x.h +1 -1
  341. esphome/components/shtcx/sensor.py +6 -6
  342. esphome/components/slow_pwm/slow_pwm_output.cpp +2 -1
  343. esphome/components/sml/text_sensor/__init__.py +4 -6
  344. esphome/components/sound_level/__init__.py +0 -0
  345. esphome/components/sound_level/sensor.py +97 -0
  346. esphome/components/sound_level/sound_level.cpp +194 -0
  347. esphome/components/sound_level/sound_level.h +73 -0
  348. esphome/components/speaker/media_player/__init__.py +4 -8
  349. esphome/components/speaker/media_player/speaker_media_player.cpp +0 -18
  350. esphome/components/speaker/media_player/speaker_media_player.h +0 -11
  351. esphome/components/speaker/speaker.h +4 -7
  352. esphome/components/speed/fan/__init__.py +17 -16
  353. esphome/components/spi/spi.h +11 -1
  354. esphome/components/sprinkler/__init__.py +18 -19
  355. esphome/components/sprinkler/sprinkler.cpp +6 -5
  356. esphome/components/switch/__init__.py +32 -42
  357. esphome/components/syslog/__init__.py +41 -0
  358. esphome/components/syslog/esphome_syslog.cpp +49 -0
  359. esphome/components/syslog/esphome_syslog.h +27 -0
  360. esphome/components/t6615/sensor.py +3 -3
  361. esphome/components/t6615/t6615.cpp +2 -1
  362. esphome/components/tca9555/tca9555.cpp +11 -6
  363. esphome/components/tcl112/climate.py +2 -9
  364. esphome/components/template/alarm_control_panel/__init__.py +7 -6
  365. esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +21 -17
  366. esphome/components/template/alarm_control_panel/template_alarm_control_panel.h +2 -1
  367. esphome/components/template/cover/__init__.py +27 -21
  368. esphome/components/template/fan/__init__.py +14 -12
  369. esphome/components/template/lock/__init__.py +20 -25
  370. esphome/components/template/lock/automation.h +18 -0
  371. esphome/components/template/text/__init__.py +4 -3
  372. esphome/components/template/valve/__init__.py +32 -21
  373. esphome/components/template/valve/automation.h +24 -0
  374. esphome/components/text/__init__.py +32 -1
  375. esphome/components/text_sensor/__init__.py +24 -29
  376. esphome/components/thermostat/climate.py +5 -5
  377. esphome/components/time_based/cover.py +17 -16
  378. esphome/components/time_based/time_based_cover.cpp +2 -1
  379. esphome/components/tm1638/switch/__init__.py +10 -7
  380. esphome/components/tormatic/cover.py +4 -5
  381. esphome/components/toshiba/climate.py +3 -5
  382. esphome/components/touchscreen/touchscreen.cpp +3 -1
  383. esphome/components/tt21100/touchscreen/tt21100.cpp +1 -1
  384. esphome/components/tuya/climate/__init__.py +5 -6
  385. esphome/components/tuya/cover/__init__.py +6 -11
  386. esphome/components/tuya/select/__init__.py +15 -5
  387. esphome/components/tuya/select/tuya_select.cpp +6 -1
  388. esphome/components/tuya/select/tuya_select.h +5 -1
  389. esphome/components/uart/packet_transport/__init__.py +20 -0
  390. esphome/components/uart/packet_transport/uart_transport.cpp +88 -0
  391. esphome/components/uart/packet_transport/uart_transport.h +41 -0
  392. esphome/components/uart/switch/uart_switch.cpp +2 -1
  393. esphome/components/udp/__init__.py +126 -128
  394. esphome/components/udp/automation.h +40 -0
  395. esphome/components/udp/binary_sensor.py +3 -25
  396. esphome/components/udp/packet_transport/__init__.py +29 -0
  397. esphome/components/udp/packet_transport/udp_transport.cpp +36 -0
  398. esphome/components/udp/packet_transport/udp_transport.h +28 -0
  399. esphome/components/udp/sensor.py +3 -25
  400. esphome/components/udp/udp_component.cpp +26 -470
  401. esphome/components/udp/udp_component.h +21 -128
  402. esphome/components/update/__init__.py +31 -1
  403. esphome/components/uponor_smatrix/climate/__init__.py +4 -9
  404. esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp +2 -1
  405. esphome/components/uponor_smatrix/uponor_smatrix.cpp +2 -1
  406. esphome/components/uptime/text_sensor/__init__.py +47 -7
  407. esphome/components/uptime/text_sensor/uptime_text_sensor.cpp +12 -7
  408. esphome/components/uptime/text_sensor/uptime_text_sensor.h +19 -0
  409. esphome/components/valve/__init__.py +34 -3
  410. esphome/components/valve/automation.h +1 -19
  411. esphome/components/vl53l0x/sensor.py +11 -0
  412. esphome/components/vl53l0x/vl53l0x_sensor.cpp +5 -1
  413. esphome/components/vl53l0x/vl53l0x_sensor.h +2 -1
  414. esphome/components/voice_assistant/__init__.py +36 -10
  415. esphome/components/voice_assistant/voice_assistant.cpp +170 -144
  416. esphome/components/voice_assistant/voice_assistant.h +26 -25
  417. esphome/components/waveshare_epaper/display.py +6 -0
  418. esphome/components/waveshare_epaper/waveshare_epaper.cpp +439 -37
  419. esphome/components/waveshare_epaper/waveshare_epaper.h +60 -11
  420. esphome/components/weikai/weikai.cpp +0 -52
  421. esphome/components/whirlpool/climate.py +3 -5
  422. esphome/components/whynter/climate.py +3 -5
  423. esphome/components/xpt2046/touchscreen/xpt2046.cpp +1 -1
  424. esphome/components/yashima/climate.py +6 -6
  425. esphome/components/zhlt01/climate.py +2 -7
  426. esphome/config.py +13 -13
  427. esphome/config_validation.py +38 -58
  428. esphome/const.py +15 -1
  429. esphome/core/__init__.py +2 -0
  430. esphome/core/application.cpp +27 -10
  431. esphome/core/application.h +9 -1
  432. esphome/core/automation.h +4 -3
  433. esphome/core/component.cpp +28 -7
  434. esphome/core/component.h +10 -1
  435. esphome/core/defines.h +23 -17
  436. esphome/core/doxygen.h +13 -0
  437. esphome/core/macros.h +4 -0
  438. esphome/core/scheduler.cpp +7 -1
  439. esphome/cpp_generator.py +6 -2
  440. esphome/dashboard/web_server.py +3 -3
  441. esphome/helpers.py +39 -0
  442. esphome/loader.py +4 -0
  443. esphome/log.py +15 -19
  444. esphome/mqtt.py +23 -10
  445. esphome/platformio_api.py +1 -1
  446. esphome/schema_extractors.py +0 -1
  447. esphome/voluptuous_schema.py +3 -1
  448. esphome/vscode.py +15 -0
  449. esphome/wizard.py +47 -37
  450. esphome/zeroconf.py +7 -3
  451. {esphome-2025.4.1.dist-info → esphome-2025.5.0.dist-info}/METADATA +10 -11
  452. {esphome-2025.4.1.dist-info → esphome-2025.5.0.dist-info}/RECORD +456 -396
  453. {esphome-2025.4.1.dist-info → esphome-2025.5.0.dist-info}/WHEEL +1 -1
  454. esphome/components/esp32_ble/const_esp32c6.h +0 -74
  455. {esphome-2025.4.1.dist-info → esphome-2025.5.0.dist-info}/entry_points.txt +0 -0
  456. {esphome-2025.4.1.dist-info → esphome-2025.5.0.dist-info}/licenses/LICENSE +0 -0
  457. {esphome-2025.4.1.dist-info → esphome-2025.5.0.dist-info}/top_level.txt +0 -0
@@ -2,20 +2,46 @@
2
2
 
3
3
  #ifdef USE_ESP32
4
4
 
5
+ #ifdef USE_I2S_LEGACY
5
6
  #include <driver/i2s.h>
7
+ #else
8
+ #include <driver/i2s_std.h>
9
+ #include <driver/i2s_pdm.h>
10
+ #endif
6
11
 
7
12
  #include "esphome/core/hal.h"
8
13
  #include "esphome/core/log.h"
9
14
 
15
+ #include "esphome/components/audio/audio.h"
16
+
10
17
  namespace esphome {
11
18
  namespace i2s_audio {
12
19
 
13
- static const size_t BUFFER_SIZE = 512;
20
+ static const UBaseType_t MAX_LISTENERS = 16;
21
+
22
+ static const uint32_t READ_DURATION_MS = 16;
23
+
24
+ static const size_t TASK_STACK_SIZE = 4096;
25
+ static const ssize_t TASK_PRIORITY = 23;
26
+
27
+ // Use an exponential moving average to correct a DC offset with weight factor 1/1000
28
+ static const int32_t DC_OFFSET_MOVING_AVERAGE_COEFFICIENT_DENOMINATOR = 1000;
14
29
 
15
30
  static const char *const TAG = "i2s_audio.microphone";
16
31
 
32
+ enum MicrophoneEventGroupBits : uint32_t {
33
+ COMMAND_STOP = (1 << 0), // stops the microphone task
34
+ TASK_STARTING = (1 << 10),
35
+ TASK_RUNNING = (1 << 11),
36
+ TASK_STOPPING = (1 << 12),
37
+ TASK_STOPPED = (1 << 13),
38
+
39
+ ALL_BITS = 0x00FFFFFF, // All valid FreeRTOS event group bits
40
+ };
41
+
17
42
  void I2SAudioMicrophone::setup() {
18
43
  ESP_LOGCONFIG(TAG, "Setting up I2S Audio Microphone...");
44
+ #ifdef USE_I2S_LEGACY
19
45
  #if SOC_I2S_SUPPORTS_ADC
20
46
  if (this->adc_) {
21
47
  if (this->parent_->get_port() != I2S_NUM_0) {
@@ -24,6 +50,7 @@ void I2SAudioMicrophone::setup() {
24
50
  return;
25
51
  }
26
52
  } else
53
+ #endif
27
54
  #endif
28
55
  {
29
56
  if (this->pdm_) {
@@ -34,19 +61,75 @@ void I2SAudioMicrophone::setup() {
34
61
  }
35
62
  }
36
63
  }
64
+
65
+ this->active_listeners_semaphore_ = xSemaphoreCreateCounting(MAX_LISTENERS, MAX_LISTENERS);
66
+ if (this->active_listeners_semaphore_ == nullptr) {
67
+ ESP_LOGE(TAG, "Failed to create semaphore");
68
+ this->mark_failed();
69
+ return;
70
+ }
71
+
72
+ this->event_group_ = xEventGroupCreate();
73
+ if (this->event_group_ == nullptr) {
74
+ ESP_LOGE(TAG, "Failed to create event group");
75
+ this->mark_failed();
76
+ return;
77
+ }
78
+
79
+ this->configure_stream_settings_();
80
+ }
81
+
82
+ void I2SAudioMicrophone::configure_stream_settings_() {
83
+ uint8_t channel_count = 1;
84
+ #ifdef USE_I2S_LEGACY
85
+ uint8_t bits_per_sample = this->bits_per_sample_;
86
+
87
+ if (this->channel_ == I2S_CHANNEL_FMT_RIGHT_LEFT) {
88
+ channel_count = 2;
89
+ }
90
+ #else
91
+ uint8_t bits_per_sample = 16;
92
+ if (this->slot_bit_width_ != I2S_SLOT_BIT_WIDTH_AUTO) {
93
+ bits_per_sample = this->slot_bit_width_;
94
+ }
95
+
96
+ if (this->slot_mode_ == I2S_SLOT_MODE_STEREO) {
97
+ channel_count = 2;
98
+ }
99
+ #endif
100
+
101
+ #ifdef USE_ESP32_VARIANT_ESP32
102
+ // ESP32 reads audio aligned to a multiple of 2 bytes. For example, if configured for 24 bits per sample, then it will
103
+ // produce 32 bits per sample, where the actual data is in the most significant bits. Other ESP32 variants produce 24
104
+ // bits per sample in this situation.
105
+ if (bits_per_sample < 16) {
106
+ bits_per_sample = 16;
107
+ } else if ((bits_per_sample > 16) && (bits_per_sample <= 32)) {
108
+ bits_per_sample = 32;
109
+ }
110
+ #endif
111
+
112
+ if (this->pdm_) {
113
+ bits_per_sample = 16; // PDM mics are always 16 bits per sample
114
+ }
115
+
116
+ this->audio_stream_info_ = audio::AudioStreamInfo(bits_per_sample, channel_count, this->sample_rate_);
37
117
  }
38
118
 
39
119
  void I2SAudioMicrophone::start() {
40
120
  if (this->is_failed())
41
121
  return;
42
- if (this->state_ == microphone::STATE_RUNNING)
43
- return; // Already running
44
- this->state_ = microphone::STATE_STARTING;
122
+
123
+ xSemaphoreTake(this->active_listeners_semaphore_, 0);
45
124
  }
46
- void I2SAudioMicrophone::start_() {
125
+
126
+ bool I2SAudioMicrophone::start_driver_() {
47
127
  if (!this->parent_->try_lock()) {
48
- return; // Waiting for another i2s to return lock
128
+ return false; // Waiting for another i2s to return lock
49
129
  }
130
+ esp_err_t err;
131
+
132
+ #ifdef USE_I2S_LEGACY
50
133
  i2s_driver_config_t config = {
51
134
  .mode = (i2s_mode_t) (this->i2s_mode_ | I2S_MODE_RX),
52
135
  .sample_rate = this->sample_rate_,
@@ -55,16 +138,14 @@ void I2SAudioMicrophone::start_() {
55
138
  .communication_format = I2S_COMM_FORMAT_STAND_I2S,
56
139
  .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
57
140
  .dma_buf_count = 4,
58
- .dma_buf_len = 256,
141
+ .dma_buf_len = 240, // Must be divisible by 3 to support 24 bits per sample on old driver and newer variants
59
142
  .use_apll = this->use_apll_,
60
143
  .tx_desc_auto_clear = false,
61
144
  .fixed_mclk = 0,
62
- .mclk_multiple = I2S_MCLK_MULTIPLE_256,
145
+ .mclk_multiple = this->mclk_multiple_,
63
146
  .bits_per_chan = this->bits_per_channel_,
64
147
  };
65
148
 
66
- esp_err_t err;
67
-
68
149
  #if SOC_I2S_SUPPORTS_ADC
69
150
  if (this->adc_) {
70
151
  config.mode = (i2s_mode_t) (config.mode | I2S_MODE_ADC_BUILT_IN);
@@ -72,20 +153,20 @@ void I2SAudioMicrophone::start_() {
72
153
  if (err != ESP_OK) {
73
154
  ESP_LOGW(TAG, "Error installing I2S driver: %s", esp_err_to_name(err));
74
155
  this->status_set_error();
75
- return;
156
+ return false;
76
157
  }
77
158
 
78
159
  err = i2s_set_adc_mode(ADC_UNIT_1, this->adc_channel_);
79
160
  if (err != ESP_OK) {
80
161
  ESP_LOGW(TAG, "Error setting ADC mode: %s", esp_err_to_name(err));
81
162
  this->status_set_error();
82
- return;
163
+ return false;
83
164
  }
84
165
  err = i2s_adc_enable(this->parent_->get_port());
85
166
  if (err != ESP_OK) {
86
167
  ESP_LOGW(TAG, "Error enabling ADC: %s", esp_err_to_name(err));
87
168
  this->status_set_error();
88
- return;
169
+ return false;
89
170
  }
90
171
 
91
172
  } else
@@ -98,7 +179,7 @@ void I2SAudioMicrophone::start_() {
98
179
  if (err != ESP_OK) {
99
180
  ESP_LOGW(TAG, "Error installing I2S driver: %s", esp_err_to_name(err));
100
181
  this->status_set_error();
101
- return;
182
+ return false;
102
183
  }
103
184
 
104
185
  i2s_pin_config_t pin_config = this->parent_->get_pin_config();
@@ -108,26 +189,122 @@ void I2SAudioMicrophone::start_() {
108
189
  if (err != ESP_OK) {
109
190
  ESP_LOGW(TAG, "Error setting I2S pin: %s", esp_err_to_name(err));
110
191
  this->status_set_error();
111
- return;
192
+ return false;
193
+ }
194
+ }
195
+ #else
196
+ i2s_chan_config_t chan_cfg = {
197
+ .id = this->parent_->get_port(),
198
+ .role = this->i2s_role_,
199
+ .dma_desc_num = 4,
200
+ .dma_frame_num = 256,
201
+ .auto_clear = false,
202
+ };
203
+ /* Allocate a new RX channel and get the handle of this channel */
204
+ err = i2s_new_channel(&chan_cfg, NULL, &this->rx_handle_);
205
+ if (err != ESP_OK) {
206
+ ESP_LOGW(TAG, "Error creating new I2S channel: %s", esp_err_to_name(err));
207
+ this->status_set_error();
208
+ return false;
209
+ }
210
+
211
+ i2s_clock_src_t clk_src = I2S_CLK_SRC_DEFAULT;
212
+ #ifdef I2S_CLK_SRC_APLL
213
+ if (this->use_apll_) {
214
+ clk_src = I2S_CLK_SRC_APLL;
215
+ }
216
+ #endif
217
+ i2s_std_gpio_config_t pin_config = this->parent_->get_pin_config();
218
+ #if SOC_I2S_SUPPORTS_PDM_RX
219
+ if (this->pdm_) {
220
+ i2s_pdm_rx_clk_config_t clk_cfg = {
221
+ .sample_rate_hz = this->sample_rate_,
222
+ .clk_src = clk_src,
223
+ .mclk_multiple = this->mclk_multiple_,
224
+ .dn_sample_mode = I2S_PDM_DSR_8S,
225
+ };
226
+
227
+ i2s_pdm_rx_slot_config_t slot_cfg = I2S_PDM_RX_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, this->slot_mode_);
228
+ switch (this->std_slot_mask_) {
229
+ case I2S_STD_SLOT_LEFT:
230
+ slot_cfg.slot_mask = I2S_PDM_SLOT_LEFT;
231
+ break;
232
+ case I2S_STD_SLOT_RIGHT:
233
+ slot_cfg.slot_mask = I2S_PDM_SLOT_RIGHT;
234
+ break;
235
+ case I2S_STD_SLOT_BOTH:
236
+ slot_cfg.slot_mask = I2S_PDM_SLOT_BOTH;
237
+ break;
112
238
  }
239
+
240
+ /* Init the channel into PDM RX mode */
241
+ i2s_pdm_rx_config_t pdm_rx_cfg = {
242
+ .clk_cfg = clk_cfg,
243
+ .slot_cfg = slot_cfg,
244
+ .gpio_cfg =
245
+ {
246
+ .clk = pin_config.ws,
247
+ .din = this->din_pin_,
248
+ .invert_flags =
249
+ {
250
+ .clk_inv = pin_config.invert_flags.ws_inv,
251
+ },
252
+ },
253
+ };
254
+ err = i2s_channel_init_pdm_rx_mode(this->rx_handle_, &pdm_rx_cfg);
255
+ } else
256
+ #endif
257
+ {
258
+ i2s_std_clk_config_t clk_cfg = {
259
+ .sample_rate_hz = this->sample_rate_,
260
+ .clk_src = clk_src,
261
+ .mclk_multiple = this->mclk_multiple_,
262
+ };
263
+ i2s_std_slot_config_t std_slot_cfg =
264
+ I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG((i2s_data_bit_width_t) this->slot_bit_width_, this->slot_mode_);
265
+ std_slot_cfg.slot_bit_width = this->slot_bit_width_;
266
+ std_slot_cfg.slot_mask = this->std_slot_mask_;
267
+
268
+ pin_config.din = this->din_pin_;
269
+
270
+ i2s_std_config_t std_cfg = {
271
+ .clk_cfg = clk_cfg,
272
+ .slot_cfg = std_slot_cfg,
273
+ .gpio_cfg = pin_config,
274
+ };
275
+ /* Initialize the channel */
276
+ err = i2s_channel_init_std_mode(this->rx_handle_, &std_cfg);
113
277
  }
114
- this->state_ = microphone::STATE_RUNNING;
115
- this->high_freq_.start();
278
+ if (err != ESP_OK) {
279
+ ESP_LOGW(TAG, "Error initializing I2S channel: %s", esp_err_to_name(err));
280
+ this->status_set_error();
281
+ return false;
282
+ }
283
+
284
+ /* Before reading data, start the RX channel first */
285
+ i2s_channel_enable(this->rx_handle_);
286
+ if (err != ESP_OK) {
287
+ ESP_LOGW(TAG, "Error enabling I2S Microphone: %s", esp_err_to_name(err));
288
+ this->status_set_error();
289
+ return false;
290
+ }
291
+ #endif
292
+
116
293
  this->status_clear_error();
294
+ this->configure_stream_settings_(); // redetermine the settings in case some settings were changed after compilation
295
+ return true;
117
296
  }
118
297
 
119
298
  void I2SAudioMicrophone::stop() {
120
299
  if (this->state_ == microphone::STATE_STOPPED || this->is_failed())
121
300
  return;
122
- if (this->state_ == microphone::STATE_STARTING) {
123
- this->state_ = microphone::STATE_STOPPED;
124
- return;
125
- }
126
- this->state_ = microphone::STATE_STOPPING;
301
+
302
+ xSemaphoreGive(this->active_listeners_semaphore_);
127
303
  }
128
304
 
129
- void I2SAudioMicrophone::stop_() {
305
+ void I2SAudioMicrophone::stop_driver_() {
130
306
  esp_err_t err;
307
+ #ifdef USE_I2S_LEGACY
131
308
  #if SOC_I2S_SUPPORTS_ADC
132
309
  if (this->adc_) {
133
310
  err = i2s_adc_disable(this->parent_->get_port());
@@ -150,68 +327,181 @@ void I2SAudioMicrophone::stop_() {
150
327
  this->status_set_error();
151
328
  return;
152
329
  }
330
+ #else
331
+ /* Have to stop the channel before deleting it */
332
+ err = i2s_channel_disable(this->rx_handle_);
333
+ if (err != ESP_OK) {
334
+ ESP_LOGW(TAG, "Error stopping I2S microphone: %s", esp_err_to_name(err));
335
+ this->status_set_error();
336
+ return;
337
+ }
338
+ /* If the handle is not needed any more, delete it to release the channel resources */
339
+ err = i2s_del_channel(this->rx_handle_);
340
+ if (err != ESP_OK) {
341
+ ESP_LOGW(TAG, "Error deleting I2S channel: %s", esp_err_to_name(err));
342
+ this->status_set_error();
343
+ return;
344
+ }
345
+ #endif
153
346
  this->parent_->unlock();
154
- this->state_ = microphone::STATE_STOPPED;
155
- this->high_freq_.stop();
156
347
  this->status_clear_error();
157
348
  }
158
349
 
159
- size_t I2SAudioMicrophone::read(int16_t *buf, size_t len) {
350
+ void I2SAudioMicrophone::mic_task(void *params) {
351
+ I2SAudioMicrophone *this_microphone = (I2SAudioMicrophone *) params;
352
+
353
+ xEventGroupSetBits(this_microphone->event_group_, MicrophoneEventGroupBits::TASK_STARTING);
354
+
355
+ uint8_t start_counter = 0;
356
+ bool started = this_microphone->start_driver_();
357
+ while (!started && start_counter < 10) {
358
+ // Attempt to load the driver again in 100 ms. Doesn't slow down main loop since its in a task.
359
+ vTaskDelay(pdMS_TO_TICKS(100));
360
+ ++start_counter;
361
+ started = this_microphone->start_driver_();
362
+ }
363
+
364
+ if (started) {
365
+ xEventGroupSetBits(this_microphone->event_group_, MicrophoneEventGroupBits::TASK_RUNNING);
366
+ const size_t bytes_to_read = this_microphone->audio_stream_info_.ms_to_bytes(READ_DURATION_MS);
367
+ std::vector<uint8_t> samples;
368
+ samples.reserve(bytes_to_read);
369
+
370
+ while (!(xEventGroupGetBits(this_microphone->event_group_) & COMMAND_STOP)) {
371
+ if (this_microphone->data_callbacks_.size() > 0) {
372
+ samples.resize(bytes_to_read);
373
+ size_t bytes_read = this_microphone->read_(samples.data(), bytes_to_read, 2 * pdMS_TO_TICKS(READ_DURATION_MS));
374
+ samples.resize(bytes_read);
375
+ if (this_microphone->correct_dc_offset_) {
376
+ this_microphone->fix_dc_offset_(samples);
377
+ }
378
+ this_microphone->data_callbacks_.call(samples);
379
+ } else {
380
+ vTaskDelay(pdMS_TO_TICKS(READ_DURATION_MS));
381
+ }
382
+ }
383
+ }
384
+
385
+ xEventGroupSetBits(this_microphone->event_group_, MicrophoneEventGroupBits::TASK_STOPPING);
386
+ this_microphone->stop_driver_();
387
+
388
+ xEventGroupSetBits(this_microphone->event_group_, MicrophoneEventGroupBits::TASK_STOPPED);
389
+ while (true) {
390
+ // Continuously delay until the loop method deletes the task
391
+ vTaskDelay(pdMS_TO_TICKS(10));
392
+ }
393
+ }
394
+
395
+ void I2SAudioMicrophone::fix_dc_offset_(std::vector<uint8_t> &data) {
396
+ const size_t bytes_per_sample = this->audio_stream_info_.samples_to_bytes(1);
397
+ const uint32_t total_samples = this->audio_stream_info_.bytes_to_samples(data.size());
398
+
399
+ if (total_samples == 0) {
400
+ return;
401
+ }
402
+
403
+ int64_t offset_accumulator = 0;
404
+ for (uint32_t sample_index = 0; sample_index < total_samples; ++sample_index) {
405
+ const uint32_t byte_index = sample_index * bytes_per_sample;
406
+ int32_t sample = audio::unpack_audio_sample_to_q31(&data[byte_index], bytes_per_sample);
407
+ offset_accumulator += sample;
408
+ sample -= this->dc_offset_;
409
+ audio::pack_q31_as_audio_sample(sample, &data[byte_index], bytes_per_sample);
410
+ }
411
+
412
+ const int32_t new_offset = offset_accumulator / total_samples;
413
+ this->dc_offset_ = new_offset / DC_OFFSET_MOVING_AVERAGE_COEFFICIENT_DENOMINATOR +
414
+ (DC_OFFSET_MOVING_AVERAGE_COEFFICIENT_DENOMINATOR - 1) * this->dc_offset_ /
415
+ DC_OFFSET_MOVING_AVERAGE_COEFFICIENT_DENOMINATOR;
416
+ }
417
+
418
+ size_t I2SAudioMicrophone::read_(uint8_t *buf, size_t len, TickType_t ticks_to_wait) {
160
419
  size_t bytes_read = 0;
161
- esp_err_t err = i2s_read(this->parent_->get_port(), buf, len, &bytes_read, (100 / portTICK_PERIOD_MS));
162
- if (err != ESP_OK) {
420
+ #ifdef USE_I2S_LEGACY
421
+ esp_err_t err = i2s_read(this->parent_->get_port(), buf, len, &bytes_read, ticks_to_wait);
422
+ #else
423
+ // i2s_channel_read expects the timeout value in ms, not ticks
424
+ esp_err_t err = i2s_channel_read(this->rx_handle_, buf, len, &bytes_read, pdTICKS_TO_MS(ticks_to_wait));
425
+ #endif
426
+ if ((err != ESP_OK) && ((err != ESP_ERR_TIMEOUT) || (ticks_to_wait != 0))) {
427
+ // Ignore ESP_ERR_TIMEOUT if ticks_to_wait = 0, as it will read the data on the next call
163
428
  ESP_LOGW(TAG, "Error reading from I2S microphone: %s", esp_err_to_name(err));
164
429
  this->status_set_warning();
165
430
  return 0;
166
431
  }
167
- if (bytes_read == 0) {
432
+ if ((bytes_read == 0) && (ticks_to_wait > 0)) {
168
433
  this->status_set_warning();
169
434
  return 0;
170
435
  }
171
436
  this->status_clear_warning();
172
- // ESP-IDF I2S implementation right-extends 8-bit data to 16 bits,
173
- // and 24-bit data to 32 bits.
174
- switch (this->bits_per_sample_) {
175
- case I2S_BITS_PER_SAMPLE_8BIT:
176
- case I2S_BITS_PER_SAMPLE_16BIT:
177
- return bytes_read;
178
- case I2S_BITS_PER_SAMPLE_24BIT:
179
- case I2S_BITS_PER_SAMPLE_32BIT: {
180
- size_t samples_read = bytes_read / sizeof(int32_t);
181
- for (size_t i = 0; i < samples_read; i++) {
182
- int32_t temp = reinterpret_cast<int32_t *>(buf)[i] >> 14;
183
- buf[i] = clamp<int16_t>(temp, INT16_MIN, INT16_MAX);
184
- }
185
- return samples_read * sizeof(int16_t);
437
+ #if defined(USE_ESP32_VARIANT_ESP32) and not defined(USE_I2S_LEGACY)
438
+ // For ESP32 8/16 bit standard mono mode samples need to be switched.
439
+ if (this->slot_mode_ == I2S_SLOT_MODE_MONO && this->slot_bit_width_ <= 16 && !this->pdm_) {
440
+ size_t samples_read = bytes_read / sizeof(int16_t);
441
+ for (int i = 0; i < samples_read; i += 2) {
442
+ int16_t tmp = buf[i];
443
+ buf[i] = buf[i + 1];
444
+ buf[i + 1] = tmp;
186
445
  }
187
- default:
188
- ESP_LOGE(TAG, "Unsupported bits per sample: %d", this->bits_per_sample_);
189
- return 0;
190
446
  }
191
- }
192
-
193
- void I2SAudioMicrophone::read_() {
194
- std::vector<int16_t> samples;
195
- samples.resize(BUFFER_SIZE);
196
- size_t bytes_read = this->read(samples.data(), BUFFER_SIZE / sizeof(int16_t));
197
- samples.resize(bytes_read / sizeof(int16_t));
198
- this->data_callbacks_.call(samples);
447
+ #endif
448
+ return bytes_read;
199
449
  }
200
450
 
201
451
  void I2SAudioMicrophone::loop() {
452
+ uint32_t event_group_bits = xEventGroupGetBits(this->event_group_);
453
+
454
+ if (event_group_bits & MicrophoneEventGroupBits::TASK_STARTING) {
455
+ ESP_LOGD(TAG, "Task has started, attempting to setup I2S audio driver");
456
+ xEventGroupClearBits(this->event_group_, MicrophoneEventGroupBits::TASK_STARTING);
457
+ }
458
+
459
+ if (event_group_bits & MicrophoneEventGroupBits::TASK_RUNNING) {
460
+ ESP_LOGD(TAG, "Task is running and reading data");
461
+
462
+ xEventGroupClearBits(this->event_group_, MicrophoneEventGroupBits::TASK_RUNNING);
463
+ this->state_ = microphone::STATE_RUNNING;
464
+ }
465
+
466
+ if (event_group_bits & MicrophoneEventGroupBits::TASK_STOPPING) {
467
+ ESP_LOGD(TAG, "Task is stopping, attempting to unload the I2S audio driver");
468
+ xEventGroupClearBits(this->event_group_, MicrophoneEventGroupBits::TASK_STOPPING);
469
+ }
470
+
471
+ if ((event_group_bits & MicrophoneEventGroupBits::TASK_STOPPED)) {
472
+ ESP_LOGD(TAG, "Task is finished, freeing resources");
473
+ vTaskDelete(this->task_handle_);
474
+ this->task_handle_ = nullptr;
475
+ xEventGroupClearBits(this->event_group_, ALL_BITS);
476
+ this->state_ = microphone::STATE_STOPPED;
477
+ }
478
+
479
+ if ((uxSemaphoreGetCount(this->active_listeners_semaphore_) < MAX_LISTENERS) &&
480
+ (this->state_ == microphone::STATE_STOPPED)) {
481
+ this->state_ = microphone::STATE_STARTING;
482
+ }
483
+ if ((uxSemaphoreGetCount(this->active_listeners_semaphore_) == MAX_LISTENERS) &&
484
+ (this->state_ == microphone::STATE_RUNNING)) {
485
+ this->state_ = microphone::STATE_STOPPING;
486
+ }
487
+
202
488
  switch (this->state_) {
203
- case microphone::STATE_STOPPED:
204
- break;
205
489
  case microphone::STATE_STARTING:
206
- this->start_();
490
+ if ((this->task_handle_ == nullptr) && !this->status_has_error()) {
491
+ xTaskCreate(I2SAudioMicrophone::mic_task, "mic_task", TASK_STACK_SIZE, (void *) this, TASK_PRIORITY,
492
+ &this->task_handle_);
493
+
494
+ if (this->task_handle_ == nullptr) {
495
+ this->status_momentary_error("Task failed to start, attempting again in 1 second", 1000);
496
+ }
497
+ }
207
498
  break;
208
499
  case microphone::STATE_RUNNING:
209
- if (this->data_callbacks_.size() > 0) {
210
- this->read_();
211
- }
212
500
  break;
213
501
  case microphone::STATE_STOPPING:
214
- this->stop_();
502
+ xEventGroupSetBits(this->event_group_, MicrophoneEventGroupBits::COMMAND_STOP);
503
+ break;
504
+ case microphone::STATE_STOPPED:
215
505
  break;
216
506
  }
217
507
  }
@@ -7,6 +7,11 @@
7
7
  #include "esphome/components/microphone/microphone.h"
8
8
  #include "esphome/core/component.h"
9
9
 
10
+ #include <freertos/FreeRTOS.h>
11
+ #include <freertos/event_groups.h>
12
+ #include <freertos/semphr.h>
13
+ #include <freertos/task.h>
14
+
10
15
  namespace esphome {
11
16
  namespace i2s_audio {
12
17
 
@@ -18,31 +23,60 @@ class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, pub
18
23
 
19
24
  void loop() override;
20
25
 
26
+ void set_correct_dc_offset(bool correct_dc_offset) { this->correct_dc_offset_ = correct_dc_offset; }
27
+
28
+ #ifdef USE_I2S_LEGACY
21
29
  void set_din_pin(int8_t pin) { this->din_pin_ = pin; }
22
- void set_pdm(bool pdm) { this->pdm_ = pdm; }
30
+ #else
31
+ void set_din_pin(int8_t pin) { this->din_pin_ = (gpio_num_t) pin; }
32
+ #endif
23
33
 
24
- size_t read(int16_t *buf, size_t len) override;
34
+ void set_pdm(bool pdm) { this->pdm_ = pdm; }
25
35
 
36
+ #ifdef USE_I2S_LEGACY
26
37
  #if SOC_I2S_SUPPORTS_ADC
27
38
  void set_adc_channel(adc1_channel_t channel) {
28
39
  this->adc_channel_ = channel;
29
40
  this->adc_ = true;
30
41
  }
42
+ #endif
31
43
  #endif
32
44
 
33
45
  protected:
34
- void start_();
35
- void stop_();
36
- void read_();
46
+ bool start_driver_();
47
+ void stop_driver_();
48
+
49
+ /// @brief Attempts to correct a microphone DC offset; e.g., a microphones silent level is offset from 0. Applies a
50
+ /// correction offset that is updated using an exponential moving average for all samples away from 0.
51
+ /// @param data
52
+ void fix_dc_offset_(std::vector<uint8_t> &data);
37
53
 
54
+ size_t read_(uint8_t *buf, size_t len, TickType_t ticks_to_wait);
55
+
56
+ /// @brief Sets the Microphone ``audio_stream_info_`` member variable to the configured I2S settings.
57
+ void configure_stream_settings_();
58
+
59
+ static void mic_task(void *params);
60
+
61
+ SemaphoreHandle_t active_listeners_semaphore_{nullptr};
62
+ EventGroupHandle_t event_group_{nullptr};
63
+
64
+ TaskHandle_t task_handle_{nullptr};
65
+
66
+ #ifdef USE_I2S_LEGACY
38
67
  int8_t din_pin_{I2S_PIN_NO_CHANGE};
39
68
  #if SOC_I2S_SUPPORTS_ADC
40
69
  adc1_channel_t adc_channel_{ADC1_CHANNEL_MAX};
41
70
  bool adc_{false};
71
+ #endif
72
+ #else
73
+ gpio_num_t din_pin_{I2S_GPIO_UNUSED};
74
+ i2s_chan_handle_t rx_handle_;
42
75
  #endif
43
76
  bool pdm_{false};
44
77
 
45
- HighFrequencyLoopRequester high_freq_;
78
+ bool correct_dc_offset_;
79
+ int32_t dc_offset_{0};
46
80
  };
47
81
 
48
82
  } // namespace i2s_audio
@@ -26,6 +26,8 @@ from .. import (
26
26
  i2s_audio_component_schema,
27
27
  i2s_audio_ns,
28
28
  register_i2s_audio_component,
29
+ use_legacy,
30
+ validate_mclk_divisible_by_3,
29
31
  )
30
32
 
31
33
  AUTO_LOAD = ["audio"]
@@ -60,7 +62,7 @@ I2C_COMM_FMT_OPTIONS = {
60
62
  "pcm_long": i2s_comm_format_t.I2S_COMM_FORMAT_PCM_LONG,
61
63
  }
62
64
 
63
- NO_INTERNAL_DAC_VARIANTS = [esp32.const.VARIANT_ESP32S2]
65
+ INTERNAL_DAC_VARIANTS = [esp32.const.VARIANT_ESP32]
64
66
 
65
67
 
66
68
  def _set_num_channels_from_config(config):
@@ -101,7 +103,7 @@ def _validate_esp32_variant(config):
101
103
  if config[CONF_DAC_TYPE] != "internal":
102
104
  return config
103
105
  variant = esp32.get_esp32_variant()
104
- if variant in NO_INTERNAL_DAC_VARIANTS:
106
+ if variant not in INTERNAL_DAC_VARIANTS:
105
107
  raise cv.Invalid(f"{variant} does not have an internal DAC")
106
108
  return config
107
109
 
@@ -143,8 +145,8 @@ CONFIG_SCHEMA = cv.All(
143
145
  cv.Required(
144
146
  CONF_I2S_DOUT_PIN
145
147
  ): pins.internal_gpio_output_pin_number,
146
- cv.Optional(CONF_I2S_COMM_FMT, default="stand_i2s"): cv.enum(
147
- I2C_COMM_FMT_OPTIONS, lower=True
148
+ cv.Optional(CONF_I2S_COMM_FMT, default="stand_i2s"): cv.one_of(
149
+ *I2C_COMM_FMT_OPTIONS, lower=True
148
150
  ),
149
151
  }
150
152
  ),
@@ -154,9 +156,23 @@ CONFIG_SCHEMA = cv.All(
154
156
  _validate_esp32_variant,
155
157
  _set_num_channels_from_config,
156
158
  _set_stream_limits,
159
+ validate_mclk_divisible_by_3,
157
160
  )
158
161
 
159
162
 
163
+ def _final_validate(config):
164
+ if not use_legacy():
165
+ if config[CONF_DAC_TYPE] == "internal":
166
+ raise cv.Invalid("Internal DAC is only compatible with legacy i2s driver.")
167
+ if config[CONF_I2S_COMM_FMT] == "stand_max":
168
+ raise cv.Invalid(
169
+ "I2S standard max format only implemented with legacy i2s driver."
170
+ )
171
+
172
+
173
+ FINAL_VALIDATE_SCHEMA = _final_validate
174
+
175
+
160
176
  async def to_code(config):
161
177
  var = cg.new_Pvariable(config[CONF_ID])
162
178
  await cg.register_component(var, config)
@@ -167,7 +183,17 @@ async def to_code(config):
167
183
  cg.add(var.set_internal_dac_mode(config[CONF_CHANNEL]))
168
184
  else:
169
185
  cg.add(var.set_dout_pin(config[CONF_I2S_DOUT_PIN]))
170
- cg.add(var.set_i2s_comm_fmt(config[CONF_I2S_COMM_FMT]))
186
+ if use_legacy():
187
+ cg.add(
188
+ var.set_i2s_comm_fmt(I2C_COMM_FMT_OPTIONS[config[CONF_I2S_COMM_FMT]])
189
+ )
190
+ else:
191
+ fmt = "std" # equals stand_i2s, stand_pcm_long, i2s_msb, pcm_long
192
+ if config[CONF_I2S_COMM_FMT] in ["stand_msb", "i2s_lsb"]:
193
+ fmt = "msb"
194
+ elif config[CONF_I2S_COMM_FMT] in ["stand_pcm_short", "pcm_short", "pcm"]:
195
+ fmt = "pcm"
196
+ cg.add(var.set_i2s_comm_fmt(fmt))
171
197
  if config[CONF_TIMEOUT] != CONF_NEVER:
172
198
  cg.add(var.set_timeout(config[CONF_TIMEOUT]))
173
199
  cg.add(var.set_buffer_duration(config[CONF_BUFFER_DURATION]))