esphome 2024.12.4__py3-none-any.whl → 2025.2.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/__main__.py +16 -3
- esphome/components/adc/__init__.py +17 -11
- esphome/components/adc/adc_sensor.h +17 -0
- esphome/components/adc/adc_sensor_common.cpp +55 -0
- esphome/components/adc/adc_sensor_esp32.cpp +8 -5
- esphome/components/adc/adc_sensor_esp8266.cpp +10 -6
- esphome/components/adc/adc_sensor_libretiny.cpp +11 -6
- esphome/components/adc/adc_sensor_rp2040.cpp +13 -10
- esphome/components/adc/sensor.py +9 -3
- esphome/components/ads1115/ads1115.cpp +56 -7
- esphome/components/ads1115/ads1115.h +13 -1
- esphome/components/ads1115/sensor/__init__.py +16 -0
- esphome/components/ads1115/sensor/ads1115_sensor.cpp +2 -1
- esphome/components/ads1115/sensor/ads1115_sensor.h +2 -0
- esphome/components/animation/__init__.py +23 -261
- esphome/components/animation/animation.cpp +2 -2
- esphome/components/animation/animation.h +2 -1
- esphome/components/api/api_pb2.cpp +14 -0
- esphome/components/api/api_pb2.h +1 -0
- esphome/components/audio/__init__.py +112 -0
- esphome/components/audio/audio.cpp +67 -0
- esphome/components/audio/audio.h +125 -7
- esphome/components/audio/audio_decoder.cpp +361 -0
- esphome/components/audio/audio_decoder.h +135 -0
- esphome/components/audio/audio_reader.cpp +308 -0
- esphome/components/audio/audio_reader.h +85 -0
- esphome/components/audio/audio_resampler.cpp +159 -0
- esphome/components/audio/audio_resampler.h +101 -0
- esphome/components/audio/audio_transfer_buffer.cpp +165 -0
- esphome/components/audio/audio_transfer_buffer.h +139 -0
- esphome/components/audio_adc/__init__.py +41 -0
- esphome/components/audio_adc/audio_adc.h +17 -0
- esphome/components/audio_adc/automation.h +23 -0
- esphome/components/bk72xx/__init__.py +1 -0
- esphome/components/ble_client/ble_client.cpp +1 -2
- esphome/components/ble_client/sensor/__init__.py +1 -1
- esphome/components/ble_client/text_sensor/__init__.py +1 -1
- esphome/components/bluetooth_proxy/bluetooth_connection.cpp +5 -0
- esphome/components/bluetooth_proxy/bluetooth_connection.h +1 -0
- esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +5 -0
- esphome/components/ch422g/ch422g.h +2 -0
- esphome/components/climate/__init__.py +1 -1
- esphome/components/climate_ir/climate_ir.cpp +2 -1
- esphome/components/coolix/coolix.cpp +2 -1
- esphome/components/cse7766/cse7766.cpp +8 -16
- esphome/components/custom/__init__.py +0 -3
- esphome/components/custom/binary_sensor/__init__.py +2 -28
- esphome/components/custom/climate/__init__.py +2 -27
- esphome/components/custom/cover/__init__.py +2 -27
- esphome/components/custom/light/__init__.py +2 -27
- esphome/components/custom/output/__init__.py +2 -58
- esphome/components/custom/sensor/__init__.py +2 -24
- esphome/components/custom/switch/__init__.py +2 -24
- esphome/components/custom/text_sensor/__init__.py +2 -29
- esphome/components/custom_component/__init__.py +3 -27
- esphome/components/daly_bms/daly_bms.cpp +6 -0
- esphome/components/daly_bms/daly_bms.h +2 -0
- esphome/components/daly_bms/sensor.py +6 -0
- esphome/components/debug/debug_component.cpp +4 -0
- esphome/components/debug/debug_component.h +14 -0
- esphome/components/debug/debug_esp32.cpp +154 -74
- esphome/components/dfplayer/dfplayer.cpp +15 -2
- esphome/components/dfrobot_sen0395/dfrobot_sen0395.cpp +2 -1
- esphome/components/dht/dht.cpp +2 -1
- esphome/components/display/__init__.py +18 -5
- esphome/components/display/display.cpp +2 -1
- esphome/components/display/rect.cpp +2 -1
- esphome/components/es7210/__init__.py +0 -0
- esphome/components/es7210/audio_adc.py +51 -0
- esphome/components/es7210/es7210.cpp +228 -0
- esphome/components/es7210/es7210.h +62 -0
- esphome/components/es7210/es7210_const.h +129 -0
- esphome/components/es7243e/__init__.py +0 -0
- esphome/components/es7243e/audio_adc.py +34 -0
- esphome/components/es7243e/es7243e.cpp +125 -0
- esphome/components/es7243e/es7243e.h +37 -0
- esphome/components/es7243e/es7243e_const.h +54 -0
- esphome/components/es8156/__init__.py +0 -0
- esphome/components/es8156/audio_dac.py +27 -0
- esphome/components/es8156/es8156.cpp +87 -0
- esphome/components/es8156/es8156.h +51 -0
- esphome/components/es8156/es8156_const.h +68 -0
- esphome/components/es8311/audio_dac.py +1 -2
- esphome/components/esp32/__init__.py +1 -0
- esphome/components/esp32/core.cpp +5 -1
- esphome/components/esp32/gpio.h +2 -0
- esphome/components/esp32_ble/__init__.py +39 -0
- esphome/components/esp32_ble/queue.h +4 -4
- esphome/components/esp32_ble_client/ble_client_base.cpp +46 -0
- esphome/components/esp32_ble_client/ble_client_base.h +2 -0
- esphome/components/esp32_ble_server/__init__.py +582 -12
- esphome/components/esp32_ble_server/ble_characteristic.cpp +48 -60
- esphome/components/esp32_ble_server/ble_characteristic.h +24 -17
- esphome/components/esp32_ble_server/ble_descriptor.cpp +21 -9
- esphome/components/esp32_ble_server/ble_descriptor.h +17 -6
- esphome/components/esp32_ble_server/ble_server.cpp +62 -67
- esphome/components/esp32_ble_server/ble_server.h +28 -32
- esphome/components/esp32_ble_server/ble_server_automations.cpp +77 -0
- esphome/components/esp32_ble_server/ble_server_automations.h +115 -0
- esphome/components/esp32_ble_server/ble_service.cpp +17 -15
- esphome/components/esp32_ble_server/ble_service.h +10 -14
- esphome/components/esp32_ble_tracker/__init__.py +6 -39
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +33 -10
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +8 -4
- esphome/components/esp32_improv/__init__.py +2 -8
- esphome/components/esp32_improv/esp32_improv_component.cpp +21 -20
- esphome/components/esp32_improv/esp32_improv_component.h +3 -4
- esphome/components/esp32_rmt/__init__.py +28 -3
- esphome/components/esp32_rmt_led_strip/led_strip.cpp +73 -6
- esphome/components/esp32_rmt_led_strip/led_strip.h +21 -3
- esphome/components/esp32_rmt_led_strip/light.py +72 -7
- esphome/components/esp32_touch/esp32_touch.cpp +5 -0
- esphome/components/esp8266/__init__.py +1 -0
- esphome/components/esp8266/gpio.h +1 -0
- esphome/components/ethernet/__init__.py +10 -10
- esphome/components/event/event.cpp +4 -2
- esphome/components/event/event.h +2 -0
- esphome/components/event_emitter/__init__.py +5 -0
- esphome/components/event_emitter/event_emitter.cpp +14 -0
- esphome/components/event_emitter/event_emitter.h +63 -0
- esphome/components/gcja5/gcja5.cpp +2 -1
- esphome/components/graph/graph.cpp +4 -9
- esphome/components/haier/haier_base.cpp +2 -1
- esphome/components/haier/hon_climate.cpp +2 -1
- esphome/components/heatpumpir/heatpumpir.cpp +2 -1
- esphome/components/host/__init__.py +1 -0
- esphome/components/host/gpio.h +1 -0
- esphome/components/http_request/http_request.h +2 -2
- esphome/components/http_request/http_request_arduino.cpp +1 -1
- esphome/components/http_request/http_request_idf.cpp +1 -1
- esphome/components/i2c/i2c_bus_esp_idf.cpp +4 -0
- esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +7 -5
- esphome/components/i2s_audio/speaker/__init__.py +53 -6
- esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +92 -46
- esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +8 -0
- esphome/components/ili9xxx/display.py +29 -11
- esphome/components/ili9xxx/ili9xxx_display.cpp +2 -5
- esphome/components/ili9xxx/ili9xxx_display.h +2 -1
- esphome/components/image/__init__.py +443 -255
- esphome/components/image/image.cpp +115 -61
- esphome/components/image/image.h +15 -24
- esphome/components/json/json_util.cpp +8 -34
- esphome/components/libretiny/__init__.py +1 -0
- esphome/components/libretiny/gpio_arduino.h +1 -0
- esphome/components/light/light_color_values.h +1 -1
- esphome/components/logger/__init__.py +43 -7
- esphome/components/logger/logger.cpp +16 -11
- esphome/components/logger/logger.h +11 -7
- esphome/components/logger/select/__init__.py +29 -0
- esphome/components/logger/select/logger_level_select.cpp +27 -0
- esphome/components/logger/select/logger_level_select.h +15 -0
- esphome/components/lvgl/__init__.py +96 -73
- esphome/components/lvgl/automation.py +39 -7
- esphome/components/lvgl/defines.py +8 -2
- esphome/components/lvgl/lvgl_esphome.cpp +8 -15
- esphome/components/lvgl/lvgl_esphome.h +20 -5
- esphome/components/lvgl/schemas.py +25 -14
- esphome/components/lvgl/trigger.py +27 -3
- esphome/components/lvgl/widgets/dropdown.py +1 -1
- esphome/components/lvgl/widgets/keyboard.py +8 -1
- esphome/components/lvgl/widgets/meter.py +2 -1
- esphome/components/lvgl/widgets/msgbox.py +1 -1
- esphome/components/lvgl/widgets/obj.py +1 -12
- esphome/components/lvgl/widgets/page.py +37 -2
- esphome/components/lvgl/widgets/tabview.py +1 -1
- esphome/components/max6956/max6956.h +2 -0
- esphome/components/mcp23016/mcp23016.h +2 -0
- esphome/components/mcp23xxx_base/mcp23xxx_base.h +2 -0
- esphome/components/mdns/__init__.py +1 -1
- esphome/components/media_player/__init__.py +37 -8
- esphome/components/media_player/automation.h +11 -2
- esphome/components/media_player/media_player.cpp +8 -0
- esphome/components/media_player/media_player.h +8 -4
- esphome/components/micronova/switch/micronova_switch.cpp +4 -2
- esphome/components/midea/ac_automations.h +3 -1
- esphome/components/midea/air_conditioner.cpp +7 -5
- esphome/components/midea/air_conditioner.h +1 -1
- esphome/components/midea/climate.py +4 -2
- esphome/components/midea/ir_transmitter.h +36 -5
- esphome/components/mixer/__init__.py +0 -0
- esphome/components/mixer/speaker/__init__.py +172 -0
- esphome/components/mixer/speaker/automation.h +19 -0
- esphome/components/mixer/speaker/mixer_speaker.cpp +624 -0
- esphome/components/mixer/speaker/mixer_speaker.h +207 -0
- esphome/components/modbus_controller/text_sensor/modbus_textsensor.cpp +7 -13
- esphome/components/mpr121/mpr121.h +2 -0
- esphome/components/mqtt/__init__.py +1 -1
- esphome/components/mqtt/mqtt_client.cpp +7 -1
- esphome/components/mqtt/mqtt_client.h +1 -1
- esphome/components/mqtt/mqtt_climate.cpp +2 -2
- esphome/components/network/ip_address.h +2 -0
- esphome/components/nextion/automation.h +17 -0
- esphome/components/nextion/display.py +42 -17
- esphome/components/nextion/nextion.cpp +4 -10
- esphome/components/nextion/nextion.h +89 -82
- esphome/components/nextion/nextion_commands.cpp +10 -10
- esphome/components/ntc/sensor.py +2 -4
- esphome/components/online_image/__init__.py +98 -46
- esphome/components/online_image/bmp_image.cpp +101 -0
- esphome/components/online_image/bmp_image.h +40 -0
- esphome/components/online_image/image_decoder.cpp +28 -2
- esphome/components/online_image/image_decoder.h +24 -15
- esphome/components/online_image/jpeg_image.cpp +90 -0
- esphome/components/online_image/jpeg_image.h +34 -0
- esphome/components/online_image/online_image.cpp +112 -53
- esphome/components/online_image/online_image.h +24 -7
- esphome/components/online_image/png_image.cpp +7 -3
- esphome/components/online_image/png_image.h +2 -1
- esphome/components/opentherm/__init__.py +73 -7
- esphome/components/opentherm/automation.h +25 -0
- esphome/components/opentherm/const.py +1 -0
- esphome/components/opentherm/generate.py +39 -6
- esphome/components/opentherm/hub.cpp +117 -79
- esphome/components/opentherm/hub.h +31 -15
- esphome/components/opentherm/opentherm.cpp +47 -23
- esphome/components/opentherm/opentherm.h +27 -6
- esphome/components/opentherm/opentherm_macros.h +11 -0
- esphome/components/opentherm/schema.py +78 -1
- esphome/components/opentherm/validate.py +7 -2
- esphome/components/pca6416a/pca6416a.h +2 -0
- esphome/components/pca9554/pca9554.h +2 -0
- esphome/components/pcf8574/pcf8574.h +2 -0
- esphome/components/preferences/__init__.py +2 -4
- esphome/components/preferences/syncer.h +10 -3
- esphome/components/prometheus/prometheus_handler.cpp +313 -0
- esphome/components/prometheus/prometheus_handler.h +48 -7
- esphome/components/psram/psram.cpp +8 -1
- esphome/components/pulse_counter/pulse_counter_sensor.cpp +14 -9
- esphome/components/pulse_counter/pulse_counter_sensor.h +4 -4
- esphome/components/pulse_meter/pulse_meter_sensor.cpp +2 -0
- esphome/components/qspi_dbi/__init__.py +3 -0
- esphome/components/qspi_dbi/display.py +74 -47
- esphome/components/qspi_dbi/models.py +245 -2
- esphome/components/qspi_dbi/qspi_dbi.cpp +9 -16
- esphome/components/qspi_dbi/qspi_dbi.h +2 -2
- esphome/components/remote_base/__init__.py +77 -25
- esphome/components/remote_base/remote_base.cpp +1 -1
- esphome/components/remote_base/remote_base.h +20 -2
- esphome/components/remote_base/toto_protocol.cpp +100 -0
- esphome/components/remote_base/toto_protocol.h +45 -0
- esphome/components/remote_receiver/__init__.py +55 -10
- esphome/components/remote_receiver/remote_receiver.h +36 -3
- esphome/components/remote_receiver/remote_receiver_esp32.cpp +145 -6
- esphome/components/remote_transmitter/__init__.py +62 -4
- esphome/components/remote_transmitter/remote_transmitter.h +21 -2
- esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +140 -4
- esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +3 -3
- esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp +3 -3
- esphome/components/resampler/__init__.py +0 -0
- esphome/components/resampler/speaker/__init__.py +103 -0
- esphome/components/resampler/speaker/resampler_speaker.cpp +318 -0
- esphome/components/resampler/speaker/resampler_speaker.h +107 -0
- esphome/components/resistance/resistance_sensor.h +2 -3
- esphome/components/resistance/sensor.py +2 -9
- esphome/components/rotary_encoder/rotary_encoder.cpp +8 -4
- esphome/components/rp2040/__init__.py +1 -0
- esphome/components/rp2040/gpio.h +1 -0
- esphome/components/rtl87xx/__init__.py +2 -0
- esphome/components/sdl/binary_sensor.py +270 -0
- esphome/components/sdl/sdl_esphome.cpp +16 -0
- esphome/components/sdl/sdl_esphome.h +9 -0
- esphome/components/seeed_mr60bha2/binary_sensor.py +25 -0
- esphome/components/seeed_mr60bha2/seeed_mr60bha2.cpp +26 -2
- esphome/components/seeed_mr60bha2/seeed_mr60bha2.h +9 -20
- esphome/components/seeed_mr60bha2/sensor.py +9 -1
- esphome/components/sn74hc165/sn74hc165.h +3 -0
- esphome/components/sn74hc595/sn74hc595.h +3 -0
- esphome/components/speaker/__init__.py +5 -4
- esphome/components/speaker/media_player/__init__.py +458 -0
- esphome/components/speaker/media_player/audio_pipeline.cpp +568 -0
- esphome/components/speaker/media_player/audio_pipeline.h +159 -0
- esphome/components/speaker/media_player/automation.h +26 -0
- esphome/components/speaker/media_player/speaker_media_player.cpp +577 -0
- esphome/components/speaker/media_player/speaker_media_player.h +160 -0
- esphome/components/speaker/speaker.h +20 -0
- esphome/components/spi/__init__.py +1 -5
- esphome/components/spi/spi.cpp +7 -1
- esphome/components/spi/spi.h +21 -2
- esphome/components/spi_led_strip/light.py +3 -5
- esphome/components/spi_led_strip/spi_led_strip.cpp +67 -0
- esphome/components/spi_led_strip/spi_led_strip.h +8 -60
- esphome/components/sprinkler/sprinkler.cpp +3 -1
- esphome/components/sx1509/sx1509_gpio_pin.h +2 -0
- esphome/components/tca9555/tca9555.h +2 -0
- esphome/components/toshiba/toshiba.cpp +2 -1
- esphome/components/tuya/light/tuya_light.cpp +4 -2
- esphome/components/uart/uart_component_esp32_arduino.cpp +2 -2
- esphome/components/uart/uart_component_esp_idf.cpp +2 -2
- esphome/components/udp/__init__.py +8 -2
- esphome/components/udp/udp_component.cpp +25 -56
- esphome/components/udp/udp_component.h +3 -0
- esphome/components/uponor_smatrix/sensor/__init__.py +14 -4
- esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.cpp +5 -0
- esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.h +1 -0
- esphome/components/uptime/text_sensor/__init__.py +19 -0
- esphome/components/uptime/text_sensor/uptime_text_sensor.cpp +63 -0
- esphome/components/uptime/text_sensor/uptime_text_sensor.h +25 -0
- esphome/components/voice_assistant/voice_assistant.cpp +24 -14
- esphome/components/voice_assistant/voice_assistant.h +8 -0
- esphome/components/waveshare_epaper/display.py +22 -1
- esphome/components/waveshare_epaper/waveshare_213v3.cpp +9 -3
- esphome/components/waveshare_epaper/waveshare_epaper.cpp +1155 -44
- esphome/components/waveshare_epaper/waveshare_epaper.h +208 -7
- esphome/components/web_server/web_server.cpp +28 -6
- esphome/components/weikai/weikai.h +2 -0
- esphome/components/wifi/__init__.py +6 -6
- esphome/components/wifi/wifi_component.cpp +1 -1
- esphome/components/wifi/wifi_component_esp32_arduino.cpp +30 -1
- esphome/components/wireguard/__init__.py +2 -2
- esphome/components/xl9535/xl9535.h +2 -0
- esphome/components/xxtea/__init__.py +3 -0
- esphome/components/xxtea/xxtea.cpp +46 -0
- esphome/components/xxtea/xxtea.h +26 -0
- esphome/components/yashima/yashima.cpp +2 -1
- esphome/config.py +9 -5
- esphome/config_validation.py +55 -17
- esphome/const.py +7 -10
- esphome/core/__init__.py +6 -13
- esphome/core/base_automation.h +1 -0
- esphome/core/config.py +59 -72
- esphome/core/defines.h +9 -1
- esphome/core/gpio.h +7 -0
- esphome/core/helpers.cpp +19 -15
- esphome/core/helpers.h +57 -8
- esphome/core/log.h +9 -7
- esphome/cpp_generator.py +2 -2
- esphome/espota2.py +3 -2
- esphome/loader.py +12 -4
- esphome/log.py +5 -7
- esphome/yaml_util.py +2 -2
- {esphome-2024.12.4.dist-info → esphome-2025.2.0b2.dist-info}/METADATA +12 -7
- {esphome-2024.12.4.dist-info → esphome-2025.2.0b2.dist-info}/RECORD +341 -292
- esphome/components/custom/binary_sensor/custom_binary_sensor.cpp +0 -16
- esphome/components/custom/binary_sensor/custom_binary_sensor.h +0 -26
- esphome/components/custom/climate/custom_climate.h +0 -22
- esphome/components/custom/cover/custom_cover.h +0 -21
- esphome/components/custom/light/custom_light_output.h +0 -24
- esphome/components/custom/output/custom_output.h +0 -37
- esphome/components/custom/sensor/custom_sensor.cpp +0 -16
- esphome/components/custom/sensor/custom_sensor.h +0 -24
- esphome/components/custom/switch/custom_switch.cpp +0 -16
- esphome/components/custom/switch/custom_switch.h +0 -24
- esphome/components/custom/text_sensor/custom_text_sensor.cpp +0 -16
- esphome/components/custom/text_sensor/custom_text_sensor.h +0 -26
- esphome/components/custom_component/custom_component.h +0 -28
- esphome/components/esp32_ble_server/ble_2901.cpp +0 -18
- esphome/components/esp32_ble_server/ble_2901.h +0 -19
- esphome/components/resistance_sampler/__init__.py +0 -6
- esphome/components/resistance_sampler/resistance_sampler.h +0 -10
- esphome/components/uptime/{sensor.py → sensor/__init__.py} +3 -3
- /esphome/components/uptime/{uptime_seconds_sensor.cpp → sensor/uptime_seconds_sensor.cpp} +0 -0
- /esphome/components/uptime/{uptime_seconds_sensor.h → sensor/uptime_seconds_sensor.h} +0 -0
- /esphome/components/uptime/{uptime_timestamp_sensor.cpp → sensor/uptime_timestamp_sensor.cpp} +0 -0
- /esphome/components/uptime/{uptime_timestamp_sensor.h → sensor/uptime_timestamp_sensor.h} +0 -0
- {esphome-2024.12.4.dist-info → esphome-2025.2.0b2.dist-info}/LICENSE +0 -0
- {esphome-2024.12.4.dist-info → esphome-2025.2.0b2.dist-info}/WHEEL +0 -0
- {esphome-2024.12.4.dist-info → esphome-2025.2.0b2.dist-info}/entry_points.txt +0 -0
- {esphome-2024.12.4.dist-info → esphome-2025.2.0b2.dist-info}/top_level.txt +0 -0
@@ -1,37 +1,526 @@
|
|
1
|
+
import encodings
|
2
|
+
|
3
|
+
from esphome import automation
|
1
4
|
import esphome.codegen as cg
|
2
5
|
from esphome.components import esp32_ble
|
3
6
|
from esphome.components.esp32 import add_idf_sdkconfig_option
|
7
|
+
from esphome.components.esp32_ble import bt_uuid
|
4
8
|
import esphome.config_validation as cv
|
5
|
-
from esphome.
|
9
|
+
from esphome.config_validation import UNDEFINED
|
10
|
+
from esphome.const import (
|
11
|
+
CONF_DATA,
|
12
|
+
CONF_ESPHOME,
|
13
|
+
CONF_ID,
|
14
|
+
CONF_MAX_LENGTH,
|
15
|
+
CONF_MODEL,
|
16
|
+
CONF_NOTIFY,
|
17
|
+
CONF_ON_CONNECT,
|
18
|
+
CONF_ON_DISCONNECT,
|
19
|
+
CONF_PROJECT,
|
20
|
+
CONF_SERVICES,
|
21
|
+
CONF_TYPE,
|
22
|
+
CONF_UUID,
|
23
|
+
CONF_VALUE,
|
24
|
+
__version__ as ESPHOME_VERSION,
|
25
|
+
)
|
6
26
|
from esphome.core import CORE
|
27
|
+
from esphome.schema_extractors import SCHEMA_EXTRACT
|
7
28
|
|
8
|
-
AUTO_LOAD = ["esp32_ble"]
|
29
|
+
AUTO_LOAD = ["esp32_ble", "bytebuffer", "event_emitter"]
|
9
30
|
CODEOWNERS = ["@jesserockz", "@clydebarrow", "@Rapsssito"]
|
10
31
|
DEPENDENCIES = ["esp32"]
|
32
|
+
DOMAIN = "esp32_ble_server"
|
11
33
|
|
34
|
+
CONF_ADVERTISE = "advertise"
|
35
|
+
CONF_BROADCAST = "broadcast"
|
36
|
+
CONF_CHARACTERISTICS = "characteristics"
|
37
|
+
CONF_DESCRIPTION = "description"
|
38
|
+
CONF_DESCRIPTORS = "descriptors"
|
39
|
+
CONF_ENDIANNESS = "endianness"
|
40
|
+
CONF_FIRMWARE_VERSION = "firmware_version"
|
41
|
+
CONF_INDICATE = "indicate"
|
12
42
|
CONF_MANUFACTURER = "manufacturer"
|
13
43
|
CONF_MANUFACTURER_DATA = "manufacturer_data"
|
44
|
+
CONF_ON_WRITE = "on_write"
|
45
|
+
CONF_READ = "read"
|
46
|
+
CONF_STRING = "string"
|
47
|
+
CONF_STRING_ENCODING = "string_encoding"
|
48
|
+
CONF_WRITE = "write"
|
49
|
+
CONF_WRITE_NO_RESPONSE = "write_no_response"
|
50
|
+
|
51
|
+
# Internal configuration keys
|
52
|
+
CONF_CHAR_VALUE_ACTION_ID_ = "char_value_action_id_"
|
53
|
+
|
54
|
+
# BLE reserverd UUIDs
|
55
|
+
CCCD_DESCRIPTOR_UUID = 0x2902
|
56
|
+
CUD_DESCRIPTOR_UUID = 0x2901
|
57
|
+
DEVICE_INFORMATION_SERVICE_UUID = 0x180A
|
58
|
+
MANUFACTURER_NAME_CHARACTERISTIC_UUID = 0x2A29
|
59
|
+
MODEL_CHARACTERISTIC_UUID = 0x2A24
|
60
|
+
FIRMWARE_VERSION_CHARACTERISTIC_UUID = 0x2A26
|
61
|
+
|
62
|
+
# Core key to store the global configuration
|
63
|
+
KEY_NOTIFY_REQUIRED = "notify_required"
|
64
|
+
KEY_SET_VALUE = "set_value"
|
14
65
|
|
15
66
|
esp32_ble_server_ns = cg.esphome_ns.namespace("esp32_ble_server")
|
67
|
+
ESPBTUUID_ns = cg.esphome_ns.namespace("esp32_ble").namespace("ESPBTUUID")
|
68
|
+
BLECharacteristic_ns = esp32_ble_server_ns.namespace("BLECharacteristic")
|
16
69
|
BLEServer = esp32_ble_server_ns.class_(
|
17
70
|
"BLEServer",
|
18
71
|
cg.Component,
|
19
72
|
esp32_ble.GATTsEventHandler,
|
20
73
|
cg.Parented.template(esp32_ble.ESP32BLE),
|
21
74
|
)
|
22
|
-
|
75
|
+
esp32_ble_server_automations_ns = esp32_ble_server_ns.namespace(
|
76
|
+
"esp32_ble_server_automations"
|
77
|
+
)
|
78
|
+
BLETriggers_ns = esp32_ble_server_automations_ns.namespace("BLETriggers")
|
79
|
+
BLEDescriptor = esp32_ble_server_ns.class_("BLEDescriptor")
|
80
|
+
BLECharacteristic = esp32_ble_server_ns.class_("BLECharacteristic")
|
81
|
+
BLEService = esp32_ble_server_ns.class_("BLEService")
|
82
|
+
BLECharacteristicSetValueAction = esp32_ble_server_automations_ns.class_(
|
83
|
+
"BLECharacteristicSetValueAction", automation.Action
|
84
|
+
)
|
85
|
+
BLEDescriptorSetValueAction = esp32_ble_server_automations_ns.class_(
|
86
|
+
"BLEDescriptorSetValueAction", automation.Action
|
87
|
+
)
|
88
|
+
BLECharacteristicNotifyAction = esp32_ble_server_automations_ns.class_(
|
89
|
+
"BLECharacteristicNotifyAction", automation.Action
|
90
|
+
)
|
91
|
+
bytebuffer_ns = cg.esphome_ns.namespace("bytebuffer")
|
92
|
+
Endianness_ns = bytebuffer_ns.namespace("Endian")
|
93
|
+
ByteBuffer_ns = bytebuffer_ns.namespace("ByteBuffer")
|
94
|
+
ByteBuffer = bytebuffer_ns.class_("ByteBuffer")
|
95
|
+
|
96
|
+
|
97
|
+
PROPERTY_MAP = {
|
98
|
+
CONF_READ: BLECharacteristic_ns.PROPERTY_READ,
|
99
|
+
CONF_WRITE: BLECharacteristic_ns.PROPERTY_WRITE,
|
100
|
+
CONF_NOTIFY: BLECharacteristic_ns.PROPERTY_NOTIFY,
|
101
|
+
CONF_BROADCAST: BLECharacteristic_ns.PROPERTY_BROADCAST,
|
102
|
+
CONF_INDICATE: BLECharacteristic_ns.PROPERTY_INDICATE,
|
103
|
+
CONF_WRITE_NO_RESPONSE: BLECharacteristic_ns.PROPERTY_WRITE_NR,
|
104
|
+
}
|
105
|
+
|
106
|
+
|
107
|
+
class ValueType:
|
108
|
+
def __init__(self, type_, validator, length):
|
109
|
+
self.type_ = type_
|
110
|
+
self.validator = validator
|
111
|
+
self.length = length
|
112
|
+
|
113
|
+
def validate(self, value, encoding):
|
114
|
+
value = self.validator(value)
|
115
|
+
if self.type_ == "string":
|
116
|
+
try:
|
117
|
+
value.encode(encoding)
|
118
|
+
except UnicodeEncodeError as e:
|
119
|
+
raise cv.Invalid(str(e)) from e
|
120
|
+
return value
|
121
|
+
|
122
|
+
|
123
|
+
VALUE_TYPES = {
|
124
|
+
type_name: ValueType(type_name, validator, length)
|
125
|
+
for type_name, validator, length in (
|
126
|
+
("uint8_t", cv.uint8_t, 1),
|
127
|
+
("uint16_t", cv.uint16_t, 2),
|
128
|
+
("uint32_t", cv.uint32_t, 4),
|
129
|
+
("uint64_t", cv.uint64_t, 8),
|
130
|
+
("int8_t", cv.int_range(-128, 127), 1),
|
131
|
+
("int16_t", cv.int_range(-32768, 32767), 2),
|
132
|
+
("int32_t", cv.int_range(-2147483648, 2147483647), 4),
|
133
|
+
("int64_t", cv.int_range(-9223372036854775808, 9223372036854775807), 8),
|
134
|
+
("float", cv.float_, 4),
|
135
|
+
("double", cv.float_, 8),
|
136
|
+
("string", cv.string_strict, None), # Length is variable
|
137
|
+
)
|
138
|
+
}
|
139
|
+
|
140
|
+
|
141
|
+
def validate_char_on_write(char_config):
|
142
|
+
if CONF_ON_WRITE in char_config:
|
143
|
+
if not char_config[CONF_WRITE] and not char_config[CONF_WRITE_NO_RESPONSE]:
|
144
|
+
raise cv.Invalid(
|
145
|
+
f"{CONF_ON_WRITE} requires the {CONF_WRITE} or {CONF_WRITE_NO_RESPONSE} property to be set"
|
146
|
+
)
|
147
|
+
return char_config
|
148
|
+
|
149
|
+
|
150
|
+
def validate_descriptor(desc_config):
|
151
|
+
if CONF_ON_WRITE in desc_config:
|
152
|
+
if not desc_config[CONF_WRITE]:
|
153
|
+
raise cv.Invalid(
|
154
|
+
f"{CONF_ON_WRITE} requires the {CONF_WRITE} property to be set"
|
155
|
+
)
|
156
|
+
if CONF_MAX_LENGTH not in desc_config:
|
157
|
+
value = desc_config[CONF_VALUE][CONF_DATA]
|
158
|
+
if cg.is_template(value):
|
159
|
+
raise cv.Invalid(
|
160
|
+
f"Descriptor {desc_config[CONF_UUID]} has a templatable value and the {CONF_MAX_LENGTH} property is not set"
|
161
|
+
)
|
162
|
+
if isinstance(value, list):
|
163
|
+
desc_config[CONF_MAX_LENGTH] = len(value)
|
164
|
+
elif isinstance(value, str):
|
165
|
+
desc_config[CONF_MAX_LENGTH] = len(
|
166
|
+
value.encode(desc_config[CONF_VALUE][CONF_STRING_ENCODING])
|
167
|
+
)
|
168
|
+
else:
|
169
|
+
desc_config[CONF_MAX_LENGTH] = VALUE_TYPES[
|
170
|
+
desc_config[CONF_VALUE][CONF_TYPE]
|
171
|
+
].length
|
172
|
+
return desc_config
|
173
|
+
|
174
|
+
|
175
|
+
def validate_notify_action(config):
|
176
|
+
# Store the characteristic ID in the global data for the final validation
|
177
|
+
data = CORE.data.setdefault(DOMAIN, {}).setdefault(KEY_NOTIFY_REQUIRED, set())
|
178
|
+
data.add(config[CONF_ID])
|
179
|
+
return config
|
180
|
+
|
181
|
+
|
182
|
+
def validate_set_value_action(config):
|
183
|
+
# Store the characteristic ID in the global data for the final validation
|
184
|
+
data = CORE.data.setdefault(DOMAIN, {}).setdefault(KEY_SET_VALUE, set())
|
185
|
+
data.add(config[CONF_ID])
|
186
|
+
return config
|
187
|
+
|
188
|
+
|
189
|
+
def create_description_cud(char_config):
|
190
|
+
if CONF_DESCRIPTION not in char_config:
|
191
|
+
return char_config
|
192
|
+
# If the config displays a description, there cannot be a descriptor with the CUD UUID
|
193
|
+
for desc in char_config[CONF_DESCRIPTORS]:
|
194
|
+
if desc[CONF_UUID] == CUD_DESCRIPTOR_UUID:
|
195
|
+
raise cv.Invalid(
|
196
|
+
f"Characteristic {char_config[CONF_UUID]} has a description, but a CUD descriptor is already present"
|
197
|
+
)
|
198
|
+
# Manually add the CUD descriptor
|
199
|
+
char_config[CONF_DESCRIPTORS].append(
|
200
|
+
DESCRIPTOR_SCHEMA(
|
201
|
+
{
|
202
|
+
CONF_UUID: CUD_DESCRIPTOR_UUID,
|
203
|
+
CONF_READ: True,
|
204
|
+
CONF_WRITE: False,
|
205
|
+
CONF_VALUE: char_config[CONF_DESCRIPTION],
|
206
|
+
}
|
207
|
+
)
|
208
|
+
)
|
209
|
+
return char_config
|
210
|
+
|
211
|
+
|
212
|
+
def create_notify_cccd(char_config):
|
213
|
+
if not char_config[CONF_NOTIFY] and not char_config[CONF_INDICATE]:
|
214
|
+
return char_config
|
215
|
+
# If the CCCD descriptor is already present, return the config
|
216
|
+
for desc in char_config[CONF_DESCRIPTORS]:
|
217
|
+
if desc[CONF_UUID] == CCCD_DESCRIPTOR_UUID:
|
218
|
+
# Check if the WRITE property is set
|
219
|
+
if not desc[CONF_WRITE]:
|
220
|
+
raise cv.Invalid(
|
221
|
+
f"Characteristic {char_config[CONF_UUID]} has notify actions, but the CCCD descriptor does not have the {CONF_WRITE} property set"
|
222
|
+
)
|
223
|
+
return char_config
|
224
|
+
# Manually add the CCCD descriptor
|
225
|
+
char_config[CONF_DESCRIPTORS].append(
|
226
|
+
DESCRIPTOR_SCHEMA(
|
227
|
+
{
|
228
|
+
CONF_UUID: CCCD_DESCRIPTOR_UUID,
|
229
|
+
CONF_READ: True,
|
230
|
+
CONF_WRITE: True,
|
231
|
+
CONF_MAX_LENGTH: 2,
|
232
|
+
CONF_VALUE: [0, 0],
|
233
|
+
}
|
234
|
+
)
|
235
|
+
)
|
236
|
+
return char_config
|
237
|
+
|
238
|
+
|
239
|
+
def create_device_information_service(config):
|
240
|
+
# If there is already a device information service,
|
241
|
+
# there cannot be CONF_MODEL, CONF_MANUFACTURER or CONF_FIRMWARE_VERSION properties
|
242
|
+
for service in config[CONF_SERVICES]:
|
243
|
+
if service[CONF_UUID] == DEVICE_INFORMATION_SERVICE_UUID:
|
244
|
+
if (
|
245
|
+
CONF_MODEL in config
|
246
|
+
or CONF_MANUFACTURER in config
|
247
|
+
or CONF_FIRMWARE_VERSION in config
|
248
|
+
):
|
249
|
+
raise cv.Invalid(
|
250
|
+
"Device information service already present, cannot add manufacturer, model or firmware version"
|
251
|
+
)
|
252
|
+
return config
|
253
|
+
project = CORE.raw_config[CONF_ESPHOME].get(CONF_PROJECT, {})
|
254
|
+
model = config.get(CONF_MODEL, project.get("name", CORE.data["esp32"]["board"]))
|
255
|
+
version = config.get(
|
256
|
+
CONF_FIRMWARE_VERSION, project.get("version", "ESPHome " + ESPHOME_VERSION)
|
257
|
+
)
|
258
|
+
# Manually add the device information service
|
259
|
+
config[CONF_SERVICES].append(
|
260
|
+
SERVICE_SCHEMA(
|
261
|
+
{
|
262
|
+
CONF_UUID: DEVICE_INFORMATION_SERVICE_UUID,
|
263
|
+
CONF_CHARACTERISTICS: [
|
264
|
+
{
|
265
|
+
CONF_UUID: MANUFACTURER_NAME_CHARACTERISTIC_UUID,
|
266
|
+
CONF_READ: True,
|
267
|
+
CONF_VALUE: config.get(CONF_MANUFACTURER, "ESPHome"),
|
268
|
+
},
|
269
|
+
{
|
270
|
+
CONF_UUID: MODEL_CHARACTERISTIC_UUID,
|
271
|
+
CONF_READ: True,
|
272
|
+
CONF_VALUE: model,
|
273
|
+
},
|
274
|
+
{
|
275
|
+
CONF_UUID: FIRMWARE_VERSION_CHARACTERISTIC_UUID,
|
276
|
+
CONF_READ: True,
|
277
|
+
CONF_VALUE: version,
|
278
|
+
},
|
279
|
+
],
|
280
|
+
}
|
281
|
+
)
|
282
|
+
)
|
283
|
+
return config
|
284
|
+
|
285
|
+
|
286
|
+
def final_validate_config(config):
|
287
|
+
# Check if all characteristics that require notifications have the notify property set
|
288
|
+
for char_id in CORE.data.get(DOMAIN, {}).get(KEY_NOTIFY_REQUIRED, set()):
|
289
|
+
# Look for the characteristic in the configuration
|
290
|
+
char_config = [
|
291
|
+
char_conf
|
292
|
+
for service_conf in config[CONF_SERVICES]
|
293
|
+
for char_conf in service_conf[CONF_CHARACTERISTICS]
|
294
|
+
if char_conf[CONF_ID] == char_id
|
295
|
+
][0]
|
296
|
+
if not char_config[CONF_NOTIFY]:
|
297
|
+
raise cv.Invalid(
|
298
|
+
f"Characteristic {char_config[CONF_UUID]} has notify actions and the {CONF_NOTIFY} property is not set"
|
299
|
+
)
|
300
|
+
for char_id in CORE.data.get(DOMAIN, {}).get(KEY_SET_VALUE, set()):
|
301
|
+
# Look for the characteristic in the configuration
|
302
|
+
char_config = [
|
303
|
+
char_conf
|
304
|
+
for service_conf in config[CONF_SERVICES]
|
305
|
+
for char_conf in service_conf[CONF_CHARACTERISTICS]
|
306
|
+
if char_conf[CONF_ID] == char_id
|
307
|
+
][0]
|
308
|
+
if isinstance(char_config.get(CONF_VALUE, {}).get(CONF_DATA), cv.Lambda):
|
309
|
+
raise cv.Invalid(
|
310
|
+
f"Characteristic {char_config[CONF_UUID]} has both a set_value action and a templated value"
|
311
|
+
)
|
312
|
+
return config
|
313
|
+
|
314
|
+
|
315
|
+
def validate_value_type(value_config):
|
316
|
+
# If the value is a not a templatable, the type must be set
|
317
|
+
value = value_config[CONF_DATA]
|
318
|
+
|
319
|
+
if type_ := value_config.get(CONF_TYPE):
|
320
|
+
if cg.is_template(value):
|
321
|
+
raise cv.Invalid(
|
322
|
+
f'The "{CONF_TYPE}" property is not allowed for templatable values'
|
323
|
+
)
|
324
|
+
value_config[CONF_DATA] = VALUE_TYPES[type_].validate(
|
325
|
+
value, value_config[CONF_STRING_ENCODING]
|
326
|
+
)
|
327
|
+
elif isinstance(value, (float, int)):
|
328
|
+
raise cv.Invalid(
|
329
|
+
f'The "{CONF_TYPE}" property is required for the value "{value}"'
|
330
|
+
)
|
331
|
+
return value_config
|
332
|
+
|
333
|
+
|
334
|
+
def validate_encoding(value):
|
335
|
+
if value == SCHEMA_EXTRACT:
|
336
|
+
return cv.one_of("utf-8", "latin-1", "ascii", "utf-16", "utf-32")
|
337
|
+
value = encodings.normalize_encoding(value)
|
338
|
+
if not value:
|
339
|
+
raise cv.Invalid("Invalid encoding")
|
340
|
+
return value
|
341
|
+
|
23
342
|
|
343
|
+
def value_schema(default_type=UNDEFINED, templatable=True):
|
344
|
+
data_validators = [
|
345
|
+
cv.string_strict,
|
346
|
+
cv.int_,
|
347
|
+
cv.float_,
|
348
|
+
cv.All([cv.uint8_t], cv.Length(min=1)),
|
349
|
+
]
|
350
|
+
if templatable:
|
351
|
+
data_validators.append(cv.returning_lambda)
|
352
|
+
|
353
|
+
return cv.maybe_simple_value(
|
354
|
+
cv.All(
|
355
|
+
{
|
356
|
+
cv.Required(CONF_DATA): cv.Any(*data_validators),
|
357
|
+
cv.Optional(CONF_TYPE, default=default_type): cv.one_of(
|
358
|
+
*VALUE_TYPES, lower=True
|
359
|
+
),
|
360
|
+
cv.Optional(CONF_STRING_ENCODING, default="utf_8"): validate_encoding,
|
361
|
+
cv.Optional(CONF_ENDIANNESS, default="LITTLE"): cv.enum(
|
362
|
+
{
|
363
|
+
"LITTLE": Endianness_ns.LITTLE,
|
364
|
+
"BIG": Endianness_ns.BIG,
|
365
|
+
}
|
366
|
+
),
|
367
|
+
},
|
368
|
+
validate_value_type,
|
369
|
+
),
|
370
|
+
key=CONF_DATA,
|
371
|
+
)
|
372
|
+
|
373
|
+
|
374
|
+
DESCRIPTOR_SCHEMA = cv.All(
|
375
|
+
{
|
376
|
+
cv.GenerateID(): cv.declare_id(BLEDescriptor),
|
377
|
+
cv.Required(CONF_UUID): cv.Any(bt_uuid, cv.hex_uint32_t),
|
378
|
+
cv.Optional(CONF_READ, default=True): cv.boolean,
|
379
|
+
cv.Optional(CONF_WRITE, default=True): cv.boolean,
|
380
|
+
cv.Optional(CONF_ON_WRITE): automation.validate_automation(single=True),
|
381
|
+
cv.Required(CONF_VALUE): value_schema(templatable=False),
|
382
|
+
cv.Optional(CONF_MAX_LENGTH): cv.uint16_t,
|
383
|
+
},
|
384
|
+
validate_descriptor,
|
385
|
+
)
|
386
|
+
|
387
|
+
CHARACTERISTIC_SCHEMA = cv.Schema(
|
388
|
+
{
|
389
|
+
cv.GenerateID(): cv.declare_id(BLECharacteristic),
|
390
|
+
cv.Required(CONF_UUID): cv.Any(bt_uuid, cv.hex_uint32_t),
|
391
|
+
cv.Optional(CONF_VALUE): value_schema(templatable=True),
|
392
|
+
cv.GenerateID(CONF_CHAR_VALUE_ACTION_ID_): cv.declare_id(
|
393
|
+
BLECharacteristicSetValueAction
|
394
|
+
),
|
395
|
+
cv.Optional(CONF_DESCRIPTORS, default=[]): cv.ensure_list(DESCRIPTOR_SCHEMA),
|
396
|
+
cv.Optional(CONF_ON_WRITE): automation.validate_automation(single=True),
|
397
|
+
cv.Optional(CONF_DESCRIPTION): value_schema(
|
398
|
+
default_type="string", templatable=False
|
399
|
+
),
|
400
|
+
},
|
401
|
+
extra_schemas=[
|
402
|
+
validate_char_on_write,
|
403
|
+
create_description_cud,
|
404
|
+
create_notify_cccd,
|
405
|
+
],
|
406
|
+
).extend({cv.Optional(k, default=False): cv.boolean for k in PROPERTY_MAP})
|
407
|
+
|
408
|
+
SERVICE_SCHEMA = cv.Schema(
|
409
|
+
{
|
410
|
+
cv.GenerateID(): cv.declare_id(BLEService),
|
411
|
+
cv.Required(CONF_UUID): cv.Any(bt_uuid, cv.hex_uint32_t),
|
412
|
+
cv.Optional(CONF_ADVERTISE, default=False): cv.boolean,
|
413
|
+
cv.Optional(CONF_CHARACTERISTICS, default=[]): cv.ensure_list(
|
414
|
+
CHARACTERISTIC_SCHEMA
|
415
|
+
),
|
416
|
+
}
|
417
|
+
)
|
24
418
|
|
25
419
|
CONFIG_SCHEMA = cv.Schema(
|
26
420
|
{
|
27
421
|
cv.GenerateID(): cv.declare_id(BLEServer),
|
28
422
|
cv.GenerateID(esp32_ble.CONF_BLE_ID): cv.use_id(esp32_ble.ESP32BLE),
|
29
|
-
cv.Optional(CONF_MANUFACTURER
|
30
|
-
cv.Optional(
|
31
|
-
cv.Optional(
|
32
|
-
|
423
|
+
cv.Optional(CONF_MANUFACTURER): value_schema("string", templatable=False),
|
424
|
+
cv.Optional(CONF_MODEL): value_schema("string", templatable=False),
|
425
|
+
cv.Optional(CONF_FIRMWARE_VERSION): value_schema("string", templatable=False),
|
426
|
+
cv.Optional(CONF_MANUFACTURER_DATA): cv.Schema([cv.uint8_t]),
|
427
|
+
cv.Optional(CONF_SERVICES, default=[]): cv.ensure_list(SERVICE_SCHEMA),
|
428
|
+
cv.Optional(CONF_ON_CONNECT): automation.validate_automation(single=True),
|
429
|
+
cv.Optional(CONF_ON_DISCONNECT): automation.validate_automation(single=True),
|
430
|
+
},
|
431
|
+
extra_schemas=[create_device_information_service],
|
33
432
|
).extend(cv.COMPONENT_SCHEMA)
|
34
433
|
|
434
|
+
FINAL_VALIDATE_SCHEMA = final_validate_config
|
435
|
+
|
436
|
+
|
437
|
+
def parse_properties(char_conf):
|
438
|
+
return sum(
|
439
|
+
(PROPERTY_MAP[k] for k in char_conf if k in PROPERTY_MAP and char_conf[k]),
|
440
|
+
start=0,
|
441
|
+
)
|
442
|
+
|
443
|
+
|
444
|
+
def parse_uuid(uuid):
|
445
|
+
# If the UUID is a int, use from_uint32
|
446
|
+
if isinstance(uuid, int):
|
447
|
+
return ESPBTUUID_ns.from_uint32(uuid)
|
448
|
+
# Otherwise, use ESPBTUUID_ns.from_raw
|
449
|
+
return ESPBTUUID_ns.from_raw(uuid)
|
450
|
+
|
451
|
+
|
452
|
+
async def parse_value(value_config, args):
|
453
|
+
value = value_config[CONF_DATA]
|
454
|
+
if isinstance(value, cv.Lambda):
|
455
|
+
return await cg.templatable(value, args, cg.std_vector.template(cg.uint8))
|
456
|
+
|
457
|
+
if isinstance(value, str):
|
458
|
+
value = list(value.encode(value_config[CONF_STRING_ENCODING]))
|
459
|
+
if isinstance(value, list):
|
460
|
+
return cg.std_vector.template(cg.uint8)(value)
|
461
|
+
val = cg.RawExpression(f"{value_config[CONF_TYPE]}({cg.safe_exp(value)})")
|
462
|
+
return ByteBuffer_ns.wrap(val, value_config[CONF_ENDIANNESS])
|
463
|
+
|
464
|
+
|
465
|
+
def calculate_num_handles(service_config):
|
466
|
+
total = 1 + len(service_config[CONF_CHARACTERISTICS]) * 2
|
467
|
+
total += sum(
|
468
|
+
len(char_conf[CONF_DESCRIPTORS])
|
469
|
+
for char_conf in service_config[CONF_CHARACTERISTICS]
|
470
|
+
)
|
471
|
+
return total
|
472
|
+
|
473
|
+
|
474
|
+
async def to_code_descriptor(descriptor_conf, char_var):
|
475
|
+
value = await parse_value(descriptor_conf[CONF_VALUE], {})
|
476
|
+
desc_var = cg.new_Pvariable(
|
477
|
+
descriptor_conf[CONF_ID],
|
478
|
+
parse_uuid(descriptor_conf[CONF_UUID]),
|
479
|
+
descriptor_conf[CONF_MAX_LENGTH],
|
480
|
+
descriptor_conf[CONF_READ],
|
481
|
+
descriptor_conf[CONF_WRITE],
|
482
|
+
)
|
483
|
+
cg.add(char_var.add_descriptor(desc_var))
|
484
|
+
cg.add(desc_var.set_value(value))
|
485
|
+
if CONF_ON_WRITE in descriptor_conf:
|
486
|
+
on_write_conf = descriptor_conf[CONF_ON_WRITE]
|
487
|
+
await automation.build_automation(
|
488
|
+
BLETriggers_ns.create_descriptor_on_write_trigger(desc_var),
|
489
|
+
[(cg.std_vector.template(cg.uint8), "x"), (cg.uint16, "id")],
|
490
|
+
on_write_conf,
|
491
|
+
)
|
492
|
+
|
493
|
+
|
494
|
+
async def to_code_characteristic(service_var, char_conf):
|
495
|
+
char_var = cg.Pvariable(
|
496
|
+
char_conf[CONF_ID],
|
497
|
+
service_var.create_characteristic(
|
498
|
+
parse_uuid(char_conf[CONF_UUID]),
|
499
|
+
parse_properties(char_conf),
|
500
|
+
),
|
501
|
+
)
|
502
|
+
if CONF_ON_WRITE in char_conf:
|
503
|
+
on_write_conf = char_conf[CONF_ON_WRITE]
|
504
|
+
await automation.build_automation(
|
505
|
+
BLETriggers_ns.create_characteristic_on_write_trigger(char_var),
|
506
|
+
[(cg.std_vector.template(cg.uint8), "x"), (cg.uint16, "id")],
|
507
|
+
on_write_conf,
|
508
|
+
)
|
509
|
+
if CONF_VALUE in char_conf:
|
510
|
+
action_conf = {
|
511
|
+
CONF_ID: char_conf[CONF_ID],
|
512
|
+
CONF_VALUE: char_conf[CONF_VALUE],
|
513
|
+
}
|
514
|
+
value_action = await ble_server_characteristic_set_value(
|
515
|
+
action_conf,
|
516
|
+
char_conf[CONF_CHAR_VALUE_ACTION_ID_],
|
517
|
+
cg.TemplateArguments(),
|
518
|
+
{},
|
519
|
+
)
|
520
|
+
cg.add(value_action.play())
|
521
|
+
for descriptor_conf in char_conf[CONF_DESCRIPTORS]:
|
522
|
+
await to_code_descriptor(descriptor_conf, char_var)
|
523
|
+
|
35
524
|
|
36
525
|
async def to_code(config):
|
37
526
|
var = cg.new_Pvariable(config[CONF_ID])
|
@@ -42,13 +531,94 @@ async def to_code(config):
|
|
42
531
|
cg.add(parent.register_gatts_event_handler(var))
|
43
532
|
cg.add(parent.register_ble_status_event_handler(var))
|
44
533
|
cg.add(var.set_parent(parent))
|
45
|
-
|
46
|
-
cg.add(var.set_manufacturer(config[CONF_MANUFACTURER]))
|
47
534
|
if CONF_MANUFACTURER_DATA in config:
|
48
535
|
cg.add(var.set_manufacturer_data(config[CONF_MANUFACTURER_DATA]))
|
49
|
-
|
50
|
-
|
536
|
+
for service_config in config[CONF_SERVICES]:
|
537
|
+
# Calculate the optimal number of handles based on the number of characteristics and descriptors
|
538
|
+
num_handles = calculate_num_handles(service_config)
|
539
|
+
service_var = cg.Pvariable(
|
540
|
+
service_config[CONF_ID],
|
541
|
+
var.create_service(
|
542
|
+
parse_uuid(service_config[CONF_UUID]),
|
543
|
+
service_config[CONF_ADVERTISE],
|
544
|
+
num_handles,
|
545
|
+
),
|
546
|
+
)
|
547
|
+
for char_conf in service_config[CONF_CHARACTERISTICS]:
|
548
|
+
await to_code_characteristic(service_var, char_conf)
|
549
|
+
if service_config[CONF_UUID] == DEVICE_INFORMATION_SERVICE_UUID:
|
550
|
+
cg.add(var.set_device_information_service(service_var))
|
551
|
+
else:
|
552
|
+
cg.add(var.enqueue_start_service(service_var))
|
553
|
+
if CONF_ON_CONNECT in config:
|
554
|
+
await automation.build_automation(
|
555
|
+
BLETriggers_ns.create_server_on_connect_trigger(var),
|
556
|
+
[(cg.uint16, "id")],
|
557
|
+
config[CONF_ON_CONNECT],
|
558
|
+
)
|
559
|
+
if CONF_ON_DISCONNECT in config:
|
560
|
+
await automation.build_automation(
|
561
|
+
BLETriggers_ns.create_server_on_disconnect_trigger(var),
|
562
|
+
[(cg.uint16, "id")],
|
563
|
+
config[CONF_ON_DISCONNECT],
|
564
|
+
)
|
51
565
|
cg.add_define("USE_ESP32_BLE_SERVER")
|
52
|
-
|
53
566
|
if CORE.using_esp_idf:
|
54
567
|
add_idf_sdkconfig_option("CONFIG_BT_ENABLED", True)
|
568
|
+
|
569
|
+
|
570
|
+
@automation.register_action(
|
571
|
+
"ble_server.characteristic.set_value",
|
572
|
+
BLECharacteristicSetValueAction,
|
573
|
+
cv.All(
|
574
|
+
cv.Schema(
|
575
|
+
{
|
576
|
+
cv.Required(CONF_ID): cv.use_id(BLECharacteristic),
|
577
|
+
cv.Required(CONF_VALUE): value_schema(),
|
578
|
+
}
|
579
|
+
),
|
580
|
+
validate_set_value_action,
|
581
|
+
),
|
582
|
+
)
|
583
|
+
async def ble_server_characteristic_set_value(config, action_id, template_arg, args):
|
584
|
+
paren = await cg.get_variable(config[CONF_ID])
|
585
|
+
var = cg.new_Pvariable(action_id, template_arg, paren)
|
586
|
+
value = await parse_value(config[CONF_VALUE], args)
|
587
|
+
cg.add(var.set_buffer(value))
|
588
|
+
return var
|
589
|
+
|
590
|
+
|
591
|
+
@automation.register_action(
|
592
|
+
"ble_server.descriptor.set_value",
|
593
|
+
BLEDescriptorSetValueAction,
|
594
|
+
cv.Schema(
|
595
|
+
{
|
596
|
+
cv.Required(CONF_ID): cv.use_id(BLEDescriptor),
|
597
|
+
cv.Required(CONF_VALUE): value_schema(),
|
598
|
+
}
|
599
|
+
),
|
600
|
+
)
|
601
|
+
async def ble_server_descriptor_set_value(config, action_id, template_arg, args):
|
602
|
+
paren = await cg.get_variable(config[CONF_ID])
|
603
|
+
var = cg.new_Pvariable(action_id, template_arg, paren)
|
604
|
+
value = await parse_value(config[CONF_VALUE], args)
|
605
|
+
cg.add(var.set_buffer(value))
|
606
|
+
return var
|
607
|
+
|
608
|
+
|
609
|
+
@automation.register_action(
|
610
|
+
"ble_server.characteristic.notify",
|
611
|
+
BLECharacteristicNotifyAction,
|
612
|
+
cv.All(
|
613
|
+
cv.Schema(
|
614
|
+
{
|
615
|
+
cv.Required(CONF_ID): cv.use_id(BLECharacteristic),
|
616
|
+
}
|
617
|
+
),
|
618
|
+
validate_notify_action,
|
619
|
+
),
|
620
|
+
)
|
621
|
+
async def ble_server_characteristic_notify(config, action_id, template_arg, args):
|
622
|
+
paren = await cg.get_variable(config[CONF_ID])
|
623
|
+
var = cg.new_Pvariable(action_id, template_arg, paren)
|
624
|
+
return var
|