esphome 2025.4.2__py3-none-any.whl → 2025.5.0b3__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 -14
- esphome/components/ac_dimmer/ac_dimmer.cpp +3 -2
- esphome/components/adc/__init__.py +51 -34
- esphome/components/airthings_wave_base/__init__.py +1 -1
- esphome/components/alarm_control_panel/__init__.py +37 -2
- esphome/components/am43/cover/__init__.py +4 -5
- esphome/components/analog_threshold/analog_threshold_binary_sensor.cpp +6 -4
- esphome/components/analog_threshold/analog_threshold_binary_sensor.h +4 -5
- esphome/components/analog_threshold/binary_sensor.py +10 -8
- esphome/components/anova/climate.py +4 -5
- esphome/components/api/__init__.py +25 -8
- esphome/components/api/api_connection.cpp +81 -13
- esphome/components/api/api_connection.h +13 -1
- esphome/components/api/api_frame_helper.cpp +232 -177
- esphome/components/api/api_frame_helper.h +61 -8
- esphome/components/api/api_noise_context.h +13 -4
- esphome/components/api/api_pb2.cpp +1422 -1
- esphome/components/api/api_pb2.h +255 -1
- esphome/components/api/api_pb2_service.cpp +162 -49
- esphome/components/api/api_pb2_service.h +90 -51
- esphome/components/api/api_pb2_size.h +361 -0
- esphome/components/api/api_server.cpp +110 -34
- esphome/components/api/api_server.h +8 -0
- esphome/components/api/proto.h +86 -17
- esphome/components/as7341/as7341.h +1 -1
- esphome/components/atm90e32/__init__.py +1 -0
- esphome/components/atm90e32/atm90e32.cpp +576 -199
- esphome/components/atm90e32/atm90e32.h +128 -31
- esphome/components/atm90e32/atm90e32_reg.h +4 -2
- esphome/components/atm90e32/button/__init__.py +62 -10
- esphome/components/atm90e32/button/atm90e32_button.cpp +63 -4
- esphome/components/atm90e32/button/atm90e32_button.h +36 -4
- esphome/components/atm90e32/number/__init__.py +130 -0
- esphome/components/atm90e32/number/atm90e32_number.h +16 -0
- esphome/components/atm90e32/sensor.py +21 -4
- esphome/components/atm90e32/text_sensor/__init__.py +48 -0
- esphome/components/audio/__init__.py +96 -49
- esphome/components/audio/audio.h +48 -0
- esphome/components/audio/audio_decoder.cpp +1 -1
- esphome/components/audio/audio_resampler.cpp +2 -0
- esphome/components/audio/audio_resampler.h +1 -0
- esphome/components/ballu/climate.py +2 -9
- esphome/components/bang_bang/climate.py +5 -6
- esphome/components/bedjet/bedjet_hub.cpp +1 -0
- esphome/components/bedjet/climate/__init__.py +3 -8
- esphome/components/bedjet/fan/__init__.py +2 -11
- esphome/components/binary/fan/__init__.py +13 -16
- esphome/components/binary_sensor/__init__.py +13 -10
- esphome/components/bl0906/constants.h +16 -16
- esphome/components/ble_client/text_sensor/__init__.py +3 -5
- esphome/components/bluetooth_proxy/bluetooth_connection.cpp +4 -6
- esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +136 -21
- esphome/components/bluetooth_proxy/bluetooth_proxy.h +7 -0
- esphome/components/button/__init__.py +11 -8
- esphome/components/canbus/canbus.cpp +3 -0
- esphome/components/canbus/canbus.h +16 -0
- esphome/components/ccs811/sensor.py +9 -6
- esphome/components/climate/__init__.py +35 -2
- esphome/components/climate/climate_mode.h +1 -1
- esphome/components/climate/climate_traits.h +63 -57
- esphome/components/climate_ir/__init__.py +57 -17
- esphome/components/climate_ir_lg/climate.py +2 -5
- esphome/components/climate_ir_lg/climate_ir_lg.cpp +7 -7
- esphome/components/climate_ir_lg/climate_ir_lg.h +1 -1
- esphome/components/color/__init__.py +2 -0
- esphome/components/const/__init__.py +5 -0
- esphome/components/coolix/climate.py +2 -9
- esphome/components/copy/cover/__init__.py +10 -9
- esphome/components/copy/fan/__init__.py +11 -9
- esphome/components/copy/lock/__init__.py +11 -9
- esphome/components/copy/text/__init__.py +9 -6
- esphome/components/cover/__init__.py +37 -2
- esphome/components/cse7766/cse7766.cpp +2 -1
- esphome/components/cst226/binary_sensor/__init__.py +28 -0
- esphome/components/cst226/binary_sensor/cs226_button.h +22 -0
- esphome/components/cst226/binary_sensor/cstt6_button.cpp +19 -0
- esphome/components/cst226/touchscreen/cst226_touchscreen.cpp +27 -5
- esphome/components/cst226/touchscreen/cst226_touchscreen.h +10 -10
- esphome/components/current_based/cover.py +37 -36
- esphome/components/current_based/current_based_cover.cpp +2 -1
- esphome/components/daikin/climate.py +2 -9
- esphome/components/daikin/daikin.cpp +15 -9
- esphome/components/daikin/daikin.h +5 -5
- esphome/components/daikin_arc/climate.py +2 -7
- esphome/components/daikin_brc/climate.py +3 -5
- esphome/components/dallas_temp/dallas_temp.cpp +17 -24
- esphome/components/dallas_temp/dallas_temp.h +0 -1
- esphome/components/daly_bms/daly_bms.cpp +2 -1
- esphome/components/debug/debug_component.cpp +6 -1
- esphome/components/debug/debug_component.h +6 -0
- esphome/components/debug/debug_esp32.cpp +109 -254
- esphome/components/debug/sensor.py +14 -0
- esphome/components/deep_sleep/deep_sleep_esp32.cpp +13 -1
- esphome/components/delonghi/climate.py +2 -9
- esphome/components/demo/__init__.py +18 -20
- esphome/components/dfrobot_sen0395/switch/__init__.py +21 -22
- esphome/components/dps310/sensor.py +6 -6
- esphome/components/ee895/sensor.py +9 -9
- esphome/components/emmeti/climate.py +2 -9
- esphome/components/endstop/cover.py +17 -16
- esphome/components/endstop/endstop_cover.cpp +2 -1
- esphome/components/ens160_base/__init__.py +12 -9
- esphome/components/esp32/__init__.py +60 -3
- esphome/components/esp32/core.cpp +11 -5
- esphome/components/esp32/gpio.cpp +86 -24
- esphome/components/esp32/gpio.py +15 -16
- esphome/components/esp32/gpio_esp32.py +1 -2
- esphome/components/esp32/gpio_esp32_c2.py +1 -1
- esphome/components/esp32/gpio_esp32_c3.py +1 -1
- esphome/components/esp32/gpio_esp32_c6.py +1 -1
- esphome/components/esp32/gpio_esp32_h2.py +1 -1
- esphome/components/esp32_ble/ble.cpp +1 -0
- esphome/components/esp32_ble/ble.h +5 -3
- esphome/components/esp32_ble/ble_advertising.cpp +2 -1
- esphome/components/esp32_ble/ble_advertising.h +1 -0
- esphome/components/esp32_ble_server/__init__.py +3 -0
- esphome/components/esp32_ble_tracker/__init__.py +7 -1
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +192 -118
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +29 -3
- esphome/components/esp32_camera/esp32_camera.cpp +2 -1
- esphome/components/esp32_camera/esp32_camera.h +1 -1
- esphome/components/esp32_can/esp32_can.cpp +1 -1
- esphome/components/esp32_improv/esp32_improv_component.cpp +1 -1
- esphome/components/esp32_rmt_led_strip/led_strip.cpp +1 -1
- esphome/components/esp32_rmt_led_strip/led_strip.h +7 -5
- esphome/components/esp32_rmt_led_strip/light.py +9 -1
- esphome/components/esp32_touch/esp32_touch.cpp +1 -1
- esphome/components/esp8266/gpio.cpp +69 -8
- esphome/components/ethernet/ethernet_component.cpp +1 -1
- esphome/components/event/__init__.py +13 -10
- esphome/components/factory_reset/switch/__init__.py +7 -21
- esphome/components/fan/__init__.py +52 -5
- esphome/components/fastled_base/__init__.py +1 -4
- esphome/components/fastled_base/fastled_light.cpp +1 -1
- esphome/components/feedback/cover.py +38 -33
- esphome/components/feedback/feedback_cover.cpp +2 -1
- esphome/components/fujitsu_general/climate.py +2 -9
- esphome/components/gcja5/gcja5.cpp +2 -1
- esphome/components/gpio/one_wire/gpio_one_wire.cpp +45 -43
- esphome/components/gpio/one_wire/gpio_one_wire.h +2 -1
- esphome/components/gpio_expander/cached_gpio.h +22 -7
- esphome/components/gps/__init__.py +47 -17
- esphome/components/gps/gps.cpp +42 -23
- esphome/components/gps/gps.h +17 -13
- esphome/components/graph/__init__.py +1 -2
- esphome/components/gree/climate.py +4 -6
- esphome/components/gree/gree.cpp +16 -2
- esphome/components/gree/gree.h +2 -2
- esphome/components/growatt_solar/growatt_solar.cpp +2 -1
- esphome/components/haier/climate.py +37 -34
- esphome/components/hbridge/fan/__init__.py +19 -17
- esphome/components/he60r/cover.py +4 -5
- esphome/components/heatpumpir/climate.py +3 -6
- esphome/components/hitachi_ac344/climate.py +2 -9
- esphome/components/hitachi_ac424/climate.py +2 -9
- esphome/components/hm3301/hm3301.h +1 -1
- esphome/components/hte501/sensor.py +6 -6
- esphome/components/http_request/__init__.py +39 -6
- esphome/components/http_request/http_request.cpp +20 -0
- esphome/components/http_request/http_request.h +57 -15
- esphome/components/http_request/http_request_arduino.cpp +22 -6
- esphome/components/http_request/http_request_arduino.h +4 -3
- esphome/components/http_request/http_request_host.cpp +141 -0
- esphome/components/http_request/http_request_host.h +37 -0
- esphome/components/http_request/http_request_idf.cpp +35 -3
- esphome/components/http_request/http_request_idf.h +10 -3
- esphome/components/http_request/httplib.h +9691 -0
- esphome/components/http_request/update/__init__.py +11 -8
- esphome/components/hyt271/sensor.py +6 -6
- esphome/components/i2c/i2c.h +4 -0
- esphome/components/i2c/i2c_bus_esp_idf.cpp +1 -1
- esphome/components/i2s_audio/__init__.py +131 -22
- esphome/components/i2s_audio/i2s_audio.h +44 -4
- esphome/components/i2s_audio/media_player/__init__.py +19 -9
- esphome/components/i2s_audio/microphone/__init__.py +63 -5
- esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +351 -61
- esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +40 -6
- esphome/components/i2s_audio/speaker/__init__.py +31 -5
- esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +155 -19
- esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +17 -4
- esphome/components/ili9xxx/ili9xxx_init.h +1 -1
- esphome/components/image/__init__.py +37 -17
- esphome/components/image/image.cpp +25 -8
- esphome/components/internal_temperature/internal_temperature.cpp +6 -4
- esphome/components/key_collector/__init__.py +35 -0
- esphome/components/key_collector/key_collector.cpp +8 -0
- esphome/components/key_collector/key_collector.h +10 -0
- esphome/components/kuntze/kuntze.cpp +2 -1
- esphome/components/ld2410/ld2410.h +1 -1
- esphome/components/ld2450/ld2450.h +1 -1
- esphome/components/light/__init__.py +57 -0
- esphome/components/lock/__init__.py +51 -4
- esphome/components/lock/automation.h +2 -13
- esphome/components/logger/__init__.py +22 -0
- esphome/components/logger/logger.cpp +154 -103
- esphome/components/logger/logger.h +211 -36
- esphome/components/logger/task_log_buffer.cpp +138 -0
- esphome/components/logger/task_log_buffer.h +69 -0
- esphome/components/lvgl/__init__.py +13 -5
- esphome/components/lvgl/automation.py +50 -1
- esphome/components/lvgl/defines.py +0 -1
- esphome/components/lvgl/lvgl_esphome.cpp +5 -1
- esphome/components/lvgl/text/__init__.py +1 -2
- esphome/components/mapping/__init__.py +134 -0
- esphome/components/matrix_keypad/matrix_keypad.cpp +2 -1
- esphome/components/max7219digit/max7219digit.cpp +28 -27
- esphome/components/mdns/__init__.py +11 -5
- esphome/components/mdns/mdns_component.cpp +11 -5
- esphome/components/mdns/mdns_component.h +3 -2
- esphome/components/mdns/mdns_esp32.cpp +4 -3
- esphome/components/mdns/mdns_esp8266.cpp +4 -2
- esphome/components/mdns/mdns_libretiny.cpp +4 -2
- esphome/components/mdns/mdns_rp2040.cpp +4 -2
- esphome/components/media_player/__init__.py +33 -1
- esphome/components/mhz19/sensor.py +11 -7
- esphome/components/micro_wake_word/__init__.py +99 -31
- esphome/components/micro_wake_word/automation.h +54 -0
- esphome/components/micro_wake_word/micro_wake_word.cpp +331 -319
- esphome/components/micro_wake_word/micro_wake_word.h +58 -105
- esphome/components/micro_wake_word/preprocessor_settings.h +19 -2
- esphome/components/micro_wake_word/streaming_model.cpp +158 -41
- esphome/components/micro_wake_word/streaming_model.h +85 -13
- esphome/components/microphone/__init__.py +139 -9
- esphome/components/microphone/automation.h +14 -2
- esphome/components/microphone/microphone.cpp +21 -0
- esphome/components/microphone/microphone.h +14 -5
- esphome/components/microphone/microphone_source.cpp +95 -0
- esphome/components/microphone/microphone_source.h +80 -0
- esphome/components/mics_4514/sensor.py +25 -14
- esphome/components/midea/climate.py +3 -4
- esphome/components/midea_ir/climate.py +3 -5
- esphome/components/mipi_spi/__init__.py +15 -0
- esphome/components/mipi_spi/display.py +474 -0
- esphome/components/mipi_spi/mipi_spi.cpp +481 -0
- esphome/components/mipi_spi/mipi_spi.h +171 -0
- esphome/components/mipi_spi/models/__init__.py +65 -0
- esphome/components/mipi_spi/models/amoled.py +72 -0
- esphome/components/mipi_spi/models/commands.py +82 -0
- esphome/components/mipi_spi/models/cyd.py +10 -0
- esphome/components/mipi_spi/models/ili.py +749 -0
- esphome/components/mipi_spi/models/jc.py +260 -0
- esphome/components/mipi_spi/models/lanbon.py +15 -0
- esphome/components/mipi_spi/models/lilygo.py +60 -0
- esphome/components/mipi_spi/models/waveshare.py +139 -0
- esphome/components/mitsubishi/climate.py +2 -5
- esphome/components/mitsubishi/mitsubishi.cpp +9 -9
- esphome/components/mixer/speaker/mixer_speaker.cpp +12 -22
- esphome/components/mixer/speaker/mixer_speaker.h +1 -3
- esphome/components/mlx90393/sensor.py +5 -0
- esphome/components/mlx90393/sensor_mlx90393.cpp +195 -13
- esphome/components/mlx90393/sensor_mlx90393.h +21 -4
- esphome/components/modbus/modbus.cpp +2 -1
- esphome/components/mqtt/__init__.py +1 -1
- esphome/components/mqtt/mqtt_client.cpp +6 -2
- esphome/components/mqtt/mqtt_const.h +4 -0
- esphome/components/mqtt/mqtt_fan.cpp +39 -0
- esphome/components/mqtt/mqtt_fan.h +2 -0
- esphome/components/ms5611/sensor.py +6 -6
- esphome/components/ms8607/sensor.py +3 -3
- esphome/components/network/__init__.py +1 -1
- esphome/components/nextion/base_component.py +17 -16
- esphome/components/nextion/display.py +11 -2
- esphome/components/nextion/nextion.cpp +39 -1
- esphome/components/nextion/nextion.h +50 -0
- esphome/components/noblex/climate.py +2 -9
- esphome/components/number/__init__.py +12 -9
- esphome/components/one_wire/one_wire_bus.cpp +14 -10
- esphome/components/one_wire/one_wire_bus.h +14 -8
- esphome/components/online_image/bmp_image.cpp +48 -11
- esphome/components/online_image/bmp_image.h +2 -0
- esphome/components/opentherm/binary_sensor/__init__.py +2 -4
- esphome/components/opentherm/number/__init__.py +11 -20
- esphome/components/opentherm/sensor/__init__.py +3 -3
- esphome/components/opentherm/switch/__init__.py +3 -5
- esphome/components/output/lock/__init__.py +11 -9
- esphome/components/packages/__init__.py +33 -31
- esphome/components/packet_transport/__init__.py +201 -0
- esphome/components/packet_transport/binary_sensor.py +19 -0
- esphome/components/packet_transport/packet_transport.cpp +534 -0
- esphome/components/packet_transport/packet_transport.h +154 -0
- esphome/components/packet_transport/sensor.py +19 -0
- esphome/components/pca9685/pca9685_output.cpp +2 -1
- esphome/components/pid/climate.py +2 -4
- esphome/components/pm2005/__init__.py +1 -0
- esphome/components/pm2005/pm2005.cpp +123 -0
- esphome/components/pm2005/pm2005.h +46 -0
- esphome/components/pm2005/sensor.py +86 -0
- esphome/components/pmsa003i/pmsa003i.cpp +43 -16
- esphome/components/pmsa003i/pmsa003i.h +25 -25
- esphome/components/pmsx003/pmsx003.cpp +195 -230
- esphome/components/pmsx003/pmsx003.h +51 -33
- esphome/components/pmsx003/sensor.py +21 -11
- esphome/components/pn7150/pn7150.h +2 -2
- esphome/components/pn7160/pn7160.h +2 -2
- esphome/components/prometheus/prometheus_handler.cpp +174 -0
- esphome/components/prometheus/prometheus_handler.h +17 -0
- esphome/components/psram/__init__.py +7 -5
- esphome/components/pulse_meter/pulse_meter_sensor.cpp +32 -12
- esphome/components/pulse_meter/pulse_meter_sensor.h +5 -5
- esphome/components/pzem004t/pzem004t.cpp +2 -1
- esphome/components/qspi_dbi/__init__.py +0 -1
- esphome/components/qspi_dbi/display.py +2 -1
- esphome/components/qspi_dbi/models.py +1 -2
- esphome/components/remote_base/__init__.py +91 -0
- esphome/components/remote_base/beo4_protocol.cpp +153 -0
- esphome/components/remote_base/beo4_protocol.h +43 -0
- esphome/components/remote_base/gobox_protocol.cpp +131 -0
- esphome/components/remote_base/gobox_protocol.h +54 -0
- esphome/components/remote_receiver/remote_receiver_esp32.cpp +16 -9
- esphome/components/resampler/speaker/resampler_speaker.cpp +12 -10
- esphome/components/resampler/speaker/resampler_speaker.h +1 -1
- esphome/components/rf_bridge/rf_bridge.cpp +2 -1
- esphome/components/scd30/sensor.py +2 -3
- esphome/components/scd4x/sensor.py +4 -5
- esphome/components/sdp3x/sensor.py +2 -1
- esphome/components/sds011/sds011.cpp +2 -1
- esphome/components/select/__init__.py +19 -20
- esphome/components/sen5x/sen5x.cpp +55 -36
- esphome/components/sen5x/sensor.py +1 -1
- esphome/components/senseair/sensor.py +3 -3
- esphome/components/sensor/__init__.py +158 -14
- esphome/components/sensor/filter.cpp +23 -0
- esphome/components/sensor/filter.h +22 -0
- esphome/components/sgp30/sensor.py +14 -16
- esphome/components/sgp4x/sensor.py +1 -1
- esphome/components/sht4x/sht4x.cpp +43 -22
- esphome/components/sht4x/sht4x.h +1 -1
- esphome/components/shtcx/sensor.py +6 -6
- esphome/components/slow_pwm/slow_pwm_output.cpp +2 -1
- esphome/components/sml/text_sensor/__init__.py +4 -6
- esphome/components/sound_level/__init__.py +0 -0
- esphome/components/sound_level/sensor.py +97 -0
- esphome/components/sound_level/sound_level.cpp +194 -0
- esphome/components/sound_level/sound_level.h +73 -0
- esphome/components/speaker/media_player/__init__.py +4 -8
- esphome/components/speaker/media_player/speaker_media_player.cpp +0 -18
- esphome/components/speaker/media_player/speaker_media_player.h +0 -11
- esphome/components/speaker/speaker.h +4 -7
- esphome/components/speed/fan/__init__.py +17 -16
- esphome/components/spi/spi.h +11 -1
- esphome/components/sprinkler/__init__.py +18 -19
- esphome/components/sprinkler/sprinkler.cpp +6 -5
- esphome/components/switch/__init__.py +32 -42
- esphome/components/syslog/__init__.py +41 -0
- esphome/components/syslog/esphome_syslog.cpp +49 -0
- esphome/components/syslog/esphome_syslog.h +27 -0
- esphome/components/t6615/sensor.py +3 -3
- esphome/components/t6615/t6615.cpp +2 -1
- esphome/components/tca9555/tca9555.cpp +11 -6
- esphome/components/tcl112/climate.py +2 -9
- esphome/components/template/alarm_control_panel/__init__.py +7 -6
- esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +21 -17
- esphome/components/template/alarm_control_panel/template_alarm_control_panel.h +2 -1
- esphome/components/template/cover/__init__.py +27 -21
- esphome/components/template/fan/__init__.py +14 -12
- esphome/components/template/lock/__init__.py +20 -25
- esphome/components/template/lock/automation.h +18 -0
- esphome/components/template/text/__init__.py +4 -3
- esphome/components/template/valve/__init__.py +32 -21
- esphome/components/template/valve/automation.h +24 -0
- esphome/components/text/__init__.py +32 -1
- esphome/components/text_sensor/__init__.py +24 -29
- esphome/components/thermostat/climate.py +5 -5
- esphome/components/time_based/cover.py +17 -16
- esphome/components/time_based/time_based_cover.cpp +2 -1
- esphome/components/tm1638/switch/__init__.py +10 -7
- esphome/components/tormatic/cover.py +4 -5
- esphome/components/toshiba/climate.py +3 -5
- esphome/components/touchscreen/touchscreen.cpp +3 -1
- esphome/components/tuya/climate/__init__.py +5 -6
- esphome/components/tuya/cover/__init__.py +6 -11
- esphome/components/tuya/select/__init__.py +15 -5
- esphome/components/tuya/select/tuya_select.cpp +6 -1
- esphome/components/tuya/select/tuya_select.h +5 -1
- esphome/components/uart/packet_transport/__init__.py +20 -0
- esphome/components/uart/packet_transport/uart_transport.cpp +88 -0
- esphome/components/uart/packet_transport/uart_transport.h +41 -0
- esphome/components/uart/switch/uart_switch.cpp +2 -1
- esphome/components/udp/__init__.py +126 -128
- esphome/components/udp/automation.h +40 -0
- esphome/components/udp/binary_sensor.py +3 -25
- esphome/components/udp/packet_transport/__init__.py +29 -0
- esphome/components/udp/packet_transport/udp_transport.cpp +36 -0
- esphome/components/udp/packet_transport/udp_transport.h +28 -0
- esphome/components/udp/sensor.py +3 -25
- esphome/components/udp/udp_component.cpp +26 -470
- esphome/components/udp/udp_component.h +21 -128
- esphome/components/update/__init__.py +31 -1
- esphome/components/uponor_smatrix/climate/__init__.py +4 -9
- esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp +2 -1
- esphome/components/uponor_smatrix/uponor_smatrix.cpp +2 -1
- esphome/components/uptime/text_sensor/__init__.py +47 -7
- esphome/components/uptime/text_sensor/uptime_text_sensor.cpp +12 -7
- esphome/components/uptime/text_sensor/uptime_text_sensor.h +19 -0
- esphome/components/valve/__init__.py +34 -3
- esphome/components/valve/automation.h +1 -19
- esphome/components/vl53l0x/sensor.py +11 -0
- esphome/components/vl53l0x/vl53l0x_sensor.cpp +5 -1
- esphome/components/vl53l0x/vl53l0x_sensor.h +2 -1
- esphome/components/voice_assistant/__init__.py +36 -10
- esphome/components/voice_assistant/voice_assistant.cpp +170 -144
- esphome/components/voice_assistant/voice_assistant.h +26 -25
- esphome/components/waveshare_epaper/display.py +6 -0
- esphome/components/waveshare_epaper/waveshare_epaper.cpp +439 -37
- esphome/components/waveshare_epaper/waveshare_epaper.h +60 -11
- esphome/components/whirlpool/climate.py +3 -5
- esphome/components/whynter/climate.py +3 -5
- esphome/components/xpt2046/touchscreen/xpt2046.cpp +1 -1
- esphome/components/yashima/climate.py +6 -6
- esphome/components/zhlt01/climate.py +2 -7
- esphome/config.py +13 -13
- esphome/config_validation.py +38 -58
- esphome/const.py +15 -1
- esphome/core/__init__.py +2 -0
- esphome/core/application.cpp +23 -10
- esphome/core/application.h +9 -1
- esphome/core/automation.h +4 -3
- esphome/core/component.cpp +28 -7
- esphome/core/component.h +10 -1
- esphome/core/defines.h +23 -17
- esphome/core/macros.h +4 -0
- esphome/core/scheduler.cpp +7 -1
- esphome/cpp_generator.py +6 -2
- esphome/dashboard/web_server.py +3 -3
- esphome/helpers.py +39 -0
- esphome/loader.py +4 -0
- esphome/log.py +15 -19
- esphome/mqtt.py +23 -10
- esphome/platformio_api.py +1 -1
- esphome/schema_extractors.py +0 -1
- esphome/voluptuous_schema.py +3 -1
- esphome/vscode.py +15 -0
- esphome/wizard.py +47 -37
- esphome/zeroconf.py +7 -3
- {esphome-2025.4.2.dist-info → esphome-2025.5.0b3.dist-info}/METADATA +10 -11
- {esphome-2025.4.2.dist-info → esphome-2025.5.0b3.dist-info}/RECORD +440 -380
- {esphome-2025.4.2.dist-info → esphome-2025.5.0b3.dist-info}/WHEEL +1 -1
- {esphome-2025.4.2.dist-info → esphome-2025.5.0b3.dist-info}/entry_points.txt +0 -0
- {esphome-2025.4.2.dist-info → esphome-2025.5.0b3.dist-info}/licenses/LICENSE +0 -0
- {esphome-2025.4.2.dist-info → esphome-2025.5.0b3.dist-info}/top_level.txt +0 -0
@@ -2,20 +2,46 @@
|
|
2
2
|
|
3
3
|
#ifdef USE_ESP32
|
4
4
|
|
5
|
+
#ifdef USE_I2S_LEGACY
|
5
6
|
#include <driver/i2s.h>
|
7
|
+
#else
|
8
|
+
#include <driver/i2s_std.h>
|
9
|
+
#include <driver/i2s_pdm.h>
|
10
|
+
#endif
|
6
11
|
|
7
12
|
#include "esphome/core/hal.h"
|
8
13
|
#include "esphome/core/log.h"
|
9
14
|
|
15
|
+
#include "esphome/components/audio/audio.h"
|
16
|
+
|
10
17
|
namespace esphome {
|
11
18
|
namespace i2s_audio {
|
12
19
|
|
13
|
-
static const
|
20
|
+
static const UBaseType_t MAX_LISTENERS = 16;
|
21
|
+
|
22
|
+
static const uint32_t READ_DURATION_MS = 16;
|
23
|
+
|
24
|
+
static const size_t TASK_STACK_SIZE = 4096;
|
25
|
+
static const ssize_t TASK_PRIORITY = 23;
|
26
|
+
|
27
|
+
// Use an exponential moving average to correct a DC offset with weight factor 1/1000
|
28
|
+
static const int32_t DC_OFFSET_MOVING_AVERAGE_COEFFICIENT_DENOMINATOR = 1000;
|
14
29
|
|
15
30
|
static const char *const TAG = "i2s_audio.microphone";
|
16
31
|
|
32
|
+
enum MicrophoneEventGroupBits : uint32_t {
|
33
|
+
COMMAND_STOP = (1 << 0), // stops the microphone task
|
34
|
+
TASK_STARTING = (1 << 10),
|
35
|
+
TASK_RUNNING = (1 << 11),
|
36
|
+
TASK_STOPPING = (1 << 12),
|
37
|
+
TASK_STOPPED = (1 << 13),
|
38
|
+
|
39
|
+
ALL_BITS = 0x00FFFFFF, // All valid FreeRTOS event group bits
|
40
|
+
};
|
41
|
+
|
17
42
|
void I2SAudioMicrophone::setup() {
|
18
43
|
ESP_LOGCONFIG(TAG, "Setting up I2S Audio Microphone...");
|
44
|
+
#ifdef USE_I2S_LEGACY
|
19
45
|
#if SOC_I2S_SUPPORTS_ADC
|
20
46
|
if (this->adc_) {
|
21
47
|
if (this->parent_->get_port() != I2S_NUM_0) {
|
@@ -24,6 +50,7 @@ void I2SAudioMicrophone::setup() {
|
|
24
50
|
return;
|
25
51
|
}
|
26
52
|
} else
|
53
|
+
#endif
|
27
54
|
#endif
|
28
55
|
{
|
29
56
|
if (this->pdm_) {
|
@@ -34,19 +61,75 @@ void I2SAudioMicrophone::setup() {
|
|
34
61
|
}
|
35
62
|
}
|
36
63
|
}
|
64
|
+
|
65
|
+
this->active_listeners_semaphore_ = xSemaphoreCreateCounting(MAX_LISTENERS, MAX_LISTENERS);
|
66
|
+
if (this->active_listeners_semaphore_ == nullptr) {
|
67
|
+
ESP_LOGE(TAG, "Failed to create semaphore");
|
68
|
+
this->mark_failed();
|
69
|
+
return;
|
70
|
+
}
|
71
|
+
|
72
|
+
this->event_group_ = xEventGroupCreate();
|
73
|
+
if (this->event_group_ == nullptr) {
|
74
|
+
ESP_LOGE(TAG, "Failed to create event group");
|
75
|
+
this->mark_failed();
|
76
|
+
return;
|
77
|
+
}
|
78
|
+
|
79
|
+
this->configure_stream_settings_();
|
80
|
+
}
|
81
|
+
|
82
|
+
void I2SAudioMicrophone::configure_stream_settings_() {
|
83
|
+
uint8_t channel_count = 1;
|
84
|
+
#ifdef USE_I2S_LEGACY
|
85
|
+
uint8_t bits_per_sample = this->bits_per_sample_;
|
86
|
+
|
87
|
+
if (this->channel_ == I2S_CHANNEL_FMT_RIGHT_LEFT) {
|
88
|
+
channel_count = 2;
|
89
|
+
}
|
90
|
+
#else
|
91
|
+
uint8_t bits_per_sample = 16;
|
92
|
+
if (this->slot_bit_width_ != I2S_SLOT_BIT_WIDTH_AUTO) {
|
93
|
+
bits_per_sample = this->slot_bit_width_;
|
94
|
+
}
|
95
|
+
|
96
|
+
if (this->slot_mode_ == I2S_SLOT_MODE_STEREO) {
|
97
|
+
channel_count = 2;
|
98
|
+
}
|
99
|
+
#endif
|
100
|
+
|
101
|
+
#ifdef USE_ESP32_VARIANT_ESP32
|
102
|
+
// ESP32 reads audio aligned to a multiple of 2 bytes. For example, if configured for 24 bits per sample, then it will
|
103
|
+
// produce 32 bits per sample, where the actual data is in the most significant bits. Other ESP32 variants produce 24
|
104
|
+
// bits per sample in this situation.
|
105
|
+
if (bits_per_sample < 16) {
|
106
|
+
bits_per_sample = 16;
|
107
|
+
} else if ((bits_per_sample > 16) && (bits_per_sample <= 32)) {
|
108
|
+
bits_per_sample = 32;
|
109
|
+
}
|
110
|
+
#endif
|
111
|
+
|
112
|
+
if (this->pdm_) {
|
113
|
+
bits_per_sample = 16; // PDM mics are always 16 bits per sample
|
114
|
+
}
|
115
|
+
|
116
|
+
this->audio_stream_info_ = audio::AudioStreamInfo(bits_per_sample, channel_count, this->sample_rate_);
|
37
117
|
}
|
38
118
|
|
39
119
|
void I2SAudioMicrophone::start() {
|
40
120
|
if (this->is_failed())
|
41
121
|
return;
|
42
|
-
|
43
|
-
|
44
|
-
this->state_ = microphone::STATE_STARTING;
|
122
|
+
|
123
|
+
xSemaphoreTake(this->active_listeners_semaphore_, 0);
|
45
124
|
}
|
46
|
-
|
125
|
+
|
126
|
+
bool I2SAudioMicrophone::start_driver_() {
|
47
127
|
if (!this->parent_->try_lock()) {
|
48
|
-
return; // Waiting for another i2s to return lock
|
128
|
+
return false; // Waiting for another i2s to return lock
|
49
129
|
}
|
130
|
+
esp_err_t err;
|
131
|
+
|
132
|
+
#ifdef USE_I2S_LEGACY
|
50
133
|
i2s_driver_config_t config = {
|
51
134
|
.mode = (i2s_mode_t) (this->i2s_mode_ | I2S_MODE_RX),
|
52
135
|
.sample_rate = this->sample_rate_,
|
@@ -55,16 +138,14 @@ void I2SAudioMicrophone::start_() {
|
|
55
138
|
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
|
56
139
|
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
|
57
140
|
.dma_buf_count = 4,
|
58
|
-
.dma_buf_len =
|
141
|
+
.dma_buf_len = 240, // Must be divisible by 3 to support 24 bits per sample on old driver and newer variants
|
59
142
|
.use_apll = this->use_apll_,
|
60
143
|
.tx_desc_auto_clear = false,
|
61
144
|
.fixed_mclk = 0,
|
62
|
-
.mclk_multiple =
|
145
|
+
.mclk_multiple = this->mclk_multiple_,
|
63
146
|
.bits_per_chan = this->bits_per_channel_,
|
64
147
|
};
|
65
148
|
|
66
|
-
esp_err_t err;
|
67
|
-
|
68
149
|
#if SOC_I2S_SUPPORTS_ADC
|
69
150
|
if (this->adc_) {
|
70
151
|
config.mode = (i2s_mode_t) (config.mode | I2S_MODE_ADC_BUILT_IN);
|
@@ -72,20 +153,20 @@ void I2SAudioMicrophone::start_() {
|
|
72
153
|
if (err != ESP_OK) {
|
73
154
|
ESP_LOGW(TAG, "Error installing I2S driver: %s", esp_err_to_name(err));
|
74
155
|
this->status_set_error();
|
75
|
-
return;
|
156
|
+
return false;
|
76
157
|
}
|
77
158
|
|
78
159
|
err = i2s_set_adc_mode(ADC_UNIT_1, this->adc_channel_);
|
79
160
|
if (err != ESP_OK) {
|
80
161
|
ESP_LOGW(TAG, "Error setting ADC mode: %s", esp_err_to_name(err));
|
81
162
|
this->status_set_error();
|
82
|
-
return;
|
163
|
+
return false;
|
83
164
|
}
|
84
165
|
err = i2s_adc_enable(this->parent_->get_port());
|
85
166
|
if (err != ESP_OK) {
|
86
167
|
ESP_LOGW(TAG, "Error enabling ADC: %s", esp_err_to_name(err));
|
87
168
|
this->status_set_error();
|
88
|
-
return;
|
169
|
+
return false;
|
89
170
|
}
|
90
171
|
|
91
172
|
} else
|
@@ -98,7 +179,7 @@ void I2SAudioMicrophone::start_() {
|
|
98
179
|
if (err != ESP_OK) {
|
99
180
|
ESP_LOGW(TAG, "Error installing I2S driver: %s", esp_err_to_name(err));
|
100
181
|
this->status_set_error();
|
101
|
-
return;
|
182
|
+
return false;
|
102
183
|
}
|
103
184
|
|
104
185
|
i2s_pin_config_t pin_config = this->parent_->get_pin_config();
|
@@ -108,26 +189,122 @@ void I2SAudioMicrophone::start_() {
|
|
108
189
|
if (err != ESP_OK) {
|
109
190
|
ESP_LOGW(TAG, "Error setting I2S pin: %s", esp_err_to_name(err));
|
110
191
|
this->status_set_error();
|
111
|
-
return;
|
192
|
+
return false;
|
193
|
+
}
|
194
|
+
}
|
195
|
+
#else
|
196
|
+
i2s_chan_config_t chan_cfg = {
|
197
|
+
.id = this->parent_->get_port(),
|
198
|
+
.role = this->i2s_role_,
|
199
|
+
.dma_desc_num = 4,
|
200
|
+
.dma_frame_num = 256,
|
201
|
+
.auto_clear = false,
|
202
|
+
};
|
203
|
+
/* Allocate a new RX channel and get the handle of this channel */
|
204
|
+
err = i2s_new_channel(&chan_cfg, NULL, &this->rx_handle_);
|
205
|
+
if (err != ESP_OK) {
|
206
|
+
ESP_LOGW(TAG, "Error creating new I2S channel: %s", esp_err_to_name(err));
|
207
|
+
this->status_set_error();
|
208
|
+
return false;
|
209
|
+
}
|
210
|
+
|
211
|
+
i2s_clock_src_t clk_src = I2S_CLK_SRC_DEFAULT;
|
212
|
+
#ifdef I2S_CLK_SRC_APLL
|
213
|
+
if (this->use_apll_) {
|
214
|
+
clk_src = I2S_CLK_SRC_APLL;
|
215
|
+
}
|
216
|
+
#endif
|
217
|
+
i2s_std_gpio_config_t pin_config = this->parent_->get_pin_config();
|
218
|
+
#if SOC_I2S_SUPPORTS_PDM_RX
|
219
|
+
if (this->pdm_) {
|
220
|
+
i2s_pdm_rx_clk_config_t clk_cfg = {
|
221
|
+
.sample_rate_hz = this->sample_rate_,
|
222
|
+
.clk_src = clk_src,
|
223
|
+
.mclk_multiple = this->mclk_multiple_,
|
224
|
+
.dn_sample_mode = I2S_PDM_DSR_8S,
|
225
|
+
};
|
226
|
+
|
227
|
+
i2s_pdm_rx_slot_config_t slot_cfg = I2S_PDM_RX_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, this->slot_mode_);
|
228
|
+
switch (this->std_slot_mask_) {
|
229
|
+
case I2S_STD_SLOT_LEFT:
|
230
|
+
slot_cfg.slot_mask = I2S_PDM_SLOT_LEFT;
|
231
|
+
break;
|
232
|
+
case I2S_STD_SLOT_RIGHT:
|
233
|
+
slot_cfg.slot_mask = I2S_PDM_SLOT_RIGHT;
|
234
|
+
break;
|
235
|
+
case I2S_STD_SLOT_BOTH:
|
236
|
+
slot_cfg.slot_mask = I2S_PDM_SLOT_BOTH;
|
237
|
+
break;
|
112
238
|
}
|
239
|
+
|
240
|
+
/* Init the channel into PDM RX mode */
|
241
|
+
i2s_pdm_rx_config_t pdm_rx_cfg = {
|
242
|
+
.clk_cfg = clk_cfg,
|
243
|
+
.slot_cfg = slot_cfg,
|
244
|
+
.gpio_cfg =
|
245
|
+
{
|
246
|
+
.clk = pin_config.ws,
|
247
|
+
.din = this->din_pin_,
|
248
|
+
.invert_flags =
|
249
|
+
{
|
250
|
+
.clk_inv = pin_config.invert_flags.ws_inv,
|
251
|
+
},
|
252
|
+
},
|
253
|
+
};
|
254
|
+
err = i2s_channel_init_pdm_rx_mode(this->rx_handle_, &pdm_rx_cfg);
|
255
|
+
} else
|
256
|
+
#endif
|
257
|
+
{
|
258
|
+
i2s_std_clk_config_t clk_cfg = {
|
259
|
+
.sample_rate_hz = this->sample_rate_,
|
260
|
+
.clk_src = clk_src,
|
261
|
+
.mclk_multiple = this->mclk_multiple_,
|
262
|
+
};
|
263
|
+
i2s_std_slot_config_t std_slot_cfg =
|
264
|
+
I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG((i2s_data_bit_width_t) this->slot_bit_width_, this->slot_mode_);
|
265
|
+
std_slot_cfg.slot_bit_width = this->slot_bit_width_;
|
266
|
+
std_slot_cfg.slot_mask = this->std_slot_mask_;
|
267
|
+
|
268
|
+
pin_config.din = this->din_pin_;
|
269
|
+
|
270
|
+
i2s_std_config_t std_cfg = {
|
271
|
+
.clk_cfg = clk_cfg,
|
272
|
+
.slot_cfg = std_slot_cfg,
|
273
|
+
.gpio_cfg = pin_config,
|
274
|
+
};
|
275
|
+
/* Initialize the channel */
|
276
|
+
err = i2s_channel_init_std_mode(this->rx_handle_, &std_cfg);
|
113
277
|
}
|
114
|
-
|
115
|
-
|
278
|
+
if (err != ESP_OK) {
|
279
|
+
ESP_LOGW(TAG, "Error initializing I2S channel: %s", esp_err_to_name(err));
|
280
|
+
this->status_set_error();
|
281
|
+
return false;
|
282
|
+
}
|
283
|
+
|
284
|
+
/* Before reading data, start the RX channel first */
|
285
|
+
i2s_channel_enable(this->rx_handle_);
|
286
|
+
if (err != ESP_OK) {
|
287
|
+
ESP_LOGW(TAG, "Error enabling I2S Microphone: %s", esp_err_to_name(err));
|
288
|
+
this->status_set_error();
|
289
|
+
return false;
|
290
|
+
}
|
291
|
+
#endif
|
292
|
+
|
116
293
|
this->status_clear_error();
|
294
|
+
this->configure_stream_settings_(); // redetermine the settings in case some settings were changed after compilation
|
295
|
+
return true;
|
117
296
|
}
|
118
297
|
|
119
298
|
void I2SAudioMicrophone::stop() {
|
120
299
|
if (this->state_ == microphone::STATE_STOPPED || this->is_failed())
|
121
300
|
return;
|
122
|
-
|
123
|
-
|
124
|
-
return;
|
125
|
-
}
|
126
|
-
this->state_ = microphone::STATE_STOPPING;
|
301
|
+
|
302
|
+
xSemaphoreGive(this->active_listeners_semaphore_);
|
127
303
|
}
|
128
304
|
|
129
|
-
void I2SAudioMicrophone::
|
305
|
+
void I2SAudioMicrophone::stop_driver_() {
|
130
306
|
esp_err_t err;
|
307
|
+
#ifdef USE_I2S_LEGACY
|
131
308
|
#if SOC_I2S_SUPPORTS_ADC
|
132
309
|
if (this->adc_) {
|
133
310
|
err = i2s_adc_disable(this->parent_->get_port());
|
@@ -150,68 +327,181 @@ void I2SAudioMicrophone::stop_() {
|
|
150
327
|
this->status_set_error();
|
151
328
|
return;
|
152
329
|
}
|
330
|
+
#else
|
331
|
+
/* Have to stop the channel before deleting it */
|
332
|
+
err = i2s_channel_disable(this->rx_handle_);
|
333
|
+
if (err != ESP_OK) {
|
334
|
+
ESP_LOGW(TAG, "Error stopping I2S microphone: %s", esp_err_to_name(err));
|
335
|
+
this->status_set_error();
|
336
|
+
return;
|
337
|
+
}
|
338
|
+
/* If the handle is not needed any more, delete it to release the channel resources */
|
339
|
+
err = i2s_del_channel(this->rx_handle_);
|
340
|
+
if (err != ESP_OK) {
|
341
|
+
ESP_LOGW(TAG, "Error deleting I2S channel: %s", esp_err_to_name(err));
|
342
|
+
this->status_set_error();
|
343
|
+
return;
|
344
|
+
}
|
345
|
+
#endif
|
153
346
|
this->parent_->unlock();
|
154
|
-
this->state_ = microphone::STATE_STOPPED;
|
155
|
-
this->high_freq_.stop();
|
156
347
|
this->status_clear_error();
|
157
348
|
}
|
158
349
|
|
159
|
-
|
350
|
+
void I2SAudioMicrophone::mic_task(void *params) {
|
351
|
+
I2SAudioMicrophone *this_microphone = (I2SAudioMicrophone *) params;
|
352
|
+
|
353
|
+
xEventGroupSetBits(this_microphone->event_group_, MicrophoneEventGroupBits::TASK_STARTING);
|
354
|
+
|
355
|
+
uint8_t start_counter = 0;
|
356
|
+
bool started = this_microphone->start_driver_();
|
357
|
+
while (!started && start_counter < 10) {
|
358
|
+
// Attempt to load the driver again in 100 ms. Doesn't slow down main loop since its in a task.
|
359
|
+
vTaskDelay(pdMS_TO_TICKS(100));
|
360
|
+
++start_counter;
|
361
|
+
started = this_microphone->start_driver_();
|
362
|
+
}
|
363
|
+
|
364
|
+
if (started) {
|
365
|
+
xEventGroupSetBits(this_microphone->event_group_, MicrophoneEventGroupBits::TASK_RUNNING);
|
366
|
+
const size_t bytes_to_read = this_microphone->audio_stream_info_.ms_to_bytes(READ_DURATION_MS);
|
367
|
+
std::vector<uint8_t> samples;
|
368
|
+
samples.reserve(bytes_to_read);
|
369
|
+
|
370
|
+
while (!(xEventGroupGetBits(this_microphone->event_group_) & COMMAND_STOP)) {
|
371
|
+
if (this_microphone->data_callbacks_.size() > 0) {
|
372
|
+
samples.resize(bytes_to_read);
|
373
|
+
size_t bytes_read = this_microphone->read_(samples.data(), bytes_to_read, 2 * pdMS_TO_TICKS(READ_DURATION_MS));
|
374
|
+
samples.resize(bytes_read);
|
375
|
+
if (this_microphone->correct_dc_offset_) {
|
376
|
+
this_microphone->fix_dc_offset_(samples);
|
377
|
+
}
|
378
|
+
this_microphone->data_callbacks_.call(samples);
|
379
|
+
} else {
|
380
|
+
vTaskDelay(pdMS_TO_TICKS(READ_DURATION_MS));
|
381
|
+
}
|
382
|
+
}
|
383
|
+
}
|
384
|
+
|
385
|
+
xEventGroupSetBits(this_microphone->event_group_, MicrophoneEventGroupBits::TASK_STOPPING);
|
386
|
+
this_microphone->stop_driver_();
|
387
|
+
|
388
|
+
xEventGroupSetBits(this_microphone->event_group_, MicrophoneEventGroupBits::TASK_STOPPED);
|
389
|
+
while (true) {
|
390
|
+
// Continuously delay until the loop method deletes the task
|
391
|
+
vTaskDelay(pdMS_TO_TICKS(10));
|
392
|
+
}
|
393
|
+
}
|
394
|
+
|
395
|
+
void I2SAudioMicrophone::fix_dc_offset_(std::vector<uint8_t> &data) {
|
396
|
+
const size_t bytes_per_sample = this->audio_stream_info_.samples_to_bytes(1);
|
397
|
+
const uint32_t total_samples = this->audio_stream_info_.bytes_to_samples(data.size());
|
398
|
+
|
399
|
+
if (total_samples == 0) {
|
400
|
+
return;
|
401
|
+
}
|
402
|
+
|
403
|
+
int64_t offset_accumulator = 0;
|
404
|
+
for (uint32_t sample_index = 0; sample_index < total_samples; ++sample_index) {
|
405
|
+
const uint32_t byte_index = sample_index * bytes_per_sample;
|
406
|
+
int32_t sample = audio::unpack_audio_sample_to_q31(&data[byte_index], bytes_per_sample);
|
407
|
+
offset_accumulator += sample;
|
408
|
+
sample -= this->dc_offset_;
|
409
|
+
audio::pack_q31_as_audio_sample(sample, &data[byte_index], bytes_per_sample);
|
410
|
+
}
|
411
|
+
|
412
|
+
const int32_t new_offset = offset_accumulator / total_samples;
|
413
|
+
this->dc_offset_ = new_offset / DC_OFFSET_MOVING_AVERAGE_COEFFICIENT_DENOMINATOR +
|
414
|
+
(DC_OFFSET_MOVING_AVERAGE_COEFFICIENT_DENOMINATOR - 1) * this->dc_offset_ /
|
415
|
+
DC_OFFSET_MOVING_AVERAGE_COEFFICIENT_DENOMINATOR;
|
416
|
+
}
|
417
|
+
|
418
|
+
size_t I2SAudioMicrophone::read_(uint8_t *buf, size_t len, TickType_t ticks_to_wait) {
|
160
419
|
size_t bytes_read = 0;
|
161
|
-
|
162
|
-
|
420
|
+
#ifdef USE_I2S_LEGACY
|
421
|
+
esp_err_t err = i2s_read(this->parent_->get_port(), buf, len, &bytes_read, ticks_to_wait);
|
422
|
+
#else
|
423
|
+
// i2s_channel_read expects the timeout value in ms, not ticks
|
424
|
+
esp_err_t err = i2s_channel_read(this->rx_handle_, buf, len, &bytes_read, pdTICKS_TO_MS(ticks_to_wait));
|
425
|
+
#endif
|
426
|
+
if ((err != ESP_OK) && ((err != ESP_ERR_TIMEOUT) || (ticks_to_wait != 0))) {
|
427
|
+
// Ignore ESP_ERR_TIMEOUT if ticks_to_wait = 0, as it will read the data on the next call
|
163
428
|
ESP_LOGW(TAG, "Error reading from I2S microphone: %s", esp_err_to_name(err));
|
164
429
|
this->status_set_warning();
|
165
430
|
return 0;
|
166
431
|
}
|
167
|
-
if (bytes_read == 0) {
|
432
|
+
if ((bytes_read == 0) && (ticks_to_wait > 0)) {
|
168
433
|
this->status_set_warning();
|
169
434
|
return 0;
|
170
435
|
}
|
171
436
|
this->status_clear_warning();
|
172
|
-
|
173
|
-
//
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
size_t samples_read = bytes_read / sizeof(int32_t);
|
181
|
-
for (size_t i = 0; i < samples_read; i++) {
|
182
|
-
int32_t temp = reinterpret_cast<int32_t *>(buf)[i] >> 14;
|
183
|
-
buf[i] = clamp<int16_t>(temp, INT16_MIN, INT16_MAX);
|
184
|
-
}
|
185
|
-
return samples_read * sizeof(int16_t);
|
437
|
+
#if defined(USE_ESP32_VARIANT_ESP32) and not defined(USE_I2S_LEGACY)
|
438
|
+
// For ESP32 8/16 bit standard mono mode samples need to be switched.
|
439
|
+
if (this->slot_mode_ == I2S_SLOT_MODE_MONO && this->slot_bit_width_ <= 16 && !this->pdm_) {
|
440
|
+
size_t samples_read = bytes_read / sizeof(int16_t);
|
441
|
+
for (int i = 0; i < samples_read; i += 2) {
|
442
|
+
int16_t tmp = buf[i];
|
443
|
+
buf[i] = buf[i + 1];
|
444
|
+
buf[i + 1] = tmp;
|
186
445
|
}
|
187
|
-
default:
|
188
|
-
ESP_LOGE(TAG, "Unsupported bits per sample: %d", this->bits_per_sample_);
|
189
|
-
return 0;
|
190
446
|
}
|
191
|
-
|
192
|
-
|
193
|
-
void I2SAudioMicrophone::read_() {
|
194
|
-
std::vector<int16_t> samples;
|
195
|
-
samples.resize(BUFFER_SIZE);
|
196
|
-
size_t bytes_read = this->read(samples.data(), BUFFER_SIZE / sizeof(int16_t));
|
197
|
-
samples.resize(bytes_read / sizeof(int16_t));
|
198
|
-
this->data_callbacks_.call(samples);
|
447
|
+
#endif
|
448
|
+
return bytes_read;
|
199
449
|
}
|
200
450
|
|
201
451
|
void I2SAudioMicrophone::loop() {
|
452
|
+
uint32_t event_group_bits = xEventGroupGetBits(this->event_group_);
|
453
|
+
|
454
|
+
if (event_group_bits & MicrophoneEventGroupBits::TASK_STARTING) {
|
455
|
+
ESP_LOGD(TAG, "Task has started, attempting to setup I2S audio driver");
|
456
|
+
xEventGroupClearBits(this->event_group_, MicrophoneEventGroupBits::TASK_STARTING);
|
457
|
+
}
|
458
|
+
|
459
|
+
if (event_group_bits & MicrophoneEventGroupBits::TASK_RUNNING) {
|
460
|
+
ESP_LOGD(TAG, "Task is running and reading data");
|
461
|
+
|
462
|
+
xEventGroupClearBits(this->event_group_, MicrophoneEventGroupBits::TASK_RUNNING);
|
463
|
+
this->state_ = microphone::STATE_RUNNING;
|
464
|
+
}
|
465
|
+
|
466
|
+
if (event_group_bits & MicrophoneEventGroupBits::TASK_STOPPING) {
|
467
|
+
ESP_LOGD(TAG, "Task is stopping, attempting to unload the I2S audio driver");
|
468
|
+
xEventGroupClearBits(this->event_group_, MicrophoneEventGroupBits::TASK_STOPPING);
|
469
|
+
}
|
470
|
+
|
471
|
+
if ((event_group_bits & MicrophoneEventGroupBits::TASK_STOPPED)) {
|
472
|
+
ESP_LOGD(TAG, "Task is finished, freeing resources");
|
473
|
+
vTaskDelete(this->task_handle_);
|
474
|
+
this->task_handle_ = nullptr;
|
475
|
+
xEventGroupClearBits(this->event_group_, ALL_BITS);
|
476
|
+
this->state_ = microphone::STATE_STOPPED;
|
477
|
+
}
|
478
|
+
|
479
|
+
if ((uxSemaphoreGetCount(this->active_listeners_semaphore_) < MAX_LISTENERS) &&
|
480
|
+
(this->state_ == microphone::STATE_STOPPED)) {
|
481
|
+
this->state_ = microphone::STATE_STARTING;
|
482
|
+
}
|
483
|
+
if ((uxSemaphoreGetCount(this->active_listeners_semaphore_) == MAX_LISTENERS) &&
|
484
|
+
(this->state_ == microphone::STATE_RUNNING)) {
|
485
|
+
this->state_ = microphone::STATE_STOPPING;
|
486
|
+
}
|
487
|
+
|
202
488
|
switch (this->state_) {
|
203
|
-
case microphone::STATE_STOPPED:
|
204
|
-
break;
|
205
489
|
case microphone::STATE_STARTING:
|
206
|
-
this->
|
490
|
+
if ((this->task_handle_ == nullptr) && !this->status_has_error()) {
|
491
|
+
xTaskCreate(I2SAudioMicrophone::mic_task, "mic_task", TASK_STACK_SIZE, (void *) this, TASK_PRIORITY,
|
492
|
+
&this->task_handle_);
|
493
|
+
|
494
|
+
if (this->task_handle_ == nullptr) {
|
495
|
+
this->status_momentary_error("Task failed to start, attempting again in 1 second", 1000);
|
496
|
+
}
|
497
|
+
}
|
207
498
|
break;
|
208
499
|
case microphone::STATE_RUNNING:
|
209
|
-
if (this->data_callbacks_.size() > 0) {
|
210
|
-
this->read_();
|
211
|
-
}
|
212
500
|
break;
|
213
501
|
case microphone::STATE_STOPPING:
|
214
|
-
this->
|
502
|
+
xEventGroupSetBits(this->event_group_, MicrophoneEventGroupBits::COMMAND_STOP);
|
503
|
+
break;
|
504
|
+
case microphone::STATE_STOPPED:
|
215
505
|
break;
|
216
506
|
}
|
217
507
|
}
|
@@ -7,6 +7,11 @@
|
|
7
7
|
#include "esphome/components/microphone/microphone.h"
|
8
8
|
#include "esphome/core/component.h"
|
9
9
|
|
10
|
+
#include <freertos/FreeRTOS.h>
|
11
|
+
#include <freertos/event_groups.h>
|
12
|
+
#include <freertos/semphr.h>
|
13
|
+
#include <freertos/task.h>
|
14
|
+
|
10
15
|
namespace esphome {
|
11
16
|
namespace i2s_audio {
|
12
17
|
|
@@ -18,31 +23,60 @@ class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, pub
|
|
18
23
|
|
19
24
|
void loop() override;
|
20
25
|
|
26
|
+
void set_correct_dc_offset(bool correct_dc_offset) { this->correct_dc_offset_ = correct_dc_offset; }
|
27
|
+
|
28
|
+
#ifdef USE_I2S_LEGACY
|
21
29
|
void set_din_pin(int8_t pin) { this->din_pin_ = pin; }
|
22
|
-
|
30
|
+
#else
|
31
|
+
void set_din_pin(int8_t pin) { this->din_pin_ = (gpio_num_t) pin; }
|
32
|
+
#endif
|
23
33
|
|
24
|
-
|
34
|
+
void set_pdm(bool pdm) { this->pdm_ = pdm; }
|
25
35
|
|
36
|
+
#ifdef USE_I2S_LEGACY
|
26
37
|
#if SOC_I2S_SUPPORTS_ADC
|
27
38
|
void set_adc_channel(adc1_channel_t channel) {
|
28
39
|
this->adc_channel_ = channel;
|
29
40
|
this->adc_ = true;
|
30
41
|
}
|
42
|
+
#endif
|
31
43
|
#endif
|
32
44
|
|
33
45
|
protected:
|
34
|
-
|
35
|
-
void
|
36
|
-
|
46
|
+
bool start_driver_();
|
47
|
+
void stop_driver_();
|
48
|
+
|
49
|
+
/// @brief Attempts to correct a microphone DC offset; e.g., a microphones silent level is offset from 0. Applies a
|
50
|
+
/// correction offset that is updated using an exponential moving average for all samples away from 0.
|
51
|
+
/// @param data
|
52
|
+
void fix_dc_offset_(std::vector<uint8_t> &data);
|
37
53
|
|
54
|
+
size_t read_(uint8_t *buf, size_t len, TickType_t ticks_to_wait);
|
55
|
+
|
56
|
+
/// @brief Sets the Microphone ``audio_stream_info_`` member variable to the configured I2S settings.
|
57
|
+
void configure_stream_settings_();
|
58
|
+
|
59
|
+
static void mic_task(void *params);
|
60
|
+
|
61
|
+
SemaphoreHandle_t active_listeners_semaphore_{nullptr};
|
62
|
+
EventGroupHandle_t event_group_{nullptr};
|
63
|
+
|
64
|
+
TaskHandle_t task_handle_{nullptr};
|
65
|
+
|
66
|
+
#ifdef USE_I2S_LEGACY
|
38
67
|
int8_t din_pin_{I2S_PIN_NO_CHANGE};
|
39
68
|
#if SOC_I2S_SUPPORTS_ADC
|
40
69
|
adc1_channel_t adc_channel_{ADC1_CHANNEL_MAX};
|
41
70
|
bool adc_{false};
|
71
|
+
#endif
|
72
|
+
#else
|
73
|
+
gpio_num_t din_pin_{I2S_GPIO_UNUSED};
|
74
|
+
i2s_chan_handle_t rx_handle_;
|
42
75
|
#endif
|
43
76
|
bool pdm_{false};
|
44
77
|
|
45
|
-
|
78
|
+
bool correct_dc_offset_;
|
79
|
+
int32_t dc_offset_{0};
|
46
80
|
};
|
47
81
|
|
48
82
|
} // namespace i2s_audio
|
@@ -26,6 +26,8 @@ from .. import (
|
|
26
26
|
i2s_audio_component_schema,
|
27
27
|
i2s_audio_ns,
|
28
28
|
register_i2s_audio_component,
|
29
|
+
use_legacy,
|
30
|
+
validate_mclk_divisible_by_3,
|
29
31
|
)
|
30
32
|
|
31
33
|
AUTO_LOAD = ["audio"]
|
@@ -60,7 +62,7 @@ I2C_COMM_FMT_OPTIONS = {
|
|
60
62
|
"pcm_long": i2s_comm_format_t.I2S_COMM_FORMAT_PCM_LONG,
|
61
63
|
}
|
62
64
|
|
63
|
-
|
65
|
+
INTERNAL_DAC_VARIANTS = [esp32.const.VARIANT_ESP32]
|
64
66
|
|
65
67
|
|
66
68
|
def _set_num_channels_from_config(config):
|
@@ -101,7 +103,7 @@ def _validate_esp32_variant(config):
|
|
101
103
|
if config[CONF_DAC_TYPE] != "internal":
|
102
104
|
return config
|
103
105
|
variant = esp32.get_esp32_variant()
|
104
|
-
if variant in
|
106
|
+
if variant not in INTERNAL_DAC_VARIANTS:
|
105
107
|
raise cv.Invalid(f"{variant} does not have an internal DAC")
|
106
108
|
return config
|
107
109
|
|
@@ -143,8 +145,8 @@ CONFIG_SCHEMA = cv.All(
|
|
143
145
|
cv.Required(
|
144
146
|
CONF_I2S_DOUT_PIN
|
145
147
|
): pins.internal_gpio_output_pin_number,
|
146
|
-
cv.Optional(CONF_I2S_COMM_FMT, default="stand_i2s"): cv.
|
147
|
-
I2C_COMM_FMT_OPTIONS, lower=True
|
148
|
+
cv.Optional(CONF_I2S_COMM_FMT, default="stand_i2s"): cv.one_of(
|
149
|
+
*I2C_COMM_FMT_OPTIONS, lower=True
|
148
150
|
),
|
149
151
|
}
|
150
152
|
),
|
@@ -154,9 +156,23 @@ CONFIG_SCHEMA = cv.All(
|
|
154
156
|
_validate_esp32_variant,
|
155
157
|
_set_num_channels_from_config,
|
156
158
|
_set_stream_limits,
|
159
|
+
validate_mclk_divisible_by_3,
|
157
160
|
)
|
158
161
|
|
159
162
|
|
163
|
+
def _final_validate(config):
|
164
|
+
if not use_legacy():
|
165
|
+
if config[CONF_DAC_TYPE] == "internal":
|
166
|
+
raise cv.Invalid("Internal DAC is only compatible with legacy i2s driver.")
|
167
|
+
if config[CONF_I2S_COMM_FMT] == "stand_max":
|
168
|
+
raise cv.Invalid(
|
169
|
+
"I2S standard max format only implemented with legacy i2s driver."
|
170
|
+
)
|
171
|
+
|
172
|
+
|
173
|
+
FINAL_VALIDATE_SCHEMA = _final_validate
|
174
|
+
|
175
|
+
|
160
176
|
async def to_code(config):
|
161
177
|
var = cg.new_Pvariable(config[CONF_ID])
|
162
178
|
await cg.register_component(var, config)
|
@@ -167,7 +183,17 @@ async def to_code(config):
|
|
167
183
|
cg.add(var.set_internal_dac_mode(config[CONF_CHANNEL]))
|
168
184
|
else:
|
169
185
|
cg.add(var.set_dout_pin(config[CONF_I2S_DOUT_PIN]))
|
170
|
-
|
186
|
+
if use_legacy():
|
187
|
+
cg.add(
|
188
|
+
var.set_i2s_comm_fmt(I2C_COMM_FMT_OPTIONS[config[CONF_I2S_COMM_FMT]])
|
189
|
+
)
|
190
|
+
else:
|
191
|
+
fmt = "std" # equals stand_i2s, stand_pcm_long, i2s_msb, pcm_long
|
192
|
+
if config[CONF_I2S_COMM_FMT] in ["stand_msb", "i2s_lsb"]:
|
193
|
+
fmt = "msb"
|
194
|
+
elif config[CONF_I2S_COMM_FMT] in ["stand_pcm_short", "pcm_short", "pcm"]:
|
195
|
+
fmt = "pcm"
|
196
|
+
cg.add(var.set_i2s_comm_fmt(fmt))
|
171
197
|
if config[CONF_TIMEOUT] != CONF_NEVER:
|
172
198
|
cg.add(var.set_timeout(config[CONF_TIMEOUT]))
|
173
199
|
cg.add(var.set_buffer_duration(config[CONF_BUFFER_DURATION]))
|