esphome 2024.12.3__py3-none-any.whl → 2025.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (366) 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/api/client.py +8 -3
  21. esphome/components/audio/__init__.py +112 -0
  22. esphome/components/audio/audio.cpp +67 -0
  23. esphome/components/audio/audio.h +125 -7
  24. esphome/components/audio/audio_decoder.cpp +361 -0
  25. esphome/components/audio/audio_decoder.h +135 -0
  26. esphome/components/audio/audio_reader.cpp +308 -0
  27. esphome/components/audio/audio_reader.h +85 -0
  28. esphome/components/audio/audio_resampler.cpp +159 -0
  29. esphome/components/audio/audio_resampler.h +101 -0
  30. esphome/components/audio/audio_transfer_buffer.cpp +165 -0
  31. esphome/components/audio/audio_transfer_buffer.h +139 -0
  32. esphome/components/audio_adc/__init__.py +41 -0
  33. esphome/components/audio_adc/audio_adc.h +17 -0
  34. esphome/components/audio_adc/automation.h +23 -0
  35. esphome/components/bk72xx/__init__.py +1 -0
  36. esphome/components/ble_client/ble_client.cpp +1 -2
  37. esphome/components/ble_client/sensor/__init__.py +1 -1
  38. esphome/components/ble_client/text_sensor/__init__.py +1 -1
  39. esphome/components/bluetooth_proxy/bluetooth_connection.cpp +5 -0
  40. esphome/components/bluetooth_proxy/bluetooth_connection.h +1 -0
  41. esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +5 -0
  42. esphome/components/ch422g/ch422g.h +2 -0
  43. esphome/components/climate/__init__.py +1 -1
  44. esphome/components/climate_ir/climate_ir.cpp +2 -1
  45. esphome/components/coolix/coolix.cpp +2 -1
  46. esphome/components/cse7766/cse7766.cpp +8 -16
  47. esphome/components/custom/__init__.py +0 -3
  48. esphome/components/custom/binary_sensor/__init__.py +2 -28
  49. esphome/components/custom/climate/__init__.py +2 -27
  50. esphome/components/custom/cover/__init__.py +2 -27
  51. esphome/components/custom/light/__init__.py +2 -27
  52. esphome/components/custom/output/__init__.py +2 -58
  53. esphome/components/custom/sensor/__init__.py +2 -24
  54. esphome/components/custom/switch/__init__.py +2 -24
  55. esphome/components/custom/text_sensor/__init__.py +2 -29
  56. esphome/components/custom_component/__init__.py +3 -27
  57. esphome/components/daly_bms/daly_bms.cpp +6 -0
  58. esphome/components/daly_bms/daly_bms.h +2 -0
  59. esphome/components/daly_bms/sensor.py +6 -0
  60. esphome/components/debug/debug_component.cpp +4 -0
  61. esphome/components/debug/debug_component.h +14 -0
  62. esphome/components/debug/debug_esp32.cpp +154 -74
  63. esphome/components/dfplayer/dfplayer.cpp +15 -2
  64. esphome/components/dfrobot_sen0395/dfrobot_sen0395.cpp +2 -1
  65. esphome/components/dht/dht.cpp +4 -2
  66. esphome/components/dht/sensor.py +1 -1
  67. esphome/components/display/__init__.py +18 -5
  68. esphome/components/display/display.cpp +16 -3
  69. esphome/components/display/rect.cpp +2 -1
  70. esphome/components/es7210/__init__.py +0 -0
  71. esphome/components/es7210/audio_adc.py +51 -0
  72. esphome/components/es7210/es7210.cpp +228 -0
  73. esphome/components/es7210/es7210.h +62 -0
  74. esphome/components/es7210/es7210_const.h +129 -0
  75. esphome/components/es7243e/__init__.py +0 -0
  76. esphome/components/es7243e/audio_adc.py +34 -0
  77. esphome/components/es7243e/es7243e.cpp +125 -0
  78. esphome/components/es7243e/es7243e.h +37 -0
  79. esphome/components/es7243e/es7243e_const.h +54 -0
  80. esphome/components/es8156/__init__.py +0 -0
  81. esphome/components/es8156/audio_dac.py +27 -0
  82. esphome/components/es8156/es8156.cpp +87 -0
  83. esphome/components/es8156/es8156.h +51 -0
  84. esphome/components/es8156/es8156_const.h +68 -0
  85. esphome/components/es8311/audio_dac.py +1 -2
  86. esphome/components/esp32/__init__.py +1 -0
  87. esphome/components/esp32/core.cpp +5 -1
  88. esphome/components/esp32/gpio.h +2 -0
  89. esphome/components/esp32_ble/__init__.py +39 -0
  90. esphome/components/esp32_ble/queue.h +4 -4
  91. esphome/components/esp32_ble_client/ble_client_base.cpp +46 -0
  92. esphome/components/esp32_ble_client/ble_client_base.h +2 -0
  93. esphome/components/esp32_ble_server/__init__.py +582 -12
  94. esphome/components/esp32_ble_server/ble_characteristic.cpp +48 -60
  95. esphome/components/esp32_ble_server/ble_characteristic.h +24 -17
  96. esphome/components/esp32_ble_server/ble_descriptor.cpp +21 -9
  97. esphome/components/esp32_ble_server/ble_descriptor.h +17 -6
  98. esphome/components/esp32_ble_server/ble_server.cpp +62 -67
  99. esphome/components/esp32_ble_server/ble_server.h +28 -32
  100. esphome/components/esp32_ble_server/ble_server_automations.cpp +77 -0
  101. esphome/components/esp32_ble_server/ble_server_automations.h +115 -0
  102. esphome/components/esp32_ble_server/ble_service.cpp +17 -15
  103. esphome/components/esp32_ble_server/ble_service.h +10 -14
  104. esphome/components/esp32_ble_tracker/__init__.py +6 -39
  105. esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +33 -10
  106. esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +8 -4
  107. esphome/components/esp32_dac/esp32_dac.cpp +16 -7
  108. esphome/components/esp32_dac/esp32_dac.h +8 -0
  109. esphome/components/esp32_dac/output.py +16 -4
  110. esphome/components/esp32_improv/__init__.py +2 -8
  111. esphome/components/esp32_improv/esp32_improv_component.cpp +21 -20
  112. esphome/components/esp32_improv/esp32_improv_component.h +3 -4
  113. esphome/components/esp32_rmt/__init__.py +28 -3
  114. esphome/components/esp32_rmt_led_strip/led_strip.cpp +73 -6
  115. esphome/components/esp32_rmt_led_strip/led_strip.h +21 -3
  116. esphome/components/esp32_rmt_led_strip/light.py +72 -7
  117. esphome/components/esp32_touch/esp32_touch.cpp +5 -0
  118. esphome/components/esp8266/__init__.py +1 -0
  119. esphome/components/esp8266/gpio.h +1 -0
  120. esphome/components/ethernet/__init__.py +10 -10
  121. esphome/components/event/event.cpp +4 -2
  122. esphome/components/event/event.h +2 -0
  123. esphome/components/event_emitter/__init__.py +5 -0
  124. esphome/components/event_emitter/event_emitter.cpp +14 -0
  125. esphome/components/event_emitter/event_emitter.h +63 -0
  126. esphome/components/font/__init__.py +1 -1
  127. esphome/components/gcja5/gcja5.cpp +2 -1
  128. esphome/components/graph/graph.cpp +4 -9
  129. esphome/components/haier/haier_base.cpp +2 -1
  130. esphome/components/haier/hon_climate.cpp +2 -1
  131. esphome/components/heatpumpir/heatpumpir.cpp +2 -1
  132. esphome/components/host/__init__.py +1 -0
  133. esphome/components/host/gpio.h +1 -0
  134. esphome/components/http_request/http_request.h +2 -2
  135. esphome/components/http_request/http_request_arduino.cpp +1 -1
  136. esphome/components/http_request/http_request_idf.cpp +1 -1
  137. esphome/components/i2c/i2c_bus_esp_idf.cpp +4 -0
  138. esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +7 -5
  139. esphome/components/i2s_audio/speaker/__init__.py +53 -6
  140. esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +92 -46
  141. esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +8 -0
  142. esphome/components/ili9xxx/display.py +29 -11
  143. esphome/components/ili9xxx/ili9xxx_display.cpp +2 -5
  144. esphome/components/ili9xxx/ili9xxx_display.h +2 -1
  145. esphome/components/image/__init__.py +443 -255
  146. esphome/components/image/image.cpp +115 -61
  147. esphome/components/image/image.h +15 -24
  148. esphome/components/json/json_util.cpp +8 -34
  149. esphome/components/libretiny/__init__.py +1 -0
  150. esphome/components/libretiny/gpio_arduino.h +1 -0
  151. esphome/components/light/light_color_values.h +1 -1
  152. esphome/components/logger/__init__.py +45 -9
  153. esphome/components/logger/logger.cpp +16 -14
  154. esphome/components/logger/logger.h +11 -7
  155. esphome/components/logger/select/__init__.py +29 -0
  156. esphome/components/logger/select/logger_level_select.cpp +27 -0
  157. esphome/components/logger/select/logger_level_select.h +15 -0
  158. esphome/components/lvgl/__init__.py +96 -73
  159. esphome/components/lvgl/automation.py +39 -7
  160. esphome/components/lvgl/defines.py +8 -2
  161. esphome/components/lvgl/lvgl_esphome.cpp +8 -15
  162. esphome/components/lvgl/lvgl_esphome.h +20 -5
  163. esphome/components/lvgl/schemas.py +25 -14
  164. esphome/components/lvgl/trigger.py +27 -3
  165. esphome/components/lvgl/widgets/dropdown.py +1 -1
  166. esphome/components/lvgl/widgets/keyboard.py +8 -1
  167. esphome/components/lvgl/widgets/meter.py +2 -1
  168. esphome/components/lvgl/widgets/msgbox.py +1 -1
  169. esphome/components/lvgl/widgets/obj.py +1 -12
  170. esphome/components/lvgl/widgets/page.py +37 -2
  171. esphome/components/lvgl/widgets/tabview.py +1 -1
  172. esphome/components/max6956/max6956.h +2 -0
  173. esphome/components/mcp23016/mcp23016.h +2 -0
  174. esphome/components/mcp23xxx_base/mcp23xxx_base.h +2 -0
  175. esphome/components/mdns/__init__.py +1 -1
  176. esphome/components/media_player/__init__.py +37 -8
  177. esphome/components/media_player/automation.h +11 -2
  178. esphome/components/media_player/media_player.cpp +8 -0
  179. esphome/components/media_player/media_player.h +8 -4
  180. esphome/components/micronova/switch/micronova_switch.cpp +4 -2
  181. esphome/components/midea/ac_automations.h +3 -1
  182. esphome/components/midea/air_conditioner.cpp +7 -5
  183. esphome/components/midea/air_conditioner.h +1 -1
  184. esphome/components/midea/climate.py +4 -2
  185. esphome/components/midea/ir_transmitter.h +36 -5
  186. esphome/components/mixer/__init__.py +0 -0
  187. esphome/components/mixer/speaker/__init__.py +172 -0
  188. esphome/components/mixer/speaker/automation.h +19 -0
  189. esphome/components/mixer/speaker/mixer_speaker.cpp +624 -0
  190. esphome/components/mixer/speaker/mixer_speaker.h +207 -0
  191. esphome/components/modbus_controller/text_sensor/modbus_textsensor.cpp +7 -13
  192. esphome/components/mpr121/mpr121.h +2 -0
  193. esphome/components/mqtt/__init__.py +1 -1
  194. esphome/components/mqtt/mqtt_client.cpp +7 -1
  195. esphome/components/mqtt/mqtt_client.h +1 -1
  196. esphome/components/mqtt/mqtt_climate.cpp +2 -2
  197. esphome/components/network/ip_address.h +2 -0
  198. esphome/components/nextion/automation.h +17 -0
  199. esphome/components/nextion/display.py +42 -17
  200. esphome/components/nextion/nextion.cpp +4 -10
  201. esphome/components/nextion/nextion.h +89 -82
  202. esphome/components/nextion/nextion_commands.cpp +10 -10
  203. esphome/components/ntc/sensor.py +2 -4
  204. esphome/components/online_image/__init__.py +98 -46
  205. esphome/components/online_image/bmp_image.cpp +101 -0
  206. esphome/components/online_image/bmp_image.h +40 -0
  207. esphome/components/online_image/image_decoder.cpp +31 -2
  208. esphome/components/online_image/image_decoder.h +24 -15
  209. esphome/components/online_image/jpeg_image.cpp +92 -0
  210. esphome/components/online_image/jpeg_image.h +34 -0
  211. esphome/components/online_image/online_image.cpp +118 -58
  212. esphome/components/online_image/online_image.h +39 -9
  213. esphome/components/online_image/png_image.cpp +7 -3
  214. esphome/components/online_image/png_image.h +2 -1
  215. esphome/components/opentherm/__init__.py +73 -7
  216. esphome/components/opentherm/automation.h +25 -0
  217. esphome/components/opentherm/const.py +1 -0
  218. esphome/components/opentherm/generate.py +39 -6
  219. esphome/components/opentherm/hub.cpp +117 -79
  220. esphome/components/opentherm/hub.h +31 -15
  221. esphome/components/opentherm/opentherm.cpp +47 -23
  222. esphome/components/opentherm/opentherm.h +27 -6
  223. esphome/components/opentherm/opentherm_macros.h +11 -0
  224. esphome/components/opentherm/schema.py +78 -1
  225. esphome/components/opentherm/validate.py +7 -2
  226. esphome/components/pca6416a/pca6416a.h +2 -0
  227. esphome/components/pca9554/pca9554.h +2 -0
  228. esphome/components/pcf8574/pcf8574.h +2 -0
  229. esphome/components/preferences/__init__.py +2 -4
  230. esphome/components/preferences/syncer.h +10 -3
  231. esphome/components/prometheus/prometheus_handler.cpp +313 -0
  232. esphome/components/prometheus/prometheus_handler.h +48 -7
  233. esphome/components/psram/psram.cpp +8 -1
  234. esphome/components/pulse_counter/pulse_counter_sensor.cpp +14 -9
  235. esphome/components/pulse_counter/pulse_counter_sensor.h +4 -4
  236. esphome/components/pulse_meter/pulse_meter_sensor.cpp +2 -0
  237. esphome/components/qspi_dbi/__init__.py +3 -0
  238. esphome/components/qspi_dbi/display.py +74 -47
  239. esphome/components/qspi_dbi/models.py +245 -2
  240. esphome/components/qspi_dbi/qspi_dbi.cpp +9 -16
  241. esphome/components/qspi_dbi/qspi_dbi.h +2 -2
  242. esphome/components/remote_base/__init__.py +77 -25
  243. esphome/components/remote_base/remote_base.cpp +1 -1
  244. esphome/components/remote_base/remote_base.h +20 -2
  245. esphome/components/remote_base/toto_protocol.cpp +100 -0
  246. esphome/components/remote_base/toto_protocol.h +45 -0
  247. esphome/components/remote_receiver/__init__.py +55 -10
  248. esphome/components/remote_receiver/remote_receiver.h +36 -3
  249. esphome/components/remote_receiver/remote_receiver_esp32.cpp +145 -6
  250. esphome/components/remote_transmitter/__init__.py +62 -4
  251. esphome/components/remote_transmitter/remote_transmitter.h +21 -2
  252. esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +140 -4
  253. esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +3 -3
  254. esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp +3 -3
  255. esphome/components/resampler/__init__.py +0 -0
  256. esphome/components/resampler/speaker/__init__.py +103 -0
  257. esphome/components/resampler/speaker/resampler_speaker.cpp +318 -0
  258. esphome/components/resampler/speaker/resampler_speaker.h +107 -0
  259. esphome/components/resistance/resistance_sensor.h +2 -3
  260. esphome/components/resistance/sensor.py +2 -9
  261. esphome/components/rotary_encoder/rotary_encoder.cpp +8 -4
  262. esphome/components/rp2040/__init__.py +1 -0
  263. esphome/components/rp2040/gpio.h +1 -0
  264. esphome/components/rtl87xx/__init__.py +2 -0
  265. esphome/components/scd30/sensor.py +1 -1
  266. esphome/components/sdl/binary_sensor.py +270 -0
  267. esphome/components/sdl/sdl_esphome.cpp +16 -0
  268. esphome/components/sdl/sdl_esphome.h +9 -0
  269. esphome/components/seeed_mr60bha2/binary_sensor.py +25 -0
  270. esphome/components/seeed_mr60bha2/seeed_mr60bha2.cpp +26 -2
  271. esphome/components/seeed_mr60bha2/seeed_mr60bha2.h +9 -20
  272. esphome/components/seeed_mr60bha2/sensor.py +9 -1
  273. esphome/components/sn74hc165/sn74hc165.h +3 -0
  274. esphome/components/sn74hc595/sn74hc595.h +3 -0
  275. esphome/components/speaker/__init__.py +5 -4
  276. esphome/components/speaker/media_player/__init__.py +458 -0
  277. esphome/components/speaker/media_player/audio_pipeline.cpp +568 -0
  278. esphome/components/speaker/media_player/audio_pipeline.h +159 -0
  279. esphome/components/speaker/media_player/automation.h +26 -0
  280. esphome/components/speaker/media_player/speaker_media_player.cpp +577 -0
  281. esphome/components/speaker/media_player/speaker_media_player.h +160 -0
  282. esphome/components/speaker/speaker.h +20 -0
  283. esphome/components/spi/__init__.py +1 -5
  284. esphome/components/spi/spi.cpp +7 -1
  285. esphome/components/spi/spi.h +21 -2
  286. esphome/components/spi_led_strip/light.py +3 -5
  287. esphome/components/spi_led_strip/spi_led_strip.cpp +67 -0
  288. esphome/components/spi_led_strip/spi_led_strip.h +8 -60
  289. esphome/components/sprinkler/sprinkler.cpp +3 -1
  290. esphome/components/sx1509/sx1509_gpio_pin.h +2 -0
  291. esphome/components/tca9555/tca9555.h +2 -0
  292. esphome/components/toshiba/toshiba.cpp +2 -1
  293. esphome/components/tuya/light/tuya_light.cpp +4 -2
  294. esphome/components/uart/uart_component_esp32_arduino.cpp +2 -2
  295. esphome/components/uart/uart_component_esp_idf.cpp +2 -2
  296. esphome/components/udp/__init__.py +8 -2
  297. esphome/components/udp/udp_component.cpp +25 -56
  298. esphome/components/udp/udp_component.h +3 -0
  299. esphome/components/uponor_smatrix/sensor/__init__.py +14 -4
  300. esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.cpp +5 -0
  301. esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.h +1 -0
  302. esphome/components/uptime/text_sensor/__init__.py +19 -0
  303. esphome/components/uptime/text_sensor/uptime_text_sensor.cpp +63 -0
  304. esphome/components/uptime/text_sensor/uptime_text_sensor.h +25 -0
  305. esphome/components/voice_assistant/voice_assistant.cpp +24 -14
  306. esphome/components/voice_assistant/voice_assistant.h +8 -0
  307. esphome/components/waveshare_epaper/display.py +22 -1
  308. esphome/components/waveshare_epaper/waveshare_213v3.cpp +9 -3
  309. esphome/components/waveshare_epaper/waveshare_epaper.cpp +1155 -44
  310. esphome/components/waveshare_epaper/waveshare_epaper.h +208 -7
  311. esphome/components/web_server/web_server.cpp +28 -6
  312. esphome/components/weikai/weikai.h +2 -0
  313. esphome/components/wifi/__init__.py +6 -6
  314. esphome/components/wifi/wifi_component.cpp +1 -1
  315. esphome/components/wifi/wifi_component_esp32_arduino.cpp +30 -1
  316. esphome/components/wireguard/__init__.py +2 -2
  317. esphome/components/xl9535/xl9535.h +2 -0
  318. esphome/components/xxtea/__init__.py +3 -0
  319. esphome/components/xxtea/xxtea.cpp +46 -0
  320. esphome/components/xxtea/xxtea.h +26 -0
  321. esphome/components/yashima/yashima.cpp +2 -1
  322. esphome/config.py +9 -5
  323. esphome/config_validation.py +55 -17
  324. esphome/const.py +7 -10
  325. esphome/core/__init__.py +6 -13
  326. esphome/core/base_automation.h +1 -0
  327. esphome/core/config.py +59 -72
  328. esphome/core/defines.h +9 -1
  329. esphome/core/gpio.h +7 -0
  330. esphome/core/helpers.cpp +19 -15
  331. esphome/core/helpers.h +57 -8
  332. esphome/core/log.h +9 -7
  333. esphome/cpp_generator.py +2 -2
  334. esphome/dashboard/web_server.py +1 -1
  335. esphome/espota2.py +3 -2
  336. esphome/loader.py +12 -4
  337. esphome/log.py +5 -7
  338. esphome/yaml_util.py +2 -2
  339. {esphome-2024.12.3.dist-info → esphome-2025.2.0.dist-info}/METADATA +14 -9
  340. {esphome-2024.12.3.dist-info → esphome-2025.2.0.dist-info}/RECORD +349 -300
  341. esphome/components/custom/binary_sensor/custom_binary_sensor.cpp +0 -16
  342. esphome/components/custom/binary_sensor/custom_binary_sensor.h +0 -26
  343. esphome/components/custom/climate/custom_climate.h +0 -22
  344. esphome/components/custom/cover/custom_cover.h +0 -21
  345. esphome/components/custom/light/custom_light_output.h +0 -24
  346. esphome/components/custom/output/custom_output.h +0 -37
  347. esphome/components/custom/sensor/custom_sensor.cpp +0 -16
  348. esphome/components/custom/sensor/custom_sensor.h +0 -24
  349. esphome/components/custom/switch/custom_switch.cpp +0 -16
  350. esphome/components/custom/switch/custom_switch.h +0 -24
  351. esphome/components/custom/text_sensor/custom_text_sensor.cpp +0 -16
  352. esphome/components/custom/text_sensor/custom_text_sensor.h +0 -26
  353. esphome/components/custom_component/custom_component.h +0 -28
  354. esphome/components/esp32_ble_server/ble_2901.cpp +0 -18
  355. esphome/components/esp32_ble_server/ble_2901.h +0 -19
  356. esphome/components/resistance_sampler/__init__.py +0 -6
  357. esphome/components/resistance_sampler/resistance_sampler.h +0 -10
  358. esphome/components/uptime/{sensor.py → sensor/__init__.py} +3 -3
  359. /esphome/components/uptime/{uptime_seconds_sensor.cpp → sensor/uptime_seconds_sensor.cpp} +0 -0
  360. /esphome/components/uptime/{uptime_seconds_sensor.h → sensor/uptime_seconds_sensor.h} +0 -0
  361. /esphome/components/uptime/{uptime_timestamp_sensor.cpp → sensor/uptime_timestamp_sensor.cpp} +0 -0
  362. /esphome/components/uptime/{uptime_timestamp_sensor.h → sensor/uptime_timestamp_sensor.h} +0 -0
  363. {esphome-2024.12.3.dist-info → esphome-2025.2.0.dist-info}/LICENSE +0 -0
  364. {esphome-2024.12.3.dist-info → esphome-2025.2.0.dist-info}/WHEEL +0 -0
  365. {esphome-2024.12.3.dist-info → esphome-2025.2.0.dist-info}/entry_points.txt +0 -0
  366. {esphome-2024.12.3.dist-info → esphome-2025.2.0.dist-info}/top_level.txt +0 -0
@@ -1,28 +1,10 @@
1
1
  import logging
2
2
 
3
- from esphome import automation, core
3
+ from esphome import automation
4
4
  import esphome.codegen as cg
5
5
  import esphome.components.image as espImage
6
- from esphome.components.image import (
7
- CONF_USE_TRANSPARENCY,
8
- LOCAL_SCHEMA,
9
- SOURCE_LOCAL,
10
- SOURCE_WEB,
11
- WEB_SCHEMA,
12
- )
13
6
  import esphome.config_validation as cv
14
- from esphome.const import (
15
- CONF_FILE,
16
- CONF_ID,
17
- CONF_PATH,
18
- CONF_RAW_DATA_ID,
19
- CONF_REPEAT,
20
- CONF_RESIZE,
21
- CONF_SOURCE,
22
- CONF_TYPE,
23
- CONF_URL,
24
- )
25
- from esphome.core import CORE, HexInt
7
+ from esphome.const import CONF_ID, CONF_REPEAT
26
8
 
27
9
  _LOGGER = logging.getLogger(__name__)
28
10
 
@@ -30,6 +12,7 @@ AUTO_LOAD = ["image"]
30
12
  CODEOWNERS = ["@syndlex"]
31
13
  DEPENDENCIES = ["display"]
32
14
  MULTI_CONF = True
15
+ MULTI_CONF_NO_DEFAULT = True
33
16
 
34
17
  CONF_LOOP = "loop"
35
18
  CONF_START_FRAME = "start_frame"
@@ -51,86 +34,19 @@ SetFrameAction = animation_ns.class_(
51
34
  "AnimationSetFrameAction", automation.Action, cg.Parented.template(Animation_)
52
35
  )
53
36
 
54
- TYPED_FILE_SCHEMA = cv.typed_schema(
37
+ CONFIG_SCHEMA = espImage.IMAGE_SCHEMA.extend(
55
38
  {
56
- SOURCE_LOCAL: LOCAL_SCHEMA,
57
- SOURCE_WEB: WEB_SCHEMA,
58
- },
59
- key=CONF_SOURCE,
60
- )
61
-
62
-
63
- def _file_schema(value):
64
- if isinstance(value, str):
65
- return validate_file_shorthand(value)
66
- return TYPED_FILE_SCHEMA(value)
67
-
68
-
69
- FILE_SCHEMA = cv.Schema(_file_schema)
70
-
71
-
72
- def validate_file_shorthand(value):
73
- value = cv.string_strict(value)
74
- if value.startswith("http://") or value.startswith("https://"):
75
- return FILE_SCHEMA(
39
+ cv.Required(CONF_ID): cv.declare_id(Animation_),
40
+ cv.Optional(CONF_LOOP): cv.All(
76
41
  {
77
- CONF_SOURCE: SOURCE_WEB,
78
- CONF_URL: value,
42
+ cv.Optional(CONF_START_FRAME, default=0): cv.positive_int,
43
+ cv.Optional(CONF_END_FRAME): cv.positive_int,
44
+ cv.Optional(CONF_REPEAT): cv.positive_int,
79
45
  }
80
- )
81
- return FILE_SCHEMA(
82
- {
83
- CONF_SOURCE: SOURCE_LOCAL,
84
- CONF_PATH: value,
85
- }
86
- )
87
-
88
-
89
- def validate_cross_dependencies(config):
90
- """
91
- Validate fields whose possible values depend on other fields.
92
- For example, validate that explicitly transparent image types
93
- have "use_transparency" set to True.
94
- Also set the default value for those kind of dependent fields.
95
- """
96
- image_type = config[CONF_TYPE]
97
- is_transparent_type = image_type in ["TRANSPARENT_BINARY", "RGBA"]
98
- # If the use_transparency option was not specified, set the default depending on the image type
99
- if CONF_USE_TRANSPARENCY not in config:
100
- config[CONF_USE_TRANSPARENCY] = is_transparent_type
101
-
102
- if is_transparent_type and not config[CONF_USE_TRANSPARENCY]:
103
- raise cv.Invalid(f"Image type {image_type} must always be transparent.")
104
-
105
- return config
106
-
107
-
108
- ANIMATION_SCHEMA = cv.Schema(
109
- cv.All(
110
- {
111
- cv.Required(CONF_ID): cv.declare_id(Animation_),
112
- cv.Required(CONF_FILE): FILE_SCHEMA,
113
- cv.Optional(CONF_RESIZE): cv.dimensions,
114
- cv.Optional(CONF_TYPE, default="BINARY"): cv.enum(
115
- espImage.IMAGE_TYPE, upper=True
116
- ),
117
- # Not setting default here on purpose; the default depends on the image type,
118
- # and thus will be set in the "validate_cross_dependencies" validator.
119
- cv.Optional(CONF_USE_TRANSPARENCY): cv.boolean,
120
- cv.Optional(CONF_LOOP): cv.All(
121
- {
122
- cv.Optional(CONF_START_FRAME, default=0): cv.positive_int,
123
- cv.Optional(CONF_END_FRAME): cv.positive_int,
124
- cv.Optional(CONF_REPEAT): cv.positive_int,
125
- }
126
- ),
127
- cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8),
128
- },
129
- validate_cross_dependencies,
130
- )
46
+ ),
47
+ },
131
48
  )
132
49
 
133
- CONFIG_SCHEMA = ANIMATION_SCHEMA
134
50
 
135
51
  NEXT_FRAME_SCHEMA = automation.maybe_simple_id(
136
52
  {
@@ -164,180 +80,26 @@ async def animation_action_to_code(config, action_id, template_arg, args):
164
80
 
165
81
 
166
82
  async def to_code(config):
167
- from PIL import Image
168
-
169
- conf_file = config[CONF_FILE]
170
- if conf_file[CONF_SOURCE] == SOURCE_LOCAL:
171
- path = CORE.relative_config_path(conf_file[CONF_PATH])
172
- elif conf_file[CONF_SOURCE] == SOURCE_WEB:
173
- path = espImage.compute_local_image_path(conf_file).as_posix()
174
- else:
175
- raise core.EsphomeError(f"Unknown animation source: {conf_file[CONF_SOURCE]}")
176
-
177
- try:
178
- image = Image.open(path)
179
- except Exception as e:
180
- raise core.EsphomeError(f"Could not load image file {path}: {e}")
181
-
182
- width, height = image.size
183
- frames = image.n_frames
184
- if CONF_RESIZE in config:
185
- new_width_max, new_height_max = config[CONF_RESIZE]
186
- ratio = min(new_width_max / width, new_height_max / height)
187
- width, height = int(width * ratio), int(height * ratio)
188
- elif width > 500 or height > 500:
189
- _LOGGER.warning(
190
- 'The image "%s" you requested is very big. Please consider'
191
- " using the resize parameter.",
192
- path,
193
- )
194
-
195
- transparent = config[CONF_USE_TRANSPARENCY]
196
-
197
- if config[CONF_TYPE] == "GRAYSCALE":
198
- data = [0 for _ in range(height * width * frames)]
199
- pos = 0
200
- for frameIndex in range(frames):
201
- image.seek(frameIndex)
202
- frame = image.convert("LA", dither=Image.Dither.NONE)
203
- if CONF_RESIZE in config:
204
- frame = frame.resize([width, height])
205
- pixels = list(frame.getdata())
206
- if len(pixels) != height * width:
207
- raise core.EsphomeError(
208
- f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height * width})"
209
- )
210
- for pix, a in pixels:
211
- if transparent:
212
- if pix == 1:
213
- pix = 0
214
- if a < 0x80:
215
- pix = 1
216
-
217
- data[pos] = pix
218
- pos += 1
219
-
220
- elif config[CONF_TYPE] == "RGBA":
221
- data = [0 for _ in range(height * width * 4 * frames)]
222
- pos = 0
223
- for frameIndex in range(frames):
224
- image.seek(frameIndex)
225
- frame = image.convert("RGBA")
226
- if CONF_RESIZE in config:
227
- frame = frame.resize([width, height])
228
- pixels = list(frame.getdata())
229
- if len(pixels) != height * width:
230
- raise core.EsphomeError(
231
- f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height * width})"
232
- )
233
- for pix in pixels:
234
- data[pos] = pix[0]
235
- pos += 1
236
- data[pos] = pix[1]
237
- pos += 1
238
- data[pos] = pix[2]
239
- pos += 1
240
- data[pos] = pix[3]
241
- pos += 1
242
-
243
- elif config[CONF_TYPE] == "RGB24":
244
- data = [0 for _ in range(height * width * 3 * frames)]
245
- pos = 0
246
- for frameIndex in range(frames):
247
- image.seek(frameIndex)
248
- frame = image.convert("RGBA")
249
- if CONF_RESIZE in config:
250
- frame = frame.resize([width, height])
251
- pixels = list(frame.getdata())
252
- if len(pixels) != height * width:
253
- raise core.EsphomeError(
254
- f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height * width})"
255
- )
256
- for r, g, b, a in pixels:
257
- if transparent:
258
- if r == 0 and g == 0 and b == 1:
259
- b = 0
260
- if a < 0x80:
261
- r = 0
262
- g = 0
263
- b = 1
264
-
265
- data[pos] = r
266
- pos += 1
267
- data[pos] = g
268
- pos += 1
269
- data[pos] = b
270
- pos += 1
271
-
272
- elif config[CONF_TYPE] in ["RGB565", "TRANSPARENT_IMAGE"]:
273
- bytes_per_pixel = 3 if transparent else 2
274
- data = [0 for _ in range(height * width * bytes_per_pixel * frames)]
275
- pos = 0
276
- for frameIndex in range(frames):
277
- image.seek(frameIndex)
278
- frame = image.convert("RGBA")
279
- if CONF_RESIZE in config:
280
- frame = frame.resize([width, height])
281
- pixels = list(frame.getdata())
282
- if len(pixels) != height * width:
283
- raise core.EsphomeError(
284
- f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height * width})"
285
- )
286
- for r, g, b, a in pixels:
287
- R = r >> 3
288
- G = g >> 2
289
- B = b >> 3
290
- rgb = (R << 11) | (G << 5) | B
291
- data[pos] = rgb >> 8
292
- pos += 1
293
- data[pos] = rgb & 0xFF
294
- pos += 1
295
- if transparent:
296
- data[pos] = a
297
- pos += 1
298
-
299
- elif config[CONF_TYPE] in ["BINARY", "TRANSPARENT_BINARY"]:
300
- width8 = ((width + 7) // 8) * 8
301
- data = [0 for _ in range((height * width8 // 8) * frames)]
302
- for frameIndex in range(frames):
303
- image.seek(frameIndex)
304
- if transparent:
305
- alpha = image.split()[-1]
306
- has_alpha = alpha.getextrema()[0] < 0xFF
307
- else:
308
- has_alpha = False
309
- frame = image.convert("1", dither=Image.Dither.NONE)
310
- if CONF_RESIZE in config:
311
- frame = frame.resize([width, height])
312
- if transparent:
313
- alpha = alpha.resize([width, height])
314
- for x, y in [(i, j) for i in range(width) for j in range(height)]:
315
- if transparent and has_alpha:
316
- if not alpha.getpixel((x, y)):
317
- continue
318
- elif frame.getpixel((x, y)):
319
- continue
320
-
321
- pos = x + y * width8 + (height * width8 * frameIndex)
322
- data[pos // 8] |= 0x80 >> (pos % 8)
323
- else:
324
- raise core.EsphomeError(
325
- f"Animation f{config[CONF_ID]} has not supported type {config[CONF_TYPE]}."
326
- )
83
+ (
84
+ prog_arr,
85
+ width,
86
+ height,
87
+ image_type,
88
+ trans_value,
89
+ frame_count,
90
+ ) = await espImage.write_image(config, all_frames=True)
327
91
 
328
- rhs = [HexInt(x) for x in data]
329
- prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
330
92
  var = cg.new_Pvariable(
331
93
  config[CONF_ID],
332
94
  prog_arr,
333
95
  width,
334
96
  height,
335
- frames,
336
- espImage.IMAGE_TYPE[config[CONF_TYPE]],
97
+ frame_count,
98
+ image_type,
99
+ trans_value,
337
100
  )
338
- cg.add(var.set_transparency(transparent))
339
101
  if loop_config := config.get(CONF_LOOP):
340
102
  start = loop_config[CONF_START_FRAME]
341
- end = loop_config.get(CONF_END_FRAME, frames)
103
+ end = loop_config.get(CONF_END_FRAME, frame_count)
342
104
  count = loop_config.get(CONF_REPEAT, -1)
343
105
  cg.add(var.set_loop(start, end, count))
@@ -6,8 +6,8 @@ namespace esphome {
6
6
  namespace animation {
7
7
 
8
8
  Animation::Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count,
9
- image::ImageType type)
10
- : Image(data_start, width, height, type),
9
+ image::ImageType type, image::Transparency transparent)
10
+ : Image(data_start, width, height, type, transparent),
11
11
  animation_data_start_(data_start),
12
12
  current_frame_(0),
13
13
  animation_frame_count_(animation_frame_count),
@@ -8,7 +8,8 @@ namespace animation {
8
8
 
9
9
  class Animation : public image::Image {
10
10
  public:
11
- Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, image::ImageType type);
11
+ Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, image::ImageType type,
12
+ image::Transparency transparent);
12
13
 
13
14
  uint32_t get_animation_frame_count() const;
14
15
  int get_current_frame() const;
@@ -6430,6 +6430,10 @@ bool BluetoothConnectionsFreeResponse::decode_varint(uint32_t field_id, ProtoVar
6430
6430
  this->limit = value.as_uint32();
6431
6431
  return true;
6432
6432
  }
6433
+ case 3: {
6434
+ this->allocated.push_back(value.as_uint64());
6435
+ return true;
6436
+ }
6433
6437
  default:
6434
6438
  return false;
6435
6439
  }
@@ -6437,6 +6441,9 @@ bool BluetoothConnectionsFreeResponse::decode_varint(uint32_t field_id, ProtoVar
6437
6441
  void BluetoothConnectionsFreeResponse::encode(ProtoWriteBuffer buffer) const {
6438
6442
  buffer.encode_uint32(1, this->free);
6439
6443
  buffer.encode_uint32(2, this->limit);
6444
+ for (auto &it : this->allocated) {
6445
+ buffer.encode_uint64(3, it, true);
6446
+ }
6440
6447
  }
6441
6448
  #ifdef HAS_PROTO_MESSAGE_DUMP
6442
6449
  void BluetoothConnectionsFreeResponse::dump_to(std::string &out) const {
@@ -6451,6 +6458,13 @@ void BluetoothConnectionsFreeResponse::dump_to(std::string &out) const {
6451
6458
  sprintf(buffer, "%" PRIu32, this->limit);
6452
6459
  out.append(buffer);
6453
6460
  out.append("\n");
6461
+
6462
+ for (const auto &it : this->allocated) {
6463
+ out.append(" allocated: ");
6464
+ sprintf(buffer, "%llu", it);
6465
+ out.append(buffer);
6466
+ out.append("\n");
6467
+ }
6454
6468
  out.append("}");
6455
6469
  }
6456
6470
  #endif
@@ -1624,6 +1624,7 @@ class BluetoothConnectionsFreeResponse : public ProtoMessage {
1624
1624
  public:
1625
1625
  uint32_t free{0};
1626
1626
  uint32_t limit{0};
1627
+ std::vector<uint64_t> allocated{};
1627
1628
  void encode(ProtoWriteBuffer buffer) const override;
1628
1629
  #ifdef HAS_PROTO_MESSAGE_DUMP
1629
1630
  void dump_to(std::string &out) const override;
@@ -1,12 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import asyncio
4
- import logging
5
4
  from datetime import datetime
6
- from typing import Any
5
+ import logging
6
+ from typing import TYPE_CHECKING, Any
7
7
 
8
8
  from aioesphomeapi import APIClient
9
- from aioesphomeapi.api_pb2 import SubscribeLogsResponse
10
9
  from aioesphomeapi.log_runner import async_run
11
10
 
12
11
  from esphome.const import CONF_KEY, CONF_PASSWORD, CONF_PORT, __version__
@@ -14,6 +13,12 @@ from esphome.core import CORE
14
13
 
15
14
  from . import CONF_ENCRYPTION
16
15
 
16
+ if TYPE_CHECKING:
17
+ from aioesphomeapi.api_pb2 import (
18
+ SubscribeLogsResponse, # pylint: disable=no-name-in-module
19
+ )
20
+
21
+
17
22
  _LOGGER = logging.getLogger(__name__)
18
23
 
19
24
 
@@ -1,9 +1,121 @@
1
1
  import esphome.codegen as cg
2
2
  import esphome.config_validation as cv
3
+ from esphome.const import CONF_BITS_PER_SAMPLE, CONF_NUM_CHANNELS, CONF_SAMPLE_RATE
4
+ import esphome.final_validate as fv
3
5
 
4
6
  CODEOWNERS = ["@kahrendt"]
5
7
  audio_ns = cg.esphome_ns.namespace("audio")
6
8
 
9
+ AudioFile = audio_ns.struct("AudioFile")
10
+ AudioFileType = audio_ns.enum("AudioFileType", is_class=True)
11
+ AUDIO_FILE_TYPE_ENUM = {
12
+ "NONE": AudioFileType.NONE,
13
+ "WAV": AudioFileType.WAV,
14
+ "MP3": AudioFileType.MP3,
15
+ "FLAC": AudioFileType.FLAC,
16
+ }
17
+
18
+
19
+ CONF_MIN_BITS_PER_SAMPLE = "min_bits_per_sample"
20
+ CONF_MAX_BITS_PER_SAMPLE = "max_bits_per_sample"
21
+ CONF_MIN_CHANNELS = "min_channels"
22
+ CONF_MAX_CHANNELS = "max_channels"
23
+ CONF_MIN_SAMPLE_RATE = "min_sample_rate"
24
+ CONF_MAX_SAMPLE_RATE = "max_sample_rate"
25
+
26
+
7
27
  CONFIG_SCHEMA = cv.All(
8
28
  cv.Schema({}),
9
29
  )
30
+
31
+ AUDIO_COMPONENT_SCHEMA = cv.Schema(
32
+ {
33
+ cv.Optional(CONF_BITS_PER_SAMPLE): cv.int_range(8, 32),
34
+ cv.Optional(CONF_NUM_CHANNELS): cv.int_range(1, 2),
35
+ cv.Optional(CONF_SAMPLE_RATE): cv.int_range(8000, 48000),
36
+ }
37
+ )
38
+
39
+
40
+ _UNDEF = object()
41
+
42
+
43
+ def set_stream_limits(
44
+ min_bits_per_sample: int = _UNDEF,
45
+ max_bits_per_sample: int = _UNDEF,
46
+ min_channels: int = _UNDEF,
47
+ max_channels: int = _UNDEF,
48
+ min_sample_rate: int = _UNDEF,
49
+ max_sample_rate: int = _UNDEF,
50
+ ):
51
+ def set_limits_in_config(config):
52
+ if min_bits_per_sample is not _UNDEF:
53
+ config[CONF_MIN_BITS_PER_SAMPLE] = min_bits_per_sample
54
+ if max_bits_per_sample is not _UNDEF:
55
+ config[CONF_MAX_BITS_PER_SAMPLE] = max_bits_per_sample
56
+ if min_channels is not _UNDEF:
57
+ config[CONF_MIN_CHANNELS] = min_channels
58
+ if max_channels is not _UNDEF:
59
+ config[CONF_MAX_CHANNELS] = max_channels
60
+ if min_sample_rate is not _UNDEF:
61
+ config[CONF_MIN_SAMPLE_RATE] = min_sample_rate
62
+ if max_sample_rate is not _UNDEF:
63
+ config[CONF_MAX_SAMPLE_RATE] = max_sample_rate
64
+
65
+ return set_limits_in_config
66
+
67
+
68
+ def final_validate_audio_schema(
69
+ name: str,
70
+ *,
71
+ audio_device: str,
72
+ bits_per_sample: int,
73
+ channels: int,
74
+ sample_rate: int,
75
+ ):
76
+ def validate_audio_compatiblity(audio_config):
77
+ audio_schema = {}
78
+
79
+ try:
80
+ cv.int_range(
81
+ min=audio_config.get(CONF_MIN_BITS_PER_SAMPLE),
82
+ max=audio_config.get(CONF_MAX_BITS_PER_SAMPLE),
83
+ )(bits_per_sample)
84
+ except cv.Invalid as exc:
85
+ raise cv.Invalid(
86
+ f"Invalid configuration for the {name} component. The {CONF_BITS_PER_SAMPLE} {str(exc)}"
87
+ ) from exc
88
+
89
+ try:
90
+ cv.int_range(
91
+ min=audio_config.get(CONF_MIN_CHANNELS),
92
+ max=audio_config.get(CONF_MAX_CHANNELS),
93
+ )(channels)
94
+ except cv.Invalid as exc:
95
+ raise cv.Invalid(
96
+ f"Invalid configuration for the {name} component. The {CONF_NUM_CHANNELS} {str(exc)}"
97
+ ) from exc
98
+
99
+ try:
100
+ cv.int_range(
101
+ min=audio_config.get(CONF_MIN_SAMPLE_RATE),
102
+ max=audio_config.get(CONF_MAX_SAMPLE_RATE),
103
+ )(sample_rate)
104
+ return cv.Schema(audio_schema, extra=cv.ALLOW_EXTRA)(audio_config)
105
+ except cv.Invalid as exc:
106
+ raise cv.Invalid(
107
+ f"Invalid configuration for the {name} component. The {CONF_SAMPLE_RATE} {str(exc)}"
108
+ ) from exc
109
+
110
+ return cv.Schema(
111
+ {
112
+ cv.Required(audio_device): fv.id_declaration_match_schema(
113
+ validate_audio_compatiblity
114
+ )
115
+ },
116
+ extra=cv.ALLOW_EXTRA,
117
+ )
118
+
119
+
120
+ async def to_code(config):
121
+ cg.add_library("esphome/esp-audio-libs", "1.1.1")
@@ -0,0 +1,67 @@
1
+ #include "audio.h"
2
+
3
+ namespace esphome {
4
+ namespace audio {
5
+
6
+ // Euclidean's algorithm for finding the greatest common divisor
7
+ static uint32_t gcd(uint32_t a, uint32_t b) {
8
+ while (b != 0) {
9
+ uint32_t t = b;
10
+ b = a % b;
11
+ a = t;
12
+ }
13
+ return a;
14
+ }
15
+
16
+ AudioStreamInfo::AudioStreamInfo(uint8_t bits_per_sample, uint8_t channels, uint32_t sample_rate)
17
+ : bits_per_sample_(bits_per_sample), channels_(channels), sample_rate_(sample_rate) {
18
+ this->ms_sample_rate_gcd_ = gcd(1000, this->sample_rate_);
19
+ this->bytes_per_sample_ = (this->bits_per_sample_ + 7) / 8;
20
+ }
21
+
22
+ uint32_t AudioStreamInfo::frames_to_microseconds(uint32_t frames) const {
23
+ return (frames * 1000000 + (this->sample_rate_ >> 1)) / this->sample_rate_;
24
+ }
25
+
26
+ uint32_t AudioStreamInfo::frames_to_milliseconds_with_remainder(uint32_t *total_frames) const {
27
+ uint32_t unprocessable_frames = *total_frames % (this->sample_rate_ / this->ms_sample_rate_gcd_);
28
+ uint32_t frames_for_ms_calculation = *total_frames - unprocessable_frames;
29
+
30
+ uint32_t playback_ms = (frames_for_ms_calculation * 1000) / this->sample_rate_;
31
+ *total_frames = unprocessable_frames;
32
+ return playback_ms;
33
+ }
34
+
35
+ bool AudioStreamInfo::operator==(const AudioStreamInfo &rhs) const {
36
+ return (this->bits_per_sample_ == rhs.get_bits_per_sample()) && (this->channels_ == rhs.get_channels()) &&
37
+ (this->sample_rate_ == rhs.get_sample_rate());
38
+ }
39
+
40
+ const char *audio_file_type_to_string(AudioFileType file_type) {
41
+ switch (file_type) {
42
+ #ifdef USE_AUDIO_FLAC_SUPPORT
43
+ case AudioFileType::FLAC:
44
+ return "FLAC";
45
+ #endif
46
+ #ifdef USE_AUDIO_MP3_SUPPORT
47
+ case AudioFileType::MP3:
48
+ return "MP3";
49
+ #endif
50
+ case AudioFileType::WAV:
51
+ return "WAV";
52
+ default:
53
+ return "unknown";
54
+ }
55
+ }
56
+
57
+ void scale_audio_samples(const int16_t *audio_samples, int16_t *output_buffer, int16_t scale_factor,
58
+ size_t samples_to_scale) {
59
+ // Note the assembly dsps_mulc function has audio glitches if the input and output buffers are the same.
60
+ for (int i = 0; i < samples_to_scale; i++) {
61
+ int32_t acc = (int32_t) audio_samples[i] * (int32_t) scale_factor;
62
+ output_buffer[i] = (int16_t) (acc >> 15);
63
+ }
64
+ }
65
+
66
+ } // namespace audio
67
+ } // namespace esphome