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.
- 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.3.dist-info → esphome-2025.2.0.dist-info}/METADATA +14 -9
- {esphome-2024.12.3.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.3.dist-info → esphome-2025.2.0.dist-info}/LICENSE +0 -0
- {esphome-2024.12.3.dist-info → esphome-2025.2.0.dist-info}/WHEEL +0 -0
- {esphome-2024.12.3.dist-info → esphome-2025.2.0.dist-info}/entry_points.txt +0 -0
- {esphome-2024.12.3.dist-info → esphome-2025.2.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,577 @@
|
|
1
|
+
#include "speaker_media_player.h"
|
2
|
+
|
3
|
+
#ifdef USE_ESP_IDF
|
4
|
+
|
5
|
+
#include "esphome/core/log.h"
|
6
|
+
|
7
|
+
#include "esphome/components/audio/audio.h"
|
8
|
+
#ifdef USE_OTA
|
9
|
+
#include "esphome/components/ota/ota_backend.h"
|
10
|
+
#endif
|
11
|
+
|
12
|
+
namespace esphome {
|
13
|
+
namespace speaker {
|
14
|
+
|
15
|
+
// Framework:
|
16
|
+
// - Media player that can handle two streams: one for media and one for announcements
|
17
|
+
// - Each stream has an individual speaker component for output
|
18
|
+
// - Each stream is handled by an ``AudioPipeline`` object with two parts/tasks
|
19
|
+
// - ``AudioReader`` handles reading from an HTTP source or from a PROGMEM flash set at compile time
|
20
|
+
// - ``AudioDecoder`` handles decoding the audio file. All formats are limited to two channels and 16 bits per sample
|
21
|
+
// - FLAC
|
22
|
+
// - MP3 (based on the libhelix decoder)
|
23
|
+
// - WAV
|
24
|
+
// - Each task runs until it is done processing the file or it receives a stop command
|
25
|
+
// - Inter-task communication uses a FreeRTOS Event Group
|
26
|
+
// - The ``AudioPipeline`` sets up a ring buffer between the reader and decoder tasks. The decoder task outputs audio
|
27
|
+
// directly to a speaker component.
|
28
|
+
// - The pipelines internal state needs to be processed by regularly calling ``process_state``.
|
29
|
+
// - Generic media player commands are received by the ``control`` function. The commands are added to the
|
30
|
+
// ``media_control_command_queue_`` to be processed in the component's loop
|
31
|
+
// - Local file play back is initiatied with ``play_file`` and adds it to the ``media_control_command_queue_``
|
32
|
+
// - Starting a stream intializes the appropriate pipeline or stops it if it is already running
|
33
|
+
// - Volume and mute commands are achieved by the ``mute``, ``unmute``, ``set_volume`` functions.
|
34
|
+
// - Volume commands are ignored if the media control queue is full to avoid crashing with rapid volume
|
35
|
+
// increases/decreases.
|
36
|
+
// - These functions all send the appropriate information to the speakers to implement.
|
37
|
+
// - Pausing is implemented in the decoder task and is also sent directly to the media speaker component to decrease
|
38
|
+
// latency.
|
39
|
+
// - The components main loop performs housekeeping:
|
40
|
+
// - It reads the media control queue and processes it directly
|
41
|
+
// - It determines the overall state of the media player by considering the state of each pipeline
|
42
|
+
// - announcement playback takes highest priority
|
43
|
+
// - Handles playlists and repeating by starting the appropriate file when a previous file is finished
|
44
|
+
// - Logging only happens in the main loop task to reduce task stack memory usage.
|
45
|
+
|
46
|
+
static const uint32_t MEDIA_CONTROLS_QUEUE_LENGTH = 20;
|
47
|
+
|
48
|
+
static const UBaseType_t MEDIA_PIPELINE_TASK_PRIORITY = 1;
|
49
|
+
static const UBaseType_t ANNOUNCEMENT_PIPELINE_TASK_PRIORITY = 1;
|
50
|
+
|
51
|
+
static const float FIRST_BOOT_DEFAULT_VOLUME = 0.5f;
|
52
|
+
|
53
|
+
static const char *const TAG = "speaker_media_player";
|
54
|
+
|
55
|
+
void SpeakerMediaPlayer::setup() {
|
56
|
+
state = media_player::MEDIA_PLAYER_STATE_IDLE;
|
57
|
+
|
58
|
+
this->media_control_command_queue_ = xQueueCreate(MEDIA_CONTROLS_QUEUE_LENGTH, sizeof(MediaCallCommand));
|
59
|
+
|
60
|
+
this->pref_ = global_preferences->make_preference<VolumeRestoreState>(this->get_object_id_hash());
|
61
|
+
|
62
|
+
VolumeRestoreState volume_restore_state;
|
63
|
+
if (this->pref_.load(&volume_restore_state)) {
|
64
|
+
this->set_volume_(volume_restore_state.volume);
|
65
|
+
this->set_mute_state_(volume_restore_state.is_muted);
|
66
|
+
} else {
|
67
|
+
this->set_volume_(FIRST_BOOT_DEFAULT_VOLUME);
|
68
|
+
this->set_mute_state_(false);
|
69
|
+
}
|
70
|
+
|
71
|
+
#ifdef USE_OTA
|
72
|
+
ota::get_global_ota_callback()->add_on_state_callback(
|
73
|
+
[this](ota::OTAState state, float progress, uint8_t error, ota::OTAComponent *comp) {
|
74
|
+
if (state == ota::OTA_STARTED) {
|
75
|
+
if (this->media_pipeline_ != nullptr) {
|
76
|
+
this->media_pipeline_->suspend_tasks();
|
77
|
+
}
|
78
|
+
if (this->announcement_pipeline_ != nullptr) {
|
79
|
+
this->announcement_pipeline_->suspend_tasks();
|
80
|
+
}
|
81
|
+
} else if (state == ota::OTA_ERROR) {
|
82
|
+
if (this->media_pipeline_ != nullptr) {
|
83
|
+
this->media_pipeline_->resume_tasks();
|
84
|
+
}
|
85
|
+
if (this->announcement_pipeline_ != nullptr) {
|
86
|
+
this->announcement_pipeline_->resume_tasks();
|
87
|
+
}
|
88
|
+
}
|
89
|
+
});
|
90
|
+
#endif
|
91
|
+
|
92
|
+
this->announcement_pipeline_ =
|
93
|
+
make_unique<AudioPipeline>(this->announcement_speaker_, this->buffer_size_, this->task_stack_in_psram_, "ann",
|
94
|
+
ANNOUNCEMENT_PIPELINE_TASK_PRIORITY);
|
95
|
+
|
96
|
+
if (this->announcement_pipeline_ == nullptr) {
|
97
|
+
ESP_LOGE(TAG, "Failed to create announcement pipeline");
|
98
|
+
this->mark_failed();
|
99
|
+
}
|
100
|
+
|
101
|
+
if (!this->single_pipeline_()) {
|
102
|
+
this->media_pipeline_ = make_unique<AudioPipeline>(this->media_speaker_, this->buffer_size_,
|
103
|
+
this->task_stack_in_psram_, "ann", MEDIA_PIPELINE_TASK_PRIORITY);
|
104
|
+
|
105
|
+
if (this->media_pipeline_ == nullptr) {
|
106
|
+
ESP_LOGE(TAG, "Failed to create media pipeline");
|
107
|
+
this->mark_failed();
|
108
|
+
}
|
109
|
+
|
110
|
+
// Setup callback to track the duration of audio played by the media pipeline
|
111
|
+
this->media_speaker_->add_audio_output_callback(
|
112
|
+
[this](uint32_t new_playback_ms, uint32_t remainder_us, uint32_t pending_ms, uint32_t write_timestamp) {
|
113
|
+
this->playback_ms_ += new_playback_ms;
|
114
|
+
this->remainder_us_ = remainder_us;
|
115
|
+
this->pending_ms_ = pending_ms;
|
116
|
+
this->last_audio_write_timestamp_ = write_timestamp;
|
117
|
+
this->playback_us_ = this->playback_ms_ * 1000 + this->remainder_us_;
|
118
|
+
});
|
119
|
+
}
|
120
|
+
|
121
|
+
ESP_LOGI(TAG, "Set up speaker media player");
|
122
|
+
}
|
123
|
+
|
124
|
+
void SpeakerMediaPlayer::set_playlist_delay_ms(AudioPipelineType pipeline_type, uint32_t delay_ms) {
|
125
|
+
switch (pipeline_type) {
|
126
|
+
case AudioPipelineType::ANNOUNCEMENT:
|
127
|
+
this->announcement_playlist_delay_ms_ = delay_ms;
|
128
|
+
break;
|
129
|
+
case AudioPipelineType::MEDIA:
|
130
|
+
this->media_playlist_delay_ms_ = delay_ms;
|
131
|
+
break;
|
132
|
+
}
|
133
|
+
}
|
134
|
+
|
135
|
+
void SpeakerMediaPlayer::watch_media_commands_() {
|
136
|
+
if (!this->is_ready()) {
|
137
|
+
return;
|
138
|
+
}
|
139
|
+
|
140
|
+
MediaCallCommand media_command;
|
141
|
+
esp_err_t err = ESP_OK;
|
142
|
+
|
143
|
+
if (xQueueReceive(this->media_control_command_queue_, &media_command, 0) == pdTRUE) {
|
144
|
+
bool new_url = media_command.new_url.has_value() && media_command.new_url.value();
|
145
|
+
bool new_file = media_command.new_file.has_value() && media_command.new_file.value();
|
146
|
+
|
147
|
+
if (new_url || new_file) {
|
148
|
+
bool enqueue = media_command.enqueue.has_value() && media_command.enqueue.value();
|
149
|
+
|
150
|
+
if (this->single_pipeline_() || (media_command.announce.has_value() && media_command.announce.value())) {
|
151
|
+
// Announcement playlist/pipeline
|
152
|
+
|
153
|
+
if (!enqueue) {
|
154
|
+
// Clear the queue and ensure the loaded next item doesn't start playing
|
155
|
+
this->cancel_timeout("next_ann");
|
156
|
+
this->announcement_playlist_.clear();
|
157
|
+
}
|
158
|
+
|
159
|
+
PlaylistItem playlist_item;
|
160
|
+
if (new_url) {
|
161
|
+
playlist_item.url = this->announcement_url_;
|
162
|
+
if (!enqueue) {
|
163
|
+
// Not adding to the queue, so directly start playback and internally unpause the pipeline
|
164
|
+
this->announcement_pipeline_->start_url(playlist_item.url.value());
|
165
|
+
this->announcement_pipeline_->set_pause_state(false);
|
166
|
+
}
|
167
|
+
} else {
|
168
|
+
playlist_item.file = this->announcement_file_;
|
169
|
+
if (!enqueue) {
|
170
|
+
// Not adding to the queue, so directly start playback and internally unpause the pipeline
|
171
|
+
this->announcement_pipeline_->start_file(playlist_item.file.value());
|
172
|
+
this->announcement_pipeline_->set_pause_state(false);
|
173
|
+
}
|
174
|
+
}
|
175
|
+
this->announcement_playlist_.push_back(playlist_item);
|
176
|
+
} else {
|
177
|
+
// Media playlist/pipeline
|
178
|
+
|
179
|
+
if (!enqueue) {
|
180
|
+
// Clear the queue and ensure the loaded next item doesn't start playing
|
181
|
+
this->cancel_timeout("next_media");
|
182
|
+
this->media_playlist_.clear();
|
183
|
+
}
|
184
|
+
|
185
|
+
this->is_paused_ = false;
|
186
|
+
PlaylistItem playlist_item;
|
187
|
+
if (new_url) {
|
188
|
+
playlist_item.url = this->media_url_;
|
189
|
+
if (!enqueue) {
|
190
|
+
// Not adding to the queue, so directly start playback and internally unpause the pipeline
|
191
|
+
this->media_pipeline_->start_url(playlist_item.url.value());
|
192
|
+
this->media_pipeline_->set_pause_state(false);
|
193
|
+
}
|
194
|
+
} else {
|
195
|
+
playlist_item.file = this->media_file_;
|
196
|
+
if (!enqueue) {
|
197
|
+
// Not adding to the queue, so directly start playback and internally unpause the pipeline
|
198
|
+
this->media_pipeline_->start_file(playlist_item.file.value());
|
199
|
+
this->media_pipeline_->set_pause_state(false);
|
200
|
+
}
|
201
|
+
}
|
202
|
+
this->media_playlist_.push_back(playlist_item);
|
203
|
+
}
|
204
|
+
|
205
|
+
if (err != ESP_OK) {
|
206
|
+
ESP_LOGE(TAG, "Error starting the audio pipeline: %s", esp_err_to_name(err));
|
207
|
+
this->status_set_error();
|
208
|
+
} else {
|
209
|
+
this->status_clear_error();
|
210
|
+
}
|
211
|
+
|
212
|
+
return; // Don't process the new file play command further
|
213
|
+
}
|
214
|
+
|
215
|
+
if (media_command.volume.has_value()) {
|
216
|
+
this->set_volume_(media_command.volume.value());
|
217
|
+
this->publish_state();
|
218
|
+
}
|
219
|
+
|
220
|
+
if (media_command.command.has_value()) {
|
221
|
+
switch (media_command.command.value()) {
|
222
|
+
case media_player::MEDIA_PLAYER_COMMAND_PLAY:
|
223
|
+
if ((this->media_pipeline_ != nullptr) && (this->is_paused_)) {
|
224
|
+
this->media_pipeline_->set_pause_state(false);
|
225
|
+
}
|
226
|
+
this->is_paused_ = false;
|
227
|
+
break;
|
228
|
+
case media_player::MEDIA_PLAYER_COMMAND_PAUSE:
|
229
|
+
if ((this->media_pipeline_ != nullptr) && (!this->is_paused_)) {
|
230
|
+
this->media_pipeline_->set_pause_state(true);
|
231
|
+
}
|
232
|
+
this->is_paused_ = true;
|
233
|
+
break;
|
234
|
+
case media_player::MEDIA_PLAYER_COMMAND_STOP:
|
235
|
+
if (this->single_pipeline_() || (media_command.announce.has_value() && media_command.announce.value())) {
|
236
|
+
if (this->announcement_pipeline_ != nullptr) {
|
237
|
+
this->cancel_timeout("next_ann");
|
238
|
+
this->announcement_playlist_.clear();
|
239
|
+
this->announcement_pipeline_->stop();
|
240
|
+
}
|
241
|
+
} else {
|
242
|
+
if (this->media_pipeline_ != nullptr) {
|
243
|
+
this->cancel_timeout("next_media");
|
244
|
+
this->media_playlist_.clear();
|
245
|
+
this->media_pipeline_->stop();
|
246
|
+
}
|
247
|
+
}
|
248
|
+
break;
|
249
|
+
case media_player::MEDIA_PLAYER_COMMAND_TOGGLE:
|
250
|
+
if (this->media_pipeline_ != nullptr) {
|
251
|
+
if (this->is_paused_) {
|
252
|
+
this->media_pipeline_->set_pause_state(false);
|
253
|
+
this->is_paused_ = false;
|
254
|
+
} else {
|
255
|
+
this->media_pipeline_->set_pause_state(true);
|
256
|
+
this->is_paused_ = true;
|
257
|
+
}
|
258
|
+
}
|
259
|
+
break;
|
260
|
+
case media_player::MEDIA_PLAYER_COMMAND_MUTE: {
|
261
|
+
this->set_mute_state_(true);
|
262
|
+
|
263
|
+
this->publish_state();
|
264
|
+
break;
|
265
|
+
}
|
266
|
+
case media_player::MEDIA_PLAYER_COMMAND_UNMUTE:
|
267
|
+
this->set_mute_state_(false);
|
268
|
+
this->publish_state();
|
269
|
+
break;
|
270
|
+
case media_player::MEDIA_PLAYER_COMMAND_VOLUME_UP:
|
271
|
+
this->set_volume_(std::min(1.0f, this->volume + this->volume_increment_));
|
272
|
+
this->publish_state();
|
273
|
+
break;
|
274
|
+
case media_player::MEDIA_PLAYER_COMMAND_VOLUME_DOWN:
|
275
|
+
this->set_volume_(std::max(0.0f, this->volume - this->volume_increment_));
|
276
|
+
this->publish_state();
|
277
|
+
break;
|
278
|
+
case media_player::MEDIA_PLAYER_COMMAND_REPEAT_ONE:
|
279
|
+
if (this->single_pipeline_() || (media_command.announce.has_value() && media_command.announce.value())) {
|
280
|
+
this->announcement_repeat_one_ = true;
|
281
|
+
} else {
|
282
|
+
this->media_repeat_one_ = true;
|
283
|
+
}
|
284
|
+
break;
|
285
|
+
case media_player::MEDIA_PLAYER_COMMAND_REPEAT_OFF:
|
286
|
+
if (this->single_pipeline_() || (media_command.announce.has_value() && media_command.announce.value())) {
|
287
|
+
this->announcement_repeat_one_ = false;
|
288
|
+
} else {
|
289
|
+
this->media_repeat_one_ = false;
|
290
|
+
}
|
291
|
+
break;
|
292
|
+
case media_player::MEDIA_PLAYER_COMMAND_CLEAR_PLAYLIST:
|
293
|
+
if (this->single_pipeline_() || (media_command.announce.has_value() && media_command.announce.value())) {
|
294
|
+
if (this->announcement_playlist_.empty()) {
|
295
|
+
this->announcement_playlist_.resize(1);
|
296
|
+
}
|
297
|
+
} else {
|
298
|
+
if (this->media_playlist_.empty()) {
|
299
|
+
this->media_playlist_.resize(1);
|
300
|
+
}
|
301
|
+
}
|
302
|
+
break;
|
303
|
+
default:
|
304
|
+
break;
|
305
|
+
}
|
306
|
+
}
|
307
|
+
}
|
308
|
+
}
|
309
|
+
|
310
|
+
void SpeakerMediaPlayer::loop() {
|
311
|
+
this->watch_media_commands_();
|
312
|
+
|
313
|
+
// Determine state of the media player
|
314
|
+
media_player::MediaPlayerState old_state = this->state;
|
315
|
+
|
316
|
+
AudioPipelineState old_media_pipeline_state = this->media_pipeline_state_;
|
317
|
+
if (this->media_pipeline_ != nullptr) {
|
318
|
+
this->media_pipeline_state_ = this->media_pipeline_->process_state();
|
319
|
+
this->decoded_playback_ms_ = this->media_pipeline_->get_playback_ms();
|
320
|
+
}
|
321
|
+
|
322
|
+
if (this->media_pipeline_state_ == AudioPipelineState::ERROR_READING) {
|
323
|
+
ESP_LOGE(TAG, "The media pipeline's file reader encountered an error.");
|
324
|
+
} else if (this->media_pipeline_state_ == AudioPipelineState::ERROR_DECODING) {
|
325
|
+
ESP_LOGE(TAG, "The media pipeline's audio decoder encountered an error.");
|
326
|
+
}
|
327
|
+
|
328
|
+
AudioPipelineState old_announcement_pipeline_state = this->announcement_pipeline_state_;
|
329
|
+
if (this->announcement_pipeline_ != nullptr) {
|
330
|
+
this->announcement_pipeline_state_ = this->announcement_pipeline_->process_state();
|
331
|
+
}
|
332
|
+
|
333
|
+
if (this->announcement_pipeline_state_ == AudioPipelineState::ERROR_READING) {
|
334
|
+
ESP_LOGE(TAG, "The announcement pipeline's file reader encountered an error.");
|
335
|
+
} else if (this->announcement_pipeline_state_ == AudioPipelineState::ERROR_DECODING) {
|
336
|
+
ESP_LOGE(TAG, "The announcement pipeline's audio decoder encountered an error.");
|
337
|
+
}
|
338
|
+
|
339
|
+
if (this->announcement_pipeline_state_ != AudioPipelineState::STOPPED) {
|
340
|
+
this->state = media_player::MEDIA_PLAYER_STATE_ANNOUNCING;
|
341
|
+
} else {
|
342
|
+
if (!this->announcement_playlist_.empty()) {
|
343
|
+
uint32_t timeout_ms = 0;
|
344
|
+
if (old_announcement_pipeline_state == AudioPipelineState::PLAYING) {
|
345
|
+
// Finished the current announcement file
|
346
|
+
if (!this->announcement_repeat_one_) {
|
347
|
+
// Pop item off the playlist if repeat is disabled
|
348
|
+
this->announcement_playlist_.pop_front();
|
349
|
+
}
|
350
|
+
// Only delay starting playback if moving on the next playlist item or repeating the current item
|
351
|
+
timeout_ms = this->announcement_playlist_delay_ms_;
|
352
|
+
}
|
353
|
+
|
354
|
+
if (!this->announcement_playlist_.empty()) {
|
355
|
+
// Start the next announcement file
|
356
|
+
PlaylistItem playlist_item = this->announcement_playlist_.front();
|
357
|
+
if (playlist_item.url.has_value()) {
|
358
|
+
this->announcement_pipeline_->start_url(playlist_item.url.value());
|
359
|
+
} else if (playlist_item.file.has_value()) {
|
360
|
+
this->announcement_pipeline_->start_file(playlist_item.file.value());
|
361
|
+
}
|
362
|
+
|
363
|
+
if (timeout_ms > 0) {
|
364
|
+
// Pause pipeline internally to facilitiate delay between items
|
365
|
+
this->announcement_pipeline_->set_pause_state(true);
|
366
|
+
// Internally unpause the pipeline after the delay between playlist items
|
367
|
+
this->set_timeout("next_ann", timeout_ms,
|
368
|
+
[this]() { this->announcement_pipeline_->set_pause_state(this->is_paused_); });
|
369
|
+
}
|
370
|
+
}
|
371
|
+
} else {
|
372
|
+
if (this->is_paused_) {
|
373
|
+
this->state = media_player::MEDIA_PLAYER_STATE_PAUSED;
|
374
|
+
} else if (this->media_pipeline_state_ == AudioPipelineState::PLAYING) {
|
375
|
+
this->state = media_player::MEDIA_PLAYER_STATE_PLAYING;
|
376
|
+
} else if (this->media_pipeline_state_ == AudioPipelineState::STOPPED) {
|
377
|
+
// Reset playback durations
|
378
|
+
this->decoded_playback_ms_ = 0;
|
379
|
+
this->playback_us_ = 0;
|
380
|
+
this->playback_ms_ = 0;
|
381
|
+
this->remainder_us_ = 0;
|
382
|
+
this->pending_ms_ = 0;
|
383
|
+
|
384
|
+
if (!media_playlist_.empty()) {
|
385
|
+
uint32_t timeout_ms = 0;
|
386
|
+
if (old_media_pipeline_state == AudioPipelineState::PLAYING) {
|
387
|
+
// Finished the current media file
|
388
|
+
if (!this->media_repeat_one_) {
|
389
|
+
// Pop item off the playlist if repeat is disabled
|
390
|
+
this->media_playlist_.pop_front();
|
391
|
+
}
|
392
|
+
// Only delay starting playback if moving on the next playlist item or repeating the current item
|
393
|
+
timeout_ms = this->announcement_playlist_delay_ms_;
|
394
|
+
}
|
395
|
+
if (!this->media_playlist_.empty()) {
|
396
|
+
PlaylistItem playlist_item = this->media_playlist_.front();
|
397
|
+
if (playlist_item.url.has_value()) {
|
398
|
+
this->media_pipeline_->start_url(playlist_item.url.value());
|
399
|
+
} else if (playlist_item.file.has_value()) {
|
400
|
+
this->media_pipeline_->start_file(playlist_item.file.value());
|
401
|
+
}
|
402
|
+
|
403
|
+
if (timeout_ms > 0) {
|
404
|
+
// Pause pipeline internally to facilitiate delay between items
|
405
|
+
this->media_pipeline_->set_pause_state(true);
|
406
|
+
// Internally unpause the pipeline after the delay between playlist items
|
407
|
+
this->set_timeout("next_media", timeout_ms,
|
408
|
+
[this]() { this->media_pipeline_->set_pause_state(this->is_paused_); });
|
409
|
+
}
|
410
|
+
}
|
411
|
+
} else {
|
412
|
+
this->state = media_player::MEDIA_PLAYER_STATE_IDLE;
|
413
|
+
}
|
414
|
+
}
|
415
|
+
}
|
416
|
+
}
|
417
|
+
|
418
|
+
if (this->state != old_state) {
|
419
|
+
this->publish_state();
|
420
|
+
ESP_LOGD(TAG, "State changed to %s", media_player::media_player_state_to_string(this->state));
|
421
|
+
}
|
422
|
+
}
|
423
|
+
|
424
|
+
void SpeakerMediaPlayer::play_file(audio::AudioFile *media_file, bool announcement, bool enqueue) {
|
425
|
+
if (!this->is_ready()) {
|
426
|
+
// Ignore any commands sent before the media player is setup
|
427
|
+
return;
|
428
|
+
}
|
429
|
+
|
430
|
+
MediaCallCommand media_command;
|
431
|
+
|
432
|
+
media_command.new_file = true;
|
433
|
+
if (this->single_pipeline_() || announcement) {
|
434
|
+
this->announcement_file_ = media_file;
|
435
|
+
media_command.announce = true;
|
436
|
+
} else {
|
437
|
+
this->media_file_ = media_file;
|
438
|
+
media_command.announce = false;
|
439
|
+
}
|
440
|
+
media_command.enqueue = enqueue;
|
441
|
+
xQueueSend(this->media_control_command_queue_, &media_command, portMAX_DELAY);
|
442
|
+
}
|
443
|
+
|
444
|
+
void SpeakerMediaPlayer::control(const media_player::MediaPlayerCall &call) {
|
445
|
+
if (!this->is_ready()) {
|
446
|
+
// Ignore any commands sent before the media player is setup
|
447
|
+
return;
|
448
|
+
}
|
449
|
+
|
450
|
+
MediaCallCommand media_command;
|
451
|
+
|
452
|
+
if (this->single_pipeline_() || (call.get_announcement().has_value() && call.get_announcement().value())) {
|
453
|
+
media_command.announce = true;
|
454
|
+
} else {
|
455
|
+
media_command.announce = false;
|
456
|
+
}
|
457
|
+
|
458
|
+
if (call.get_media_url().has_value()) {
|
459
|
+
std::string new_uri = call.get_media_url().value();
|
460
|
+
|
461
|
+
media_command.new_url = true;
|
462
|
+
if (this->single_pipeline_() || (call.get_announcement().has_value() && call.get_announcement().value())) {
|
463
|
+
this->announcement_url_ = new_uri;
|
464
|
+
} else {
|
465
|
+
this->media_url_ = new_uri;
|
466
|
+
}
|
467
|
+
|
468
|
+
if (call.get_command().has_value()) {
|
469
|
+
if (call.get_command().value() == media_player::MEDIA_PLAYER_COMMAND_ENQUEUE) {
|
470
|
+
media_command.enqueue = true;
|
471
|
+
}
|
472
|
+
}
|
473
|
+
|
474
|
+
xQueueSend(this->media_control_command_queue_, &media_command, portMAX_DELAY);
|
475
|
+
return;
|
476
|
+
}
|
477
|
+
|
478
|
+
if (call.get_volume().has_value()) {
|
479
|
+
media_command.volume = call.get_volume().value();
|
480
|
+
// Wait 0 ticks for queue to be free, volume sets aren't that important!
|
481
|
+
xQueueSend(this->media_control_command_queue_, &media_command, 0);
|
482
|
+
return;
|
483
|
+
}
|
484
|
+
|
485
|
+
if (call.get_command().has_value()) {
|
486
|
+
media_command.command = call.get_command().value();
|
487
|
+
TickType_t ticks_to_wait = portMAX_DELAY;
|
488
|
+
if ((call.get_command().value() == media_player::MEDIA_PLAYER_COMMAND_VOLUME_UP) ||
|
489
|
+
(call.get_command().value() == media_player::MEDIA_PLAYER_COMMAND_VOLUME_DOWN)) {
|
490
|
+
ticks_to_wait = 0; // Wait 0 ticks for queue to be free, volume sets aren't that important!
|
491
|
+
}
|
492
|
+
xQueueSend(this->media_control_command_queue_, &media_command, ticks_to_wait);
|
493
|
+
return;
|
494
|
+
}
|
495
|
+
}
|
496
|
+
|
497
|
+
media_player::MediaPlayerTraits SpeakerMediaPlayer::get_traits() {
|
498
|
+
auto traits = media_player::MediaPlayerTraits();
|
499
|
+
if (!this->single_pipeline_()) {
|
500
|
+
traits.set_supports_pause(true);
|
501
|
+
}
|
502
|
+
|
503
|
+
if (this->announcement_format_.has_value()) {
|
504
|
+
traits.get_supported_formats().push_back(this->announcement_format_.value());
|
505
|
+
}
|
506
|
+
if (this->media_format_.has_value()) {
|
507
|
+
traits.get_supported_formats().push_back(this->media_format_.value());
|
508
|
+
} else if (this->single_pipeline_() && this->announcement_format_.has_value()) {
|
509
|
+
// Only one pipeline is defined, so use the announcement format (if configured) for the default purpose
|
510
|
+
media_player::MediaPlayerSupportedFormat media_format = this->announcement_format_.value();
|
511
|
+
media_format.purpose = media_player::MediaPlayerFormatPurpose::PURPOSE_DEFAULT;
|
512
|
+
traits.get_supported_formats().push_back(media_format);
|
513
|
+
}
|
514
|
+
|
515
|
+
return traits;
|
516
|
+
};
|
517
|
+
|
518
|
+
void SpeakerMediaPlayer::save_volume_restore_state_() {
|
519
|
+
VolumeRestoreState volume_restore_state;
|
520
|
+
volume_restore_state.volume = this->volume;
|
521
|
+
volume_restore_state.is_muted = this->is_muted_;
|
522
|
+
this->pref_.save(&volume_restore_state);
|
523
|
+
}
|
524
|
+
|
525
|
+
void SpeakerMediaPlayer::set_mute_state_(bool mute_state) {
|
526
|
+
if (this->media_speaker_ != nullptr) {
|
527
|
+
this->media_speaker_->set_mute_state(mute_state);
|
528
|
+
}
|
529
|
+
if (this->announcement_speaker_ != nullptr) {
|
530
|
+
this->announcement_speaker_->set_mute_state(mute_state);
|
531
|
+
}
|
532
|
+
|
533
|
+
bool old_mute_state = this->is_muted_;
|
534
|
+
this->is_muted_ = mute_state;
|
535
|
+
|
536
|
+
this->save_volume_restore_state_();
|
537
|
+
|
538
|
+
if (old_mute_state != mute_state) {
|
539
|
+
if (mute_state) {
|
540
|
+
this->defer([this]() { this->mute_trigger_->trigger(); });
|
541
|
+
} else {
|
542
|
+
this->defer([this]() { this->unmute_trigger_->trigger(); });
|
543
|
+
}
|
544
|
+
}
|
545
|
+
}
|
546
|
+
|
547
|
+
void SpeakerMediaPlayer::set_volume_(float volume, bool publish) {
|
548
|
+
// Remap the volume to fit with in the configured limits
|
549
|
+
float bounded_volume = remap<float, float>(volume, 0.0f, 1.0f, this->volume_min_, this->volume_max_);
|
550
|
+
|
551
|
+
if (this->media_speaker_ != nullptr) {
|
552
|
+
this->media_speaker_->set_volume(bounded_volume);
|
553
|
+
}
|
554
|
+
|
555
|
+
if (this->announcement_speaker_ != nullptr) {
|
556
|
+
this->announcement_speaker_->set_volume(bounded_volume);
|
557
|
+
}
|
558
|
+
|
559
|
+
if (publish) {
|
560
|
+
this->volume = volume;
|
561
|
+
this->save_volume_restore_state_();
|
562
|
+
}
|
563
|
+
|
564
|
+
// Turn on the mute state if the volume is effectively zero, off otherwise
|
565
|
+
if (volume < 0.001) {
|
566
|
+
this->set_mute_state_(true);
|
567
|
+
} else {
|
568
|
+
this->set_mute_state_(false);
|
569
|
+
}
|
570
|
+
|
571
|
+
this->defer([this, volume]() { this->volume_trigger_->trigger(volume); });
|
572
|
+
}
|
573
|
+
|
574
|
+
} // namespace speaker
|
575
|
+
} // namespace esphome
|
576
|
+
|
577
|
+
#endif
|