esphome 2024.12.3__py3-none-any.whl → 2025.2.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.
Files changed (355) hide show
  1. esphome/__main__.py +16 -3
  2. esphome/components/adc/__init__.py +17 -11
  3. esphome/components/adc/adc_sensor.h +17 -0
  4. esphome/components/adc/adc_sensor_common.cpp +55 -0
  5. esphome/components/adc/adc_sensor_esp32.cpp +8 -5
  6. esphome/components/adc/adc_sensor_esp8266.cpp +10 -6
  7. esphome/components/adc/adc_sensor_libretiny.cpp +11 -6
  8. esphome/components/adc/adc_sensor_rp2040.cpp +13 -10
  9. esphome/components/adc/sensor.py +9 -3
  10. esphome/components/ads1115/ads1115.cpp +56 -7
  11. esphome/components/ads1115/ads1115.h +13 -1
  12. esphome/components/ads1115/sensor/__init__.py +16 -0
  13. esphome/components/ads1115/sensor/ads1115_sensor.cpp +2 -1
  14. esphome/components/ads1115/sensor/ads1115_sensor.h +2 -0
  15. esphome/components/animation/__init__.py +23 -261
  16. esphome/components/animation/animation.cpp +2 -2
  17. esphome/components/animation/animation.h +2 -1
  18. esphome/components/api/api_pb2.cpp +14 -0
  19. esphome/components/api/api_pb2.h +1 -0
  20. esphome/components/audio/__init__.py +112 -0
  21. esphome/components/audio/audio.cpp +67 -0
  22. esphome/components/audio/audio.h +125 -7
  23. esphome/components/audio/audio_decoder.cpp +361 -0
  24. esphome/components/audio/audio_decoder.h +135 -0
  25. esphome/components/audio/audio_reader.cpp +308 -0
  26. esphome/components/audio/audio_reader.h +85 -0
  27. esphome/components/audio/audio_resampler.cpp +159 -0
  28. esphome/components/audio/audio_resampler.h +101 -0
  29. esphome/components/audio/audio_transfer_buffer.cpp +165 -0
  30. esphome/components/audio/audio_transfer_buffer.h +139 -0
  31. esphome/components/audio_adc/__init__.py +41 -0
  32. esphome/components/audio_adc/audio_adc.h +17 -0
  33. esphome/components/audio_adc/automation.h +23 -0
  34. esphome/components/bk72xx/__init__.py +1 -0
  35. esphome/components/ble_client/ble_client.cpp +1 -2
  36. esphome/components/ble_client/sensor/__init__.py +1 -1
  37. esphome/components/ble_client/text_sensor/__init__.py +1 -1
  38. esphome/components/bluetooth_proxy/bluetooth_connection.cpp +5 -0
  39. esphome/components/bluetooth_proxy/bluetooth_connection.h +1 -0
  40. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +5 -0
  41. esphome/components/ch422g/ch422g.h +2 -0
  42. esphome/components/climate/__init__.py +1 -1
  43. esphome/components/climate_ir/climate_ir.cpp +2 -1
  44. esphome/components/coolix/coolix.cpp +2 -1
  45. esphome/components/custom/__init__.py +0 -3
  46. esphome/components/custom/binary_sensor/__init__.py +2 -28
  47. esphome/components/custom/climate/__init__.py +2 -27
  48. esphome/components/custom/cover/__init__.py +2 -27
  49. esphome/components/custom/light/__init__.py +2 -27
  50. esphome/components/custom/output/__init__.py +2 -58
  51. esphome/components/custom/sensor/__init__.py +2 -24
  52. esphome/components/custom/switch/__init__.py +2 -24
  53. esphome/components/custom/text_sensor/__init__.py +2 -29
  54. esphome/components/custom_component/__init__.py +3 -27
  55. esphome/components/daly_bms/daly_bms.cpp +6 -0
  56. esphome/components/daly_bms/daly_bms.h +2 -0
  57. esphome/components/daly_bms/sensor.py +6 -0
  58. esphome/components/debug/debug_component.cpp +4 -0
  59. esphome/components/debug/debug_component.h +14 -0
  60. esphome/components/debug/debug_esp32.cpp +154 -74
  61. esphome/components/dfplayer/dfplayer.cpp +15 -2
  62. esphome/components/dfrobot_sen0395/dfrobot_sen0395.cpp +2 -1
  63. esphome/components/dht/dht.cpp +2 -1
  64. esphome/components/display/__init__.py +18 -5
  65. esphome/components/display/display.cpp +2 -1
  66. esphome/components/display/rect.cpp +2 -1
  67. esphome/components/es7210/__init__.py +0 -0
  68. esphome/components/es7210/audio_adc.py +51 -0
  69. esphome/components/es7210/es7210.cpp +228 -0
  70. esphome/components/es7210/es7210.h +62 -0
  71. esphome/components/es7210/es7210_const.h +129 -0
  72. esphome/components/es7243e/__init__.py +0 -0
  73. esphome/components/es7243e/audio_adc.py +34 -0
  74. esphome/components/es7243e/es7243e.cpp +125 -0
  75. esphome/components/es7243e/es7243e.h +37 -0
  76. esphome/components/es7243e/es7243e_const.h +54 -0
  77. esphome/components/es8156/__init__.py +0 -0
  78. esphome/components/es8156/audio_dac.py +27 -0
  79. esphome/components/es8156/es8156.cpp +87 -0
  80. esphome/components/es8156/es8156.h +51 -0
  81. esphome/components/es8156/es8156_const.h +68 -0
  82. esphome/components/es8311/audio_dac.py +1 -2
  83. esphome/components/esp32/__init__.py +1 -0
  84. esphome/components/esp32/core.cpp +5 -1
  85. esphome/components/esp32/gpio.h +2 -0
  86. esphome/components/esp32_ble/__init__.py +39 -0
  87. esphome/components/esp32_ble/queue.h +4 -4
  88. esphome/components/esp32_ble_client/ble_client_base.cpp +46 -0
  89. esphome/components/esp32_ble_client/ble_client_base.h +2 -0
  90. esphome/components/esp32_ble_server/__init__.py +582 -12
  91. esphome/components/esp32_ble_server/ble_characteristic.cpp +48 -60
  92. esphome/components/esp32_ble_server/ble_characteristic.h +24 -17
  93. esphome/components/esp32_ble_server/ble_descriptor.cpp +21 -9
  94. esphome/components/esp32_ble_server/ble_descriptor.h +17 -6
  95. esphome/components/esp32_ble_server/ble_server.cpp +62 -67
  96. esphome/components/esp32_ble_server/ble_server.h +28 -32
  97. esphome/components/esp32_ble_server/ble_server_automations.cpp +77 -0
  98. esphome/components/esp32_ble_server/ble_server_automations.h +115 -0
  99. esphome/components/esp32_ble_server/ble_service.cpp +17 -15
  100. esphome/components/esp32_ble_server/ble_service.h +10 -14
  101. esphome/components/esp32_ble_tracker/__init__.py +6 -39
  102. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +33 -10
  103. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +8 -4
  104. esphome/components/esp32_improv/__init__.py +2 -8
  105. esphome/components/esp32_improv/esp32_improv_component.cpp +21 -20
  106. esphome/components/esp32_improv/esp32_improv_component.h +3 -4
  107. esphome/components/esp32_rmt/__init__.py +28 -3
  108. esphome/components/esp32_rmt_led_strip/led_strip.cpp +73 -6
  109. esphome/components/esp32_rmt_led_strip/led_strip.h +21 -3
  110. esphome/components/esp32_rmt_led_strip/light.py +72 -7
  111. esphome/components/esp32_touch/esp32_touch.cpp +5 -0
  112. esphome/components/esp8266/__init__.py +1 -0
  113. esphome/components/esp8266/gpio.h +1 -0
  114. esphome/components/ethernet/__init__.py +10 -10
  115. esphome/components/event/event.cpp +4 -2
  116. esphome/components/event/event.h +2 -0
  117. esphome/components/event_emitter/__init__.py +5 -0
  118. esphome/components/event_emitter/event_emitter.cpp +14 -0
  119. esphome/components/event_emitter/event_emitter.h +63 -0
  120. esphome/components/gcja5/gcja5.cpp +2 -1
  121. esphome/components/haier/haier_base.cpp +2 -1
  122. esphome/components/haier/hon_climate.cpp +2 -1
  123. esphome/components/heatpumpir/heatpumpir.cpp +2 -1
  124. esphome/components/host/__init__.py +1 -0
  125. esphome/components/host/gpio.h +1 -0
  126. esphome/components/http_request/http_request.h +2 -2
  127. esphome/components/http_request/http_request_arduino.cpp +1 -1
  128. esphome/components/http_request/http_request_idf.cpp +1 -1
  129. esphome/components/i2c/i2c_bus_esp_idf.cpp +4 -0
  130. esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +7 -5
  131. esphome/components/i2s_audio/speaker/__init__.py +53 -6
  132. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +92 -46
  133. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +8 -0
  134. esphome/components/ili9xxx/display.py +29 -11
  135. esphome/components/ili9xxx/ili9xxx_display.cpp +2 -5
  136. esphome/components/ili9xxx/ili9xxx_display.h +2 -1
  137. esphome/components/image/__init__.py +443 -255
  138. esphome/components/image/image.cpp +115 -61
  139. esphome/components/image/image.h +15 -24
  140. esphome/components/json/json_util.cpp +8 -34
  141. esphome/components/libretiny/__init__.py +1 -0
  142. esphome/components/libretiny/gpio_arduino.h +1 -0
  143. esphome/components/light/light_color_values.h +1 -1
  144. esphome/components/logger/__init__.py +43 -7
  145. esphome/components/logger/logger.cpp +16 -11
  146. esphome/components/logger/logger.h +11 -7
  147. esphome/components/logger/select/__init__.py +29 -0
  148. esphome/components/logger/select/logger_level_select.cpp +27 -0
  149. esphome/components/logger/select/logger_level_select.h +15 -0
  150. esphome/components/lvgl/__init__.py +96 -73
  151. esphome/components/lvgl/automation.py +39 -7
  152. esphome/components/lvgl/defines.py +8 -2
  153. esphome/components/lvgl/lvgl_esphome.cpp +8 -15
  154. esphome/components/lvgl/lvgl_esphome.h +20 -5
  155. esphome/components/lvgl/schemas.py +25 -14
  156. esphome/components/lvgl/trigger.py +27 -3
  157. esphome/components/lvgl/widgets/dropdown.py +1 -1
  158. esphome/components/lvgl/widgets/keyboard.py +8 -1
  159. esphome/components/lvgl/widgets/meter.py +2 -1
  160. esphome/components/lvgl/widgets/msgbox.py +1 -1
  161. esphome/components/lvgl/widgets/obj.py +1 -12
  162. esphome/components/lvgl/widgets/page.py +37 -2
  163. esphome/components/lvgl/widgets/tabview.py +1 -1
  164. esphome/components/max6956/max6956.h +2 -0
  165. esphome/components/mcp23016/mcp23016.h +2 -0
  166. esphome/components/mcp23xxx_base/mcp23xxx_base.h +2 -0
  167. esphome/components/mdns/__init__.py +1 -1
  168. esphome/components/media_player/__init__.py +37 -8
  169. esphome/components/media_player/automation.h +11 -2
  170. esphome/components/media_player/media_player.cpp +8 -0
  171. esphome/components/media_player/media_player.h +8 -4
  172. esphome/components/micronova/switch/micronova_switch.cpp +4 -2
  173. esphome/components/midea/ac_automations.h +3 -1
  174. esphome/components/midea/air_conditioner.cpp +7 -5
  175. esphome/components/midea/air_conditioner.h +1 -1
  176. esphome/components/midea/climate.py +4 -2
  177. esphome/components/midea/ir_transmitter.h +36 -5
  178. esphome/components/mixer/__init__.py +0 -0
  179. esphome/components/mixer/speaker/__init__.py +172 -0
  180. esphome/components/mixer/speaker/automation.h +19 -0
  181. esphome/components/mixer/speaker/mixer_speaker.cpp +624 -0
  182. esphome/components/mixer/speaker/mixer_speaker.h +207 -0
  183. esphome/components/mpr121/mpr121.h +2 -0
  184. esphome/components/mqtt/__init__.py +1 -1
  185. esphome/components/mqtt/mqtt_client.cpp +7 -1
  186. esphome/components/mqtt/mqtt_client.h +1 -1
  187. esphome/components/mqtt/mqtt_climate.cpp +2 -2
  188. esphome/components/network/ip_address.h +2 -0
  189. esphome/components/nextion/automation.h +17 -0
  190. esphome/components/nextion/display.py +42 -17
  191. esphome/components/nextion/nextion.cpp +4 -10
  192. esphome/components/nextion/nextion.h +89 -82
  193. esphome/components/nextion/nextion_commands.cpp +10 -10
  194. esphome/components/ntc/sensor.py +2 -4
  195. esphome/components/online_image/__init__.py +98 -46
  196. esphome/components/online_image/bmp_image.cpp +101 -0
  197. esphome/components/online_image/bmp_image.h +40 -0
  198. esphome/components/online_image/image_decoder.cpp +28 -2
  199. esphome/components/online_image/image_decoder.h +24 -15
  200. esphome/components/online_image/jpeg_image.cpp +90 -0
  201. esphome/components/online_image/jpeg_image.h +34 -0
  202. esphome/components/online_image/online_image.cpp +112 -53
  203. esphome/components/online_image/online_image.h +24 -7
  204. esphome/components/online_image/png_image.cpp +7 -3
  205. esphome/components/online_image/png_image.h +2 -1
  206. esphome/components/opentherm/__init__.py +73 -7
  207. esphome/components/opentherm/automation.h +25 -0
  208. esphome/components/opentherm/const.py +1 -0
  209. esphome/components/opentherm/generate.py +39 -6
  210. esphome/components/opentherm/hub.cpp +117 -79
  211. esphome/components/opentherm/hub.h +31 -15
  212. esphome/components/opentherm/opentherm.cpp +47 -23
  213. esphome/components/opentherm/opentherm.h +27 -6
  214. esphome/components/opentherm/opentherm_macros.h +11 -0
  215. esphome/components/opentherm/schema.py +78 -1
  216. esphome/components/opentherm/validate.py +7 -2
  217. esphome/components/pca6416a/pca6416a.h +2 -0
  218. esphome/components/pca9554/pca9554.h +2 -0
  219. esphome/components/pcf8574/pcf8574.h +2 -0
  220. esphome/components/preferences/__init__.py +2 -4
  221. esphome/components/preferences/syncer.h +10 -3
  222. esphome/components/prometheus/prometheus_handler.cpp +313 -0
  223. esphome/components/prometheus/prometheus_handler.h +48 -7
  224. esphome/components/psram/psram.cpp +8 -1
  225. esphome/components/pulse_counter/pulse_counter_sensor.cpp +14 -9
  226. esphome/components/pulse_counter/pulse_counter_sensor.h +4 -4
  227. esphome/components/pulse_meter/pulse_meter_sensor.cpp +2 -0
  228. esphome/components/qspi_dbi/__init__.py +3 -0
  229. esphome/components/qspi_dbi/display.py +74 -47
  230. esphome/components/qspi_dbi/models.py +245 -2
  231. esphome/components/qspi_dbi/qspi_dbi.cpp +9 -16
  232. esphome/components/qspi_dbi/qspi_dbi.h +2 -2
  233. esphome/components/remote_base/__init__.py +77 -25
  234. esphome/components/remote_base/remote_base.cpp +1 -1
  235. esphome/components/remote_base/remote_base.h +20 -2
  236. esphome/components/remote_base/toto_protocol.cpp +100 -0
  237. esphome/components/remote_base/toto_protocol.h +45 -0
  238. esphome/components/remote_receiver/__init__.py +55 -10
  239. esphome/components/remote_receiver/remote_receiver.h +36 -3
  240. esphome/components/remote_receiver/remote_receiver_esp32.cpp +145 -6
  241. esphome/components/remote_transmitter/__init__.py +62 -4
  242. esphome/components/remote_transmitter/remote_transmitter.h +21 -2
  243. esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +140 -4
  244. esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +3 -3
  245. esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp +3 -3
  246. esphome/components/resampler/__init__.py +0 -0
  247. esphome/components/resampler/speaker/__init__.py +103 -0
  248. esphome/components/resampler/speaker/resampler_speaker.cpp +318 -0
  249. esphome/components/resampler/speaker/resampler_speaker.h +107 -0
  250. esphome/components/resistance/resistance_sensor.h +2 -3
  251. esphome/components/resistance/sensor.py +2 -9
  252. esphome/components/rotary_encoder/rotary_encoder.cpp +8 -4
  253. esphome/components/rp2040/__init__.py +1 -0
  254. esphome/components/rp2040/gpio.h +1 -0
  255. esphome/components/rtl87xx/__init__.py +2 -0
  256. esphome/components/sdl/binary_sensor.py +270 -0
  257. esphome/components/sdl/sdl_esphome.cpp +16 -0
  258. esphome/components/sdl/sdl_esphome.h +9 -0
  259. esphome/components/seeed_mr60bha2/binary_sensor.py +25 -0
  260. esphome/components/seeed_mr60bha2/seeed_mr60bha2.cpp +26 -2
  261. esphome/components/seeed_mr60bha2/seeed_mr60bha2.h +9 -20
  262. esphome/components/seeed_mr60bha2/sensor.py +9 -1
  263. esphome/components/sn74hc165/sn74hc165.h +3 -0
  264. esphome/components/sn74hc595/sn74hc595.h +3 -0
  265. esphome/components/speaker/__init__.py +5 -4
  266. esphome/components/speaker/media_player/__init__.py +458 -0
  267. esphome/components/speaker/media_player/audio_pipeline.cpp +568 -0
  268. esphome/components/speaker/media_player/audio_pipeline.h +159 -0
  269. esphome/components/speaker/media_player/automation.h +26 -0
  270. esphome/components/speaker/media_player/speaker_media_player.cpp +577 -0
  271. esphome/components/speaker/media_player/speaker_media_player.h +160 -0
  272. esphome/components/speaker/speaker.h +20 -0
  273. esphome/components/spi/__init__.py +1 -5
  274. esphome/components/spi/spi.cpp +7 -1
  275. esphome/components/spi/spi.h +21 -2
  276. esphome/components/spi_led_strip/light.py +3 -5
  277. esphome/components/spi_led_strip/spi_led_strip.cpp +67 -0
  278. esphome/components/spi_led_strip/spi_led_strip.h +8 -60
  279. esphome/components/sprinkler/sprinkler.cpp +3 -1
  280. esphome/components/sx1509/sx1509_gpio_pin.h +2 -0
  281. esphome/components/tca9555/tca9555.h +2 -0
  282. esphome/components/toshiba/toshiba.cpp +2 -1
  283. esphome/components/tuya/light/tuya_light.cpp +4 -2
  284. esphome/components/uart/uart_component_esp32_arduino.cpp +2 -2
  285. esphome/components/uart/uart_component_esp_idf.cpp +2 -2
  286. esphome/components/udp/__init__.py +8 -2
  287. esphome/components/udp/udp_component.cpp +25 -56
  288. esphome/components/udp/udp_component.h +3 -0
  289. esphome/components/uponor_smatrix/sensor/__init__.py +14 -4
  290. esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.cpp +5 -0
  291. esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.h +1 -0
  292. esphome/components/uptime/text_sensor/__init__.py +19 -0
  293. esphome/components/uptime/text_sensor/uptime_text_sensor.cpp +63 -0
  294. esphome/components/uptime/text_sensor/uptime_text_sensor.h +25 -0
  295. esphome/components/voice_assistant/voice_assistant.cpp +24 -14
  296. esphome/components/voice_assistant/voice_assistant.h +8 -0
  297. esphome/components/waveshare_epaper/display.py +22 -1
  298. esphome/components/waveshare_epaper/waveshare_213v3.cpp +9 -3
  299. esphome/components/waveshare_epaper/waveshare_epaper.cpp +1155 -44
  300. esphome/components/waveshare_epaper/waveshare_epaper.h +208 -7
  301. esphome/components/web_server/web_server.cpp +28 -6
  302. esphome/components/weikai/weikai.h +2 -0
  303. esphome/components/wifi/__init__.py +6 -6
  304. esphome/components/wifi/wifi_component.cpp +1 -1
  305. esphome/components/wifi/wifi_component_esp32_arduino.cpp +30 -1
  306. esphome/components/wireguard/__init__.py +2 -2
  307. esphome/components/xl9535/xl9535.h +2 -0
  308. esphome/components/xxtea/__init__.py +3 -0
  309. esphome/components/xxtea/xxtea.cpp +46 -0
  310. esphome/components/xxtea/xxtea.h +26 -0
  311. esphome/components/yashima/yashima.cpp +2 -1
  312. esphome/config.py +9 -5
  313. esphome/config_validation.py +55 -17
  314. esphome/const.py +7 -10
  315. esphome/core/__init__.py +6 -13
  316. esphome/core/base_automation.h +1 -0
  317. esphome/core/config.py +57 -72
  318. esphome/core/defines.h +9 -1
  319. esphome/core/gpio.h +7 -0
  320. esphome/core/helpers.cpp +19 -15
  321. esphome/core/helpers.h +57 -8
  322. esphome/core/log.h +9 -7
  323. esphome/cpp_generator.py +2 -2
  324. esphome/espota2.py +3 -2
  325. esphome/loader.py +12 -4
  326. esphome/log.py +5 -7
  327. esphome/yaml_util.py +2 -2
  328. {esphome-2024.12.3.dist-info → esphome-2025.2.0b1.dist-info}/METADATA +12 -7
  329. {esphome-2024.12.3.dist-info → esphome-2025.2.0b1.dist-info}/RECORD +338 -289
  330. esphome/components/custom/binary_sensor/custom_binary_sensor.cpp +0 -16
  331. esphome/components/custom/binary_sensor/custom_binary_sensor.h +0 -26
  332. esphome/components/custom/climate/custom_climate.h +0 -22
  333. esphome/components/custom/cover/custom_cover.h +0 -21
  334. esphome/components/custom/light/custom_light_output.h +0 -24
  335. esphome/components/custom/output/custom_output.h +0 -37
  336. esphome/components/custom/sensor/custom_sensor.cpp +0 -16
  337. esphome/components/custom/sensor/custom_sensor.h +0 -24
  338. esphome/components/custom/switch/custom_switch.cpp +0 -16
  339. esphome/components/custom/switch/custom_switch.h +0 -24
  340. esphome/components/custom/text_sensor/custom_text_sensor.cpp +0 -16
  341. esphome/components/custom/text_sensor/custom_text_sensor.h +0 -26
  342. esphome/components/custom_component/custom_component.h +0 -28
  343. esphome/components/esp32_ble_server/ble_2901.cpp +0 -18
  344. esphome/components/esp32_ble_server/ble_2901.h +0 -19
  345. esphome/components/resistance_sampler/__init__.py +0 -6
  346. esphome/components/resistance_sampler/resistance_sampler.h +0 -10
  347. esphome/components/uptime/{sensor.py → sensor/__init__.py} +3 -3
  348. /esphome/components/uptime/{uptime_seconds_sensor.cpp → sensor/uptime_seconds_sensor.cpp} +0 -0
  349. /esphome/components/uptime/{uptime_seconds_sensor.h → sensor/uptime_seconds_sensor.h} +0 -0
  350. /esphome/components/uptime/{uptime_timestamp_sensor.cpp → sensor/uptime_timestamp_sensor.cpp} +0 -0
  351. /esphome/components/uptime/{uptime_timestamp_sensor.h → sensor/uptime_timestamp_sensor.h} +0 -0
  352. {esphome-2024.12.3.dist-info → esphome-2025.2.0b1.dist-info}/LICENSE +0 -0
  353. {esphome-2024.12.3.dist-info → esphome-2025.2.0b1.dist-info}/WHEEL +0 -0
  354. {esphome-2024.12.3.dist-info → esphome-2025.2.0b1.dist-info}/entry_points.txt +0 -0
  355. {esphome-2024.12.3.dist-info → esphome-2025.2.0b1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,63 @@
1
+ #pragma once
2
+ #include <unordered_map>
3
+ #include <vector>
4
+ #include <functional>
5
+ #include <limits>
6
+
7
+ #include "esphome/core/log.h"
8
+
9
+ namespace esphome {
10
+ namespace event_emitter {
11
+
12
+ using EventEmitterListenerID = uint32_t;
13
+ void raise_event_emitter_full_error();
14
+
15
+ // EventEmitter class that can emit events with a specific name (it is highly recommended to use an enum class for this)
16
+ // and a list of arguments. Supports multiple listeners for each event.
17
+ template<typename EvtType, typename... Args> class EventEmitter {
18
+ public:
19
+ EventEmitterListenerID on(EvtType event, std::function<void(Args...)> listener) {
20
+ EventEmitterListenerID listener_id = get_next_id_(event);
21
+ listeners_[event][listener_id] = listener;
22
+ return listener_id;
23
+ }
24
+
25
+ void off(EvtType event, EventEmitterListenerID id) {
26
+ if (listeners_.count(event) == 0)
27
+ return;
28
+ listeners_[event].erase(id);
29
+ }
30
+
31
+ protected:
32
+ void emit_(EvtType event, Args... args) {
33
+ if (listeners_.count(event) == 0)
34
+ return;
35
+ for (const auto &listener : listeners_[event]) {
36
+ listener.second(args...);
37
+ }
38
+ }
39
+
40
+ EventEmitterListenerID get_next_id_(EvtType event) {
41
+ // Check if the map is full
42
+ if (listeners_[event].size() == std::numeric_limits<EventEmitterListenerID>::max()) {
43
+ // Raise an error if the map is full
44
+ raise_event_emitter_full_error();
45
+ off(event, 0);
46
+ return 0;
47
+ }
48
+ // Get the next ID for the given event.
49
+ EventEmitterListenerID next_id = (current_id_ + 1) % std::numeric_limits<EventEmitterListenerID>::max();
50
+ while (listeners_[event].count(next_id) > 0) {
51
+ next_id = (next_id + 1) % std::numeric_limits<EventEmitterListenerID>::max();
52
+ }
53
+ current_id_ = next_id;
54
+ return current_id_;
55
+ }
56
+
57
+ private:
58
+ std::unordered_map<EvtType, std::unordered_map<EventEmitterListenerID, std::function<void(Args...)>>> listeners_;
59
+ EventEmitterListenerID current_id_ = 0;
60
+ };
61
+
62
+ } // namespace event_emitter
63
+ } // namespace esphome
@@ -97,8 +97,9 @@ void GCJA5Component::parse_data_() {
97
97
  if (this->rx_message_[0] != 0x02 || this->rx_message_[31] != 0x03 || !this->calculate_checksum_()) {
98
98
  ESP_LOGVV(TAG, "Discarding bad packet - failed checks.");
99
99
  return;
100
- } else
100
+ } else {
101
101
  ESP_LOGVV(TAG, "Good packet found.");
102
+ }
102
103
 
103
104
  this->have_good_data_ = true;
104
105
  uint8_t status = this->rx_message_[29];
@@ -342,8 +342,9 @@ bool HaierClimateBase::prepare_pending_action() {
342
342
  this->action_request_.reset();
343
343
  return false;
344
344
  }
345
- } else
345
+ } else {
346
346
  return false;
347
+ }
347
348
  }
348
349
 
349
350
  ClimateTraits HaierClimateBase::traits() { return traits_; }
@@ -710,8 +710,9 @@ void HonClimate::process_alarm_message_(const uint8_t *packet, uint8_t size, boo
710
710
  alarm_code++;
711
711
  }
712
712
  active_alarms_[i] = packet[2 + i];
713
- } else
713
+ } else {
714
714
  alarm_code += 8;
715
+ }
715
716
  }
716
717
  } else {
717
718
  float alarm_count = 0.0f;
@@ -87,8 +87,9 @@ void HeatpumpIRClimate::setup() {
87
87
  this->publish_state();
88
88
  });
89
89
  this->current_temperature = this->sensor_->state;
90
- } else
90
+ } else {
91
91
  this->current_temperature = NAN;
92
+ }
92
93
  }
93
94
 
94
95
  void HeatpumpIRClimate::transmit_state() {
@@ -17,6 +17,7 @@ from .gpio import host_pin_to_code # noqa
17
17
 
18
18
  CODEOWNERS = ["@esphome/core", "@clydebarrow"]
19
19
  AUTO_LOAD = ["network", "preferences"]
20
+ IS_TARGET_PLATFORM = True
20
21
 
21
22
 
22
23
  def set_core_data(config):
@@ -21,6 +21,7 @@ class HostGPIOPin : public InternalGPIOPin {
21
21
  void detach_interrupt() const override;
22
22
  ISRInternalGPIOPin to_isr() const override;
23
23
  uint8_t get_pin() const override { return pin_; }
24
+ gpio::Flags get_flags() const override { return flags_; }
24
25
  bool is_inverted() const override { return inverted_; }
25
26
 
26
27
  protected:
@@ -18,8 +18,8 @@ namespace esphome {
18
18
  namespace http_request {
19
19
 
20
20
  struct Header {
21
- const char *name;
22
- const char *value;
21
+ std::string name;
22
+ std::string value;
23
23
  };
24
24
 
25
25
  // Some common HTTP status codes
@@ -96,7 +96,7 @@ std::shared_ptr<HttpContainer> HttpRequestArduino::start(std::string url, std::s
96
96
  container->client_.setUserAgent(this->useragent_);
97
97
  }
98
98
  for (const auto &header : headers) {
99
- container->client_.addHeader(header.name, header.value, false, true);
99
+ container->client_.addHeader(header.name.c_str(), header.value.c_str(), false, true);
100
100
  }
101
101
 
102
102
  // returned needed headers must be collected before the requests
@@ -84,7 +84,7 @@ std::shared_ptr<HttpContainer> HttpRequestIDF::start(std::string url, std::strin
84
84
  container->set_secure(secure);
85
85
 
86
86
  for (const auto &header : headers) {
87
- esp_http_client_set_header(client, header.name, header.value);
87
+ esp_http_client_set_header(client, header.name.c_str(), header.value.c_str());
88
88
  }
89
89
 
90
90
  const int body_len = body.length();
@@ -39,6 +39,10 @@ void IDFI2CBus::setup() {
39
39
  conf.scl_io_num = scl_pin_;
40
40
  conf.scl_pullup_en = scl_pullup_enabled_;
41
41
  conf.master.clk_speed = frequency_;
42
+ #ifdef USE_ESP32_VARIANT_ESP32S2
43
+ // workaround for https://github.com/esphome/issues/issues/6718
44
+ conf.clk_flags = I2C_SCLK_SRC_FLAG_AWARE_DFS;
45
+ #endif
42
46
  esp_err_t err = i2c_param_config(port_, &conf);
43
47
  if (err != ESP_OK) {
44
48
  ESP_LOGW(TAG, "i2c_param_config failed: %s", esp_err_to_name(err));
@@ -25,11 +25,13 @@ void I2SAudioMicrophone::setup() {
25
25
  }
26
26
  } else
27
27
  #endif
28
- if (this->pdm_) {
29
- if (this->parent_->get_port() != I2S_NUM_0) {
30
- ESP_LOGE(TAG, "PDM only works on I2S0!");
31
- this->mark_failed();
32
- return;
28
+ {
29
+ if (this->pdm_) {
30
+ if (this->parent_->get_port() != I2S_NUM_0) {
31
+ ESP_LOGE(TAG, "PDM only works on I2S0!");
32
+ this->mark_failed();
33
+ return;
34
+ }
33
35
  }
34
36
  }
35
37
  }
@@ -1,13 +1,25 @@
1
1
  from esphome import pins
2
2
  import esphome.codegen as cg
3
- from esphome.components import esp32, speaker
3
+ from esphome.components import audio, esp32, speaker
4
4
  import esphome.config_validation as cv
5
- from esphome.const import CONF_CHANNEL, CONF_ID, CONF_MODE, CONF_TIMEOUT
5
+ from esphome.const import (
6
+ CONF_BITS_PER_SAMPLE,
7
+ CONF_BUFFER_DURATION,
8
+ CONF_CHANNEL,
9
+ CONF_ID,
10
+ CONF_MODE,
11
+ CONF_NEVER,
12
+ CONF_NUM_CHANNELS,
13
+ CONF_SAMPLE_RATE,
14
+ CONF_TIMEOUT,
15
+ )
6
16
 
7
17
  from .. import (
8
18
  CONF_I2S_DOUT_PIN,
19
+ CONF_I2S_MODE,
9
20
  CONF_LEFT,
10
21
  CONF_MONO,
22
+ CONF_PRIMARY,
11
23
  CONF_RIGHT,
12
24
  CONF_STEREO,
13
25
  I2SAudioOut,
@@ -24,10 +36,8 @@ I2SAudioSpeaker = i2s_audio_ns.class_(
24
36
  "I2SAudioSpeaker", cg.Component, speaker.Speaker, I2SAudioOut
25
37
  )
26
38
 
27
- CONF_BUFFER_DURATION = "buffer_duration"
28
39
  CONF_DAC_TYPE = "dac_type"
29
40
  CONF_I2S_COMM_FMT = "i2s_comm_fmt"
30
- CONF_NEVER = "never"
31
41
 
32
42
  i2s_dac_mode_t = cg.global_ns.enum("i2s_dac_mode_t")
33
43
  INTERNAL_DAC_OPTIONS = {
@@ -53,7 +63,41 @@ I2C_COMM_FMT_OPTIONS = {
53
63
  NO_INTERNAL_DAC_VARIANTS = [esp32.const.VARIANT_ESP32S2]
54
64
 
55
65
 
56
- def validate_esp32_variant(config):
66
+ def _set_num_channels_from_config(config):
67
+ if config[CONF_CHANNEL] in (CONF_MONO, CONF_LEFT, CONF_RIGHT):
68
+ config[CONF_NUM_CHANNELS] = 1
69
+ else:
70
+ config[CONF_NUM_CHANNELS] = 2
71
+
72
+ return config
73
+
74
+
75
+ def _set_stream_limits(config):
76
+ if config[CONF_I2S_MODE] == CONF_PRIMARY:
77
+ # Primary mode has modifiable stream settings
78
+ audio.set_stream_limits(
79
+ min_bits_per_sample=8,
80
+ max_bits_per_sample=32,
81
+ min_channels=1,
82
+ max_channels=2,
83
+ min_sample_rate=16000,
84
+ max_sample_rate=48000,
85
+ )(config)
86
+ else:
87
+ # Secondary mode has unmodifiable max bits per sample and min/max sample rates
88
+ audio.set_stream_limits(
89
+ min_bits_per_sample=8,
90
+ max_bits_per_sample=config.get(CONF_BITS_PER_SAMPLE),
91
+ min_channels=1,
92
+ max_channels=2,
93
+ min_sample_rate=config.get(CONF_SAMPLE_RATE),
94
+ max_sample_rate=config.get(CONF_SAMPLE_RATE),
95
+ )
96
+
97
+ return config
98
+
99
+
100
+ def _validate_esp32_variant(config):
57
101
  if config[CONF_DAC_TYPE] != "internal":
58
102
  return config
59
103
  variant = esp32.get_esp32_variant()
@@ -85,6 +129,7 @@ BASE_SCHEMA = (
85
129
  .extend(cv.COMPONENT_SCHEMA)
86
130
  )
87
131
 
132
+
88
133
  CONFIG_SCHEMA = cv.All(
89
134
  cv.typed_schema(
90
135
  {
@@ -106,7 +151,9 @@ CONFIG_SCHEMA = cv.All(
106
151
  },
107
152
  key=CONF_DAC_TYPE,
108
153
  ),
109
- validate_esp32_variant,
154
+ _validate_esp32_variant,
155
+ _set_num_channels_from_config,
156
+ _set_stream_limits,
110
157
  )
111
158
 
112
159
 
@@ -148,9 +148,11 @@ void I2SAudioSpeaker::loop() {
148
148
  this->status_set_error("Failed to adjust I2S bus to match the incoming audio");
149
149
  ESP_LOGE(TAG,
150
150
  "Incompatible audio format: sample rate = %" PRIu32 ", channels = %" PRIu8 ", bits per sample = %" PRIu8,
151
- this->audio_stream_info_.sample_rate, this->audio_stream_info_.channels,
152
- this->audio_stream_info_.bits_per_sample);
151
+ this->audio_stream_info_.get_sample_rate(), this->audio_stream_info_.get_channels(),
152
+ this->audio_stream_info_.get_bits_per_sample());
153
153
  }
154
+
155
+ xEventGroupClearBits(this->event_group_, ALL_ERR_ESP_BITS);
154
156
  }
155
157
 
156
158
  void I2SAudioSpeaker::set_volume(float volume) {
@@ -201,6 +203,12 @@ size_t I2SAudioSpeaker::play(const uint8_t *data, size_t length, TickType_t tick
201
203
  this->start();
202
204
  }
203
205
 
206
+ if ((this->state_ != speaker::STATE_RUNNING) || (this->audio_ring_buffer_.use_count() == 1)) {
207
+ // Unable to write data to a running speaker, so delay the max amount of time so it can get ready
208
+ vTaskDelay(ticks_to_wait);
209
+ ticks_to_wait = 0;
210
+ }
211
+
204
212
  size_t bytes_written = 0;
205
213
  if ((this->state_ == speaker::STATE_RUNNING) && (this->audio_ring_buffer_.use_count() == 1)) {
206
214
  // Only one owner of the ring buffer (the speaker task), so the ring buffer is allocated and no other components are
@@ -223,6 +231,8 @@ bool I2SAudioSpeaker::has_buffered_data() const {
223
231
 
224
232
  void I2SAudioSpeaker::speaker_task(void *params) {
225
233
  I2SAudioSpeaker *this_speaker = (I2SAudioSpeaker *) params;
234
+ this_speaker->task_created_ = true;
235
+
226
236
  uint32_t event_group_bits =
227
237
  xEventGroupWaitBits(this_speaker->event_group_,
228
238
  SpeakerEventGroupBits::COMMAND_START | SpeakerEventGroupBits::COMMAND_STOP |
@@ -240,19 +250,20 @@ void I2SAudioSpeaker::speaker_task(void *params) {
240
250
 
241
251
  audio::AudioStreamInfo audio_stream_info = this_speaker->audio_stream_info_;
242
252
 
243
- const uint32_t bytes_per_ms =
244
- audio_stream_info.channels * audio_stream_info.get_bytes_per_sample() * audio_stream_info.sample_rate / 1000;
253
+ const uint32_t dma_buffers_duration_ms = DMA_BUFFER_DURATION_MS * DMA_BUFFERS_COUNT;
254
+ // Ensure ring buffer duration is at least the duration of all DMA buffers
255
+ const uint32_t ring_buffer_duration = std::max(dma_buffers_duration_ms, this_speaker->buffer_duration_ms_);
245
256
 
246
- const size_t dma_buffers_size = DMA_BUFFERS_COUNT * DMA_BUFFER_DURATION_MS * bytes_per_ms;
257
+ // The DMA buffers may have more bits per sample, so calculate buffer sizes based in the input audio stream info
258
+ const size_t data_buffer_size = audio_stream_info.ms_to_bytes(dma_buffers_duration_ms);
259
+ const size_t ring_buffer_size = audio_stream_info.ms_to_bytes(ring_buffer_duration);
247
260
 
248
- // Ensure ring buffer is at least as large as the total size of the DMA buffers
249
- const size_t ring_buffer_size =
250
- std::max((uint32_t) dma_buffers_size, this_speaker->buffer_duration_ms_ * bytes_per_ms);
261
+ const size_t single_dma_buffer_input_size = data_buffer_size / DMA_BUFFERS_COUNT;
251
262
 
252
- if (this_speaker->send_esp_err_to_event_group_(this_speaker->allocate_buffers_(dma_buffers_size, ring_buffer_size))) {
263
+ if (this_speaker->send_esp_err_to_event_group_(this_speaker->allocate_buffers_(data_buffer_size, ring_buffer_size))) {
253
264
  // Failed to allocate buffers
254
265
  xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::ERR_ESP_NO_MEM);
255
- this_speaker->delete_task_(dma_buffers_size);
266
+ this_speaker->delete_task_(data_buffer_size);
256
267
  }
257
268
 
258
269
  if (!this_speaker->send_esp_err_to_event_group_(this_speaker->start_i2s_driver_(audio_stream_info))) {
@@ -262,20 +273,25 @@ void I2SAudioSpeaker::speaker_task(void *params) {
262
273
  uint32_t last_data_received_time = millis();
263
274
  bool tx_dma_underflow = false;
264
275
 
265
- while (!this_speaker->timeout_.has_value() ||
276
+ this_speaker->accumulated_frames_written_ = 0;
277
+
278
+ // Keep looping if paused, there is no timeout configured, or data was received more recently than the configured
279
+ // timeout
280
+ while (this_speaker->pause_state_ || !this_speaker->timeout_.has_value() ||
266
281
  (millis() - last_data_received_time) <= this_speaker->timeout_.value()) {
267
282
  event_group_bits = xEventGroupGetBits(this_speaker->event_group_);
268
283
 
269
284
  if (event_group_bits & SpeakerEventGroupBits::COMMAND_STOP) {
285
+ xEventGroupClearBits(this_speaker->event_group_, SpeakerEventGroupBits::COMMAND_STOP);
270
286
  break;
271
287
  }
272
288
  if (event_group_bits & SpeakerEventGroupBits::COMMAND_STOP_GRACEFULLY) {
289
+ xEventGroupClearBits(this_speaker->event_group_, SpeakerEventGroupBits::COMMAND_STOP_GRACEFULLY);
273
290
  stop_gracefully = true;
274
291
  }
275
292
 
276
293
  if (this_speaker->audio_stream_info_ != audio_stream_info) {
277
- // Audio stream info has changed, stop the speaker task so it will restart with the proper settings.
278
-
294
+ // Audio stream info changed, stop the speaker task so it will restart with the proper settings.
279
295
  break;
280
296
  }
281
297
 
@@ -286,33 +302,64 @@ void I2SAudioSpeaker::speaker_task(void *params) {
286
302
  }
287
303
  }
288
304
 
289
- size_t bytes_to_read = dma_buffers_size;
290
- size_t bytes_read = this_speaker->audio_ring_buffer_->read((void *) this_speaker->data_buffer_, bytes_to_read,
305
+ if (this_speaker->pause_state_) {
306
+ // Pause state is accessed atomically, so thread safe
307
+ // Delay so the task can yields, then skip transferring audio data
308
+ delay(TASK_DELAY_MS);
309
+ continue;
310
+ }
311
+
312
+ size_t bytes_read = this_speaker->audio_ring_buffer_->read((void *) this_speaker->data_buffer_, data_buffer_size,
291
313
  pdMS_TO_TICKS(TASK_DELAY_MS));
292
314
 
293
315
  if (bytes_read > 0) {
294
- size_t bytes_written = 0;
295
-
296
- if ((audio_stream_info.bits_per_sample == 16) && (this_speaker->q15_volume_factor_ < INT16_MAX)) {
316
+ if ((audio_stream_info.get_bits_per_sample() == 16) && (this_speaker->q15_volume_factor_ < INT16_MAX)) {
297
317
  // Scale samples by the volume factor in place
298
318
  q15_multiplication((int16_t *) this_speaker->data_buffer_, (int16_t *) this_speaker->data_buffer_,
299
319
  bytes_read / sizeof(int16_t), this_speaker->q15_volume_factor_);
300
320
  }
301
321
 
302
- if (audio_stream_info.bits_per_sample == (uint8_t) this_speaker->bits_per_sample_) {
303
- i2s_write(this_speaker->parent_->get_port(), this_speaker->data_buffer_, bytes_read, &bytes_written,
304
- portMAX_DELAY);
305
- } else if (audio_stream_info.bits_per_sample < (uint8_t) this_speaker->bits_per_sample_) {
306
- i2s_write_expand(this_speaker->parent_->get_port(), this_speaker->data_buffer_, bytes_read,
307
- audio_stream_info.bits_per_sample, this_speaker->bits_per_sample_, &bytes_written,
308
- portMAX_DELAY);
309
- }
322
+ // Write the audio data to a single DMA buffer at a time to reduce latency for the audio duration played
323
+ // callback.
324
+ const uint32_t batches = (bytes_read + single_dma_buffer_input_size - 1) / single_dma_buffer_input_size;
325
+
326
+ for (uint32_t i = 0; i < batches; ++i) {
327
+ size_t bytes_written = 0;
328
+ size_t bytes_to_write = std::min(single_dma_buffer_input_size, bytes_read);
310
329
 
311
- if (bytes_written != bytes_read) {
312
- xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::ERR_ESP_INVALID_SIZE);
330
+ if (audio_stream_info.get_bits_per_sample() == (uint8_t) this_speaker->bits_per_sample_) {
331
+ i2s_write(this_speaker->parent_->get_port(), this_speaker->data_buffer_ + i * single_dma_buffer_input_size,
332
+ bytes_to_write, &bytes_written, pdMS_TO_TICKS(DMA_BUFFER_DURATION_MS * 5));
333
+ } else if (audio_stream_info.get_bits_per_sample() < (uint8_t) this_speaker->bits_per_sample_) {
334
+ i2s_write_expand(this_speaker->parent_->get_port(),
335
+ this_speaker->data_buffer_ + i * single_dma_buffer_input_size, bytes_to_write,
336
+ audio_stream_info.get_bits_per_sample(), this_speaker->bits_per_sample_, &bytes_written,
337
+ pdMS_TO_TICKS(DMA_BUFFER_DURATION_MS * 5));
338
+ }
339
+
340
+ uint32_t write_timestamp = micros();
341
+
342
+ if (bytes_written != bytes_to_write) {
343
+ xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::ERR_ESP_INVALID_SIZE);
344
+ }
345
+
346
+ bytes_read -= bytes_written;
347
+
348
+ this_speaker->accumulated_frames_written_ += audio_stream_info.bytes_to_frames(bytes_written);
349
+ const uint32_t new_playback_ms =
350
+ audio_stream_info.frames_to_milliseconds_with_remainder(&this_speaker->accumulated_frames_written_);
351
+ const uint32_t remainder_us =
352
+ audio_stream_info.frames_to_microseconds(this_speaker->accumulated_frames_written_);
353
+
354
+ uint32_t pending_frames =
355
+ audio_stream_info.bytes_to_frames(bytes_read + this_speaker->audio_ring_buffer_->available());
356
+ const uint32_t pending_ms = audio_stream_info.frames_to_milliseconds_with_remainder(&pending_frames);
357
+
358
+ this_speaker->audio_output_callback_(new_playback_ms, remainder_us, pending_ms, write_timestamp);
359
+
360
+ tx_dma_underflow = false;
361
+ last_data_received_time = millis();
313
362
  }
314
- tx_dma_underflow = false;
315
- last_data_received_time = millis();
316
363
  } else {
317
364
  // No data received
318
365
  if (stop_gracefully && tx_dma_underflow) {
@@ -328,7 +375,7 @@ void I2SAudioSpeaker::speaker_task(void *params) {
328
375
  this_speaker->parent_->unlock();
329
376
  }
330
377
 
331
- this_speaker->delete_task_(dma_buffers_size);
378
+ this_speaker->delete_task_(data_buffer_size);
332
379
  }
333
380
 
334
381
  void I2SAudioSpeaker::start() {
@@ -337,16 +384,15 @@ void I2SAudioSpeaker::start() {
337
384
  if ((this->state_ == speaker::STATE_STARTING) || (this->state_ == speaker::STATE_RUNNING))
338
385
  return;
339
386
 
340
- if (this->speaker_task_handle_ == nullptr) {
387
+ if (!this->task_created_ && (this->speaker_task_handle_ == nullptr)) {
341
388
  xTaskCreate(I2SAudioSpeaker::speaker_task, "speaker_task", TASK_STACK_SIZE, (void *) this, TASK_PRIORITY,
342
389
  &this->speaker_task_handle_);
343
- }
344
390
 
345
- if (this->speaker_task_handle_ != nullptr) {
346
- xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::COMMAND_START);
347
- this->task_created_ = true;
348
- } else {
349
- xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START);
391
+ if (this->speaker_task_handle_ != nullptr) {
392
+ xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::COMMAND_START);
393
+ } else {
394
+ xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START);
395
+ }
350
396
  }
351
397
  }
352
398
 
@@ -416,12 +462,12 @@ esp_err_t I2SAudioSpeaker::allocate_buffers_(size_t data_buffer_size, size_t rin
416
462
  }
417
463
 
418
464
  esp_err_t I2SAudioSpeaker::start_i2s_driver_(audio::AudioStreamInfo &audio_stream_info) {
419
- if ((this->i2s_mode_ & I2S_MODE_SLAVE) && (this->sample_rate_ != audio_stream_info.sample_rate)) { // NOLINT
420
- // Can't reconfigure I2S bus, so the sample rate must match the configured value
465
+ if ((this->i2s_mode_ & I2S_MODE_SLAVE) && (this->sample_rate_ != audio_stream_info.get_sample_rate())) { // NOLINT
466
+ // Can't reconfigure I2S bus, so the sample rate must match the configured value
421
467
  return ESP_ERR_NOT_SUPPORTED;
422
468
  }
423
469
 
424
- if ((i2s_bits_per_sample_t) audio_stream_info.bits_per_sample > this->bits_per_sample_) {
470
+ if ((i2s_bits_per_sample_t) audio_stream_info.get_bits_per_sample() > this->bits_per_sample_) {
425
471
  // Currently can't handle the case when the incoming audio has more bits per sample than the configured value
426
472
  return ESP_ERR_NOT_SUPPORTED;
427
473
  }
@@ -432,21 +478,21 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver_(audio::AudioStreamInfo &audio_strea
432
478
 
433
479
  i2s_channel_fmt_t channel = this->channel_;
434
480
 
435
- if (audio_stream_info.channels == 1) {
481
+ if (audio_stream_info.get_channels() == 1) {
436
482
  if (this->channel_ == I2S_CHANNEL_FMT_ONLY_LEFT) {
437
483
  channel = I2S_CHANNEL_FMT_ONLY_LEFT;
438
484
  } else {
439
485
  channel = I2S_CHANNEL_FMT_ONLY_RIGHT;
440
486
  }
441
- } else if (audio_stream_info.channels == 2) {
487
+ } else if (audio_stream_info.get_channels() == 2) {
442
488
  channel = I2S_CHANNEL_FMT_RIGHT_LEFT;
443
489
  }
444
490
 
445
- int dma_buffer_length = DMA_BUFFER_DURATION_MS * this->sample_rate_ / 1000;
491
+ int dma_buffer_length = audio_stream_info.ms_to_frames(DMA_BUFFER_DURATION_MS);
446
492
 
447
493
  i2s_driver_config_t config = {
448
494
  .mode = (i2s_mode_t) (this->i2s_mode_ | I2S_MODE_TX),
449
- .sample_rate = audio_stream_info.sample_rate,
495
+ .sample_rate = audio_stream_info.get_sample_rate(),
450
496
  .bits_per_sample = this->bits_per_sample_,
451
497
  .channel_format = channel,
452
498
  .communication_format = this->i2s_comm_fmt_,
@@ -504,7 +550,7 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver_(audio::AudioStreamInfo &audio_strea
504
550
  }
505
551
 
506
552
  void I2SAudioSpeaker::delete_task_(size_t buffer_size) {
507
- this->audio_ring_buffer_.reset(); // Releases onwership of the shared_ptr
553
+ this->audio_ring_buffer_.reset(); // Releases ownership of the shared_ptr
508
554
 
509
555
  if (this->data_buffer_ != nullptr) {
510
556
  ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
@@ -40,6 +40,9 @@ class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Comp
40
40
  void stop() override;
41
41
  void finish() override;
42
42
 
43
+ void set_pause_state(bool pause_state) override { this->pause_state_ = pause_state; }
44
+ bool get_pause_state() const override { return this->pause_state_; }
45
+
43
46
  /// @brief Plays the provided audio data.
44
47
  /// Starts the speaker task, if necessary. Writes the audio data to the ring buffer.
45
48
  /// @param data Audio data in the format set by the parent speaker classes ``set_audio_stream_info`` method.
@@ -121,13 +124,18 @@ class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Comp
121
124
  uint8_t dout_pin_;
122
125
 
123
126
  bool task_created_{false};
127
+ bool pause_state_{false};
124
128
 
125
129
  int16_t q15_volume_factor_{INT16_MAX};
126
130
 
131
+ size_t bytes_written_{0};
132
+
127
133
  #if SOC_I2S_SUPPORTS_DAC
128
134
  i2s_dac_mode_t internal_dac_mode_{I2S_DAC_CHANNEL_DISABLE};
129
135
  #endif
130
136
  i2s_comm_format_t i2s_comm_fmt_;
137
+
138
+ uint32_t accumulated_frames_written_{0};
131
139
  };
132
140
 
133
141
  } // namespace i2s_audio
@@ -1,9 +1,12 @@
1
+ import logging
2
+
1
3
  from esphome import core, pins
2
4
  import esphome.codegen as cg
3
5
  from esphome.components import display, spi
4
6
  from esphome.components.display import validate_rotation
5
7
  import esphome.config_validation as cv
6
8
  from esphome.const import (
9
+ CONF_AUTO_CLEAR_ENABLED,
7
10
  CONF_COLOR_ORDER,
8
11
  CONF_COLOR_PALETTE,
9
12
  CONF_DC_PIN,
@@ -27,17 +30,12 @@ from esphome.const import (
27
30
  CONF_WIDTH,
28
31
  )
29
32
  from esphome.core import CORE, HexInt
33
+ from esphome.final_validate import full_config
30
34
 
31
35
  DEPENDENCIES = ["spi"]
32
36
 
33
-
34
- def AUTO_LOAD():
35
- if CORE.is_esp32:
36
- return ["psram"]
37
- return []
38
-
39
-
40
37
  CODEOWNERS = ["@nielsnl68", "@clydebarrow"]
38
+ LOGGER = logging.getLogger(__name__)
41
39
 
42
40
  ili9xxx_ns = cg.esphome_ns.namespace("ili9xxx")
43
41
  ILI9XXXDisplay = ili9xxx_ns.class_(
@@ -84,7 +82,7 @@ COLOR_ORDERS = {
84
82
  "BGR": ColorOrder.COLOR_ORDER_BGR,
85
83
  }
86
84
 
87
- COLOR_PALETTE = cv.one_of("NONE", "GRAYSCALE", "IMAGE_ADAPTIVE")
85
+ COLOR_PALETTE = cv.one_of("NONE", "GRAYSCALE", "IMAGE_ADAPTIVE", "8BIT", upper=True)
88
86
 
89
87
  CONF_LED_PIN = "led_pin"
90
88
  CONF_COLOR_PALETTE_IMAGES = "color_palette_images"
@@ -195,9 +193,27 @@ CONFIG_SCHEMA = cv.All(
195
193
  _validate,
196
194
  )
197
195
 
198
- FINAL_VALIDATE_SCHEMA = spi.final_validate_device_schema(
199
- "ili9xxx", require_miso=False, require_mosi=True
200
- )
196
+
197
+ def final_validate(config):
198
+ global_config = full_config.get()
199
+ # Ideally would calculate buffer size here, but that info is not available on the Python side
200
+ needs_buffer = (
201
+ CONF_LAMBDA in config or CONF_PAGES in config or config[CONF_AUTO_CLEAR_ENABLED]
202
+ )
203
+ if (
204
+ CORE.is_esp32
205
+ and config[CONF_COLOR_PALETTE] == "NONE"
206
+ and "psram" not in global_config
207
+ and needs_buffer
208
+ ):
209
+ LOGGER.info("Consider enabling PSRAM if available for the display buffer")
210
+
211
+ return spi.final_validate_device_schema(
212
+ "ili9xxx", require_miso=False, require_mosi=True
213
+ )
214
+
215
+
216
+ FINAL_VALIDATE_SCHEMA = final_validate
201
217
 
202
218
 
203
219
  async def to_code(config):
@@ -283,6 +299,8 @@ async def to_code(config):
283
299
  palette = converted.getpalette()
284
300
  assert len(palette) == 256 * 3
285
301
  rhs = palette
302
+ elif config[CONF_COLOR_PALETTE] == "8BIT":
303
+ cg.add(var.set_buffer_color_mode(ILI9XXXColorMode.BITS_8))
286
304
  else:
287
305
  cg.add(var.set_buffer_color_mode(ILI9XXXColorMode.BITS_16))
288
306