esphome 2024.12.4__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.
- esphome/__main__.py +16 -3
- esphome/components/adc/__init__.py +17 -11
- esphome/components/adc/adc_sensor.h +17 -0
- esphome/components/adc/adc_sensor_common.cpp +55 -0
- esphome/components/adc/adc_sensor_esp32.cpp +8 -5
- esphome/components/adc/adc_sensor_esp8266.cpp +10 -6
- esphome/components/adc/adc_sensor_libretiny.cpp +11 -6
- esphome/components/adc/adc_sensor_rp2040.cpp +13 -10
- esphome/components/adc/sensor.py +9 -3
- esphome/components/ads1115/ads1115.cpp +56 -7
- esphome/components/ads1115/ads1115.h +13 -1
- esphome/components/ads1115/sensor/__init__.py +16 -0
- esphome/components/ads1115/sensor/ads1115_sensor.cpp +2 -1
- esphome/components/ads1115/sensor/ads1115_sensor.h +2 -0
- esphome/components/animation/__init__.py +23 -261
- esphome/components/animation/animation.cpp +2 -2
- esphome/components/animation/animation.h +2 -1
- esphome/components/api/api_pb2.cpp +14 -0
- esphome/components/api/api_pb2.h +1 -0
- esphome/components/api/client.py +8 -3
- esphome/components/audio/__init__.py +112 -0
- esphome/components/audio/audio.cpp +67 -0
- esphome/components/audio/audio.h +125 -7
- esphome/components/audio/audio_decoder.cpp +361 -0
- esphome/components/audio/audio_decoder.h +135 -0
- esphome/components/audio/audio_reader.cpp +308 -0
- esphome/components/audio/audio_reader.h +85 -0
- esphome/components/audio/audio_resampler.cpp +159 -0
- esphome/components/audio/audio_resampler.h +101 -0
- esphome/components/audio/audio_transfer_buffer.cpp +165 -0
- esphome/components/audio/audio_transfer_buffer.h +139 -0
- esphome/components/audio_adc/__init__.py +41 -0
- esphome/components/audio_adc/audio_adc.h +17 -0
- esphome/components/audio_adc/automation.h +23 -0
- esphome/components/bk72xx/__init__.py +1 -0
- esphome/components/ble_client/ble_client.cpp +1 -2
- esphome/components/ble_client/sensor/__init__.py +1 -1
- esphome/components/ble_client/text_sensor/__init__.py +1 -1
- esphome/components/bluetooth_proxy/bluetooth_connection.cpp +5 -0
- esphome/components/bluetooth_proxy/bluetooth_connection.h +1 -0
- esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +5 -0
- esphome/components/ch422g/ch422g.h +2 -0
- esphome/components/climate/__init__.py +1 -1
- esphome/components/climate_ir/climate_ir.cpp +2 -1
- esphome/components/coolix/coolix.cpp +2 -1
- esphome/components/cse7766/cse7766.cpp +8 -16
- esphome/components/custom/__init__.py +0 -3
- esphome/components/custom/binary_sensor/__init__.py +2 -28
- esphome/components/custom/climate/__init__.py +2 -27
- esphome/components/custom/cover/__init__.py +2 -27
- esphome/components/custom/light/__init__.py +2 -27
- esphome/components/custom/output/__init__.py +2 -58
- esphome/components/custom/sensor/__init__.py +2 -24
- esphome/components/custom/switch/__init__.py +2 -24
- esphome/components/custom/text_sensor/__init__.py +2 -29
- esphome/components/custom_component/__init__.py +3 -27
- esphome/components/daly_bms/daly_bms.cpp +6 -0
- esphome/components/daly_bms/daly_bms.h +2 -0
- esphome/components/daly_bms/sensor.py +6 -0
- esphome/components/debug/debug_component.cpp +4 -0
- esphome/components/debug/debug_component.h +14 -0
- esphome/components/debug/debug_esp32.cpp +154 -74
- esphome/components/dfplayer/dfplayer.cpp +15 -2
- esphome/components/dfrobot_sen0395/dfrobot_sen0395.cpp +2 -1
- esphome/components/dht/dht.cpp +4 -2
- esphome/components/dht/sensor.py +1 -1
- esphome/components/display/__init__.py +18 -5
- esphome/components/display/display.cpp +16 -3
- esphome/components/display/rect.cpp +2 -1
- esphome/components/es7210/__init__.py +0 -0
- esphome/components/es7210/audio_adc.py +51 -0
- esphome/components/es7210/es7210.cpp +228 -0
- esphome/components/es7210/es7210.h +62 -0
- esphome/components/es7210/es7210_const.h +129 -0
- esphome/components/es7243e/__init__.py +0 -0
- esphome/components/es7243e/audio_adc.py +34 -0
- esphome/components/es7243e/es7243e.cpp +125 -0
- esphome/components/es7243e/es7243e.h +37 -0
- esphome/components/es7243e/es7243e_const.h +54 -0
- esphome/components/es8156/__init__.py +0 -0
- esphome/components/es8156/audio_dac.py +27 -0
- esphome/components/es8156/es8156.cpp +87 -0
- esphome/components/es8156/es8156.h +51 -0
- esphome/components/es8156/es8156_const.h +68 -0
- esphome/components/es8311/audio_dac.py +1 -2
- esphome/components/esp32/__init__.py +1 -0
- esphome/components/esp32/core.cpp +5 -1
- esphome/components/esp32/gpio.h +2 -0
- esphome/components/esp32_ble/__init__.py +39 -0
- esphome/components/esp32_ble/queue.h +4 -4
- esphome/components/esp32_ble_client/ble_client_base.cpp +46 -0
- esphome/components/esp32_ble_client/ble_client_base.h +2 -0
- esphome/components/esp32_ble_server/__init__.py +582 -12
- esphome/components/esp32_ble_server/ble_characteristic.cpp +48 -60
- esphome/components/esp32_ble_server/ble_characteristic.h +24 -17
- esphome/components/esp32_ble_server/ble_descriptor.cpp +21 -9
- esphome/components/esp32_ble_server/ble_descriptor.h +17 -6
- esphome/components/esp32_ble_server/ble_server.cpp +62 -67
- esphome/components/esp32_ble_server/ble_server.h +28 -32
- esphome/components/esp32_ble_server/ble_server_automations.cpp +77 -0
- esphome/components/esp32_ble_server/ble_server_automations.h +115 -0
- esphome/components/esp32_ble_server/ble_service.cpp +17 -15
- esphome/components/esp32_ble_server/ble_service.h +10 -14
- esphome/components/esp32_ble_tracker/__init__.py +6 -39
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +33 -10
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +8 -4
- esphome/components/esp32_dac/esp32_dac.cpp +16 -7
- esphome/components/esp32_dac/esp32_dac.h +8 -0
- esphome/components/esp32_dac/output.py +16 -4
- esphome/components/esp32_improv/__init__.py +2 -8
- esphome/components/esp32_improv/esp32_improv_component.cpp +21 -20
- esphome/components/esp32_improv/esp32_improv_component.h +3 -4
- esphome/components/esp32_rmt/__init__.py +28 -3
- esphome/components/esp32_rmt_led_strip/led_strip.cpp +73 -6
- esphome/components/esp32_rmt_led_strip/led_strip.h +21 -3
- esphome/components/esp32_rmt_led_strip/light.py +72 -7
- esphome/components/esp32_touch/esp32_touch.cpp +5 -0
- esphome/components/esp8266/__init__.py +1 -0
- esphome/components/esp8266/gpio.h +1 -0
- esphome/components/ethernet/__init__.py +10 -10
- esphome/components/event/event.cpp +4 -2
- esphome/components/event/event.h +2 -0
- esphome/components/event_emitter/__init__.py +5 -0
- esphome/components/event_emitter/event_emitter.cpp +14 -0
- esphome/components/event_emitter/event_emitter.h +63 -0
- esphome/components/font/__init__.py +1 -1
- esphome/components/gcja5/gcja5.cpp +2 -1
- esphome/components/graph/graph.cpp +4 -9
- esphome/components/haier/haier_base.cpp +2 -1
- esphome/components/haier/hon_climate.cpp +2 -1
- esphome/components/heatpumpir/heatpumpir.cpp +2 -1
- esphome/components/host/__init__.py +1 -0
- esphome/components/host/gpio.h +1 -0
- esphome/components/http_request/http_request.h +2 -2
- esphome/components/http_request/http_request_arduino.cpp +1 -1
- esphome/components/http_request/http_request_idf.cpp +1 -1
- esphome/components/i2c/i2c_bus_esp_idf.cpp +4 -0
- esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +7 -5
- esphome/components/i2s_audio/speaker/__init__.py +53 -6
- esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +92 -46
- esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +8 -0
- esphome/components/ili9xxx/display.py +29 -11
- esphome/components/ili9xxx/ili9xxx_display.cpp +2 -5
- esphome/components/ili9xxx/ili9xxx_display.h +2 -1
- esphome/components/image/__init__.py +443 -255
- esphome/components/image/image.cpp +115 -61
- esphome/components/image/image.h +15 -24
- esphome/components/json/json_util.cpp +8 -34
- esphome/components/libretiny/__init__.py +1 -0
- esphome/components/libretiny/gpio_arduino.h +1 -0
- esphome/components/light/light_color_values.h +1 -1
- esphome/components/logger/__init__.py +45 -9
- esphome/components/logger/logger.cpp +16 -14
- esphome/components/logger/logger.h +11 -7
- esphome/components/logger/select/__init__.py +29 -0
- esphome/components/logger/select/logger_level_select.cpp +27 -0
- esphome/components/logger/select/logger_level_select.h +15 -0
- esphome/components/lvgl/__init__.py +96 -73
- esphome/components/lvgl/automation.py +39 -7
- esphome/components/lvgl/defines.py +8 -2
- esphome/components/lvgl/lvgl_esphome.cpp +8 -15
- esphome/components/lvgl/lvgl_esphome.h +20 -5
- esphome/components/lvgl/schemas.py +25 -14
- esphome/components/lvgl/trigger.py +27 -3
- esphome/components/lvgl/widgets/dropdown.py +1 -1
- esphome/components/lvgl/widgets/keyboard.py +8 -1
- esphome/components/lvgl/widgets/meter.py +2 -1
- esphome/components/lvgl/widgets/msgbox.py +1 -1
- esphome/components/lvgl/widgets/obj.py +1 -12
- esphome/components/lvgl/widgets/page.py +37 -2
- esphome/components/lvgl/widgets/tabview.py +1 -1
- esphome/components/max6956/max6956.h +2 -0
- esphome/components/mcp23016/mcp23016.h +2 -0
- esphome/components/mcp23xxx_base/mcp23xxx_base.h +2 -0
- esphome/components/mdns/__init__.py +1 -1
- esphome/components/media_player/__init__.py +37 -8
- esphome/components/media_player/automation.h +11 -2
- esphome/components/media_player/media_player.cpp +8 -0
- esphome/components/media_player/media_player.h +8 -4
- esphome/components/micronova/switch/micronova_switch.cpp +4 -2
- esphome/components/midea/ac_automations.h +3 -1
- esphome/components/midea/air_conditioner.cpp +7 -5
- esphome/components/midea/air_conditioner.h +1 -1
- esphome/components/midea/climate.py +4 -2
- esphome/components/midea/ir_transmitter.h +36 -5
- esphome/components/mixer/__init__.py +0 -0
- esphome/components/mixer/speaker/__init__.py +172 -0
- esphome/components/mixer/speaker/automation.h +19 -0
- esphome/components/mixer/speaker/mixer_speaker.cpp +624 -0
- esphome/components/mixer/speaker/mixer_speaker.h +207 -0
- esphome/components/modbus_controller/text_sensor/modbus_textsensor.cpp +7 -13
- esphome/components/mpr121/mpr121.h +2 -0
- esphome/components/mqtt/__init__.py +1 -1
- esphome/components/mqtt/mqtt_client.cpp +7 -1
- esphome/components/mqtt/mqtt_client.h +1 -1
- esphome/components/mqtt/mqtt_climate.cpp +2 -2
- esphome/components/network/ip_address.h +2 -0
- esphome/components/nextion/automation.h +17 -0
- esphome/components/nextion/display.py +42 -17
- esphome/components/nextion/nextion.cpp +4 -10
- esphome/components/nextion/nextion.h +89 -82
- esphome/components/nextion/nextion_commands.cpp +10 -10
- esphome/components/ntc/sensor.py +2 -4
- esphome/components/online_image/__init__.py +98 -46
- esphome/components/online_image/bmp_image.cpp +101 -0
- esphome/components/online_image/bmp_image.h +40 -0
- esphome/components/online_image/image_decoder.cpp +31 -2
- esphome/components/online_image/image_decoder.h +24 -15
- esphome/components/online_image/jpeg_image.cpp +92 -0
- esphome/components/online_image/jpeg_image.h +34 -0
- esphome/components/online_image/online_image.cpp +118 -58
- esphome/components/online_image/online_image.h +39 -9
- esphome/components/online_image/png_image.cpp +7 -3
- esphome/components/online_image/png_image.h +2 -1
- esphome/components/opentherm/__init__.py +73 -7
- esphome/components/opentherm/automation.h +25 -0
- esphome/components/opentherm/const.py +1 -0
- esphome/components/opentherm/generate.py +39 -6
- esphome/components/opentherm/hub.cpp +117 -79
- esphome/components/opentherm/hub.h +31 -15
- esphome/components/opentherm/opentherm.cpp +47 -23
- esphome/components/opentherm/opentherm.h +27 -6
- esphome/components/opentherm/opentherm_macros.h +11 -0
- esphome/components/opentherm/schema.py +78 -1
- esphome/components/opentherm/validate.py +7 -2
- esphome/components/pca6416a/pca6416a.h +2 -0
- esphome/components/pca9554/pca9554.h +2 -0
- esphome/components/pcf8574/pcf8574.h +2 -0
- esphome/components/preferences/__init__.py +2 -4
- esphome/components/preferences/syncer.h +10 -3
- esphome/components/prometheus/prometheus_handler.cpp +313 -0
- esphome/components/prometheus/prometheus_handler.h +48 -7
- esphome/components/psram/psram.cpp +8 -1
- esphome/components/pulse_counter/pulse_counter_sensor.cpp +14 -9
- esphome/components/pulse_counter/pulse_counter_sensor.h +4 -4
- esphome/components/pulse_meter/pulse_meter_sensor.cpp +2 -0
- esphome/components/qspi_dbi/__init__.py +3 -0
- esphome/components/qspi_dbi/display.py +74 -47
- esphome/components/qspi_dbi/models.py +245 -2
- esphome/components/qspi_dbi/qspi_dbi.cpp +9 -16
- esphome/components/qspi_dbi/qspi_dbi.h +2 -2
- esphome/components/remote_base/__init__.py +77 -25
- esphome/components/remote_base/remote_base.cpp +1 -1
- esphome/components/remote_base/remote_base.h +20 -2
- esphome/components/remote_base/toto_protocol.cpp +100 -0
- esphome/components/remote_base/toto_protocol.h +45 -0
- esphome/components/remote_receiver/__init__.py +55 -10
- esphome/components/remote_receiver/remote_receiver.h +36 -3
- esphome/components/remote_receiver/remote_receiver_esp32.cpp +145 -6
- esphome/components/remote_transmitter/__init__.py +62 -4
- esphome/components/remote_transmitter/remote_transmitter.h +21 -2
- esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +140 -4
- esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +3 -3
- esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp +3 -3
- esphome/components/resampler/__init__.py +0 -0
- esphome/components/resampler/speaker/__init__.py +103 -0
- esphome/components/resampler/speaker/resampler_speaker.cpp +318 -0
- esphome/components/resampler/speaker/resampler_speaker.h +107 -0
- esphome/components/resistance/resistance_sensor.h +2 -3
- esphome/components/resistance/sensor.py +2 -9
- esphome/components/rotary_encoder/rotary_encoder.cpp +8 -4
- esphome/components/rp2040/__init__.py +1 -0
- esphome/components/rp2040/gpio.h +1 -0
- esphome/components/rtl87xx/__init__.py +2 -0
- esphome/components/scd30/sensor.py +1 -1
- esphome/components/sdl/binary_sensor.py +270 -0
- esphome/components/sdl/sdl_esphome.cpp +16 -0
- esphome/components/sdl/sdl_esphome.h +9 -0
- esphome/components/seeed_mr60bha2/binary_sensor.py +25 -0
- esphome/components/seeed_mr60bha2/seeed_mr60bha2.cpp +26 -2
- esphome/components/seeed_mr60bha2/seeed_mr60bha2.h +9 -20
- esphome/components/seeed_mr60bha2/sensor.py +9 -1
- esphome/components/sn74hc165/sn74hc165.h +3 -0
- esphome/components/sn74hc595/sn74hc595.h +3 -0
- esphome/components/speaker/__init__.py +5 -4
- esphome/components/speaker/media_player/__init__.py +458 -0
- esphome/components/speaker/media_player/audio_pipeline.cpp +568 -0
- esphome/components/speaker/media_player/audio_pipeline.h +159 -0
- esphome/components/speaker/media_player/automation.h +26 -0
- esphome/components/speaker/media_player/speaker_media_player.cpp +577 -0
- esphome/components/speaker/media_player/speaker_media_player.h +160 -0
- esphome/components/speaker/speaker.h +20 -0
- esphome/components/spi/__init__.py +1 -5
- esphome/components/spi/spi.cpp +7 -1
- esphome/components/spi/spi.h +21 -2
- esphome/components/spi_led_strip/light.py +3 -5
- esphome/components/spi_led_strip/spi_led_strip.cpp +67 -0
- esphome/components/spi_led_strip/spi_led_strip.h +8 -60
- esphome/components/sprinkler/sprinkler.cpp +3 -1
- esphome/components/sx1509/sx1509_gpio_pin.h +2 -0
- esphome/components/tca9555/tca9555.h +2 -0
- esphome/components/toshiba/toshiba.cpp +2 -1
- esphome/components/tuya/light/tuya_light.cpp +4 -2
- esphome/components/uart/uart_component_esp32_arduino.cpp +2 -2
- esphome/components/uart/uart_component_esp_idf.cpp +2 -2
- esphome/components/udp/__init__.py +8 -2
- esphome/components/udp/udp_component.cpp +25 -56
- esphome/components/udp/udp_component.h +3 -0
- esphome/components/uponor_smatrix/sensor/__init__.py +14 -4
- esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.cpp +5 -0
- esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.h +1 -0
- esphome/components/uptime/text_sensor/__init__.py +19 -0
- esphome/components/uptime/text_sensor/uptime_text_sensor.cpp +63 -0
- esphome/components/uptime/text_sensor/uptime_text_sensor.h +25 -0
- esphome/components/voice_assistant/voice_assistant.cpp +24 -14
- esphome/components/voice_assistant/voice_assistant.h +8 -0
- esphome/components/waveshare_epaper/display.py +22 -1
- esphome/components/waveshare_epaper/waveshare_213v3.cpp +9 -3
- esphome/components/waveshare_epaper/waveshare_epaper.cpp +1155 -44
- esphome/components/waveshare_epaper/waveshare_epaper.h +208 -7
- esphome/components/web_server/web_server.cpp +28 -6
- esphome/components/weikai/weikai.h +2 -0
- esphome/components/wifi/__init__.py +6 -6
- esphome/components/wifi/wifi_component.cpp +1 -1
- esphome/components/wifi/wifi_component_esp32_arduino.cpp +30 -1
- esphome/components/wireguard/__init__.py +2 -2
- esphome/components/xl9535/xl9535.h +2 -0
- esphome/components/xxtea/__init__.py +3 -0
- esphome/components/xxtea/xxtea.cpp +46 -0
- esphome/components/xxtea/xxtea.h +26 -0
- esphome/components/yashima/yashima.cpp +2 -1
- esphome/config.py +9 -5
- esphome/config_validation.py +55 -17
- esphome/const.py +7 -10
- esphome/core/__init__.py +6 -13
- esphome/core/base_automation.h +1 -0
- esphome/core/config.py +59 -72
- esphome/core/defines.h +9 -1
- esphome/core/gpio.h +7 -0
- esphome/core/helpers.cpp +19 -15
- esphome/core/helpers.h +57 -8
- esphome/core/log.h +9 -7
- esphome/cpp_generator.py +2 -2
- esphome/dashboard/web_server.py +1 -1
- esphome/espota2.py +3 -2
- esphome/loader.py +12 -4
- esphome/log.py +5 -7
- esphome/yaml_util.py +2 -2
- {esphome-2024.12.4.dist-info → esphome-2025.2.0.dist-info}/METADATA +14 -9
- {esphome-2024.12.4.dist-info → esphome-2025.2.0.dist-info}/RECORD +349 -300
- esphome/components/custom/binary_sensor/custom_binary_sensor.cpp +0 -16
- esphome/components/custom/binary_sensor/custom_binary_sensor.h +0 -26
- esphome/components/custom/climate/custom_climate.h +0 -22
- esphome/components/custom/cover/custom_cover.h +0 -21
- esphome/components/custom/light/custom_light_output.h +0 -24
- esphome/components/custom/output/custom_output.h +0 -37
- esphome/components/custom/sensor/custom_sensor.cpp +0 -16
- esphome/components/custom/sensor/custom_sensor.h +0 -24
- esphome/components/custom/switch/custom_switch.cpp +0 -16
- esphome/components/custom/switch/custom_switch.h +0 -24
- esphome/components/custom/text_sensor/custom_text_sensor.cpp +0 -16
- esphome/components/custom/text_sensor/custom_text_sensor.h +0 -26
- esphome/components/custom_component/custom_component.h +0 -28
- esphome/components/esp32_ble_server/ble_2901.cpp +0 -18
- esphome/components/esp32_ble_server/ble_2901.h +0 -19
- esphome/components/resistance_sampler/__init__.py +0 -6
- esphome/components/resistance_sampler/resistance_sampler.h +0 -10
- esphome/components/uptime/{sensor.py → sensor/__init__.py} +3 -3
- /esphome/components/uptime/{uptime_seconds_sensor.cpp → sensor/uptime_seconds_sensor.cpp} +0 -0
- /esphome/components/uptime/{uptime_seconds_sensor.h → sensor/uptime_seconds_sensor.h} +0 -0
- /esphome/components/uptime/{uptime_timestamp_sensor.cpp → sensor/uptime_timestamp_sensor.cpp} +0 -0
- /esphome/components/uptime/{uptime_timestamp_sensor.h → sensor/uptime_timestamp_sensor.h} +0 -0
- {esphome-2024.12.4.dist-info → esphome-2025.2.0.dist-info}/LICENSE +0 -0
- {esphome-2024.12.4.dist-info → esphome-2025.2.0.dist-info}/WHEEL +0 -0
- {esphome-2024.12.4.dist-info → esphome-2025.2.0.dist-info}/entry_points.txt +0 -0
- {esphome-2024.12.4.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
|
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
|
-
|
37
|
+
CONFIG_SCHEMA = espImage.IMAGE_SCHEMA.extend(
|
55
38
|
{
|
56
|
-
|
57
|
-
|
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
|
-
|
78
|
-
|
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
|
-
|
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
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
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
|
-
|
336
|
-
|
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,
|
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
|
esphome/components/api/api_pb2.h
CHANGED
@@ -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;
|
esphome/components/api/client.py
CHANGED
@@ -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
|
-
|
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
|