esphome 2025.4.1__py3-none-any.whl → 2025.5.0b2__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/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 +77 -10
- esphome/components/api/api_connection.h +6 -1
- esphome/components/api/api_frame_helper.cpp +98 -130
- esphome/components/api/api_frame_helper.h +12 -2
- 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 +38 -9
- esphome/components/as3935_i2c/as3935_i2c.h +0 -3
- 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/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/binary_sensor/binary_sensor.cpp +6 -10
- esphome/components/binary_sensor/binary_sensor.h +1 -1
- esphome/components/binary_sensor/filter.cpp +21 -21
- esphome/components/binary_sensor/filter.h +10 -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 +135 -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/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/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/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/debug/debug_component.cpp +5 -0
- 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/display/rect.cpp +4 -9
- esphome/components/display/rect.h +1 -1
- esphome/components/emmeti/climate.py +2 -9
- esphome/components/endstop/cover.py +17 -16
- 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 -8
- esphome/components/esp32_ble/ble.h +5 -3
- 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_can/esp32_can.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/esp8266/gpio.cpp +69 -8
- 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/fujitsu_general/climate.py +2 -9
- 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 +11 -2
- esphome/components/gps/gps.cpp +11 -8
- esphome/components/gps/gps.h +9 -6
- 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/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/hlw8012/hlw8012.cpp +1 -1
- esphome/components/hm3301/hm3301.h +1 -1
- 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/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/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 +21 -0
- esphome/components/logger/logger.cpp +125 -95
- esphome/components/logger/logger.h +160 -35
- 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/lv_validation.py +10 -1
- esphome/components/lvgl/lvgl_esphome.cpp +5 -1
- esphome/components/lvgl/schemas.py +14 -14
- esphome/components/lvgl/text/__init__.py +1 -2
- esphome/components/lvgl/widgets/arc.py +7 -6
- esphome/components/lvgl/widgets/buttonmatrix.py +3 -3
- esphome/components/lvgl/widgets/checkbox.py +2 -2
- esphome/components/lvgl/widgets/dropdown.py +2 -1
- esphome/components/lvgl/widgets/img.py +15 -12
- esphome/components/mapping/__init__.py +134 -0
- esphome/components/max7219digit/max7219digit.cpp +27 -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 +40 -6
- 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/mqtt/__init__.py +1 -1
- esphome/components/mqtt/mqtt_client.cpp +5 -1
- 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/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 +193 -229
- 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/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/scd30/sensor.py +2 -3
- esphome/components/scd4x/sensor.py +4 -5
- esphome/components/sdp3x/sensor.py +2 -1
- esphome/components/select/__init__.py +19 -20
- esphome/components/sen5x/sensor.py +1 -1
- esphome/components/sensor/__init__.py +158 -14
- esphome/components/sensor/filter.cpp +23 -0
- esphome/components/sensor/filter.h +22 -0
- esphome/components/sgp4x/sensor.py +1 -1
- esphome/components/sht4x/sht4x.cpp +43 -22
- esphome/components/sht4x/sht4x.h +1 -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/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/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/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/tt21100/touchscreen/tt21100.cpp +1 -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/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/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_validation.py +38 -58
- esphome/const.py +15 -1
- esphome/core/__init__.py +2 -0
- esphome/core/application.cpp +1 -0
- esphome/core/application.h +4 -0
- esphome/core/automation.h +4 -3
- esphome/core/component.cpp +19 -3
- esphome/core/component.h +5 -0
- esphome/core/defines.h +23 -17
- esphome/core/macros.h +4 -0
- esphome/core/scheduler.cpp +3 -0
- esphome/cpp_generator.py +6 -2
- esphome/dashboard/web_server.py +3 -3
- esphome/helpers.py +39 -0
- esphome/loader.py +4 -0
- esphome/mqtt.py +21 -8
- esphome/platformio_api.py +1 -1
- esphome/schema_extractors.py +0 -1
- esphome/vscode.py +15 -0
- esphome/wizard.py +2 -2
- esphome/zeroconf.py +7 -3
- {esphome-2025.4.1.dist-info → esphome-2025.5.0b2.dist-info}/METADATA +10 -11
- {esphome-2025.4.1.dist-info → esphome-2025.5.0b2.dist-info}/RECORD +411 -352
- {esphome-2025.4.1.dist-info → esphome-2025.5.0b2.dist-info}/WHEEL +1 -1
- esphome/components/esp32_ble/const_esp32c6.h +0 -74
- {esphome-2025.4.1.dist-info → esphome-2025.5.0b2.dist-info}/entry_points.txt +0 -0
- {esphome-2025.4.1.dist-info → esphome-2025.5.0b2.dist-info}/licenses/LICENSE +0 -0
- {esphome-2025.4.1.dist-info → esphome-2025.5.0b2.dist-info}/top_level.txt +0 -0
@@ -67,7 +67,7 @@ class BinarySensor : public EntityBase, public EntityBase_DeviceClass {
|
|
67
67
|
|
68
68
|
// ========== INTERNAL METHODS ==========
|
69
69
|
// (In most use cases you won't need these)
|
70
|
-
void send_state_internal(bool state
|
70
|
+
void send_state_internal(bool state);
|
71
71
|
|
72
72
|
/// Return whether this binary sensor has outputted a state.
|
73
73
|
virtual bool has_state() const;
|
@@ -9,37 +9,37 @@ namespace binary_sensor {
|
|
9
9
|
|
10
10
|
static const char *const TAG = "sensor.filter";
|
11
11
|
|
12
|
-
void Filter::output(bool value
|
12
|
+
void Filter::output(bool value) {
|
13
13
|
if (!this->dedup_.next(value))
|
14
14
|
return;
|
15
15
|
|
16
16
|
if (this->next_ == nullptr) {
|
17
|
-
this->parent_->send_state_internal(value
|
17
|
+
this->parent_->send_state_internal(value);
|
18
18
|
} else {
|
19
|
-
this->next_->input(value
|
19
|
+
this->next_->input(value);
|
20
20
|
}
|
21
21
|
}
|
22
|
-
void Filter::input(bool value
|
23
|
-
auto b = this->new_value(value
|
22
|
+
void Filter::input(bool value) {
|
23
|
+
auto b = this->new_value(value);
|
24
24
|
if (b.has_value()) {
|
25
|
-
this->output(*b
|
25
|
+
this->output(*b);
|
26
26
|
}
|
27
27
|
}
|
28
28
|
|
29
|
-
optional<bool> DelayedOnOffFilter::new_value(bool value
|
29
|
+
optional<bool> DelayedOnOffFilter::new_value(bool value) {
|
30
30
|
if (value) {
|
31
|
-
this->set_timeout("ON_OFF", this->on_delay_.value(), [this
|
31
|
+
this->set_timeout("ON_OFF", this->on_delay_.value(), [this]() { this->output(true); });
|
32
32
|
} else {
|
33
|
-
this->set_timeout("ON_OFF", this->off_delay_.value(), [this
|
33
|
+
this->set_timeout("ON_OFF", this->off_delay_.value(), [this]() { this->output(false); });
|
34
34
|
}
|
35
35
|
return {};
|
36
36
|
}
|
37
37
|
|
38
38
|
float DelayedOnOffFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
|
39
39
|
|
40
|
-
optional<bool> DelayedOnFilter::new_value(bool value
|
40
|
+
optional<bool> DelayedOnFilter::new_value(bool value) {
|
41
41
|
if (value) {
|
42
|
-
this->set_timeout("ON", this->delay_.value(), [this
|
42
|
+
this->set_timeout("ON", this->delay_.value(), [this]() { this->output(true); });
|
43
43
|
return {};
|
44
44
|
} else {
|
45
45
|
this->cancel_timeout("ON");
|
@@ -49,9 +49,9 @@ optional<bool> DelayedOnFilter::new_value(bool value, bool is_initial) {
|
|
49
49
|
|
50
50
|
float DelayedOnFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
|
51
51
|
|
52
|
-
optional<bool> DelayedOffFilter::new_value(bool value
|
52
|
+
optional<bool> DelayedOffFilter::new_value(bool value) {
|
53
53
|
if (!value) {
|
54
|
-
this->set_timeout("OFF", this->delay_.value(), [this
|
54
|
+
this->set_timeout("OFF", this->delay_.value(), [this]() { this->output(false); });
|
55
55
|
return {};
|
56
56
|
} else {
|
57
57
|
this->cancel_timeout("OFF");
|
@@ -61,11 +61,11 @@ optional<bool> DelayedOffFilter::new_value(bool value, bool is_initial) {
|
|
61
61
|
|
62
62
|
float DelayedOffFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
|
63
63
|
|
64
|
-
optional<bool> InvertFilter::new_value(bool value
|
64
|
+
optional<bool> InvertFilter::new_value(bool value) { return !value; }
|
65
65
|
|
66
66
|
AutorepeatFilter::AutorepeatFilter(std::vector<AutorepeatFilterTiming> timings) : timings_(std::move(timings)) {}
|
67
67
|
|
68
|
-
optional<bool> AutorepeatFilter::new_value(bool value
|
68
|
+
optional<bool> AutorepeatFilter::new_value(bool value) {
|
69
69
|
if (value) {
|
70
70
|
// Ignore if already running
|
71
71
|
if (this->active_timing_ != 0)
|
@@ -101,7 +101,7 @@ void AutorepeatFilter::next_timing_() {
|
|
101
101
|
|
102
102
|
void AutorepeatFilter::next_value_(bool val) {
|
103
103
|
const AutorepeatFilterTiming &timing = this->timings_[this->active_timing_ - 2];
|
104
|
-
this->output(val
|
104
|
+
this->output(val);
|
105
105
|
this->set_timeout("ON_OFF", val ? timing.time_on : timing.time_off, [this, val]() { this->next_value_(!val); });
|
106
106
|
}
|
107
107
|
|
@@ -109,18 +109,18 @@ float AutorepeatFilter::get_setup_priority() const { return setup_priority::HARD
|
|
109
109
|
|
110
110
|
LambdaFilter::LambdaFilter(std::function<optional<bool>(bool)> f) : f_(std::move(f)) {}
|
111
111
|
|
112
|
-
optional<bool> LambdaFilter::new_value(bool value
|
112
|
+
optional<bool> LambdaFilter::new_value(bool value) { return this->f_(value); }
|
113
113
|
|
114
|
-
optional<bool> SettleFilter::new_value(bool value
|
114
|
+
optional<bool> SettleFilter::new_value(bool value) {
|
115
115
|
if (!this->steady_) {
|
116
|
-
this->set_timeout("SETTLE", this->delay_.value(), [this, value
|
116
|
+
this->set_timeout("SETTLE", this->delay_.value(), [this, value]() {
|
117
117
|
this->steady_ = true;
|
118
|
-
this->output(value
|
118
|
+
this->output(value);
|
119
119
|
});
|
120
120
|
return {};
|
121
121
|
} else {
|
122
122
|
this->steady_ = false;
|
123
|
-
this->output(value
|
123
|
+
this->output(value);
|
124
124
|
this->set_timeout("SETTLE", this->delay_.value(), [this]() { this->steady_ = true; });
|
125
125
|
return value;
|
126
126
|
}
|
@@ -14,11 +14,11 @@ class BinarySensor;
|
|
14
14
|
|
15
15
|
class Filter {
|
16
16
|
public:
|
17
|
-
virtual optional<bool> new_value(bool value
|
17
|
+
virtual optional<bool> new_value(bool value) = 0;
|
18
18
|
|
19
|
-
void input(bool value
|
19
|
+
void input(bool value);
|
20
20
|
|
21
|
-
void output(bool value
|
21
|
+
void output(bool value);
|
22
22
|
|
23
23
|
protected:
|
24
24
|
friend BinarySensor;
|
@@ -30,7 +30,7 @@ class Filter {
|
|
30
30
|
|
31
31
|
class DelayedOnOffFilter : public Filter, public Component {
|
32
32
|
public:
|
33
|
-
optional<bool> new_value(bool value
|
33
|
+
optional<bool> new_value(bool value) override;
|
34
34
|
|
35
35
|
float get_setup_priority() const override;
|
36
36
|
|
@@ -44,7 +44,7 @@ class DelayedOnOffFilter : public Filter, public Component {
|
|
44
44
|
|
45
45
|
class DelayedOnFilter : public Filter, public Component {
|
46
46
|
public:
|
47
|
-
optional<bool> new_value(bool value
|
47
|
+
optional<bool> new_value(bool value) override;
|
48
48
|
|
49
49
|
float get_setup_priority() const override;
|
50
50
|
|
@@ -56,7 +56,7 @@ class DelayedOnFilter : public Filter, public Component {
|
|
56
56
|
|
57
57
|
class DelayedOffFilter : public Filter, public Component {
|
58
58
|
public:
|
59
|
-
optional<bool> new_value(bool value
|
59
|
+
optional<bool> new_value(bool value) override;
|
60
60
|
|
61
61
|
float get_setup_priority() const override;
|
62
62
|
|
@@ -68,7 +68,7 @@ class DelayedOffFilter : public Filter, public Component {
|
|
68
68
|
|
69
69
|
class InvertFilter : public Filter {
|
70
70
|
public:
|
71
|
-
optional<bool> new_value(bool value
|
71
|
+
optional<bool> new_value(bool value) override;
|
72
72
|
};
|
73
73
|
|
74
74
|
struct AutorepeatFilterTiming {
|
@@ -86,7 +86,7 @@ class AutorepeatFilter : public Filter, public Component {
|
|
86
86
|
public:
|
87
87
|
explicit AutorepeatFilter(std::vector<AutorepeatFilterTiming> timings);
|
88
88
|
|
89
|
-
optional<bool> new_value(bool value
|
89
|
+
optional<bool> new_value(bool value) override;
|
90
90
|
|
91
91
|
float get_setup_priority() const override;
|
92
92
|
|
@@ -102,7 +102,7 @@ class LambdaFilter : public Filter {
|
|
102
102
|
public:
|
103
103
|
explicit LambdaFilter(std::function<optional<bool>(bool)> f);
|
104
104
|
|
105
|
-
optional<bool> new_value(bool value
|
105
|
+
optional<bool> new_value(bool value) override;
|
106
106
|
|
107
107
|
protected:
|
108
108
|
std::function<optional<bool>(bool)> f_;
|
@@ -110,7 +110,7 @@ class LambdaFilter : public Filter {
|
|
110
110
|
|
111
111
|
class SettleFilter : public Filter, public Component {
|
112
112
|
public:
|
113
|
-
optional<bool> new_value(bool value
|
113
|
+
optional<bool> new_value(bool value) override;
|
114
114
|
|
115
115
|
float get_setup_priority() const override;
|
116
116
|
|
@@ -45,7 +45,7 @@ static const uint8_t BL0906_WRITE_COMMAND = 0xCA;
|
|
45
45
|
static const uint8_t BL0906_V_RMS = 0x16;
|
46
46
|
|
47
47
|
// Total power
|
48
|
-
static const uint8_t BL0906_WATT_SUM =
|
48
|
+
static const uint8_t BL0906_WATT_SUM = 0x2C;
|
49
49
|
|
50
50
|
// Current1~6
|
51
51
|
static const uint8_t BL0906_I_1_RMS = 0x0D; // current_1
|
@@ -56,29 +56,29 @@ static const uint8_t BL0906_I_5_RMS = 0x13;
|
|
56
56
|
static const uint8_t BL0906_I_6_RMS = 0x14; // current_6
|
57
57
|
|
58
58
|
// Power1~6
|
59
|
-
static const uint8_t BL0906_WATT_1 =
|
60
|
-
static const uint8_t BL0906_WATT_2 =
|
61
|
-
static const uint8_t BL0906_WATT_3 =
|
62
|
-
static const uint8_t BL0906_WATT_4 =
|
63
|
-
static const uint8_t BL0906_WATT_5 =
|
64
|
-
static const uint8_t BL0906_WATT_6 =
|
59
|
+
static const uint8_t BL0906_WATT_1 = 0x23; // power_1
|
60
|
+
static const uint8_t BL0906_WATT_2 = 0x24;
|
61
|
+
static const uint8_t BL0906_WATT_3 = 0x25;
|
62
|
+
static const uint8_t BL0906_WATT_4 = 0x26;
|
63
|
+
static const uint8_t BL0906_WATT_5 = 0x29;
|
64
|
+
static const uint8_t BL0906_WATT_6 = 0x2A; // power_6
|
65
65
|
|
66
66
|
// Active pulse count, unsigned
|
67
|
-
static const uint8_t BL0906_CF_1_CNT =
|
68
|
-
static const uint8_t BL0906_CF_2_CNT =
|
69
|
-
static const uint8_t BL0906_CF_3_CNT =
|
70
|
-
static const uint8_t BL0906_CF_4_CNT =
|
71
|
-
static const uint8_t BL0906_CF_5_CNT =
|
72
|
-
static const uint8_t BL0906_CF_6_CNT =
|
67
|
+
static const uint8_t BL0906_CF_1_CNT = 0x30; // Channel_1
|
68
|
+
static const uint8_t BL0906_CF_2_CNT = 0x31;
|
69
|
+
static const uint8_t BL0906_CF_3_CNT = 0x32;
|
70
|
+
static const uint8_t BL0906_CF_4_CNT = 0x33;
|
71
|
+
static const uint8_t BL0906_CF_5_CNT = 0x36;
|
72
|
+
static const uint8_t BL0906_CF_6_CNT = 0x37; // Channel_6
|
73
73
|
|
74
74
|
// Total active pulse count, unsigned
|
75
|
-
static const uint8_t BL0906_CF_SUM_CNT =
|
75
|
+
static const uint8_t BL0906_CF_SUM_CNT = 0x39;
|
76
76
|
|
77
77
|
// Voltage frequency cycle
|
78
|
-
static const uint8_t BL0906_FREQUENCY =
|
78
|
+
static const uint8_t BL0906_FREQUENCY = 0x4E;
|
79
79
|
|
80
80
|
// Internal temperature
|
81
|
-
static const uint8_t BL0906_TEMPERATURE =
|
81
|
+
static const uint8_t BL0906_TEMPERATURE = 0x5E;
|
82
82
|
|
83
83
|
// Calibration register
|
84
84
|
// RMS gain adjustment register
|
@@ -4,7 +4,6 @@ from esphome.components import ble_client, esp32_ble_tracker, text_sensor
|
|
4
4
|
import esphome.config_validation as cv
|
5
5
|
from esphome.const import (
|
6
6
|
CONF_CHARACTERISTIC_UUID,
|
7
|
-
CONF_ID,
|
8
7
|
CONF_NOTIFY,
|
9
8
|
CONF_SERVICE_UUID,
|
10
9
|
CONF_TRIGGER_ID,
|
@@ -32,9 +31,9 @@ BLETextSensorNotifyTrigger = ble_client_ns.class_(
|
|
32
31
|
)
|
33
32
|
|
34
33
|
CONFIG_SCHEMA = cv.All(
|
35
|
-
text_sensor.
|
34
|
+
text_sensor.text_sensor_schema(BLETextSensor)
|
35
|
+
.extend(
|
36
36
|
{
|
37
|
-
cv.GenerateID(): cv.declare_id(BLETextSensor),
|
38
37
|
cv.Required(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
|
39
38
|
cv.Required(CONF_CHARACTERISTIC_UUID): esp32_ble_tracker.bt_uuid,
|
40
39
|
cv.Optional(CONF_DESCRIPTOR_UUID): esp32_ble_tracker.bt_uuid,
|
@@ -54,7 +53,7 @@ CONFIG_SCHEMA = cv.All(
|
|
54
53
|
|
55
54
|
|
56
55
|
async def to_code(config):
|
57
|
-
var =
|
56
|
+
var = await text_sensor.new_text_sensor(config)
|
58
57
|
if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
|
59
58
|
cg.add(
|
60
59
|
var.set_service_uuid16(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))
|
@@ -101,7 +100,6 @@ async def to_code(config):
|
|
101
100
|
await cg.register_component(var, config)
|
102
101
|
await ble_client.register_ble_node(var, config)
|
103
102
|
cg.add(var.set_enable_notify(config[CONF_NOTIFY]))
|
104
|
-
await text_sensor.register_text_sensor(var, config)
|
105
103
|
for conf in config.get(CONF_ON_NOTIFY, []):
|
106
104
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
107
105
|
await ble_client.register_ble_node(trigger, config)
|
@@ -73,9 +73,8 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
|
|
73
73
|
resp.address = this->address_;
|
74
74
|
resp.handle = param->read.handle;
|
75
75
|
resp.data.reserve(param->read.value_len);
|
76
|
-
|
77
|
-
|
78
|
-
}
|
76
|
+
// Use bulk insert instead of individual push_backs
|
77
|
+
resp.data.insert(resp.data.end(), param->read.value, param->read.value + param->read.value_len);
|
79
78
|
this->proxy_->get_api_connection()->send_bluetooth_gatt_read_response(resp);
|
80
79
|
break;
|
81
80
|
}
|
@@ -127,9 +126,8 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
|
|
127
126
|
resp.address = this->address_;
|
128
127
|
resp.handle = param->notify.handle;
|
129
128
|
resp.data.reserve(param->notify.value_len);
|
130
|
-
|
131
|
-
|
132
|
-
}
|
129
|
+
// Use bulk insert instead of individual push_backs
|
130
|
+
resp.data.insert(resp.data.end(), param->notify.value, param->notify.value + param->notify.value_len);
|
133
131
|
this->proxy_->get_api_connection()->send_bluetooth_gatt_notify_data_response(resp);
|
134
132
|
break;
|
135
133
|
}
|
@@ -25,6 +25,22 @@ std::vector<uint64_t> get_128bit_uuid_vec(esp_bt_uuid_t uuid_source) {
|
|
25
25
|
|
26
26
|
BluetoothProxy::BluetoothProxy() { global_bluetooth_proxy = this; }
|
27
27
|
|
28
|
+
void BluetoothProxy::setup() {
|
29
|
+
this->parent_->add_scanner_state_callback([this](esp32_ble_tracker::ScannerState state) {
|
30
|
+
if (this->api_connection_ != nullptr) {
|
31
|
+
this->send_bluetooth_scanner_state_(state);
|
32
|
+
}
|
33
|
+
});
|
34
|
+
}
|
35
|
+
|
36
|
+
void BluetoothProxy::send_bluetooth_scanner_state_(esp32_ble_tracker::ScannerState state) {
|
37
|
+
api::BluetoothScannerStateResponse resp;
|
38
|
+
resp.state = static_cast<api::enums::BluetoothScannerState>(state);
|
39
|
+
resp.mode = this->parent_->get_scan_active() ? api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_ACTIVE
|
40
|
+
: api::enums::BluetoothScannerMode::BLUETOOTH_SCANNER_MODE_PASSIVE;
|
41
|
+
this->api_connection_->send_bluetooth_scanner_state_response(resp);
|
42
|
+
}
|
43
|
+
|
28
44
|
bool BluetoothProxy::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
29
45
|
if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr || this->raw_advertisements_)
|
30
46
|
return false;
|
@@ -35,33 +51,60 @@ bool BluetoothProxy::parse_device(const esp32_ble_tracker::ESPBTDevice &device)
|
|
35
51
|
return true;
|
36
52
|
}
|
37
53
|
|
54
|
+
static constexpr size_t FLUSH_BATCH_SIZE = 8;
|
55
|
+
static std::vector<api::BluetoothLERawAdvertisement> &get_batch_buffer() {
|
56
|
+
static std::vector<api::BluetoothLERawAdvertisement> batch_buffer;
|
57
|
+
return batch_buffer;
|
58
|
+
}
|
59
|
+
|
38
60
|
bool BluetoothProxy::parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) {
|
39
61
|
if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr || !this->raw_advertisements_)
|
40
62
|
return false;
|
41
63
|
|
42
|
-
|
64
|
+
// Get the batch buffer reference
|
65
|
+
auto &batch_buffer = get_batch_buffer();
|
66
|
+
|
67
|
+
// Reserve additional capacity if needed
|
68
|
+
size_t new_size = batch_buffer.size() + count;
|
69
|
+
if (batch_buffer.capacity() < new_size) {
|
70
|
+
batch_buffer.reserve(new_size);
|
71
|
+
}
|
72
|
+
|
73
|
+
// Add new advertisements to the batch buffer
|
43
74
|
for (size_t i = 0; i < count; i++) {
|
44
75
|
auto &result = advertisements[i];
|
45
|
-
|
76
|
+
uint8_t length = result.adv_data_len + result.scan_rsp_len;
|
77
|
+
|
78
|
+
batch_buffer.emplace_back();
|
79
|
+
auto &adv = batch_buffer.back();
|
46
80
|
adv.address = esp32_ble::ble_addr_to_uint64(result.bda);
|
47
81
|
adv.rssi = result.rssi;
|
48
82
|
adv.address_type = result.ble_addr_type;
|
83
|
+
adv.data.assign(&result.ble_adv[0], &result.ble_adv[length]);
|
49
84
|
|
50
|
-
|
51
|
-
adv.data.reserve(length);
|
52
|
-
for (uint16_t i = 0; i < length; i++) {
|
53
|
-
adv.data.push_back(result.ble_adv[i]);
|
54
|
-
}
|
55
|
-
|
56
|
-
resp.advertisements.push_back(std::move(adv));
|
57
|
-
|
58
|
-
ESP_LOGV(TAG, "Proxying raw packet from %02X:%02X:%02X:%02X:%02X:%02X, length %d. RSSI: %d dB", result.bda[0],
|
85
|
+
ESP_LOGV(TAG, "Queuing raw packet from %02X:%02X:%02X:%02X:%02X:%02X, length %d. RSSI: %d dB", result.bda[0],
|
59
86
|
result.bda[1], result.bda[2], result.bda[3], result.bda[4], result.bda[5], length, result.rssi);
|
60
87
|
}
|
61
|
-
|
62
|
-
|
88
|
+
|
89
|
+
// Only send if we've accumulated a good batch size to maximize batching efficiency
|
90
|
+
// https://github.com/esphome/backlog/issues/21
|
91
|
+
if (batch_buffer.size() >= FLUSH_BATCH_SIZE) {
|
92
|
+
this->flush_pending_advertisements();
|
93
|
+
}
|
94
|
+
|
63
95
|
return true;
|
64
96
|
}
|
97
|
+
|
98
|
+
void BluetoothProxy::flush_pending_advertisements() {
|
99
|
+
auto &batch_buffer = get_batch_buffer();
|
100
|
+
if (batch_buffer.empty() || !api::global_api_server->is_connected() || this->api_connection_ == nullptr)
|
101
|
+
return;
|
102
|
+
|
103
|
+
api::BluetoothLERawAdvertisementsResponse resp;
|
104
|
+
resp.advertisements.swap(batch_buffer);
|
105
|
+
this->api_connection_->send_bluetooth_le_raw_advertisements_response(resp);
|
106
|
+
}
|
107
|
+
|
65
108
|
void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device) {
|
66
109
|
api::BluetoothLEAdvertisementResponse resp;
|
67
110
|
resp.address = device.address_uint64();
|
@@ -69,21 +112,34 @@ void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &devi
|
|
69
112
|
if (!device.get_name().empty())
|
70
113
|
resp.name = device.get_name();
|
71
114
|
resp.rssi = device.get_rssi();
|
72
|
-
|
73
|
-
|
115
|
+
|
116
|
+
// Pre-allocate vectors based on known sizes
|
117
|
+
auto service_uuids = device.get_service_uuids();
|
118
|
+
resp.service_uuids.reserve(service_uuids.size());
|
119
|
+
for (auto &uuid : service_uuids) {
|
120
|
+
resp.service_uuids.emplace_back(uuid.to_string());
|
74
121
|
}
|
75
|
-
|
76
|
-
|
122
|
+
|
123
|
+
// Pre-allocate service data vector
|
124
|
+
auto service_datas = device.get_service_datas();
|
125
|
+
resp.service_data.reserve(service_datas.size());
|
126
|
+
for (auto &data : service_datas) {
|
127
|
+
resp.service_data.emplace_back();
|
128
|
+
auto &service_data = resp.service_data.back();
|
77
129
|
service_data.uuid = data.uuid.to_string();
|
78
130
|
service_data.data.assign(data.data.begin(), data.data.end());
|
79
|
-
resp.service_data.push_back(std::move(service_data));
|
80
131
|
}
|
81
|
-
|
82
|
-
|
132
|
+
|
133
|
+
// Pre-allocate manufacturer data vector
|
134
|
+
auto manufacturer_datas = device.get_manufacturer_datas();
|
135
|
+
resp.manufacturer_data.reserve(manufacturer_datas.size());
|
136
|
+
for (auto &data : manufacturer_datas) {
|
137
|
+
resp.manufacturer_data.emplace_back();
|
138
|
+
auto &manufacturer_data = resp.manufacturer_data.back();
|
83
139
|
manufacturer_data.uuid = data.uuid.to_string();
|
84
140
|
manufacturer_data.data.assign(data.data.begin(), data.data.end());
|
85
|
-
resp.manufacturer_data.push_back(std::move(manufacturer_data));
|
86
141
|
}
|
142
|
+
|
87
143
|
this->api_connection_->send_bluetooth_le_advertisement(resp);
|
88
144
|
}
|
89
145
|
|
@@ -117,6 +173,18 @@ void BluetoothProxy::loop() {
|
|
117
173
|
}
|
118
174
|
return;
|
119
175
|
}
|
176
|
+
|
177
|
+
// Flush any pending BLE advertisements that have been accumulated but not yet sent
|
178
|
+
if (this->raw_advertisements_) {
|
179
|
+
static uint32_t last_flush_time = 0;
|
180
|
+
uint32_t now = millis();
|
181
|
+
|
182
|
+
// Flush accumulated advertisements every 100ms
|
183
|
+
if (now - last_flush_time >= 100) {
|
184
|
+
this->flush_pending_advertisements();
|
185
|
+
last_flush_time = now;
|
186
|
+
}
|
187
|
+
}
|
120
188
|
for (auto *connection : this->connections_) {
|
121
189
|
if (connection->send_service_ == connection->service_count_) {
|
122
190
|
connection->send_service_ = DONE_SENDING_SERVICES;
|
@@ -145,11 +213,27 @@ void BluetoothProxy::loop() {
|
|
145
213
|
}
|
146
214
|
api::BluetoothGATTGetServicesResponse resp;
|
147
215
|
resp.address = connection->get_address();
|
216
|
+
resp.services.reserve(1); // Always one service per response in this implementation
|
148
217
|
api::BluetoothGATTService service_resp;
|
149
218
|
service_resp.uuid = get_128bit_uuid_vec(service_result.uuid);
|
150
219
|
service_resp.handle = service_result.start_handle;
|
151
220
|
uint16_t char_offset = 0;
|
152
221
|
esp_gattc_char_elem_t char_result;
|
222
|
+
// Get the number of characteristics directly with one call
|
223
|
+
uint16_t total_char_count = 0;
|
224
|
+
esp_gatt_status_t char_count_status = esp_ble_gattc_get_attr_count(
|
225
|
+
connection->get_gattc_if(), connection->get_conn_id(), ESP_GATT_DB_CHARACTERISTIC,
|
226
|
+
service_result.start_handle, service_result.end_handle, 0, &total_char_count);
|
227
|
+
|
228
|
+
if (char_count_status == ESP_GATT_OK && total_char_count > 0) {
|
229
|
+
// Only reserve if we successfully got a count
|
230
|
+
service_resp.characteristics.reserve(total_char_count);
|
231
|
+
} else if (char_count_status != ESP_GATT_OK) {
|
232
|
+
ESP_LOGW(TAG, "[%d] [%s] Error getting characteristic count, status=%d", connection->get_connection_index(),
|
233
|
+
connection->address_str().c_str(), char_count_status);
|
234
|
+
}
|
235
|
+
|
236
|
+
// Now process characteristics
|
153
237
|
while (true) { // characteristics
|
154
238
|
uint16_t char_count = 1;
|
155
239
|
esp_gatt_status_t char_status = esp_ble_gattc_get_all_char(
|
@@ -171,6 +255,23 @@ void BluetoothProxy::loop() {
|
|
171
255
|
characteristic_resp.handle = char_result.char_handle;
|
172
256
|
characteristic_resp.properties = char_result.properties;
|
173
257
|
char_offset++;
|
258
|
+
|
259
|
+
// Get the number of descriptors directly with one call
|
260
|
+
uint16_t total_desc_count = 0;
|
261
|
+
esp_gatt_status_t desc_count_status =
|
262
|
+
esp_ble_gattc_get_attr_count(connection->get_gattc_if(), connection->get_conn_id(), ESP_GATT_DB_DESCRIPTOR,
|
263
|
+
char_result.char_handle, service_result.end_handle, 0, &total_desc_count);
|
264
|
+
|
265
|
+
if (desc_count_status == ESP_GATT_OK && total_desc_count > 0) {
|
266
|
+
// Only reserve if we successfully got a count
|
267
|
+
characteristic_resp.descriptors.reserve(total_desc_count);
|
268
|
+
} else if (desc_count_status != ESP_GATT_OK) {
|
269
|
+
ESP_LOGW(TAG, "[%d] [%s] Error getting descriptor count for char handle %d, status=%d",
|
270
|
+
connection->get_connection_index(), connection->address_str().c_str(), char_result.char_handle,
|
271
|
+
desc_count_status);
|
272
|
+
}
|
273
|
+
|
274
|
+
// Now process descriptors
|
174
275
|
uint16_t desc_offset = 0;
|
175
276
|
esp_gattc_descr_elem_t desc_result;
|
176
277
|
while (true) { // descriptors
|
@@ -453,6 +554,8 @@ void BluetoothProxy::subscribe_api_connection(api::APIConnection *api_connection
|
|
453
554
|
this->api_connection_ = api_connection;
|
454
555
|
this->raw_advertisements_ = flags & BluetoothProxySubscriptionFlag::SUBSCRIPTION_RAW_ADVERTISEMENTS;
|
455
556
|
this->parent_->recalculate_advertisement_parser_types();
|
557
|
+
|
558
|
+
this->send_bluetooth_scanner_state_(this->parent_->get_scanner_state());
|
456
559
|
}
|
457
560
|
|
458
561
|
void BluetoothProxy::unsubscribe_api_connection(api::APIConnection *api_connection) {
|
@@ -525,6 +628,17 @@ void BluetoothProxy::send_device_unpairing(uint64_t address, bool success, esp_e
|
|
525
628
|
this->api_connection_->send_bluetooth_device_unpairing_response(call);
|
526
629
|
}
|
527
630
|
|
631
|
+
void BluetoothProxy::bluetooth_scanner_set_mode(bool active) {
|
632
|
+
if (this->parent_->get_scan_active() == active) {
|
633
|
+
return;
|
634
|
+
}
|
635
|
+
ESP_LOGD(TAG, "Setting scanner mode to %s", active ? "active" : "passive");
|
636
|
+
this->parent_->set_scan_active(active);
|
637
|
+
this->parent_->stop_scan();
|
638
|
+
this->parent_->set_scan_continuous(
|
639
|
+
true); // Set this to true to automatically start scanning again when it has cleaned up.
|
640
|
+
}
|
641
|
+
|
528
642
|
BluetoothProxy *global_bluetooth_proxy = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
529
643
|
|
530
644
|
} // namespace bluetooth_proxy
|
@@ -41,6 +41,7 @@ enum BluetoothProxyFeature : uint32_t {
|
|
41
41
|
FEATURE_PAIRING = 1 << 3,
|
42
42
|
FEATURE_CACHE_CLEARING = 1 << 4,
|
43
43
|
FEATURE_RAW_ADVERTISEMENTS = 1 << 5,
|
44
|
+
FEATURE_STATE_AND_MODE = 1 << 6,
|
44
45
|
};
|
45
46
|
|
46
47
|
enum BluetoothProxySubscriptionFlag : uint32_t {
|
@@ -53,7 +54,9 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com
|
|
53
54
|
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
|
54
55
|
bool parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) override;
|
55
56
|
void dump_config() override;
|
57
|
+
void setup() override;
|
56
58
|
void loop() override;
|
59
|
+
void flush_pending_advertisements();
|
57
60
|
esp32_ble_tracker::AdvertisementParserType get_advertisement_parser_type() override;
|
58
61
|
|
59
62
|
void register_connection(BluetoothConnection *connection) {
|
@@ -84,6 +87,8 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com
|
|
84
87
|
void send_device_unpairing(uint64_t address, bool success, esp_err_t error = ESP_OK);
|
85
88
|
void send_device_clear_cache(uint64_t address, bool success, esp_err_t error = ESP_OK);
|
86
89
|
|
90
|
+
void bluetooth_scanner_set_mode(bool active);
|
91
|
+
|
87
92
|
static void uint64_to_bd_addr(uint64_t address, esp_bd_addr_t bd_addr) {
|
88
93
|
bd_addr[0] = (address >> 40) & 0xff;
|
89
94
|
bd_addr[1] = (address >> 32) & 0xff;
|
@@ -107,6 +112,7 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com
|
|
107
112
|
uint32_t flags = 0;
|
108
113
|
flags |= BluetoothProxyFeature::FEATURE_PASSIVE_SCAN;
|
109
114
|
flags |= BluetoothProxyFeature::FEATURE_RAW_ADVERTISEMENTS;
|
115
|
+
flags |= BluetoothProxyFeature::FEATURE_STATE_AND_MODE;
|
110
116
|
if (this->active_) {
|
111
117
|
flags |= BluetoothProxyFeature::FEATURE_ACTIVE_CONNECTIONS;
|
112
118
|
flags |= BluetoothProxyFeature::FEATURE_REMOTE_CACHING;
|
@@ -124,6 +130,7 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com
|
|
124
130
|
|
125
131
|
protected:
|
126
132
|
void send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device);
|
133
|
+
void send_bluetooth_scanner_state_(esp32_ble_tracker::ScannerState state);
|
127
134
|
|
128
135
|
BluetoothConnection *get_connection_(uint64_t address, bool reserve);
|
129
136
|
|
@@ -44,7 +44,7 @@ ButtonPressTrigger = button_ns.class_(
|
|
44
44
|
validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
|
45
45
|
|
46
46
|
|
47
|
-
|
47
|
+
_BUTTON_SCHEMA = (
|
48
48
|
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
|
49
49
|
.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
|
50
50
|
.extend(
|
@@ -60,15 +60,13 @@ BUTTON_SCHEMA = (
|
|
60
60
|
)
|
61
61
|
)
|
62
62
|
|
63
|
-
_UNDEF = object()
|
64
|
-
|
65
63
|
|
66
64
|
def button_schema(
|
67
65
|
class_: MockObjClass,
|
68
66
|
*,
|
69
|
-
icon: str =
|
70
|
-
entity_category: str =
|
71
|
-
device_class: str =
|
67
|
+
icon: str = cv.UNDEFINED,
|
68
|
+
entity_category: str = cv.UNDEFINED,
|
69
|
+
device_class: str = cv.UNDEFINED,
|
72
70
|
) -> cv.Schema:
|
73
71
|
schema = {cv.GenerateID(): cv.declare_id(class_)}
|
74
72
|
|
@@ -77,10 +75,15 @@ def button_schema(
|
|
77
75
|
(CONF_ENTITY_CATEGORY, entity_category, cv.entity_category),
|
78
76
|
(CONF_DEVICE_CLASS, device_class, validate_device_class),
|
79
77
|
]:
|
80
|
-
if default is not
|
78
|
+
if default is not cv.UNDEFINED:
|
81
79
|
schema[cv.Optional(key, default=default)] = validator
|
82
80
|
|
83
|
-
return
|
81
|
+
return _BUTTON_SCHEMA.extend(schema)
|
82
|
+
|
83
|
+
|
84
|
+
# Remove before 2025.11.0
|
85
|
+
BUTTON_SCHEMA = button_schema(Button)
|
86
|
+
BUTTON_SCHEMA.add_extra(cv.deprecated_schema_constant("button"))
|
84
87
|
|
85
88
|
|
86
89
|
async def setup_button_core_(var, config):
|
@@ -86,6 +86,9 @@ void Canbus::loop() {
|
|
86
86
|
data.push_back(can_message.data[i]);
|
87
87
|
}
|
88
88
|
|
89
|
+
this->callback_manager_(can_message.can_id, can_message.use_extended_id, can_message.remote_transmission_request,
|
90
|
+
data);
|
91
|
+
|
89
92
|
// fire all triggers
|
90
93
|
for (auto *trigger : this->triggers_) {
|
91
94
|
if ((trigger->can_id_ == (can_message.can_id & trigger->can_id_mask_)) &&
|
@@ -81,6 +81,20 @@ class Canbus : public Component {
|
|
81
81
|
void set_bitrate(CanSpeed bit_rate) { this->bit_rate_ = bit_rate; }
|
82
82
|
|
83
83
|
void add_trigger(CanbusTrigger *trigger);
|
84
|
+
/**
|
85
|
+
* Add a callback to be called when a CAN message is received. All received messages
|
86
|
+
* are passed to the callback without filtering.
|
87
|
+
*
|
88
|
+
* The callback function receives:
|
89
|
+
* - can_id of the received data
|
90
|
+
* - extended_id True if the can_id is an extended id
|
91
|
+
* - rtr If this is a remote transmission request
|
92
|
+
* - data The message data
|
93
|
+
*/
|
94
|
+
void add_callback(
|
95
|
+
std::function<void(uint32_t can_id, bool extended_id, bool rtr, const std::vector<uint8_t> &data)> callback) {
|
96
|
+
this->callback_manager_.add(std::move(callback));
|
97
|
+
}
|
84
98
|
|
85
99
|
protected:
|
86
100
|
template<typename... Ts> friend class CanbusSendAction;
|
@@ -88,6 +102,8 @@ class Canbus : public Component {
|
|
88
102
|
uint32_t can_id_;
|
89
103
|
bool use_extended_id_;
|
90
104
|
CanSpeed bit_rate_;
|
105
|
+
CallbackManager<void(uint32_t can_id, bool extended_id, bool rtr, const std::vector<uint8_t> &data)>
|
106
|
+
callback_manager_{};
|
91
107
|
|
92
108
|
virtual bool setup_internal();
|
93
109
|
virtual Error send_message(struct CanFrame *frame);
|