esphome 2025.7.4__py3-none-any.whl → 2025.8.0b1__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.

Potentially problematic release.


This version of esphome might be problematic. Click here for more details.

Files changed (754) hide show
  1. esphome/__main__.py +189 -82
  2. esphome/automation.py +2 -4
  3. esphome/build_gen/__init__.py +0 -0
  4. esphome/build_gen/platformio.py +102 -0
  5. esphome/components/a4988/a4988.cpp +0 -1
  6. esphome/components/absolute_humidity/absolute_humidity.cpp +0 -2
  7. esphome/components/absolute_humidity/sensor.py +2 -2
  8. esphome/components/adc/__init__.py +123 -85
  9. esphome/components/adc/adc_sensor.h +98 -35
  10. esphome/components/adc/adc_sensor_common.cpp +10 -4
  11. esphome/components/adc/adc_sensor_esp32.cpp +291 -123
  12. esphome/components/adc/adc_sensor_esp8266.cpp +1 -4
  13. esphome/components/adc/adc_sensor_libretiny.cpp +1 -2
  14. esphome/components/adc/adc_sensor_rp2040.cpp +1 -2
  15. esphome/components/adc/adc_sensor_zephyr.cpp +207 -0
  16. esphome/components/adc/sensor.py +61 -27
  17. esphome/components/adc128s102/adc128s102.cpp +1 -4
  18. esphome/components/ade7880/sensor.py +75 -49
  19. esphome/components/ads1115/ads1115.cpp +0 -1
  20. esphome/components/ads1118/ads1118.cpp +0 -1
  21. esphome/components/ags10/ags10.cpp +0 -4
  22. esphome/components/aht10/aht10.cpp +0 -4
  23. esphome/components/aic3204/aic3204.cpp +0 -2
  24. esphome/components/airthings_wave_plus/__init__.py +1 -1
  25. esphome/components/airthings_wave_plus/airthings_wave_plus.cpp +22 -4
  26. esphome/components/airthings_wave_plus/airthings_wave_plus.h +10 -1
  27. esphome/components/airthings_wave_plus/sensor.py +55 -28
  28. esphome/components/alarm_control_panel/__init__.py +4 -9
  29. esphome/components/am2315c/am2315c.cpp +0 -2
  30. esphome/components/am2320/am2320.cpp +0 -1
  31. esphome/components/animation/__init__.py +14 -11
  32. esphome/components/apds9306/apds9306.cpp +0 -4
  33. esphome/components/apds9960/apds9960.cpp +0 -1
  34. esphome/components/api/__init__.py +29 -4
  35. esphome/components/api/api_connection.cpp +378 -401
  36. esphome/components/api/api_connection.h +112 -56
  37. esphome/components/api/api_frame_helper.cpp +69 -896
  38. esphome/components/api/api_frame_helper.h +31 -126
  39. esphome/components/api/api_frame_helper_noise.cpp +583 -0
  40. esphome/components/api/api_frame_helper_noise.h +68 -0
  41. esphome/components/api/api_frame_helper_plaintext.cpp +290 -0
  42. esphome/components/api/api_frame_helper_plaintext.h +53 -0
  43. esphome/components/api/api_noise_context.h +2 -4
  44. esphome/components/api/api_pb2.cpp +1601 -1808
  45. esphome/components/api/api_pb2.h +367 -323
  46. esphome/components/api/api_pb2_dump.cpp +1137 -3466
  47. esphome/components/api/api_pb2_includes.h +34 -0
  48. esphome/components/api/api_pb2_service.cpp +94 -105
  49. esphome/components/api/api_pb2_service.h +27 -16
  50. esphome/components/api/api_server.cpp +18 -17
  51. esphome/components/api/api_server.h +8 -5
  52. esphome/components/api/client.py +16 -8
  53. esphome/components/api/custom_api_device.h +68 -14
  54. esphome/components/api/homeassistant_service.h +24 -19
  55. esphome/components/api/list_entities.cpp +3 -5
  56. esphome/components/api/list_entities.h +2 -4
  57. esphome/components/api/proto.cpp +3 -5
  58. esphome/components/api/proto.h +239 -274
  59. esphome/components/api/subscribe_state.cpp +2 -4
  60. esphome/components/api/subscribe_state.h +2 -4
  61. esphome/components/api/user_services.cpp +2 -4
  62. esphome/components/api/user_services.h +8 -8
  63. esphome/components/as3935/as3935.cpp +0 -2
  64. esphome/components/as3935_spi/as3935_spi.cpp +0 -2
  65. esphome/components/as5600/__init__.py +1 -1
  66. esphome/components/as5600/as5600.cpp +0 -2
  67. esphome/components/as5600/sensor/__init__.py +0 -1
  68. esphome/components/as7341/as7341.cpp +0 -1
  69. esphome/components/async_tcp/__init__.py +1 -1
  70. esphome/components/at581x/at581x.cpp +1 -1
  71. esphome/components/atm90e26/atm90e26.cpp +0 -1
  72. esphome/components/atm90e32/atm90e32.cpp +488 -118
  73. esphome/components/atm90e32/atm90e32.h +44 -5
  74. esphome/components/audio/audio.h +2 -2
  75. esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp +0 -2
  76. esphome/components/beken_spi_led_strip/led_strip.cpp +0 -2
  77. esphome/components/bh1750/bh1750.cpp +0 -1
  78. esphome/components/binary_sensor/__init__.py +14 -12
  79. esphome/components/ble_client/__init__.py +4 -7
  80. esphome/components/bluetooth_proxy/__init__.py +40 -3
  81. esphome/components/bluetooth_proxy/bluetooth_connection.cpp +387 -82
  82. esphome/components/bluetooth_proxy/bluetooth_connection.h +16 -5
  83. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +102 -311
  84. esphome/components/bluetooth_proxy/bluetooth_proxy.h +30 -14
  85. esphome/components/bme280_base/bme280_base.cpp +15 -16
  86. esphome/components/bme680/bme680.cpp +2 -3
  87. esphome/components/bme680_bsec/bme680_bsec.cpp +0 -2
  88. esphome/components/bme68x_bsec2/bme68x_bsec2.cpp +0 -2
  89. esphome/components/bmi160/bmi160.cpp +0 -1
  90. esphome/components/bmp085/bmp085.cpp +0 -1
  91. esphome/components/bmp280_base/bmp280_base.cpp +13 -14
  92. esphome/components/bmp3xx_base/bmp3xx_base.cpp +0 -1
  93. esphome/components/bmp581/bmp581.cpp +0 -2
  94. esphome/components/bp1658cj/bp1658cj.cpp +0 -1
  95. esphome/components/bp5758d/bp5758d.cpp +0 -1
  96. esphome/components/button/__init__.py +0 -1
  97. esphome/components/canbus/__init__.py +2 -3
  98. esphome/components/canbus/canbus.cpp +0 -1
  99. esphome/components/cap1188/cap1188.cpp +0 -2
  100. esphome/components/captive_portal/__init__.py +1 -1
  101. esphome/components/cd74hc4067/cd74hc4067.cpp +0 -2
  102. esphome/components/ch422g/ch422g.cpp +0 -1
  103. esphome/components/chsc6x/chsc6x_touchscreen.cpp +0 -3
  104. esphome/components/climate/__init__.py +0 -1
  105. esphome/components/climate/climate_traits.h +24 -0
  106. esphome/components/cm1106/cm1106.cpp +0 -1
  107. esphome/components/const/__init__.py +6 -0
  108. esphome/components/cover/__init__.py +0 -1
  109. esphome/components/cover/cover.cpp +9 -13
  110. esphome/components/cs5460a/cs5460a.cpp +0 -2
  111. esphome/components/cse7761/cse7761.cpp +0 -1
  112. esphome/components/cst226/touchscreen/cst226_touchscreen.cpp +0 -2
  113. esphome/components/cst816/touchscreen/cst816_touchscreen.cpp +0 -2
  114. esphome/components/dac7678/dac7678_output.cpp +0 -2
  115. esphome/components/dallas_temp/dallas_temp.cpp +0 -1
  116. esphome/components/datetime/__init__.py +0 -2
  117. esphome/components/debug/__init__.py +15 -1
  118. esphome/components/debug/debug_zephyr.cpp +281 -0
  119. esphome/components/debug/sensor.py +2 -1
  120. esphome/components/deep_sleep/deep_sleep_component.cpp +0 -1
  121. esphome/components/deep_sleep/deep_sleep_esp32.cpp +20 -1
  122. esphome/components/dfrobot_sen0395/__init__.py +1 -2
  123. esphome/components/dht/dht.cpp +0 -1
  124. esphome/components/dht12/dht12.cpp +0 -1
  125. esphome/components/display/__init__.py +16 -3
  126. esphome/components/display_menu_base/__init__.py +1 -1
  127. esphome/components/dps310/dps310.cpp +0 -2
  128. esphome/components/ds1307/ds1307.cpp +0 -1
  129. esphome/components/ds2484/ds2484.cpp +0 -1
  130. esphome/components/duty_cycle/duty_cycle_sensor.cpp +0 -1
  131. esphome/components/ee895/ee895.cpp +0 -1
  132. esphome/components/ektf2232/touchscreen/ektf2232.cpp +0 -1
  133. esphome/components/emc2101/emc2101.cpp +0 -2
  134. esphome/components/ens160_base/ens160_base.cpp +0 -2
  135. esphome/components/ens210/ens210.cpp +0 -1
  136. esphome/components/es7210/es7210.cpp +0 -2
  137. esphome/components/es7243e/es7243e.cpp +0 -2
  138. esphome/components/es8156/es8156.cpp +0 -2
  139. esphome/components/es8311/es8311.cpp +0 -2
  140. esphome/components/es8388/es8388.cpp +0 -2
  141. esphome/components/esp32/__init__.py +199 -58
  142. esphome/components/esp32/boards.py +17 -0
  143. esphome/components/esp32/gpio.cpp +0 -1
  144. esphome/components/esp32/gpio.py +1 -2
  145. esphome/components/esp32/gpio_esp32_h2.py +2 -7
  146. esphome/components/esp32/gpio_esp32_p4.py +2 -7
  147. esphome/components/esp32/post_build.py.script +112 -61
  148. esphome/components/esp32_ble/__init__.py +40 -2
  149. esphome/components/esp32_ble/ble.cpp +12 -8
  150. esphome/components/esp32_ble/ble.h +18 -18
  151. esphome/components/esp32_ble/ble_advertising.cpp +5 -5
  152. esphome/components/esp32_ble/ble_advertising.h +7 -5
  153. esphome/components/esp32_ble/ble_event.h +2 -4
  154. esphome/components/esp32_ble/ble_scan_result.h +2 -4
  155. esphome/components/esp32_ble/ble_uuid.cpp +5 -5
  156. esphome/components/esp32_ble/ble_uuid.h +6 -5
  157. esphome/components/esp32_ble_beacon/__init__.py +4 -0
  158. esphome/components/esp32_ble_client/__init__.py +1 -1
  159. esphome/components/esp32_ble_client/ble_characteristic.cpp +4 -4
  160. esphome/components/esp32_ble_client/ble_characteristic.h +6 -4
  161. esphome/components/esp32_ble_client/ble_client_base.cpp +155 -104
  162. esphome/components/esp32_ble_client/ble_client_base.h +17 -6
  163. esphome/components/esp32_ble_client/ble_descriptor.h +6 -4
  164. esphome/components/esp32_ble_client/ble_service.cpp +4 -4
  165. esphome/components/esp32_ble_client/ble_service.h +6 -4
  166. esphome/components/esp32_ble_server/__init__.py +15 -12
  167. esphome/components/esp32_ble_tracker/__init__.py +79 -11
  168. esphome/components/esp32_ble_tracker/automation.h +4 -4
  169. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +264 -261
  170. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +103 -37
  171. esphome/components/esp32_camera/__init__.py +13 -1
  172. esphome/components/esp32_camera/esp32_camera.cpp +7 -2
  173. esphome/components/esp32_camera/esp32_camera.h +1 -0
  174. esphome/components/esp32_dac/esp32_dac.cpp +3 -19
  175. esphome/components/esp32_dac/esp32_dac.h +4 -8
  176. esphome/components/esp32_rmt_led_strip/led_strip.cpp +1 -6
  177. esphome/components/esp32_rmt_led_strip/light.py +1 -1
  178. esphome/components/esp32_touch/__init__.py +2 -3
  179. esphome/components/esp32_touch/esp32_touch.h +9 -6
  180. esphome/components/esp32_touch/esp32_touch_common.cpp +2 -0
  181. esphome/components/esp32_touch/esp32_touch_v1.cpp +7 -9
  182. esphome/components/esp32_touch/esp32_touch_v2.cpp +10 -6
  183. esphome/components/esp8266/__init__.py +3 -1
  184. esphome/components/esp8266_pwm/esp8266_pwm.cpp +0 -1
  185. esphome/components/esphome/ota/__init__.py +1 -2
  186. esphome/components/esphome/ota/ota_esphome.cpp +150 -77
  187. esphome/components/esphome/ota/ota_esphome.h +8 -1
  188. esphome/components/espnow/__init__.py +309 -0
  189. esphome/components/espnow/automation.h +175 -0
  190. esphome/components/espnow/espnow_component.cpp +468 -0
  191. esphome/components/espnow/espnow_component.h +183 -0
  192. esphome/components/espnow/espnow_err.h +19 -0
  193. esphome/components/espnow/espnow_packet.h +166 -0
  194. esphome/components/ethernet/__init__.py +7 -1
  195. esphome/components/ethernet/esp_eth_phy_jl1101.c +5 -0
  196. esphome/components/ethernet/ethernet_component.cpp +0 -1
  197. esphome/components/ethernet/ethernet_component.h +4 -0
  198. esphome/components/ethernet_info/ethernet_info_text_sensor.h +0 -3
  199. esphome/components/event/__init__.py +0 -1
  200. esphome/components/factory_reset/__init__.py +92 -0
  201. esphome/components/factory_reset/factory_reset.cpp +76 -0
  202. esphome/components/factory_reset/factory_reset.h +43 -0
  203. esphome/components/fan/__init__.py +0 -1
  204. esphome/components/fan/fan_traits.h +16 -0
  205. esphome/components/fastled_base/fastled_light.cpp +0 -1
  206. esphome/components/fastled_spi/light.py +1 -3
  207. esphome/components/fingerprint_grow/fingerprint_grow.cpp +0 -2
  208. esphome/components/font/__init__.py +9 -1
  209. esphome/components/fs3000/fs3000.cpp +0 -2
  210. esphome/components/ft5x06/touchscreen/ft5x06_touchscreen.cpp +0 -2
  211. esphome/components/ft63x6/ft63x6.cpp +0 -1
  212. esphome/components/gdk101/gdk101.cpp +0 -1
  213. esphome/components/gl_r01_i2c/gl_r01_i2c.cpp +0 -1
  214. esphome/components/gl_r01_i2c/sensor.py +1 -1
  215. esphome/components/gpio/one_wire/gpio_one_wire.cpp +0 -1
  216. esphome/components/gpio/switch/gpio_switch.cpp +0 -2
  217. esphome/components/gpio_expander/cached_gpio.h +24 -15
  218. esphome/components/gps/__init__.py +6 -2
  219. esphome/components/gps/gps.cpp +50 -49
  220. esphome/components/gps/gps.h +4 -8
  221. esphome/components/gps/time/gps_time.cpp +3 -9
  222. esphome/components/gps/time/gps_time.h +4 -7
  223. esphome/components/graph/__init__.py +1 -1
  224. esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.cpp +0 -1
  225. esphome/components/grove_tb6612fng/grove_tb6612fng.cpp +0 -1
  226. esphome/components/gt911/touchscreen/gt911_touchscreen.cpp +21 -12
  227. esphome/components/gt911/touchscreen/gt911_touchscreen.h +26 -2
  228. esphome/components/haier/climate.py +5 -10
  229. esphome/components/haier/haier_base.cpp +0 -1
  230. esphome/components/hbridge/switch/hbridge_switch.cpp +0 -2
  231. esphome/components/hdc1080/hdc1080.cpp +0 -2
  232. esphome/components/heatpumpir/climate.py +2 -2
  233. esphome/components/hlw8012/hlw8012.cpp +0 -1
  234. esphome/components/hm3301/hm3301.cpp +0 -1
  235. esphome/components/hmc5883l/hmc5883l.cpp +0 -1
  236. esphome/components/homeassistant/__init__.py +1 -0
  237. esphome/components/homeassistant/number/__init__.py +1 -0
  238. esphome/components/homeassistant/number/homeassistant_number.cpp +11 -7
  239. esphome/components/homeassistant/switch/__init__.py +1 -0
  240. esphome/components/homeassistant/switch/homeassistant_switch.cpp +9 -5
  241. esphome/components/honeywellabp/honeywellabp.cpp +1 -4
  242. esphome/components/host/__init__.py +2 -0
  243. esphome/components/hte501/hte501.cpp +0 -1
  244. esphome/components/http_request/__init__.py +2 -3
  245. esphome/components/http_request/http_request_idf.cpp +2 -2
  246. esphome/components/htu21d/htu21d.cpp +0 -2
  247. esphome/components/htu31d/htu31d.cpp +0 -2
  248. esphome/components/hx711/hx711.cpp +0 -1
  249. esphome/components/hydreon_rgxx/hydreon_rgxx.cpp +0 -1
  250. esphome/components/hydreon_rgxx/sensor.py +4 -5
  251. esphome/components/i2c/i2c_bus.h +1 -1
  252. esphome/components/i2c/i2c_bus_arduino.cpp +1 -2
  253. esphome/components/i2c/i2c_bus_esp_idf.cpp +192 -17
  254. esphome/components/i2c/i2c_bus_esp_idf.h +11 -1
  255. esphome/components/i2s_audio/__init__.py +6 -5
  256. esphome/components/i2s_audio/i2s_audio.cpp +0 -2
  257. esphome/components/i2s_audio/media_player/i2s_audio_media_player.cpp +1 -4
  258. esphome/components/i2s_audio/microphone/__init__.py +4 -6
  259. esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +46 -19
  260. esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +4 -3
  261. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +273 -269
  262. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +19 -34
  263. esphome/components/ili9xxx/display.py +4 -3
  264. esphome/components/ili9xxx/ili9xxx_display.cpp +0 -2
  265. esphome/components/image/__init__.py +123 -92
  266. esphome/components/improv_serial/__init__.py +7 -8
  267. esphome/components/ina219/ina219.cpp +0 -1
  268. esphome/components/ina226/ina226.cpp +0 -2
  269. esphome/components/ina260/ina260.cpp +0 -2
  270. esphome/components/ina2xx_base/__init__.py +2 -5
  271. esphome/components/ina2xx_base/ina2xx_base.cpp +0 -2
  272. esphome/components/ina3221/ina3221.cpp +0 -1
  273. esphome/components/internal_temperature/internal_temperature.cpp +0 -2
  274. esphome/components/interval/interval.h +5 -9
  275. esphome/components/json/__init__.py +1 -1
  276. esphome/components/kmeteriso/kmeteriso.cpp +0 -2
  277. esphome/components/lc709203f/lc709203f.cpp +0 -2
  278. esphome/components/lcd_gpio/display.py +1 -3
  279. esphome/components/lcd_gpio/gpio_lcd_display.cpp +0 -1
  280. esphome/components/lcd_pcf8574/pcf8574_display.cpp +0 -1
  281. esphome/components/ld2410/__init__.py +4 -6
  282. esphome/components/ld2410/binary_sensor.py +4 -0
  283. esphome/components/ld2410/ld2410.cpp +56 -100
  284. esphome/components/ld2410/ld2410.h +17 -15
  285. esphome/components/ld2410/sensor.py +24 -10
  286. esphome/components/ld2412/__init__.py +46 -0
  287. esphome/components/ld2412/binary_sensor.py +70 -0
  288. esphome/components/ld2412/button/__init__.py +74 -0
  289. esphome/components/ld2412/button/factory_reset_button.cpp +9 -0
  290. esphome/components/ld2412/button/factory_reset_button.h +18 -0
  291. esphome/components/ld2412/button/query_button.cpp +9 -0
  292. esphome/components/ld2412/button/query_button.h +18 -0
  293. esphome/components/ld2412/button/restart_button.cpp +9 -0
  294. esphome/components/ld2412/button/restart_button.h +18 -0
  295. esphome/components/ld2412/button/start_dynamic_background_correction_button.cpp +11 -0
  296. esphome/components/ld2412/button/start_dynamic_background_correction_button.h +18 -0
  297. esphome/components/ld2412/ld2412.cpp +861 -0
  298. esphome/components/ld2412/ld2412.h +141 -0
  299. esphome/components/ld2412/number/__init__.py +126 -0
  300. esphome/components/ld2412/number/gate_threshold_number.cpp +14 -0
  301. esphome/components/ld2412/number/gate_threshold_number.h +19 -0
  302. esphome/components/ld2412/number/light_threshold_number.cpp +12 -0
  303. esphome/components/ld2412/number/light_threshold_number.h +18 -0
  304. esphome/components/ld2412/number/max_distance_timeout_number.cpp +12 -0
  305. esphome/components/ld2412/number/max_distance_timeout_number.h +18 -0
  306. esphome/components/ld2412/select/__init__.py +82 -0
  307. esphome/components/ld2412/select/baud_rate_select.cpp +12 -0
  308. esphome/components/ld2412/select/baud_rate_select.h +18 -0
  309. esphome/components/ld2412/select/distance_resolution_select.cpp +12 -0
  310. esphome/components/ld2412/select/distance_resolution_select.h +18 -0
  311. esphome/components/ld2412/select/light_out_control_select.cpp +12 -0
  312. esphome/components/ld2412/select/light_out_control_select.h +18 -0
  313. esphome/components/ld2412/sensor.py +124 -0
  314. esphome/components/ld2412/switch/__init__.py +45 -0
  315. esphome/components/ld2412/switch/bluetooth_switch.cpp +12 -0
  316. esphome/components/ld2412/switch/bluetooth_switch.h +18 -0
  317. esphome/components/ld2412/switch/engineering_mode_switch.cpp +12 -0
  318. esphome/components/ld2412/switch/engineering_mode_switch.h +18 -0
  319. esphome/components/ld2412/text_sensor.py +34 -0
  320. esphome/components/ld2420/ld2420.cpp +0 -1
  321. esphome/components/ld2450/__init__.py +3 -4
  322. esphome/components/ld2450/binary_sensor.py +3 -0
  323. esphome/components/ld2450/ld2450.cpp +77 -165
  324. esphome/components/ld2450/ld2450.h +26 -54
  325. esphome/components/ld2450/sensor.py +120 -6
  326. esphome/components/ld2450/text_sensor.py +5 -4
  327. esphome/components/ld24xx/__init__.py +1 -0
  328. esphome/components/ld24xx/ld24xx.h +65 -0
  329. esphome/components/ledc/ledc_output.cpp +0 -1
  330. esphome/components/libretiny/__init__.py +2 -0
  331. esphome/components/light/__init__.py +0 -1
  332. esphome/components/light/effects.py +70 -45
  333. esphome/components/light/light_call.cpp +101 -66
  334. esphome/components/light/light_color_values.h +16 -11
  335. esphome/components/light/light_json_schema.cpp +46 -44
  336. esphome/components/light/light_state.cpp +8 -11
  337. esphome/components/light/light_traits.h +17 -0
  338. esphome/components/lightwaverf/lightwaverf.cpp +0 -2
  339. esphome/components/lilygo_t5_47/touchscreen/lilygo_t5_47_touchscreen.cpp +0 -1
  340. esphome/components/lock/__init__.py +0 -1
  341. esphome/components/logger/__init__.py +31 -9
  342. esphome/components/logger/logger.cpp +12 -7
  343. esphome/components/logger/logger.h +25 -14
  344. esphome/components/logger/logger_esp32.cpp +2 -7
  345. esphome/components/logger/logger_esp8266.cpp +2 -4
  346. esphome/components/logger/logger_host.cpp +2 -4
  347. esphome/components/logger/logger_libretiny.cpp +2 -4
  348. esphome/components/logger/logger_rp2040.cpp +2 -4
  349. esphome/components/logger/logger_zephyr.cpp +86 -0
  350. esphome/components/logger/select/logger_level_select.cpp +2 -4
  351. esphome/components/logger/select/logger_level_select.h +2 -4
  352. esphome/components/logger/task_log_buffer.cpp +2 -4
  353. esphome/components/logger/task_log_buffer.h +2 -4
  354. esphome/components/lps22/sensor.py +5 -5
  355. esphome/components/ltr390/ltr390.cpp +0 -2
  356. esphome/components/ltr501/ltr501.cpp +0 -1
  357. esphome/components/ltr_als_ps/ltr_als_ps.cpp +0 -1
  358. esphome/components/lvgl/__init__.py +14 -13
  359. esphome/components/lvgl/automation.py +2 -4
  360. esphome/components/lvgl/defines.py +0 -2
  361. esphome/components/lvgl/helpers.py +1 -1
  362. esphome/components/lvgl/lv_validation.py +7 -4
  363. esphome/components/lvgl/lvgl_esphome.cpp +2 -3
  364. esphome/components/lvgl/styles.py +2 -2
  365. esphome/components/lvgl/types.py +1 -1
  366. esphome/components/lvgl/widgets/__init__.py +2 -2
  367. esphome/components/lvgl/widgets/arc.py +14 -11
  368. esphome/components/lvgl/widgets/buttonmatrix.py +1 -1
  369. esphome/components/lvgl/widgets/qrcode.py +7 -7
  370. esphome/components/lvgl/widgets/spinner.py +6 -6
  371. esphome/components/lvgl/widgets/switch.py +2 -2
  372. esphome/components/lvgl/widgets/tabview.py +3 -3
  373. esphome/components/lvgl/widgets/tileview.py +15 -7
  374. esphome/components/m5stack_8angle/m5stack_8angle.cpp +0 -1
  375. esphome/components/matrix_keypad/__init__.py +4 -3
  376. esphome/components/max17043/max17043.cpp +0 -2
  377. esphome/components/max31855/max31855.cpp +1 -4
  378. esphome/components/max31856/max31856.cpp +0 -4
  379. esphome/components/max31865/max31865.cpp +0 -1
  380. esphome/components/max44009/max44009.cpp +0 -1
  381. esphome/components/max6675/max6675.cpp +1 -4
  382. esphome/components/max6956/max6956.cpp +0 -1
  383. esphome/components/max7219/max7219.cpp +0 -1
  384. esphome/components/max7219digit/display.py +1 -1
  385. esphome/components/max7219digit/max7219digit.cpp +0 -1
  386. esphome/components/max9611/max9611.cpp +0 -1
  387. esphome/components/mcp23008/__init__.py +1 -1
  388. esphome/components/mcp23008/mcp23008.cpp +0 -1
  389. esphome/components/mcp23016/mcp23016.cpp +0 -1
  390. esphome/components/mcp23017/__init__.py +1 -1
  391. esphome/components/mcp23017/mcp23017.cpp +0 -1
  392. esphome/components/mcp23s08/__init__.py +1 -1
  393. esphome/components/mcp23s08/mcp23s08.cpp +0 -1
  394. esphome/components/mcp23s17/__init__.py +1 -1
  395. esphome/components/mcp23s17/mcp23s17.cpp +0 -1
  396. esphome/components/mcp23x08_base/__init__.py +2 -0
  397. esphome/components/mcp23x08_base/mcp23x08_base.cpp +9 -7
  398. esphome/components/mcp23x08_base/mcp23x08_base.h +9 -4
  399. esphome/components/mcp23x17_base/__init__.py +2 -0
  400. esphome/components/mcp23x17_base/mcp23x17_base.cpp +20 -7
  401. esphome/components/mcp23x17_base/mcp23x17_base.h +9 -4
  402. esphome/components/mcp23xxx_base/__init__.py +11 -5
  403. esphome/components/mcp23xxx_base/mcp23xxx_base.cpp +15 -12
  404. esphome/components/mcp23xxx_base/mcp23xxx_base.h +8 -7
  405. esphome/components/mcp3008/mcp3008.cpp +1 -4
  406. esphome/components/mcp3204/mcp3204.cpp +1 -4
  407. esphome/components/mcp4461/mcp4461.cpp +0 -1
  408. esphome/components/mcp4725/mcp4725.cpp +0 -1
  409. esphome/components/mcp4728/mcp4728.cpp +0 -1
  410. esphome/components/mcp9600/mcp9600.cpp +0 -2
  411. esphome/components/mcp9808/mcp9808.cpp +0 -2
  412. esphome/components/mdns/__init__.py +3 -0
  413. esphome/components/mdns/mdns_component.cpp +2 -0
  414. esphome/components/mdns/mdns_component.h +4 -0
  415. esphome/components/media_player/__init__.py +40 -0
  416. esphome/components/media_player/automation.h +16 -0
  417. esphome/components/media_player/media_player.cpp +13 -0
  418. esphome/components/media_player/media_player.h +50 -3
  419. esphome/components/micro_wake_word/micro_wake_word.cpp +0 -3
  420. esphome/components/mics_4514/mics_4514.cpp +1 -6
  421. esphome/components/midea/ir_transmitter.h +4 -4
  422. esphome/components/mipi/__init__.py +416 -0
  423. esphome/components/mipi_dsi/__init__.py +5 -0
  424. esphome/components/mipi_dsi/display.py +233 -0
  425. esphome/components/mipi_dsi/mipi_dsi.cpp +379 -0
  426. esphome/components/mipi_dsi/mipi_dsi.h +123 -0
  427. esphome/components/mipi_dsi/models/__init__.py +0 -0
  428. esphome/components/mipi_dsi/models/guition.py +38 -0
  429. esphome/components/mipi_dsi/models/m5stack.py +57 -0
  430. esphome/components/mipi_dsi/models/waveshare.py +105 -0
  431. esphome/components/mipi_rgb/models/lilygo.py +0 -0
  432. esphome/components/mipi_spi/__init__.py +0 -9
  433. esphome/components/mipi_spi/display.py +220 -256
  434. esphome/components/mipi_spi/mipi_spi.cpp +1 -485
  435. esphome/components/mipi_spi/mipi_spi.h +556 -108
  436. esphome/components/mipi_spi/models/__init__.py +0 -65
  437. esphome/components/mipi_spi/models/adafruit.py +30 -0
  438. esphome/components/mipi_spi/models/amoled.py +41 -5
  439. esphome/components/mipi_spi/models/ili.py +5 -5
  440. esphome/components/mipi_spi/models/jc.py +1 -3
  441. esphome/components/mipi_spi/models/lilygo.py +1 -1
  442. esphome/components/mipi_spi/models/waveshare.py +16 -1
  443. esphome/components/mixer/speaker/__init__.py +4 -5
  444. esphome/components/mlx90393/sensor.py +7 -5
  445. esphome/components/mlx90393/sensor_mlx90393.cpp +0 -1
  446. esphome/components/mlx90614/mlx90614.cpp +0 -1
  447. esphome/components/mmc5603/mmc5603.cpp +0 -1
  448. esphome/components/mmc5983/mmc5983.cpp +0 -2
  449. esphome/components/mpl3115a2/mpl3115a2.cpp +0 -2
  450. esphome/components/mpr121/__init__.py +7 -6
  451. esphome/components/mpr121/mpr121.cpp +0 -1
  452. esphome/components/mpu6050/mpu6050.cpp +0 -1
  453. esphome/components/mpu6886/mpu6886.cpp +0 -1
  454. esphome/components/mqtt/__init__.py +1 -2
  455. esphome/components/mqtt/mqtt_button.cpp +1 -1
  456. esphome/components/mqtt/mqtt_client.cpp +0 -1
  457. esphome/components/mqtt/mqtt_component.cpp +8 -14
  458. esphome/components/mqtt/mqtt_component.h +0 -7
  459. esphome/components/mqtt/mqtt_sensor.cpp +0 -1
  460. esphome/components/mqtt/mqtt_sensor.h +0 -1
  461. esphome/components/mqtt/mqtt_text_sensor.cpp +0 -1
  462. esphome/components/mqtt/mqtt_text_sensor.h +0 -1
  463. esphome/components/ms5611/ms5611.cpp +0 -1
  464. esphome/components/ms8607/ms8607.cpp +0 -1
  465. esphome/components/msa3xx/msa3xx.cpp +0 -2
  466. esphome/components/my9231/my9231.cpp +0 -2
  467. esphome/components/nau7802/nau7802.cpp +0 -1
  468. esphome/components/neopixelbus/light.py +3 -0
  469. esphome/components/network/util.cpp +29 -0
  470. esphome/components/nextion/nextion.cpp +0 -1
  471. esphome/components/nfc/binary_sensor/{binary_sensor.cpp → nfc_binary_sensor.cpp} +1 -1
  472. esphome/components/npi19/npi19.cpp +0 -2
  473. esphome/components/nrf52/__init__.py +223 -0
  474. esphome/components/nrf52/boards.py +34 -0
  475. esphome/components/nrf52/const.py +18 -0
  476. esphome/components/nrf52/gpio.py +79 -0
  477. esphome/components/number/__init__.py +2 -1
  478. esphome/components/one_wire/__init__.py +1 -2
  479. esphome/components/one_wire/one_wire.cpp +0 -2
  480. esphome/components/one_wire/one_wire.h +0 -2
  481. esphome/components/opentherm/hub.cpp +0 -1
  482. esphome/components/opentherm/number/__init__.py +2 -2
  483. esphome/components/openthread/__init__.py +2 -3
  484. esphome/components/openthread/openthread.cpp +30 -13
  485. esphome/components/openthread/openthread.h +3 -0
  486. esphome/components/openthread/openthread_esp.cpp +3 -1
  487. esphome/components/opt3001/sensor.py +2 -6
  488. esphome/components/output/__init__.py +38 -0
  489. esphome/components/output/automation.h +24 -0
  490. esphome/components/output/switch/output_switch.cpp +0 -2
  491. esphome/components/packages/__init__.py +1 -2
  492. esphome/components/packet_transport/__init__.py +4 -3
  493. esphome/components/pca6416a/pca6416a.cpp +0 -1
  494. esphome/components/pca9554/pca9554.cpp +0 -1
  495. esphome/components/pca9685/pca9685_output.cpp +0 -2
  496. esphome/components/pcf85063/pcf85063.cpp +0 -1
  497. esphome/components/pcf8563/pcf8563.cpp +0 -1
  498. esphome/components/pcf8574/pcf8574.cpp +0 -1
  499. esphome/components/pi4ioe5v6408/pi4ioe5v6408.cpp +0 -1
  500. esphome/components/pipsolar/sensor/__init__.py +1 -1
  501. esphome/components/pm2005/pm2005.cpp +0 -1
  502. esphome/components/pmsa003i/pmsa003i.cpp +0 -2
  503. esphome/components/pmwcs3/sensor.py +1 -2
  504. esphome/components/pn532/pn532.cpp +0 -2
  505. esphome/components/pn532_spi/pn532_spi.cpp +0 -2
  506. esphome/components/power_supply/power_supply.cpp +7 -10
  507. esphome/components/power_supply/power_supply.h +1 -1
  508. esphome/components/psram/__init__.py +2 -1
  509. esphome/components/pulse_counter/pulse_counter_sensor.cpp +0 -1
  510. esphome/components/pulse_counter/sensor.py +9 -6
  511. esphome/components/pylontech/pylontech.cpp +0 -1
  512. esphome/components/qmc5883l/qmc5883l.cpp +0 -1
  513. esphome/components/qmp6988/qmp6988.cpp +0 -2
  514. esphome/components/qspi_dbi/display.py +2 -3
  515. esphome/components/qspi_dbi/qspi_dbi.cpp +0 -2
  516. esphome/components/qwiic_pir/binary_sensor.py +2 -3
  517. esphome/components/qwiic_pir/qwiic_pir.cpp +0 -2
  518. esphome/components/rc522/rc522.cpp +9 -31
  519. esphome/components/rc522_spi/rc522_spi.cpp +0 -1
  520. esphome/components/remote_base/__init__.py +5 -6
  521. esphome/components/remote_receiver/remote_receiver_esp32.cpp +0 -1
  522. esphome/components/remote_receiver/remote_receiver_esp8266.cpp +0 -1
  523. esphome/components/remote_receiver/remote_receiver_libretiny.cpp +0 -1
  524. esphome/components/remote_transmitter/__init__.py +26 -0
  525. esphome/components/remote_transmitter/automation.h +18 -0
  526. esphome/components/remote_transmitter/remote_transmitter.h +2 -1
  527. esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +0 -1
  528. esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +2 -0
  529. esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp +2 -0
  530. esphome/components/resampler/speaker/__init__.py +4 -5
  531. esphome/components/rf_bridge/__init__.py +4 -8
  532. esphome/components/rotary_encoder/rotary_encoder.cpp +0 -2
  533. esphome/components/rp2040/__init__.py +3 -1
  534. esphome/components/rp2040_pio_led_strip/led_strip.cpp +0 -2
  535. esphome/components/rp2040_pio_led_strip/light.py +1 -2
  536. esphome/components/rp2040_pwm/rp2040_pwm.cpp +1 -5
  537. esphome/components/rpi_dpi_rgb/display.py +13 -15
  538. esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp +0 -3
  539. esphome/components/runtime_stats/__init__.py +34 -0
  540. esphome/components/runtime_stats/runtime_stats.cpp +102 -0
  541. esphome/components/runtime_stats/runtime_stats.h +132 -0
  542. esphome/components/scd30/scd30.cpp +0 -2
  543. esphome/components/scd30/sensor.py +1 -2
  544. esphome/components/scd4x/scd4x.cpp +0 -1
  545. esphome/components/scd4x/sensor.py +1 -3
  546. esphome/components/sdl/display.py +3 -1
  547. esphome/components/sdl/sdl_esphome.cpp +0 -2
  548. esphome/components/sdp3x/sdp3x.cpp +0 -2
  549. esphome/components/seeed_mr24hpc1/seeed_mr24hpc1.cpp +0 -2
  550. esphome/components/seeed_mr60fda2/seeed_mr60fda2.cpp +0 -3
  551. esphome/components/select/__init__.py +2 -3
  552. esphome/components/select/select_traits.cpp +1 -1
  553. esphome/components/select/select_traits.h +1 -1
  554. esphome/components/sen0321/sen0321.cpp +0 -1
  555. esphome/components/sen5x/sen5x.cpp +0 -2
  556. esphome/components/sensor/__init__.py +36 -4
  557. esphome/components/sensor/filter.cpp +49 -10
  558. esphome/components/sensor/filter.h +22 -7
  559. esphome/components/sensor/sensor.cpp +0 -1
  560. esphome/components/sensor/sensor.h +0 -9
  561. esphome/components/sfa30/sfa30.cpp +0 -4
  562. esphome/components/sgp30/sgp30.cpp +0 -2
  563. esphome/components/sgp4x/sensor.py +1 -1
  564. esphome/components/sgp4x/sgp4x.cpp +0 -2
  565. esphome/components/shelly_dimmer/shelly_dimmer.cpp +0 -2
  566. esphome/components/sht3xd/sht3xd.cpp +0 -2
  567. esphome/components/sht4x/sht4x.cpp +0 -2
  568. esphome/components/shtcx/shtcx.cpp +0 -1
  569. esphome/components/sim800l/__init__.py +2 -4
  570. esphome/components/sm16716/sm16716.cpp +0 -1
  571. esphome/components/sm2135/sm2135.cpp +0 -1
  572. esphome/components/sm2235/sm2235.cpp +0 -1
  573. esphome/components/sm2335/sm2335.cpp +0 -1
  574. esphome/components/sn74hc165/sn74hc165.cpp +0 -1
  575. esphome/components/sn74hc595/sn74hc595.cpp +0 -1
  576. esphome/components/sntp/sntp_component.cpp +0 -1
  577. esphome/components/sound_level/sensor.py +1 -1
  578. esphome/components/speaker/media_player/__init__.py +21 -33
  579. esphome/components/speaker/media_player/audio_pipeline.cpp +4 -7
  580. esphome/components/spi/__init__.py +29 -13
  581. esphome/components/spi/spi.cpp +0 -2
  582. esphome/components/spi_device/spi_device.cpp +1 -4
  583. esphome/components/sprinkler/__init__.py +4 -4
  584. esphome/components/sps30/sps30.cpp +0 -1
  585. esphome/components/ssd1306_base/__init__.py +11 -11
  586. esphome/components/ssd1306_base/ssd1306_base.cpp +1 -1
  587. esphome/components/ssd1306_i2c/ssd1306_i2c.cpp +0 -1
  588. esphome/components/ssd1306_spi/ssd1306_spi.cpp +0 -1
  589. esphome/components/ssd1322_spi/ssd1322_spi.cpp +0 -1
  590. esphome/components/ssd1325_spi/ssd1325_spi.cpp +0 -1
  591. esphome/components/ssd1327_i2c/ssd1327_i2c.cpp +0 -1
  592. esphome/components/ssd1327_spi/ssd1327_spi.cpp +0 -1
  593. esphome/components/ssd1331_spi/ssd1331_spi.cpp +0 -1
  594. esphome/components/ssd1351_spi/ssd1351_spi.cpp +0 -1
  595. esphome/components/st7567_i2c/st7567_i2c.cpp +0 -1
  596. esphome/components/st7567_spi/st7567_spi.cpp +0 -1
  597. esphome/components/st7701s/display.py +10 -14
  598. esphome/components/st7701s/st7701s.cpp +0 -3
  599. esphome/components/st7735/st7735.cpp +0 -1
  600. esphome/components/st7789v/st7789v.cpp +0 -1
  601. esphome/components/st7920/st7920.cpp +0 -1
  602. esphome/components/status_led/light/status_led_light.cpp +0 -2
  603. esphome/components/status_led/status_led.cpp +0 -1
  604. esphome/components/stepper/__init__.py +2 -4
  605. esphome/components/sts3x/sts3x.cpp +0 -1
  606. esphome/components/substitutions/__init__.py +10 -16
  607. esphome/components/substitutions/jinja.py +24 -1
  608. esphome/components/sun/__init__.py +2 -3
  609. esphome/components/switch/__init__.py +31 -1
  610. esphome/components/switch/automation.h +24 -0
  611. esphome/components/switch/switch.cpp +8 -0
  612. esphome/components/switch/switch.h +8 -0
  613. esphome/components/sx126x/sx126x.cpp +0 -2
  614. esphome/components/sx127x/sx127x.cpp +0 -2
  615. esphome/components/sx1509/__init__.py +7 -5
  616. esphome/components/sx1509/output/sx1509_float_output.cpp +1 -1
  617. esphome/components/sx1509/sx1509.cpp +0 -2
  618. esphome/components/syslog/esphome_syslog.cpp +1 -1
  619. esphome/components/tc74/tc74.cpp +0 -1
  620. esphome/components/tca9548a/tca9548a.cpp +0 -1
  621. esphome/components/tca9555/tca9555.cpp +0 -1
  622. esphome/components/tcs34725/tcs34725.cpp +0 -1
  623. esphome/components/tee501/tee501.cpp +0 -1
  624. esphome/components/tem3200/tem3200.cpp +0 -2
  625. esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +0 -1
  626. esphome/components/template/cover/template_cover.cpp +0 -1
  627. esphome/components/template/select/template_select.cpp +0 -1
  628. esphome/components/template/text/template_text.cpp +0 -2
  629. esphome/components/template/valve/template_valve.cpp +0 -1
  630. esphome/components/text/__init__.py +0 -1
  631. esphome/components/text/text_traits.h +2 -0
  632. esphome/components/text_sensor/__init__.py +2 -1
  633. esphome/components/text_sensor/text_sensor.cpp +0 -2
  634. esphome/components/text_sensor/text_sensor.h +0 -8
  635. esphome/components/thermostat/climate.py +4 -4
  636. esphome/components/time/__init__.py +7 -4
  637. esphome/components/time/real_time_clock.cpp +16 -3
  638. esphome/components/tlc59208f/tlc59208f_output.cpp +0 -2
  639. esphome/components/tlc5947/tlc5947.cpp +0 -2
  640. esphome/components/tlc5971/tlc5971.cpp +0 -2
  641. esphome/components/tm1621/tm1621.cpp +0 -2
  642. esphome/components/tm1637/tm1637.cpp +0 -2
  643. esphome/components/tm1638/tm1638.cpp +0 -2
  644. esphome/components/tm1651/__init__.py +45 -48
  645. esphome/components/tm1651/tm1651.cpp +213 -47
  646. esphome/components/tm1651/tm1651.h +37 -32
  647. esphome/components/tmp117/tmp117.cpp +0 -2
  648. esphome/components/tsl2561/tsl2561.cpp +0 -1
  649. esphome/components/tsl2591/tsl2591.cpp +0 -1
  650. esphome/components/tt21100/touchscreen/tt21100.cpp +0 -2
  651. esphome/components/ttp229_bsf/ttp229_bsf.cpp +0 -1
  652. esphome/components/ttp229_lsf/ttp229_lsf.cpp +0 -1
  653. esphome/components/tuya/climate/__init__.py +9 -10
  654. esphome/components/tuya/number/__init__.py +8 -6
  655. esphome/components/tx20/tx20.cpp +0 -1
  656. esphome/components/uart/uart_component_esp32_arduino.cpp +0 -1
  657. esphome/components/uart/uart_component_esp8266.cpp +0 -1
  658. esphome/components/uart/uart_component_esp_idf.cpp +0 -2
  659. esphome/components/uart/uart_component_libretiny.cpp +0 -2
  660. esphome/components/uart/uart_component_rp2040.cpp +0 -2
  661. esphome/components/udp/__init__.py +1 -1
  662. esphome/components/ufire_ec/sensor.py +1 -2
  663. esphome/components/ufire_ec/ufire_ec.cpp +0 -2
  664. esphome/components/ufire_ise/sensor.py +1 -2
  665. esphome/components/ufire_ise/ufire_ise.cpp +0 -2
  666. esphome/components/ultrasonic/ultrasonic_sensor.cpp +0 -1
  667. esphome/components/update/__init__.py +0 -1
  668. esphome/components/uptime/sensor/uptime_seconds_sensor.cpp +0 -1
  669. esphome/components/uptime/sensor/uptime_seconds_sensor.h +0 -2
  670. esphome/components/usb_host/usb_host_client.cpp +0 -1
  671. esphome/components/usb_host/usb_host_component.cpp +0 -1
  672. esphome/components/valve/__init__.py +0 -1
  673. esphome/components/veml3235/veml3235.cpp +0 -3
  674. esphome/components/veml7700/veml7700.cpp +0 -2
  675. esphome/components/version/version_text_sensor.cpp +0 -1
  676. esphome/components/version/version_text_sensor.h +0 -1
  677. esphome/components/vl53l0x/vl53l0x_sensor.cpp +0 -4
  678. esphome/components/voice_assistant/voice_assistant.cpp +9 -8
  679. esphome/components/web_server/__init__.py +13 -0
  680. esphome/components/web_server/web_server.cpp +187 -352
  681. esphome/components/web_server/web_server.h +61 -1
  682. esphome/components/web_server_base/__init__.py +1 -1
  683. esphome/components/web_server_base/web_server_base.cpp +2 -0
  684. esphome/components/web_server_base/web_server_base.h +6 -0
  685. esphome/components/web_server_idf/web_server_idf.cpp +10 -8
  686. esphome/components/web_server_idf/web_server_idf.h +2 -0
  687. esphome/components/weikai_i2c/weikai_i2c.cpp +1 -2
  688. esphome/components/weikai_spi/weikai_spi.cpp +1 -1
  689. esphome/components/wifi/__init__.py +8 -43
  690. esphome/components/wifi/wifi_component.cpp +100 -36
  691. esphome/components/wifi/wifi_component.h +5 -1
  692. esphome/components/wifi/wifi_component_esp32_arduino.cpp +30 -0
  693. esphome/components/wifi/wifi_component_esp_idf.cpp +30 -0
  694. esphome/components/wifi_info/wifi_info_text_sensor.h +0 -6
  695. esphome/components/wifi_signal/wifi_signal_sensor.h +0 -1
  696. esphome/components/wireguard/wireguard.cpp +0 -2
  697. esphome/components/x9c/x9c.cpp +0 -2
  698. esphome/components/xgzp68xx/xgzp68xx.cpp +0 -1
  699. esphome/components/xl9535/xl9535.cpp +0 -2
  700. esphome/components/zephyr/__init__.py +252 -0
  701. esphome/components/zephyr/const.py +16 -0
  702. esphome/components/zephyr/core.cpp +90 -0
  703. esphome/components/zephyr/gpio.cpp +120 -0
  704. esphome/components/zephyr/gpio.h +38 -0
  705. esphome/components/zephyr/pre_build.py.script +4 -0
  706. esphome/components/zephyr/preferences.cpp +156 -0
  707. esphome/components/zephyr/preferences.h +13 -0
  708. esphome/config.py +38 -16
  709. esphome/config_helpers.py +1 -2
  710. esphome/config_validation.py +8 -15
  711. esphome/const.py +26 -1
  712. esphome/core/__init__.py +88 -51
  713. esphome/core/application.cpp +75 -21
  714. esphome/core/application.h +106 -171
  715. esphome/core/color.h +10 -0
  716. esphome/core/component.cpp +41 -25
  717. esphome/core/component.h +9 -6
  718. esphome/core/component_iterator.cpp +61 -261
  719. esphome/core/component_iterator.h +15 -0
  720. esphome/core/config.py +26 -11
  721. esphome/core/defines.h +40 -2
  722. esphome/core/entity_base.h +18 -0
  723. esphome/core/entity_helpers.py +41 -6
  724. esphome/core/helpers.cpp +8 -15
  725. esphome/core/helpers.h +60 -6
  726. esphome/core/lock_free_queue.h +1 -1
  727. esphome/core/scheduler.cpp +277 -74
  728. esphome/core/scheduler.h +89 -27
  729. esphome/cpp_generator.py +2 -6
  730. esphome/cpp_helpers.py +1 -1
  731. esphome/dashboard/dashboard.py +2 -3
  732. esphome/dashboard/dns.py +2 -8
  733. esphome/dashboard/web_server.py +34 -19
  734. esphome/espota2.py +1 -4
  735. esphome/git.py +3 -1
  736. esphome/helpers.py +23 -4
  737. esphome/log.py +3 -1
  738. esphome/mqtt.py +3 -5
  739. esphome/platformio_api.py +7 -4
  740. esphome/types.py +12 -0
  741. esphome/util.py +20 -8
  742. esphome/voluptuous_schema.py +4 -3
  743. esphome/vscode.py +1 -2
  744. esphome/wizard.py +1 -4
  745. esphome/writer.py +5 -107
  746. esphome/yaml_util.py +7 -5
  747. {esphome-2025.7.4.dist-info → esphome-2025.8.0b1.dist-info}/METADATA +12 -13
  748. {esphome-2025.7.4.dist-info → esphome-2025.8.0b1.dist-info}/RECORD +753 -673
  749. esphome/components/mipi_spi/models/commands.py +0 -82
  750. /esphome/components/nfc/binary_sensor/{binary_sensor.h → nfc_binary_sensor.h} +0 -0
  751. {esphome-2025.7.4.dist-info → esphome-2025.8.0b1.dist-info}/WHEEL +0 -0
  752. {esphome-2025.7.4.dist-info → esphome-2025.8.0b1.dist-info}/entry_points.txt +0 -0
  753. {esphome-2025.7.4.dist-info → esphome-2025.8.0b1.dist-info}/licenses/LICENSE +0 -0
  754. {esphome-2025.7.4.dist-info → esphome-2025.8.0b1.dist-info}/top_level.txt +0 -0
@@ -9,6 +9,7 @@
9
9
  #endif
10
10
 
11
11
  #include "esphome/components/audio/audio.h"
12
+ #include "esphome/components/audio/audio_transfer_buffer.h"
12
13
 
13
14
  #include "esphome/core/application.h"
14
15
  #include "esphome/core/hal.h"
@@ -19,71 +20,32 @@
19
20
  namespace esphome {
20
21
  namespace i2s_audio {
21
22
 
22
- static const uint8_t DMA_BUFFER_DURATION_MS = 15;
23
+ static const uint32_t DMA_BUFFER_DURATION_MS = 15;
23
24
  static const size_t DMA_BUFFERS_COUNT = 4;
24
25
 
25
- static const size_t TASK_DELAY_MS = DMA_BUFFER_DURATION_MS * DMA_BUFFERS_COUNT / 2;
26
-
27
26
  static const size_t TASK_STACK_SIZE = 4096;
28
- static const ssize_t TASK_PRIORITY = 23;
27
+ static const ssize_t TASK_PRIORITY = 19;
29
28
 
30
29
  static const size_t I2S_EVENT_QUEUE_COUNT = DMA_BUFFERS_COUNT + 1;
31
30
 
32
31
  static const char *const TAG = "i2s_audio.speaker";
33
32
 
34
33
  enum SpeakerEventGroupBits : uint32_t {
35
- COMMAND_START = (1 << 0), // starts the speaker task
34
+ COMMAND_START = (1 << 0), // indicates loop should start speaker task
36
35
  COMMAND_STOP = (1 << 1), // stops the speaker task
37
36
  COMMAND_STOP_GRACEFULLY = (1 << 2), // Stops the speaker task once all data has been written
38
- STATE_STARTING = (1 << 10),
39
- STATE_RUNNING = (1 << 11),
40
- STATE_STOPPING = (1 << 12),
41
- STATE_STOPPED = (1 << 13),
42
- ERR_TASK_FAILED_TO_START = (1 << 14),
43
- ERR_ESP_INVALID_STATE = (1 << 15),
44
- ERR_ESP_NOT_SUPPORTED = (1 << 16),
45
- ERR_ESP_INVALID_ARG = (1 << 17),
46
- ERR_ESP_INVALID_SIZE = (1 << 18),
37
+
38
+ TASK_STARTING = (1 << 10),
39
+ TASK_RUNNING = (1 << 11),
40
+ TASK_STOPPING = (1 << 12),
41
+ TASK_STOPPED = (1 << 13),
42
+
47
43
  ERR_ESP_NO_MEM = (1 << 19),
48
- ERR_ESP_FAIL = (1 << 20),
49
- ALL_ERR_ESP_BITS = ERR_ESP_INVALID_STATE | ERR_ESP_NOT_SUPPORTED | ERR_ESP_INVALID_ARG | ERR_ESP_INVALID_SIZE |
50
- ERR_ESP_NO_MEM | ERR_ESP_FAIL,
51
- ALL_BITS = 0x00FFFFFF, // All valid FreeRTOS event group bits
52
- };
53
44
 
54
- // Translates a SpeakerEventGroupBits ERR_ESP bit to the coressponding esp_err_t
55
- static esp_err_t err_bit_to_esp_err(uint32_t bit) {
56
- switch (bit) {
57
- case SpeakerEventGroupBits::ERR_ESP_INVALID_STATE:
58
- return ESP_ERR_INVALID_STATE;
59
- case SpeakerEventGroupBits::ERR_ESP_INVALID_ARG:
60
- return ESP_ERR_INVALID_ARG;
61
- case SpeakerEventGroupBits::ERR_ESP_INVALID_SIZE:
62
- return ESP_ERR_INVALID_SIZE;
63
- case SpeakerEventGroupBits::ERR_ESP_NO_MEM:
64
- return ESP_ERR_NO_MEM;
65
- case SpeakerEventGroupBits::ERR_ESP_NOT_SUPPORTED:
66
- return ESP_ERR_NOT_SUPPORTED;
67
- default:
68
- return ESP_FAIL;
69
- }
70
- }
45
+ WARN_DROPPED_EVENT = (1 << 20),
71
46
 
72
- /// @brief Multiplies the input array of Q15 numbers by a Q15 constant factor
73
- ///
74
- /// Based on `dsps_mulc_s16_ansi` from the esp-dsp library:
75
- /// https://github.com/espressif/esp-dsp/blob/master/modules/math/mulc/fixed/dsps_mulc_s16_ansi.c
76
- /// (accessed on 2024-09-30).
77
- /// @param input Array of Q15 numbers
78
- /// @param output Array of Q15 numbers
79
- /// @param len Length of array
80
- /// @param c Q15 constant factor
81
- static void q15_multiplication(const int16_t *input, int16_t *output, size_t len, int16_t c) {
82
- for (int i = 0; i < len; i++) {
83
- int32_t acc = (int32_t) input[i] * (int32_t) c;
84
- output[i] = (int16_t) (acc >> 15);
85
- }
86
- }
47
+ ALL_BITS = 0x00FFFFFF, // All valid FreeRTOS event group bits
48
+ };
87
49
 
88
50
  // Lists the Q15 fixed point scaling factor for volume reduction.
89
51
  // Has 100 values representing silence and a reduction [49, 48.5, ... 0.5, 0] dB.
@@ -99,8 +61,6 @@ static const std::vector<int16_t> Q15_VOLUME_SCALING_FACTORS = {
99
61
  19508, 20665, 21891, 23189, 24565, 26022, 27566, 29201, 30933, 32767};
100
62
 
101
63
  void I2SAudioSpeaker::setup() {
102
- ESP_LOGCONFIG(TAG, "Running setup");
103
-
104
64
  this->event_group_ = xEventGroupCreate();
105
65
 
106
66
  if (this->event_group_ == nullptr) {
@@ -132,51 +92,80 @@ void I2SAudioSpeaker::dump_config() {
132
92
  void I2SAudioSpeaker::loop() {
133
93
  uint32_t event_group_bits = xEventGroupGetBits(this->event_group_);
134
94
 
135
- if (event_group_bits & SpeakerEventGroupBits::STATE_STARTING) {
136
- ESP_LOGD(TAG, "Starting");
95
+ if ((event_group_bits & SpeakerEventGroupBits::COMMAND_START) && (this->state_ == speaker::STATE_STOPPED)) {
137
96
  this->state_ = speaker::STATE_STARTING;
138
- xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_STARTING);
97
+ xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::COMMAND_START);
98
+ }
99
+
100
+ // Handle the task's state
101
+ if (event_group_bits & SpeakerEventGroupBits::TASK_STARTING) {
102
+ ESP_LOGD(TAG, "Starting");
103
+ xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::TASK_STARTING);
139
104
  }
140
- if (event_group_bits & SpeakerEventGroupBits::STATE_RUNNING) {
105
+ if (event_group_bits & SpeakerEventGroupBits::TASK_RUNNING) {
141
106
  ESP_LOGD(TAG, "Started");
107
+ xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::TASK_RUNNING);
142
108
  this->state_ = speaker::STATE_RUNNING;
143
- xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_RUNNING);
144
- this->status_clear_warning();
145
- this->status_clear_error();
146
109
  }
147
- if (event_group_bits & SpeakerEventGroupBits::STATE_STOPPING) {
110
+ if (event_group_bits & SpeakerEventGroupBits::TASK_STOPPING) {
148
111
  ESP_LOGD(TAG, "Stopping");
112
+ xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::TASK_STOPPING);
149
113
  this->state_ = speaker::STATE_STOPPING;
150
- xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_STOPPING);
151
- }
152
- if (event_group_bits & SpeakerEventGroupBits::STATE_STOPPED) {
153
- if (!this->task_created_) {
154
- ESP_LOGD(TAG, "Stopped");
155
- this->state_ = speaker::STATE_STOPPED;
156
- xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ALL_BITS);
157
- this->speaker_task_handle_ = nullptr;
158
- }
159
114
  }
115
+ if (event_group_bits & SpeakerEventGroupBits::TASK_STOPPED) {
116
+ ESP_LOGD(TAG, "Stopped");
117
+
118
+ vTaskDelete(this->speaker_task_handle_);
119
+ this->speaker_task_handle_ = nullptr;
120
+
121
+ this->stop_i2s_driver_();
122
+ xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ALL_BITS);
123
+ this->status_clear_error();
160
124
 
161
- if (event_group_bits & SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START) {
162
- this->status_set_error("Failed to start task");
163
- xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START);
125
+ this->state_ = speaker::STATE_STOPPED;
164
126
  }
165
127
 
166
- if (event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS) {
167
- uint32_t error_bits = event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS;
168
- ESP_LOGW(TAG, "Writing failed: %s", esp_err_to_name(err_bit_to_esp_err(error_bits)));
169
- this->status_set_warning();
128
+ // Log any errors encounted by the task
129
+ if (event_group_bits & SpeakerEventGroupBits::ERR_ESP_NO_MEM) {
130
+ ESP_LOGE(TAG, "Not enough memory");
131
+ xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_NO_MEM);
170
132
  }
171
133
 
172
- if (event_group_bits & SpeakerEventGroupBits::ERR_ESP_NOT_SUPPORTED) {
173
- this->status_set_error("Failed to adjust bus to match incoming audio");
174
- ESP_LOGE(TAG, "Incompatible audio format: sample rate = %" PRIu32 ", channels = %u, bits per sample = %u",
175
- this->audio_stream_info_.get_sample_rate(), this->audio_stream_info_.get_channels(),
176
- this->audio_stream_info_.get_bits_per_sample());
134
+ // Warn if any playback timestamp events are dropped, which drastically reduces synced playback accuracy
135
+ if (event_group_bits & SpeakerEventGroupBits::WARN_DROPPED_EVENT) {
136
+ ESP_LOGW(TAG, "Event dropped, synchronized playback accuracy is reduced");
137
+ xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::WARN_DROPPED_EVENT);
177
138
  }
178
139
 
179
- xEventGroupClearBits(this->event_group_, ALL_ERR_ESP_BITS);
140
+ // Handle the speaker's state
141
+ switch (this->state_) {
142
+ case speaker::STATE_STARTING:
143
+ if (this->status_has_error()) {
144
+ break;
145
+ }
146
+
147
+ if (this->start_i2s_driver_(this->audio_stream_info_) != ESP_OK) {
148
+ ESP_LOGE(TAG, "Driver failed to start; retrying in 1 second");
149
+ this->status_momentary_error("driver-faiure", 1000);
150
+ break;
151
+ }
152
+
153
+ if (this->speaker_task_handle_ == nullptr) {
154
+ xTaskCreate(I2SAudioSpeaker::speaker_task, "speaker_task", TASK_STACK_SIZE, (void *) this, TASK_PRIORITY,
155
+ &this->speaker_task_handle_);
156
+
157
+ if (this->speaker_task_handle_ == nullptr) {
158
+ ESP_LOGE(TAG, "Task failed to start, retrying in 1 second");
159
+ this->status_momentary_error("task-failure", 1000);
160
+ this->stop_i2s_driver_(); // Stops the driver to return the lock; will be reloaded in next attempt
161
+ }
162
+ }
163
+ break;
164
+ case speaker::STATE_RUNNING: // Intentional fallthrough
165
+ case speaker::STATE_STOPPING: // Intentional fallthrough
166
+ case speaker::STATE_STOPPED:
167
+ break;
168
+ }
180
169
  }
181
170
 
182
171
  void I2SAudioSpeaker::set_volume(float volume) {
@@ -227,83 +216,76 @@ size_t I2SAudioSpeaker::play(const uint8_t *data, size_t length, TickType_t tick
227
216
  this->start();
228
217
  }
229
218
 
230
- if ((this->state_ != speaker::STATE_RUNNING) || (this->audio_ring_buffer_.use_count() != 1)) {
219
+ if (this->state_ != speaker::STATE_RUNNING) {
231
220
  // Unable to write data to a running speaker, so delay the max amount of time so it can get ready
232
221
  vTaskDelay(ticks_to_wait);
233
222
  ticks_to_wait = 0;
234
223
  }
235
224
 
236
225
  size_t bytes_written = 0;
237
- if ((this->state_ == speaker::STATE_RUNNING) && (this->audio_ring_buffer_.use_count() == 1)) {
238
- // Only one owner of the ring buffer (the speaker task), so the ring buffer is allocated and no other components are
239
- // attempting to write to it.
240
-
241
- // Temporarily share ownership of the ring buffer so it won't be deallocated while writing
242
- std::shared_ptr<RingBuffer> temp_ring_buffer = this->audio_ring_buffer_;
243
- bytes_written = temp_ring_buffer->write_without_replacement((void *) data, length, ticks_to_wait);
226
+ if (this->state_ == speaker::STATE_RUNNING) {
227
+ std::shared_ptr<RingBuffer> temp_ring_buffer = this->audio_ring_buffer_.lock();
228
+ if (temp_ring_buffer.use_count() == 2) {
229
+ // Only the speaker task and this temp_ring_buffer own the ring buffer, so its safe to write to
230
+ bytes_written = temp_ring_buffer->write_without_replacement((void *) data, length, ticks_to_wait);
231
+ }
244
232
  }
245
233
 
246
234
  return bytes_written;
247
235
  }
248
236
 
249
237
  bool I2SAudioSpeaker::has_buffered_data() const {
250
- if (this->audio_ring_buffer_ != nullptr) {
251
- return this->audio_ring_buffer_->available() > 0;
238
+ if (this->audio_ring_buffer_.use_count() > 0) {
239
+ std::shared_ptr<RingBuffer> temp_ring_buffer = this->audio_ring_buffer_.lock();
240
+ return temp_ring_buffer->available() > 0;
252
241
  }
253
242
  return false;
254
243
  }
255
244
 
256
245
  void I2SAudioSpeaker::speaker_task(void *params) {
257
246
  I2SAudioSpeaker *this_speaker = (I2SAudioSpeaker *) params;
258
- this_speaker->task_created_ = true;
259
-
260
- uint32_t event_group_bits =
261
- xEventGroupWaitBits(this_speaker->event_group_,
262
- SpeakerEventGroupBits::COMMAND_START | SpeakerEventGroupBits::COMMAND_STOP |
263
- SpeakerEventGroupBits::COMMAND_STOP_GRACEFULLY, // Bit message to read
264
- pdTRUE, // Clear the bits on exit
265
- pdFALSE, // Don't wait for all the bits,
266
- portMAX_DELAY); // Block indefinitely until a bit is set
267
-
268
- if (event_group_bits & (SpeakerEventGroupBits::COMMAND_STOP | SpeakerEventGroupBits::COMMAND_STOP_GRACEFULLY)) {
269
- // Received a stop signal before the task was requested to start
270
- this_speaker->delete_task_(0);
271
- }
272
-
273
- xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_STARTING);
274
247
 
275
- audio::AudioStreamInfo audio_stream_info = this_speaker->audio_stream_info_;
248
+ xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::TASK_STARTING);
276
249
 
277
250
  const uint32_t dma_buffers_duration_ms = DMA_BUFFER_DURATION_MS * DMA_BUFFERS_COUNT;
278
251
  // Ensure ring buffer duration is at least the duration of all DMA buffers
279
252
  const uint32_t ring_buffer_duration = std::max(dma_buffers_duration_ms, this_speaker->buffer_duration_ms_);
280
253
 
281
254
  // The DMA buffers may have more bits per sample, so calculate buffer sizes based in the input audio stream info
282
- const size_t data_buffer_size = audio_stream_info.ms_to_bytes(dma_buffers_duration_ms);
283
- const size_t ring_buffer_size = audio_stream_info.ms_to_bytes(ring_buffer_duration);
284
-
285
- const size_t single_dma_buffer_input_size = data_buffer_size / DMA_BUFFERS_COUNT;
286
-
287
- if (this_speaker->send_esp_err_to_event_group_(this_speaker->allocate_buffers_(data_buffer_size, ring_buffer_size))) {
288
- // Failed to allocate buffers
289
- xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::ERR_ESP_NO_MEM);
290
- this_speaker->delete_task_(data_buffer_size);
255
+ const size_t ring_buffer_size = this_speaker->current_stream_info_.ms_to_bytes(ring_buffer_duration);
256
+
257
+ const uint32_t frames_to_fill_single_dma_buffer =
258
+ this_speaker->current_stream_info_.ms_to_frames(DMA_BUFFER_DURATION_MS);
259
+ const size_t bytes_to_fill_single_dma_buffer =
260
+ this_speaker->current_stream_info_.frames_to_bytes(frames_to_fill_single_dma_buffer);
261
+
262
+ bool successful_setup = false;
263
+ std::unique_ptr<audio::AudioSourceTransferBuffer> transfer_buffer =
264
+ audio::AudioSourceTransferBuffer::create(bytes_to_fill_single_dma_buffer);
265
+
266
+ if (transfer_buffer != nullptr) {
267
+ std::shared_ptr<RingBuffer> temp_ring_buffer = RingBuffer::create(ring_buffer_size);
268
+ if (temp_ring_buffer.use_count() == 1) {
269
+ transfer_buffer->set_source(temp_ring_buffer);
270
+ this_speaker->audio_ring_buffer_ = temp_ring_buffer;
271
+ successful_setup = true;
272
+ }
291
273
  }
292
274
 
293
- if (!this_speaker->send_esp_err_to_event_group_(this_speaker->start_i2s_driver_(audio_stream_info))) {
294
- xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_RUNNING);
295
-
275
+ if (!successful_setup) {
276
+ xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::ERR_ESP_NO_MEM);
277
+ } else {
296
278
  bool stop_gracefully = false;
279
+ bool tx_dma_underflow = true;
280
+
281
+ uint32_t frames_written = 0;
297
282
  uint32_t last_data_received_time = millis();
298
- bool tx_dma_underflow = false;
299
283
 
300
- this_speaker->accumulated_frames_written_ = 0;
284
+ xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::TASK_RUNNING);
301
285
 
302
- // Keep looping if paused, there is no timeout configured, or data was received more recently than the configured
303
- // timeout
304
286
  while (this_speaker->pause_state_ || !this_speaker->timeout_.has_value() ||
305
287
  (millis() - last_data_received_time) <= this_speaker->timeout_.value()) {
306
- event_group_bits = xEventGroupGetBits(this_speaker->event_group_);
288
+ uint32_t event_group_bits = xEventGroupGetBits(this_speaker->event_group_);
307
289
 
308
290
  if (event_group_bits & SpeakerEventGroupBits::COMMAND_STOP) {
309
291
  xEventGroupClearBits(this_speaker->event_group_, SpeakerEventGroupBits::COMMAND_STOP);
@@ -314,7 +296,7 @@ void I2SAudioSpeaker::speaker_task(void *params) {
314
296
  stop_gracefully = true;
315
297
  }
316
298
 
317
- if (this_speaker->audio_stream_info_ != audio_stream_info) {
299
+ if (this_speaker->audio_stream_info_ != this_speaker->current_stream_info_) {
318
300
  // Audio stream info changed, stop the speaker task so it will restart with the proper settings.
319
301
  break;
320
302
  }
@@ -326,36 +308,75 @@ void I2SAudioSpeaker::speaker_task(void *params) {
326
308
  }
327
309
  }
328
310
  #else
329
- bool overflow;
330
- while (xQueueReceive(this_speaker->i2s_event_queue_, &overflow, 0)) {
331
- if (overflow) {
311
+ int64_t write_timestamp;
312
+ while (xQueueReceive(this_speaker->i2s_event_queue_, &write_timestamp, 0)) {
313
+ // Receives timing events from the I2S on_sent callback. If actual audio data was sent in this event, it passes
314
+ // on the timing info via the audio_output_callback.
315
+ uint32_t frames_sent = frames_to_fill_single_dma_buffer;
316
+ if (frames_to_fill_single_dma_buffer > frames_written) {
332
317
  tx_dma_underflow = true;
318
+ frames_sent = frames_written;
319
+ const uint32_t frames_zeroed = frames_to_fill_single_dma_buffer - frames_written;
320
+ write_timestamp -= this_speaker->current_stream_info_.frames_to_microseconds(frames_zeroed);
321
+ } else {
322
+ tx_dma_underflow = false;
323
+ }
324
+ frames_written -= frames_sent;
325
+ if (frames_sent > 0) {
326
+ this_speaker->audio_output_callback_(frames_sent, write_timestamp);
333
327
  }
334
328
  }
335
329
  #endif
336
330
 
337
331
  if (this_speaker->pause_state_) {
338
332
  // Pause state is accessed atomically, so thread safe
339
- // Delay so the task can yields, then skip transferring audio data
340
- delay(TASK_DELAY_MS);
333
+ // Delay so the task yields, then skip transferring audio data
334
+ vTaskDelay(pdMS_TO_TICKS(DMA_BUFFER_DURATION_MS));
341
335
  continue;
342
336
  }
343
337
 
344
- size_t bytes_read = this_speaker->audio_ring_buffer_->read((void *) this_speaker->data_buffer_, data_buffer_size,
345
- pdMS_TO_TICKS(TASK_DELAY_MS));
338
+ // Wait half the duration of the data already written to the DMA buffers for new audio data
339
+ // The millisecond helper modifies the frames_written variable, so use the microsecond helper and divide by 1000
340
+ const uint32_t read_delay =
341
+ (this_speaker->current_stream_info_.frames_to_microseconds(frames_written) / 1000) / 2;
342
+
343
+ uint8_t *new_data = transfer_buffer->get_buffer_end(); // track start of any newly copied bytes
344
+ size_t bytes_read = transfer_buffer->transfer_data_from_source(pdMS_TO_TICKS(read_delay));
346
345
 
347
346
  if (bytes_read > 0) {
348
- if ((audio_stream_info.get_bits_per_sample() == 16) && (this_speaker->q15_volume_factor_ < INT16_MAX)) {
349
- // Scale samples by the volume factor in place
350
- q15_multiplication((int16_t *) this_speaker->data_buffer_, (int16_t *) this_speaker->data_buffer_,
351
- bytes_read / sizeof(int16_t), this_speaker->q15_volume_factor_);
347
+ if (this_speaker->q15_volume_factor_ < INT16_MAX) {
348
+ // Apply the software volume adjustment by unpacking the sample into a Q31 fixed-point number, shifting it,
349
+ // multiplying by the volume factor, and packing the sample back into the original bytes per sample.
350
+
351
+ const size_t bytes_per_sample = this_speaker->current_stream_info_.samples_to_bytes(1);
352
+ const uint32_t len = bytes_read / bytes_per_sample;
353
+
354
+ // Use Q16 for samples with 1 or 2 bytes: shifted_sample * gain_factor is Q16 * Q15 -> Q31
355
+ int32_t shift = 15; // Q31 -> Q16
356
+ int32_t gain_factor = this_speaker->q15_volume_factor_; // Q15
357
+
358
+ if (bytes_per_sample >= 3) {
359
+ // Use Q23 for samples with 3 or 4 bytes: shifted_sample * gain_factor is Q23 * Q8 -> Q31
360
+
361
+ shift = 8; // Q31 -> Q23
362
+ gain_factor >>= 7; // Q15 -> Q8
363
+ }
364
+
365
+ for (uint32_t i = 0; i < len; ++i) {
366
+ int32_t sample =
367
+ audio::unpack_audio_sample_to_q31(&new_data[i * bytes_per_sample], bytes_per_sample); // Q31
368
+ sample >>= shift;
369
+ sample *= gain_factor; // Q31
370
+ audio::pack_q31_as_audio_sample(sample, &new_data[i * bytes_per_sample], bytes_per_sample);
371
+ }
352
372
  }
353
373
 
354
374
  #ifdef USE_ESP32_VARIANT_ESP32
355
375
  // For ESP32 8/16 bit mono mode samples need to be switched.
356
- if (audio_stream_info.get_channels() == 1 && audio_stream_info.get_bits_per_sample() <= 16) {
376
+ if (this_speaker->current_stream_info_.get_channels() == 1 &&
377
+ this_speaker->current_stream_info_.get_bits_per_sample() <= 16) {
357
378
  size_t len = bytes_read / sizeof(int16_t);
358
- int16_t *tmp_buf = (int16_t *) this_speaker->data_buffer_;
379
+ int16_t *tmp_buf = (int16_t *) new_data;
359
380
  for (int i = 0; i < len; i += 2) {
360
381
  int16_t tmp = tmp_buf[i];
361
382
  tmp_buf[i] = tmp_buf[i + 1];
@@ -363,62 +384,87 @@ void I2SAudioSpeaker::speaker_task(void *params) {
363
384
  }
364
385
  }
365
386
  #endif
366
- // Write the audio data to a single DMA buffer at a time to reduce latency for the audio duration played
367
- // callback.
368
- const uint32_t batches = (bytes_read + single_dma_buffer_input_size - 1) / single_dma_buffer_input_size;
369
-
370
- for (uint32_t i = 0; i < batches; ++i) {
371
- size_t bytes_written = 0;
372
- size_t bytes_to_write = std::min(single_dma_buffer_input_size, bytes_read);
387
+ }
373
388
 
389
+ if (transfer_buffer->available() == 0) {
390
+ if (stop_gracefully && tx_dma_underflow) {
391
+ break;
392
+ }
393
+ vTaskDelay(pdMS_TO_TICKS(DMA_BUFFER_DURATION_MS / 2));
394
+ } else {
395
+ size_t bytes_written = 0;
374
396
  #ifdef USE_I2S_LEGACY
375
- if (audio_stream_info.get_bits_per_sample() == (uint8_t) this_speaker->bits_per_sample_) {
376
- i2s_write(this_speaker->parent_->get_port(), this_speaker->data_buffer_ + i * single_dma_buffer_input_size,
377
- bytes_to_write, &bytes_written, pdMS_TO_TICKS(DMA_BUFFER_DURATION_MS * 5));
378
- } else if (audio_stream_info.get_bits_per_sample() < (uint8_t) this_speaker->bits_per_sample_) {
379
- i2s_write_expand(this_speaker->parent_->get_port(),
380
- this_speaker->data_buffer_ + i * single_dma_buffer_input_size, bytes_to_write,
381
- audio_stream_info.get_bits_per_sample(), this_speaker->bits_per_sample_, &bytes_written,
382
- pdMS_TO_TICKS(DMA_BUFFER_DURATION_MS * 5));
383
- }
397
+ if (this_speaker->current_stream_info_.get_bits_per_sample() == (uint8_t) this_speaker->bits_per_sample_) {
398
+ i2s_write(this_speaker->parent_->get_port(), transfer_buffer->get_buffer_start(),
399
+ transfer_buffer->available(), &bytes_written, pdMS_TO_TICKS(DMA_BUFFER_DURATION_MS));
400
+ } else if (this_speaker->current_stream_info_.get_bits_per_sample() <
401
+ (uint8_t) this_speaker->bits_per_sample_) {
402
+ i2s_write_expand(this_speaker->parent_->get_port(), transfer_buffer->get_buffer_start(),
403
+ transfer_buffer->available(), this_speaker->current_stream_info_.get_bits_per_sample(),
404
+ this_speaker->bits_per_sample_, &bytes_written, pdMS_TO_TICKS(DMA_BUFFER_DURATION_MS));
405
+ }
384
406
  #else
385
- i2s_channel_write(this_speaker->tx_handle_, this_speaker->data_buffer_ + i * single_dma_buffer_input_size,
386
- bytes_to_write, &bytes_written, pdMS_TO_TICKS(DMA_BUFFER_DURATION_MS * 5));
407
+ if (tx_dma_underflow) {
408
+ // Temporarily disable channel and callback to reset the I2S driver's internal DMA buffer queue so timing
409
+ // callbacks are accurate. Preload the data.
410
+ i2s_channel_disable(this_speaker->tx_handle_);
411
+ const i2s_event_callbacks_t callbacks = {
412
+ .on_sent = nullptr,
413
+ };
414
+
415
+ i2s_channel_register_event_callback(this_speaker->tx_handle_, &callbacks, this_speaker);
416
+ i2s_channel_preload_data(this_speaker->tx_handle_, transfer_buffer->get_buffer_start(),
417
+ transfer_buffer->available(), &bytes_written);
418
+ } else {
419
+ // Audio is already playing, use regular I2S write to add to the DMA buffers
420
+ i2s_channel_write(this_speaker->tx_handle_, transfer_buffer->get_buffer_start(), transfer_buffer->available(),
421
+ &bytes_written, DMA_BUFFER_DURATION_MS);
422
+ }
387
423
  #endif
424
+ if (bytes_written > 0) {
425
+ last_data_received_time = millis();
426
+ frames_written += this_speaker->current_stream_info_.bytes_to_frames(bytes_written);
427
+ transfer_buffer->decrease_buffer_length(bytes_written);
428
+ if (tx_dma_underflow) {
429
+ tx_dma_underflow = false;
430
+ #ifndef USE_I2S_LEGACY
431
+ // Reset the event queue timestamps
432
+ // Enable the on_sent callback to accurately track the timestamps of played audio
433
+ // Enable the I2S channel to start sending the preloaded audio
388
434
 
389
- int64_t now = esp_timer_get_time();
390
-
391
- if (bytes_written != bytes_to_write) {
392
- xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::ERR_ESP_INVALID_SIZE);
393
- }
394
- bytes_read -= bytes_written;
435
+ xQueueReset(this_speaker->i2s_event_queue_);
395
436
 
396
- this_speaker->audio_output_callback_(audio_stream_info.bytes_to_frames(bytes_written),
397
- now + dma_buffers_duration_ms * 1000);
437
+ const i2s_event_callbacks_t callbacks = {
438
+ .on_sent = i2s_on_sent_cb,
439
+ };
440
+ i2s_channel_register_event_callback(this_speaker->tx_handle_, &callbacks, this_speaker);
398
441
 
399
- tx_dma_underflow = false;
400
- last_data_received_time = millis();
401
- }
402
- } else {
403
- // No data received
404
- if (stop_gracefully && tx_dma_underflow) {
405
- break;
442
+ i2s_channel_enable(this_speaker->tx_handle_);
443
+ #endif
444
+ }
445
+ #ifdef USE_I2S_LEGACY
446
+ // The legacy driver doesn't easily support the callback approach for timestamps, so fall back to a direct but
447
+ // less accurate approach.
448
+ this_speaker->audio_output_callback_(this_speaker->current_stream_info_.bytes_to_frames(bytes_written),
449
+ esp_timer_get_time() + dma_buffers_duration_ms * 1000);
450
+ #endif
406
451
  }
407
452
  }
408
453
  }
454
+ }
409
455
 
410
- xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_STOPPING);
411
- #ifdef USE_I2S_LEGACY
412
- i2s_driver_uninstall(this_speaker->parent_->get_port());
413
- #else
414
- i2s_channel_disable(this_speaker->tx_handle_);
415
- i2s_del_channel(this_speaker->tx_handle_);
416
- #endif
456
+ xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::TASK_STOPPING);
417
457
 
418
- this_speaker->parent_->unlock();
458
+ if (transfer_buffer != nullptr) {
459
+ transfer_buffer.reset();
419
460
  }
420
461
 
421
- this_speaker->delete_task_(data_buffer_size);
462
+ xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::TASK_STOPPED);
463
+
464
+ while (true) {
465
+ // Continuously delay until the loop method deletes the task
466
+ vTaskDelay(pdMS_TO_TICKS(10));
467
+ }
422
468
  }
423
469
 
424
470
  void I2SAudioSpeaker::start() {
@@ -427,16 +473,7 @@ void I2SAudioSpeaker::start() {
427
473
  if ((this->state_ == speaker::STATE_STARTING) || (this->state_ == speaker::STATE_RUNNING))
428
474
  return;
429
475
 
430
- if (!this->task_created_ && (this->speaker_task_handle_ == nullptr)) {
431
- xTaskCreate(I2SAudioSpeaker::speaker_task, "speaker_task", TASK_STACK_SIZE, (void *) this, TASK_PRIORITY,
432
- &this->speaker_task_handle_);
433
-
434
- if (this->speaker_task_handle_ != nullptr) {
435
- xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::COMMAND_START);
436
- } else {
437
- xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START);
438
- }
439
- }
476
+ xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::COMMAND_START);
440
477
  }
441
478
 
442
479
  void I2SAudioSpeaker::stop() { this->stop_(false); }
@@ -456,61 +493,16 @@ void I2SAudioSpeaker::stop_(bool wait_on_empty) {
456
493
  }
457
494
  }
458
495
 
459
- bool I2SAudioSpeaker::send_esp_err_to_event_group_(esp_err_t err) {
460
- switch (err) {
461
- case ESP_OK:
462
- return false;
463
- case ESP_ERR_INVALID_STATE:
464
- xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_INVALID_STATE);
465
- return true;
466
- case ESP_ERR_INVALID_ARG:
467
- xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_INVALID_ARG);
468
- return true;
469
- case ESP_ERR_INVALID_SIZE:
470
- xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_INVALID_SIZE);
471
- return true;
472
- case ESP_ERR_NO_MEM:
473
- xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_NO_MEM);
474
- return true;
475
- case ESP_ERR_NOT_SUPPORTED:
476
- xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_NOT_SUPPORTED);
477
- return true;
478
- default:
479
- xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_FAIL);
480
- return true;
481
- }
482
- }
483
-
484
- esp_err_t I2SAudioSpeaker::allocate_buffers_(size_t data_buffer_size, size_t ring_buffer_size) {
485
- if (this->data_buffer_ == nullptr) {
486
- // Allocate data buffer for temporarily storing audio from the ring buffer before writing to the I2S bus
487
- RAMAllocator<uint8_t> allocator;
488
- this->data_buffer_ = allocator.allocate(data_buffer_size);
489
- }
490
-
491
- if (this->data_buffer_ == nullptr) {
492
- return ESP_ERR_NO_MEM;
493
- }
494
-
495
- if (this->audio_ring_buffer_.use_count() == 0) {
496
- // Allocate ring buffer. Uses a shared_ptr to ensure it isn't improperly deallocated.
497
- this->audio_ring_buffer_ = RingBuffer::create(ring_buffer_size);
498
- }
499
-
500
- if (this->audio_ring_buffer_ == nullptr) {
501
- return ESP_ERR_NO_MEM;
502
- }
503
-
504
- return ESP_OK;
505
- }
506
-
507
496
  esp_err_t I2SAudioSpeaker::start_i2s_driver_(audio::AudioStreamInfo &audio_stream_info) {
497
+ this->current_stream_info_ = audio_stream_info; // store the stream info settings the driver will use
498
+
508
499
  #ifdef USE_I2S_LEGACY
509
500
  if ((this->i2s_mode_ & I2S_MODE_SLAVE) && (this->sample_rate_ != audio_stream_info.get_sample_rate())) { // NOLINT
510
501
  #else
511
502
  if ((this->i2s_role_ & I2S_ROLE_SLAVE) && (this->sample_rate_ != audio_stream_info.get_sample_rate())) { // NOLINT
512
503
  #endif
513
504
  // Can't reconfigure I2S bus, so the sample rate must match the configured value
505
+ ESP_LOGE(TAG, "Audio stream settings are not compatible with this I2S configuration");
514
506
  return ESP_ERR_NOT_SUPPORTED;
515
507
  }
516
508
 
@@ -521,10 +513,12 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver_(audio::AudioStreamInfo &audio_strea
521
513
  (i2s_slot_bit_width_t) audio_stream_info.get_bits_per_sample() > this->slot_bit_width_) {
522
514
  #endif
523
515
  // Currently can't handle the case when the incoming audio has more bits per sample than the configured value
516
+ ESP_LOGE(TAG, "Audio streams with more bits per sample than the I2S speaker's configuration is not supported");
524
517
  return ESP_ERR_NOT_SUPPORTED;
525
518
  }
526
519
 
527
520
  if (!this->parent_->try_lock()) {
521
+ ESP_LOGE(TAG, "Parent I2S bus not free");
528
522
  return ESP_ERR_INVALID_STATE;
529
523
  }
530
524
 
@@ -575,6 +569,7 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver_(audio::AudioStreamInfo &audio_strea
575
569
  esp_err_t err =
576
570
  i2s_driver_install(this->parent_->get_port(), &config, I2S_EVENT_QUEUE_COUNT, &this->i2s_event_queue_);
577
571
  if (err != ESP_OK) {
572
+ ESP_LOGE(TAG, "Failed to install I2S legacy driver");
578
573
  // Failed to install the driver, so unlock the I2S port
579
574
  this->parent_->unlock();
580
575
  return err;
@@ -595,6 +590,7 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver_(audio::AudioStreamInfo &audio_strea
595
590
 
596
591
  if (err != ESP_OK) {
597
592
  // Failed to set the data out pin, so uninstall the driver and unlock the I2S port
593
+ ESP_LOGE(TAG, "Failed to set the data out pin");
598
594
  i2s_driver_uninstall(this->parent_->get_port());
599
595
  this->parent_->unlock();
600
596
  }
@@ -605,10 +601,12 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver_(audio::AudioStreamInfo &audio_strea
605
601
  .dma_desc_num = DMA_BUFFERS_COUNT,
606
602
  .dma_frame_num = dma_buffer_length,
607
603
  .auto_clear = true,
604
+ .intr_priority = 3,
608
605
  };
609
606
  /* Allocate a new TX channel and get the handle of this channel */
610
607
  esp_err_t err = i2s_new_channel(&chan_cfg, &this->tx_handle_, NULL);
611
608
  if (err != ESP_OK) {
609
+ ESP_LOGE(TAG, "Failed to allocate new I2S channel");
612
610
  this->parent_->unlock();
613
611
  return err;
614
612
  }
@@ -652,7 +650,11 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver_(audio::AudioStreamInfo &audio_strea
652
650
  // per sample causes the audio to play too fast. Setting the ws_width to the configured slot bit width seems to
653
651
  // make it play at the correct speed while sending more bits per slot.
654
652
  if (this->slot_bit_width_ != I2S_SLOT_BIT_WIDTH_AUTO) {
655
- std_slot_cfg.ws_width = static_cast<uint32_t>(this->slot_bit_width_);
653
+ uint32_t configured_bit_width = static_cast<uint32_t>(this->slot_bit_width_);
654
+ std_slot_cfg.ws_width = configured_bit_width;
655
+ if (configured_bit_width > 16) {
656
+ std_slot_cfg.msb_right = false;
657
+ }
656
658
  }
657
659
  #else
658
660
  std_slot_cfg.slot_bit_width = this->slot_bit_width_;
@@ -670,53 +672,55 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver_(audio::AudioStreamInfo &audio_strea
670
672
  err = i2s_channel_init_std_mode(this->tx_handle_, &std_cfg);
671
673
 
672
674
  if (err != ESP_OK) {
675
+ ESP_LOGE(TAG, "Failed to initialize channel");
673
676
  i2s_del_channel(this->tx_handle_);
677
+ this->tx_handle_ = nullptr;
674
678
  this->parent_->unlock();
675
679
  return err;
676
680
  }
677
681
  if (this->i2s_event_queue_ == nullptr) {
678
- this->i2s_event_queue_ = xQueueCreate(1, sizeof(bool));
682
+ this->i2s_event_queue_ = xQueueCreate(I2S_EVENT_QUEUE_COUNT, sizeof(int64_t));
679
683
  }
680
- const i2s_event_callbacks_t callbacks = {
681
- .on_send_q_ovf = i2s_overflow_cb,
682
- };
683
-
684
- i2s_channel_register_event_callback(this->tx_handle_, &callbacks, this);
685
684
 
686
- /* Before reading data, start the TX channel first */
687
685
  i2s_channel_enable(this->tx_handle_);
688
- if (err != ESP_OK) {
689
- i2s_del_channel(this->tx_handle_);
690
- this->parent_->unlock();
691
- }
692
686
  #endif
693
687
 
694
688
  return err;
695
689
  }
696
690
 
697
- void I2SAudioSpeaker::delete_task_(size_t buffer_size) {
698
- this->audio_ring_buffer_.reset(); // Releases ownership of the shared_ptr
691
+ #ifndef USE_I2S_LEGACY
692
+ bool IRAM_ATTR I2SAudioSpeaker::i2s_on_sent_cb(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx) {
693
+ int64_t now = esp_timer_get_time();
694
+
695
+ BaseType_t need_yield1 = pdFALSE;
696
+ BaseType_t need_yield2 = pdFALSE;
697
+ BaseType_t need_yield3 = pdFALSE;
698
+
699
+ I2SAudioSpeaker *this_speaker = (I2SAudioSpeaker *) user_ctx;
699
700
 
700
- if (this->data_buffer_ != nullptr) {
701
- RAMAllocator<uint8_t> allocator;
702
- allocator.deallocate(this->data_buffer_, buffer_size);
703
- this->data_buffer_ = nullptr;
701
+ if (xQueueIsQueueFullFromISR(this_speaker->i2s_event_queue_)) {
702
+ // Queue is full, so discard the oldest event and set the warning flag to inform the user
703
+ int64_t dummy;
704
+ xQueueReceiveFromISR(this_speaker->i2s_event_queue_, &dummy, &need_yield1);
705
+ xEventGroupSetBitsFromISR(this_speaker->event_group_, SpeakerEventGroupBits::WARN_DROPPED_EVENT, &need_yield2);
704
706
  }
705
707
 
706
- xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::STATE_STOPPED);
708
+ xQueueSendToBackFromISR(this_speaker->i2s_event_queue_, &now, &need_yield3);
707
709
 
708
- this->task_created_ = false;
709
- vTaskDelete(nullptr);
710
+ return need_yield1 | need_yield2 | need_yield3;
710
711
  }
712
+ #endif
711
713
 
712
- #ifndef USE_I2S_LEGACY
713
- bool IRAM_ATTR I2SAudioSpeaker::i2s_overflow_cb(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx) {
714
- I2SAudioSpeaker *this_speaker = (I2SAudioSpeaker *) user_ctx;
715
- bool overflow = true;
716
- xQueueOverwrite(this_speaker->i2s_event_queue_, &overflow);
717
- return false;
718
- }
714
+ void I2SAudioSpeaker::stop_i2s_driver_() {
715
+ #ifdef USE_I2S_LEGACY
716
+ i2s_driver_uninstall(this->parent_->get_port());
717
+ #else
718
+ i2s_channel_disable(this->tx_handle_);
719
+ i2s_del_channel(this->tx_handle_);
720
+ this->tx_handle_ = nullptr;
719
721
  #endif
722
+ this->parent_->unlock();
723
+ }
720
724
 
721
725
  } // namespace i2s_audio
722
726
  } // namespace esphome