esphome 2024.12.3__py3-none-any.whl → 2025.2.0b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/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/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 +2 -1
- esphome/components/display/__init__.py +18 -5
- esphome/components/display/display.cpp +2 -1
- 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_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/gcja5/gcja5.cpp +2 -1
- 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 +43 -7
- esphome/components/logger/logger.cpp +16 -11
- 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/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 +28 -2
- esphome/components/online_image/image_decoder.h +24 -15
- esphome/components/online_image/jpeg_image.cpp +90 -0
- esphome/components/online_image/jpeg_image.h +34 -0
- esphome/components/online_image/online_image.cpp +112 -53
- esphome/components/online_image/online_image.h +24 -7
- 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/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 +57 -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/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.0b1.dist-info}/METADATA +12 -7
- {esphome-2024.12.3.dist-info → esphome-2025.2.0b1.dist-info}/RECORD +338 -289
- 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.0b1.dist-info}/LICENSE +0 -0
- {esphome-2024.12.3.dist-info → esphome-2025.2.0b1.dist-info}/WHEEL +0 -0
- {esphome-2024.12.3.dist-info → esphome-2025.2.0b1.dist-info}/entry_points.txt +0 -0
- {esphome-2024.12.3.dist-info → esphome-2025.2.0b1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,458 @@
|
|
1
|
+
"""Speaker Media Player Setup."""
|
2
|
+
|
3
|
+
import hashlib
|
4
|
+
import logging
|
5
|
+
from pathlib import Path
|
6
|
+
|
7
|
+
from esphome import automation, external_files
|
8
|
+
import esphome.codegen as cg
|
9
|
+
from esphome.components import audio, esp32, media_player, speaker
|
10
|
+
import esphome.config_validation as cv
|
11
|
+
from esphome.const import (
|
12
|
+
CONF_BUFFER_SIZE,
|
13
|
+
CONF_FILE,
|
14
|
+
CONF_FILES,
|
15
|
+
CONF_FORMAT,
|
16
|
+
CONF_ID,
|
17
|
+
CONF_NUM_CHANNELS,
|
18
|
+
CONF_PATH,
|
19
|
+
CONF_RAW_DATA_ID,
|
20
|
+
CONF_SAMPLE_RATE,
|
21
|
+
CONF_SPEAKER,
|
22
|
+
CONF_TASK_STACK_IN_PSRAM,
|
23
|
+
CONF_TYPE,
|
24
|
+
CONF_URL,
|
25
|
+
)
|
26
|
+
from esphome.core import CORE, HexInt
|
27
|
+
from esphome.core.entity_helpers import inherit_property_from
|
28
|
+
from esphome.external_files import download_content
|
29
|
+
|
30
|
+
_LOGGER = logging.getLogger(__name__)
|
31
|
+
|
32
|
+
AUTO_LOAD = ["audio", "psram"]
|
33
|
+
|
34
|
+
CODEOWNERS = ["@kahrendt", "@synesthesiam"]
|
35
|
+
DOMAIN = "media_player"
|
36
|
+
|
37
|
+
TYPE_LOCAL = "local"
|
38
|
+
TYPE_WEB = "web"
|
39
|
+
|
40
|
+
CONF_ANNOUNCEMENT = "announcement"
|
41
|
+
CONF_ANNOUNCEMENT_PIPELINE = "announcement_pipeline"
|
42
|
+
CONF_CODEC_SUPPORT_ENABLED = "codec_support_enabled"
|
43
|
+
CONF_ENQUEUE = "enqueue"
|
44
|
+
CONF_MEDIA_FILE = "media_file"
|
45
|
+
CONF_MEDIA_PIPELINE = "media_pipeline"
|
46
|
+
CONF_ON_MUTE = "on_mute"
|
47
|
+
CONF_ON_UNMUTE = "on_unmute"
|
48
|
+
CONF_ON_VOLUME = "on_volume"
|
49
|
+
CONF_STREAM = "stream"
|
50
|
+
CONF_VOLUME_INCREMENT = "volume_increment"
|
51
|
+
CONF_VOLUME_MIN = "volume_min"
|
52
|
+
CONF_VOLUME_MAX = "volume_max"
|
53
|
+
|
54
|
+
|
55
|
+
speaker_ns = cg.esphome_ns.namespace("speaker")
|
56
|
+
SpeakerMediaPlayer = speaker_ns.class_(
|
57
|
+
"SpeakerMediaPlayer",
|
58
|
+
media_player.MediaPlayer,
|
59
|
+
cg.Component,
|
60
|
+
)
|
61
|
+
|
62
|
+
AudioPipeline = speaker_ns.class_("AudioPipeline")
|
63
|
+
AudioPipelineType = speaker_ns.enum("AudioPipelineType", is_class=True)
|
64
|
+
AUDIO_PIPELINE_TYPE_ENUM = {
|
65
|
+
"MEDIA": AudioPipelineType.MEDIA,
|
66
|
+
"ANNOUNCEMENT": AudioPipelineType.ANNOUNCEMENT,
|
67
|
+
}
|
68
|
+
|
69
|
+
PlayOnDeviceMediaAction = speaker_ns.class_(
|
70
|
+
"PlayOnDeviceMediaAction",
|
71
|
+
automation.Action,
|
72
|
+
cg.Parented.template(SpeakerMediaPlayer),
|
73
|
+
)
|
74
|
+
StopStreamAction = speaker_ns.class_(
|
75
|
+
"StopStreamAction", automation.Action, cg.Parented.template(SpeakerMediaPlayer)
|
76
|
+
)
|
77
|
+
|
78
|
+
|
79
|
+
def _compute_local_file_path(value: dict) -> Path:
|
80
|
+
url = value[CONF_URL]
|
81
|
+
h = hashlib.new("sha256")
|
82
|
+
h.update(url.encode())
|
83
|
+
key = h.hexdigest()[:8]
|
84
|
+
base_dir = external_files.compute_local_file_dir(DOMAIN)
|
85
|
+
_LOGGER.debug("_compute_local_file_path: base_dir=%s", base_dir / key)
|
86
|
+
return base_dir / key
|
87
|
+
|
88
|
+
|
89
|
+
def _download_web_file(value):
|
90
|
+
url = value[CONF_URL]
|
91
|
+
path = _compute_local_file_path(value)
|
92
|
+
|
93
|
+
download_content(url, path)
|
94
|
+
_LOGGER.debug("download_web_file: path=%s", path)
|
95
|
+
return value
|
96
|
+
|
97
|
+
|
98
|
+
# Returns a media_player.MediaPlayerSupportedFormat struct with the configured
|
99
|
+
# format, sample rate, number of channels, purpose, and bytes per sample
|
100
|
+
def _get_supported_format_struct(pipeline, type):
|
101
|
+
args = [
|
102
|
+
media_player.MediaPlayerSupportedFormat,
|
103
|
+
]
|
104
|
+
|
105
|
+
if pipeline[CONF_FORMAT] == "FLAC":
|
106
|
+
args.append(("format", "flac"))
|
107
|
+
elif pipeline[CONF_FORMAT] == "MP3":
|
108
|
+
args.append(("format", "mp3"))
|
109
|
+
elif pipeline[CONF_FORMAT] == "WAV":
|
110
|
+
args.append(("format", "wav"))
|
111
|
+
|
112
|
+
args.append(("sample_rate", pipeline[CONF_SAMPLE_RATE]))
|
113
|
+
args.append(("num_channels", pipeline[CONF_NUM_CHANNELS]))
|
114
|
+
|
115
|
+
if type == "MEDIA":
|
116
|
+
args.append(
|
117
|
+
(
|
118
|
+
"purpose",
|
119
|
+
media_player.MEDIA_PLAYER_FORMAT_PURPOSE_ENUM["default"],
|
120
|
+
)
|
121
|
+
)
|
122
|
+
elif type == "ANNOUNCEMENT":
|
123
|
+
args.append(
|
124
|
+
(
|
125
|
+
"purpose",
|
126
|
+
media_player.MEDIA_PLAYER_FORMAT_PURPOSE_ENUM["announcement"],
|
127
|
+
)
|
128
|
+
)
|
129
|
+
if pipeline[CONF_FORMAT] != "MP3":
|
130
|
+
args.append(("sample_bytes", 2))
|
131
|
+
|
132
|
+
return cg.StructInitializer(*args)
|
133
|
+
|
134
|
+
|
135
|
+
def _file_schema(value):
|
136
|
+
if isinstance(value, str):
|
137
|
+
return _validate_file_shorthand(value)
|
138
|
+
return TYPED_FILE_SCHEMA(value)
|
139
|
+
|
140
|
+
|
141
|
+
def _read_audio_file_and_type(file_config):
|
142
|
+
conf_file = file_config[CONF_FILE]
|
143
|
+
file_source = conf_file[CONF_TYPE]
|
144
|
+
if file_source == TYPE_LOCAL:
|
145
|
+
path = CORE.relative_config_path(conf_file[CONF_PATH])
|
146
|
+
elif file_source == TYPE_WEB:
|
147
|
+
path = _compute_local_file_path(conf_file)
|
148
|
+
else:
|
149
|
+
raise cv.Invalid("Unsupported file source.")
|
150
|
+
|
151
|
+
with open(path, "rb") as f:
|
152
|
+
data = f.read()
|
153
|
+
|
154
|
+
import puremagic
|
155
|
+
|
156
|
+
file_type: str = puremagic.from_string(data)
|
157
|
+
if file_type.startswith("."):
|
158
|
+
file_type = file_type[1:]
|
159
|
+
|
160
|
+
media_file_type = audio.AUDIO_FILE_TYPE_ENUM["NONE"]
|
161
|
+
if file_type in ("wav"):
|
162
|
+
media_file_type = audio.AUDIO_FILE_TYPE_ENUM["WAV"]
|
163
|
+
elif file_type in ("mp3", "mpeg", "mpga"):
|
164
|
+
media_file_type = audio.AUDIO_FILE_TYPE_ENUM["MP3"]
|
165
|
+
elif file_type in ("flac"):
|
166
|
+
media_file_type = audio.AUDIO_FILE_TYPE_ENUM["FLAC"]
|
167
|
+
|
168
|
+
return data, media_file_type
|
169
|
+
|
170
|
+
|
171
|
+
def _validate_file_shorthand(value):
|
172
|
+
value = cv.string_strict(value)
|
173
|
+
if value.startswith("http://") or value.startswith("https://"):
|
174
|
+
return _file_schema(
|
175
|
+
{
|
176
|
+
CONF_TYPE: TYPE_WEB,
|
177
|
+
CONF_URL: value,
|
178
|
+
}
|
179
|
+
)
|
180
|
+
return _file_schema(
|
181
|
+
{
|
182
|
+
CONF_TYPE: TYPE_LOCAL,
|
183
|
+
CONF_PATH: value,
|
184
|
+
}
|
185
|
+
)
|
186
|
+
|
187
|
+
|
188
|
+
def _validate_pipeline(config):
|
189
|
+
# Inherit transcoder settings from speaker if not manually set
|
190
|
+
inherit_property_from(CONF_NUM_CHANNELS, CONF_SPEAKER)(config)
|
191
|
+
inherit_property_from(CONF_SAMPLE_RATE, CONF_SPEAKER)(config)
|
192
|
+
|
193
|
+
# Validate the transcoder settings is compatible with the speaker
|
194
|
+
audio.final_validate_audio_schema(
|
195
|
+
"speaker media_player",
|
196
|
+
audio_device=CONF_SPEAKER,
|
197
|
+
bits_per_sample=16,
|
198
|
+
channels=config.get(CONF_NUM_CHANNELS),
|
199
|
+
sample_rate=config.get(CONF_SAMPLE_RATE),
|
200
|
+
)(config)
|
201
|
+
|
202
|
+
return config
|
203
|
+
|
204
|
+
|
205
|
+
def _validate_repeated_speaker(config):
|
206
|
+
if (announcement_config := config.get(CONF_ANNOUNCEMENT_PIPELINE)) and (
|
207
|
+
media_config := config.get(CONF_MEDIA_PIPELINE)
|
208
|
+
):
|
209
|
+
if announcement_config[CONF_SPEAKER] == media_config[CONF_SPEAKER]:
|
210
|
+
raise cv.Invalid(
|
211
|
+
"The announcement and media pipelines cannot use the same speaker. Use the `mixer` speaker component to create two source speakers."
|
212
|
+
)
|
213
|
+
|
214
|
+
return config
|
215
|
+
|
216
|
+
|
217
|
+
def _validate_supported_local_file(config):
|
218
|
+
for file_config in config.get(CONF_FILES, []):
|
219
|
+
_, media_file_type = _read_audio_file_and_type(file_config)
|
220
|
+
if str(media_file_type) == str(audio.AUDIO_FILE_TYPE_ENUM["NONE"]):
|
221
|
+
raise cv.Invalid("Unsupported local media file.")
|
222
|
+
if not config[CONF_CODEC_SUPPORT_ENABLED] and str(media_file_type) != str(
|
223
|
+
audio.AUDIO_FILE_TYPE_ENUM["WAV"]
|
224
|
+
):
|
225
|
+
# Only wav files are supported
|
226
|
+
raise cv.Invalid(
|
227
|
+
f"Unsupported local media file type, set {CONF_CODEC_SUPPORT_ENABLED} to true or convert the media file to wav"
|
228
|
+
)
|
229
|
+
|
230
|
+
return config
|
231
|
+
|
232
|
+
|
233
|
+
LOCAL_SCHEMA = cv.Schema(
|
234
|
+
{
|
235
|
+
cv.Required(CONF_PATH): cv.file_,
|
236
|
+
}
|
237
|
+
)
|
238
|
+
|
239
|
+
WEB_SCHEMA = cv.All(
|
240
|
+
{
|
241
|
+
cv.Required(CONF_URL): cv.url,
|
242
|
+
},
|
243
|
+
_download_web_file,
|
244
|
+
)
|
245
|
+
|
246
|
+
|
247
|
+
TYPED_FILE_SCHEMA = cv.typed_schema(
|
248
|
+
{
|
249
|
+
TYPE_LOCAL: LOCAL_SCHEMA,
|
250
|
+
TYPE_WEB: WEB_SCHEMA,
|
251
|
+
},
|
252
|
+
)
|
253
|
+
|
254
|
+
|
255
|
+
MEDIA_FILE_TYPE_SCHEMA = cv.Schema(
|
256
|
+
{
|
257
|
+
cv.Required(CONF_ID): cv.declare_id(audio.AudioFile),
|
258
|
+
cv.Required(CONF_FILE): _file_schema,
|
259
|
+
cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8),
|
260
|
+
}
|
261
|
+
)
|
262
|
+
|
263
|
+
PIPELINE_SCHEMA = cv.Schema(
|
264
|
+
{
|
265
|
+
cv.GenerateID(): cv.declare_id(AudioPipeline),
|
266
|
+
cv.Required(CONF_SPEAKER): cv.use_id(speaker.Speaker),
|
267
|
+
cv.Optional(CONF_FORMAT, default="FLAC"): cv.enum(audio.AUDIO_FILE_TYPE_ENUM),
|
268
|
+
cv.Optional(CONF_SAMPLE_RATE): cv.int_range(min=1),
|
269
|
+
cv.Optional(CONF_NUM_CHANNELS): cv.int_range(1, 2),
|
270
|
+
}
|
271
|
+
)
|
272
|
+
|
273
|
+
CONFIG_SCHEMA = cv.All(
|
274
|
+
media_player.MEDIA_PLAYER_SCHEMA.extend(
|
275
|
+
{
|
276
|
+
cv.GenerateID(): cv.declare_id(SpeakerMediaPlayer),
|
277
|
+
cv.Required(CONF_ANNOUNCEMENT_PIPELINE): PIPELINE_SCHEMA,
|
278
|
+
cv.Optional(CONF_MEDIA_PIPELINE): PIPELINE_SCHEMA,
|
279
|
+
cv.Optional(CONF_BUFFER_SIZE, default=1000000): cv.int_range(
|
280
|
+
min=4000, max=4000000
|
281
|
+
),
|
282
|
+
cv.Optional(CONF_CODEC_SUPPORT_ENABLED, default=True): cv.boolean,
|
283
|
+
cv.Optional(CONF_FILES): cv.ensure_list(MEDIA_FILE_TYPE_SCHEMA),
|
284
|
+
cv.Optional(CONF_TASK_STACK_IN_PSRAM, default=False): cv.boolean,
|
285
|
+
cv.Optional(CONF_VOLUME_INCREMENT, default=0.05): cv.percentage,
|
286
|
+
cv.Optional(CONF_VOLUME_MAX, default=1.0): cv.percentage,
|
287
|
+
cv.Optional(CONF_VOLUME_MIN, default=0.0): cv.percentage,
|
288
|
+
cv.Optional(CONF_ON_MUTE): automation.validate_automation(single=True),
|
289
|
+
cv.Optional(CONF_ON_UNMUTE): automation.validate_automation(single=True),
|
290
|
+
cv.Optional(CONF_ON_VOLUME): automation.validate_automation(single=True),
|
291
|
+
}
|
292
|
+
),
|
293
|
+
cv.only_with_esp_idf,
|
294
|
+
_validate_repeated_speaker,
|
295
|
+
)
|
296
|
+
|
297
|
+
|
298
|
+
FINAL_VALIDATE_SCHEMA = cv.All(
|
299
|
+
cv.Schema(
|
300
|
+
{
|
301
|
+
cv.Optional(CONF_ANNOUNCEMENT_PIPELINE): _validate_pipeline,
|
302
|
+
cv.Optional(CONF_MEDIA_PIPELINE): _validate_pipeline,
|
303
|
+
},
|
304
|
+
extra=cv.ALLOW_EXTRA,
|
305
|
+
),
|
306
|
+
_validate_supported_local_file,
|
307
|
+
)
|
308
|
+
|
309
|
+
|
310
|
+
async def to_code(config):
|
311
|
+
if config[CONF_CODEC_SUPPORT_ENABLED]:
|
312
|
+
# Compile all supported audio codecs and optimize the wifi settings
|
313
|
+
|
314
|
+
cg.add_define("USE_AUDIO_FLAC_SUPPORT", True)
|
315
|
+
cg.add_define("USE_AUDIO_MP3_SUPPORT", True)
|
316
|
+
|
317
|
+
# Wifi settings based on https://github.com/espressif/esp-adf/issues/297#issuecomment-783811702
|
318
|
+
esp32.add_idf_sdkconfig_option("CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM", 16)
|
319
|
+
esp32.add_idf_sdkconfig_option("CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM", 512)
|
320
|
+
esp32.add_idf_sdkconfig_option("CONFIG_ESP32_WIFI_STATIC_TX_BUFFER", True)
|
321
|
+
esp32.add_idf_sdkconfig_option("CONFIG_ESP32_WIFI_TX_BUFFER_TYPE", 0)
|
322
|
+
esp32.add_idf_sdkconfig_option("CONFIG_ESP32_WIFI_STATIC_TX_BUFFER_NUM", 8)
|
323
|
+
esp32.add_idf_sdkconfig_option("CONFIG_ESP32_WIFI_CACHE_TX_BUFFER_NUM", 32)
|
324
|
+
esp32.add_idf_sdkconfig_option("CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED", True)
|
325
|
+
esp32.add_idf_sdkconfig_option("CONFIG_ESP32_WIFI_TX_BA_WIN", 16)
|
326
|
+
esp32.add_idf_sdkconfig_option("CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED", True)
|
327
|
+
esp32.add_idf_sdkconfig_option("CONFIG_ESP32_WIFI_RX_BA_WIN", 32)
|
328
|
+
esp32.add_idf_sdkconfig_option("CONFIG_LWIP_MAX_ACTIVE_TCP", 16)
|
329
|
+
esp32.add_idf_sdkconfig_option("CONFIG_LWIP_MAX_LISTENING_TCP", 16)
|
330
|
+
esp32.add_idf_sdkconfig_option("CONFIG_TCP_MAXRTX", 12)
|
331
|
+
esp32.add_idf_sdkconfig_option("CONFIG_TCP_SYNMAXRTX", 6)
|
332
|
+
esp32.add_idf_sdkconfig_option("CONFIG_TCP_MSS", 1436)
|
333
|
+
esp32.add_idf_sdkconfig_option("CONFIG_TCP_MSL", 60000)
|
334
|
+
esp32.add_idf_sdkconfig_option("CONFIG_TCP_SND_BUF_DEFAULT", 65535)
|
335
|
+
esp32.add_idf_sdkconfig_option(
|
336
|
+
"CONFIG_TCP_WND_DEFAULT", 65535
|
337
|
+
) # Adjusted from referenced settings to avoid compilation error
|
338
|
+
esp32.add_idf_sdkconfig_option("CONFIG_TCP_RECVMBOX_SIZE", 512)
|
339
|
+
esp32.add_idf_sdkconfig_option("CONFIG_TCP_QUEUE_OOSEQ", True)
|
340
|
+
esp32.add_idf_sdkconfig_option("CONFIG_TCP_OVERSIZE_MSS", True)
|
341
|
+
esp32.add_idf_sdkconfig_option("CONFIG_LWIP_WND_SCALE", True)
|
342
|
+
esp32.add_idf_sdkconfig_option("CONFIG_TCP_RCV_SCALE", 3)
|
343
|
+
esp32.add_idf_sdkconfig_option("CONFIG_LWIP_TCPIP_RECVMBOX_SIZE", 512)
|
344
|
+
|
345
|
+
# Allocate wifi buffers in PSRAM
|
346
|
+
esp32.add_idf_sdkconfig_option("CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP", True)
|
347
|
+
|
348
|
+
var = cg.new_Pvariable(config[CONF_ID])
|
349
|
+
await cg.register_component(var, config)
|
350
|
+
await media_player.register_media_player(var, config)
|
351
|
+
|
352
|
+
cg.add_define("USE_OTA_STATE_CALLBACK")
|
353
|
+
|
354
|
+
cg.add(var.set_buffer_size(config[CONF_BUFFER_SIZE]))
|
355
|
+
|
356
|
+
cg.add(var.set_task_stack_in_psram(config[CONF_TASK_STACK_IN_PSRAM]))
|
357
|
+
if config[CONF_TASK_STACK_IN_PSRAM]:
|
358
|
+
esp32.add_idf_sdkconfig_option(
|
359
|
+
"CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY", True
|
360
|
+
)
|
361
|
+
|
362
|
+
cg.add(var.set_volume_increment(config[CONF_VOLUME_INCREMENT]))
|
363
|
+
cg.add(var.set_volume_max(config[CONF_VOLUME_MAX]))
|
364
|
+
cg.add(var.set_volume_min(config[CONF_VOLUME_MIN]))
|
365
|
+
|
366
|
+
announcement_pipeline_config = config[CONF_ANNOUNCEMENT_PIPELINE]
|
367
|
+
spkr = await cg.get_variable(announcement_pipeline_config[CONF_SPEAKER])
|
368
|
+
cg.add(var.set_announcement_speaker(spkr))
|
369
|
+
if announcement_pipeline_config[CONF_FORMAT] != "NONE":
|
370
|
+
cg.add(
|
371
|
+
var.set_announcement_format(
|
372
|
+
_get_supported_format_struct(
|
373
|
+
announcement_pipeline_config, "ANNOUNCEMENT"
|
374
|
+
)
|
375
|
+
)
|
376
|
+
)
|
377
|
+
|
378
|
+
if media_pipeline_config := config.get(CONF_MEDIA_PIPELINE):
|
379
|
+
spkr = await cg.get_variable(media_pipeline_config[CONF_SPEAKER])
|
380
|
+
cg.add(var.set_media_speaker(spkr))
|
381
|
+
if media_pipeline_config[CONF_FORMAT] != "NONE":
|
382
|
+
cg.add(
|
383
|
+
var.set_media_format(
|
384
|
+
_get_supported_format_struct(media_pipeline_config, "MEDIA")
|
385
|
+
)
|
386
|
+
)
|
387
|
+
|
388
|
+
if on_mute := config.get(CONF_ON_MUTE):
|
389
|
+
await automation.build_automation(
|
390
|
+
var.get_mute_trigger(),
|
391
|
+
[],
|
392
|
+
on_mute,
|
393
|
+
)
|
394
|
+
if on_unmute := config.get(CONF_ON_UNMUTE):
|
395
|
+
await automation.build_automation(
|
396
|
+
var.get_unmute_trigger(),
|
397
|
+
[],
|
398
|
+
on_unmute,
|
399
|
+
)
|
400
|
+
if on_volume := config.get(CONF_ON_VOLUME):
|
401
|
+
await automation.build_automation(
|
402
|
+
var.get_volume_trigger(),
|
403
|
+
[(cg.float_, "x")],
|
404
|
+
on_volume,
|
405
|
+
)
|
406
|
+
|
407
|
+
for file_config in config.get(CONF_FILES, []):
|
408
|
+
data, media_file_type = _read_audio_file_and_type(file_config)
|
409
|
+
|
410
|
+
rhs = [HexInt(x) for x in data]
|
411
|
+
prog_arr = cg.progmem_array(file_config[CONF_RAW_DATA_ID], rhs)
|
412
|
+
|
413
|
+
media_files_struct = cg.StructInitializer(
|
414
|
+
audio.AudioFile,
|
415
|
+
(
|
416
|
+
"data",
|
417
|
+
prog_arr,
|
418
|
+
),
|
419
|
+
(
|
420
|
+
"length",
|
421
|
+
len(rhs),
|
422
|
+
),
|
423
|
+
(
|
424
|
+
"file_type",
|
425
|
+
media_file_type,
|
426
|
+
),
|
427
|
+
)
|
428
|
+
|
429
|
+
cg.new_Pvariable(
|
430
|
+
file_config[CONF_ID],
|
431
|
+
media_files_struct,
|
432
|
+
)
|
433
|
+
|
434
|
+
|
435
|
+
@automation.register_action(
|
436
|
+
"media_player.speaker.play_on_device_media_file",
|
437
|
+
PlayOnDeviceMediaAction,
|
438
|
+
cv.maybe_simple_value(
|
439
|
+
{
|
440
|
+
cv.GenerateID(): cv.use_id(SpeakerMediaPlayer),
|
441
|
+
cv.Required(CONF_MEDIA_FILE): cv.use_id(audio.AudioFile),
|
442
|
+
cv.Optional(CONF_ANNOUNCEMENT, default=False): cv.templatable(cv.boolean),
|
443
|
+
cv.Optional(CONF_ENQUEUE, default=False): cv.templatable(cv.boolean),
|
444
|
+
},
|
445
|
+
key=CONF_MEDIA_FILE,
|
446
|
+
),
|
447
|
+
)
|
448
|
+
async def play_on_device_media_media_action(config, action_id, template_arg, args):
|
449
|
+
var = cg.new_Pvariable(action_id, template_arg)
|
450
|
+
await cg.register_parented(var, config[CONF_ID])
|
451
|
+
media_file = await cg.get_variable(config[CONF_MEDIA_FILE])
|
452
|
+
announcement = await cg.templatable(config[CONF_ANNOUNCEMENT], args, cg.bool_)
|
453
|
+
enqueue = await cg.templatable(config[CONF_ENQUEUE], args, cg.bool_)
|
454
|
+
|
455
|
+
cg.add(var.set_audio_file(media_file))
|
456
|
+
cg.add(var.set_announcement(announcement))
|
457
|
+
cg.add(var.set_enqueue(enqueue))
|
458
|
+
return var
|