esphome 2025.4.1__py3-none-any.whl → 2025.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- esphome/__main__.py +16 -14
- esphome/components/ac_dimmer/ac_dimmer.cpp +3 -2
- esphome/components/adc/__init__.py +51 -34
- esphome/components/airthings_wave_base/__init__.py +1 -1
- esphome/components/alarm_control_panel/__init__.py +37 -2
- esphome/components/am43/cover/__init__.py +4 -5
- esphome/components/analog_threshold/analog_threshold_binary_sensor.cpp +6 -4
- esphome/components/analog_threshold/analog_threshold_binary_sensor.h +4 -5
- esphome/components/analog_threshold/binary_sensor.py +10 -8
- esphome/components/anova/climate.py +4 -5
- esphome/components/api/__init__.py +25 -8
- esphome/components/api/api_connection.cpp +416 -662
- esphome/components/api/api_connection.h +256 -57
- esphome/components/api/api_frame_helper.cpp +232 -177
- esphome/components/api/api_frame_helper.h +61 -8
- esphome/components/api/api_noise_context.h +13 -4
- esphome/components/api/api_pb2.cpp +1422 -1
- esphome/components/api/api_pb2.h +255 -1
- esphome/components/api/api_pb2_service.cpp +162 -49
- esphome/components/api/api_pb2_service.h +90 -51
- esphome/components/api/api_pb2_size.h +361 -0
- esphome/components/api/api_server.cpp +110 -34
- esphome/components/api/api_server.h +8 -0
- esphome/components/api/proto.h +86 -17
- esphome/components/as3935_i2c/as3935_i2c.h +0 -3
- esphome/components/as7341/as7341.h +1 -1
- esphome/components/at581x/at581x.h +4 -4
- esphome/components/atm90e32/__init__.py +1 -0
- esphome/components/atm90e32/atm90e32.cpp +576 -199
- esphome/components/atm90e32/atm90e32.h +128 -31
- esphome/components/atm90e32/atm90e32_reg.h +4 -2
- esphome/components/atm90e32/button/__init__.py +62 -10
- esphome/components/atm90e32/button/atm90e32_button.cpp +63 -4
- esphome/components/atm90e32/button/atm90e32_button.h +36 -4
- esphome/components/atm90e32/number/__init__.py +130 -0
- esphome/components/atm90e32/number/atm90e32_number.h +16 -0
- esphome/components/atm90e32/sensor.py +21 -4
- esphome/components/atm90e32/text_sensor/__init__.py +48 -0
- esphome/components/audio/__init__.py +96 -49
- esphome/components/audio/audio.h +48 -0
- esphome/components/audio/audio_decoder.cpp +1 -1
- esphome/components/audio/audio_resampler.cpp +2 -0
- esphome/components/audio/audio_resampler.h +1 -0
- esphome/components/ballu/climate.py +2 -9
- esphome/components/bang_bang/climate.py +5 -6
- esphome/components/bedjet/bedjet_hub.cpp +1 -0
- esphome/components/bedjet/climate/__init__.py +3 -8
- esphome/components/bedjet/fan/__init__.py +2 -11
- esphome/components/binary/fan/__init__.py +13 -16
- esphome/components/binary_sensor/__init__.py +13 -10
- esphome/components/bl0906/constants.h +16 -16
- esphome/components/ble_client/text_sensor/__init__.py +3 -5
- esphome/components/bluetooth_proxy/bluetooth_connection.cpp +4 -6
- esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +136 -21
- esphome/components/bluetooth_proxy/bluetooth_proxy.h +7 -0
- esphome/components/button/__init__.py +11 -8
- esphome/components/canbus/canbus.cpp +3 -0
- esphome/components/canbus/canbus.h +16 -0
- esphome/components/ccs811/sensor.py +9 -6
- esphome/components/climate/__init__.py +35 -2
- esphome/components/climate/climate_mode.h +1 -1
- esphome/components/climate/climate_traits.h +63 -57
- esphome/components/climate_ir/__init__.py +57 -17
- esphome/components/climate_ir_lg/climate.py +2 -5
- esphome/components/climate_ir_lg/climate_ir_lg.cpp +7 -7
- esphome/components/climate_ir_lg/climate_ir_lg.h +1 -1
- esphome/components/color/__init__.py +2 -0
- esphome/components/const/__init__.py +5 -0
- esphome/components/coolix/climate.py +2 -9
- esphome/components/copy/cover/__init__.py +10 -9
- esphome/components/copy/fan/__init__.py +11 -9
- esphome/components/copy/lock/__init__.py +11 -9
- esphome/components/copy/text/__init__.py +9 -6
- esphome/components/cover/__init__.py +37 -2
- esphome/components/cse7766/cse7766.cpp +2 -1
- esphome/components/cst226/binary_sensor/__init__.py +28 -0
- esphome/components/cst226/binary_sensor/cs226_button.h +22 -0
- esphome/components/cst226/binary_sensor/cstt6_button.cpp +19 -0
- esphome/components/cst226/touchscreen/cst226_touchscreen.cpp +27 -5
- esphome/components/cst226/touchscreen/cst226_touchscreen.h +10 -10
- esphome/components/current_based/cover.py +37 -36
- esphome/components/current_based/current_based_cover.cpp +2 -1
- esphome/components/daikin/climate.py +2 -9
- esphome/components/daikin/daikin.cpp +15 -9
- esphome/components/daikin/daikin.h +5 -5
- esphome/components/daikin_arc/climate.py +2 -7
- esphome/components/daikin_brc/climate.py +3 -5
- esphome/components/dallas_temp/dallas_temp.cpp +17 -24
- esphome/components/dallas_temp/dallas_temp.h +0 -1
- esphome/components/daly_bms/daly_bms.cpp +2 -1
- esphome/components/debug/debug_component.cpp +6 -1
- esphome/components/debug/debug_component.h +8 -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/dps310/sensor.py +6 -6
- esphome/components/ee895/sensor.py +9 -9
- esphome/components/emmeti/climate.py +2 -9
- esphome/components/endstop/cover.py +17 -16
- esphome/components/endstop/endstop_cover.cpp +2 -1
- esphome/components/ens160_base/__init__.py +12 -9
- esphome/components/esp32/__init__.py +60 -3
- esphome/components/esp32/core.cpp +11 -5
- esphome/components/esp32/gpio.cpp +86 -24
- esphome/components/esp32/gpio.py +15 -16
- esphome/components/esp32/gpio_esp32.py +1 -2
- esphome/components/esp32/gpio_esp32_c2.py +1 -1
- esphome/components/esp32/gpio_esp32_c3.py +1 -1
- esphome/components/esp32/gpio_esp32_c6.py +1 -1
- esphome/components/esp32/gpio_esp32_h2.py +1 -1
- esphome/components/esp32_ble/ble.cpp +1 -8
- esphome/components/esp32_ble/ble.h +5 -3
- esphome/components/esp32_ble/ble_advertising.cpp +2 -1
- esphome/components/esp32_ble/ble_advertising.h +1 -0
- esphome/components/esp32_ble_server/__init__.py +3 -0
- esphome/components/esp32_ble_tracker/__init__.py +7 -1
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +192 -118
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +29 -3
- esphome/components/esp32_camera/__init__.py +1 -1
- esphome/components/esp32_camera/esp32_camera.cpp +2 -10
- esphome/components/esp32_camera/esp32_camera.h +1 -1
- esphome/components/esp32_can/esp32_can.cpp +1 -1
- esphome/components/esp32_improv/esp32_improv_component.cpp +1 -1
- esphome/components/esp32_rmt_led_strip/led_strip.cpp +1 -1
- esphome/components/esp32_rmt_led_strip/led_strip.h +7 -5
- esphome/components/esp32_rmt_led_strip/light.py +9 -1
- esphome/components/esp32_touch/esp32_touch.cpp +1 -1
- esphome/components/esp8266/gpio.cpp +69 -8
- esphome/components/ethernet/ethernet_component.cpp +1 -1
- esphome/components/event/__init__.py +13 -10
- esphome/components/factory_reset/switch/__init__.py +7 -21
- esphome/components/fan/__init__.py +52 -5
- esphome/components/fastled_base/__init__.py +1 -4
- esphome/components/fastled_base/fastled_light.cpp +1 -1
- esphome/components/feedback/cover.py +38 -33
- esphome/components/feedback/feedback_cover.cpp +2 -1
- esphome/components/fujitsu_general/climate.py +2 -9
- esphome/components/gcja5/gcja5.cpp +2 -1
- esphome/components/gpio/one_wire/gpio_one_wire.cpp +45 -43
- esphome/components/gpio/one_wire/gpio_one_wire.h +2 -1
- esphome/components/gpio_expander/cached_gpio.h +22 -7
- esphome/components/gps/__init__.py +47 -17
- esphome/components/gps/gps.cpp +42 -23
- esphome/components/gps/gps.h +17 -13
- esphome/components/graph/__init__.py +1 -2
- esphome/components/gree/climate.py +4 -6
- esphome/components/gree/gree.cpp +16 -2
- esphome/components/gree/gree.h +2 -2
- esphome/components/growatt_solar/growatt_solar.cpp +2 -1
- esphome/components/haier/climate.py +37 -34
- esphome/components/hbridge/fan/__init__.py +19 -17
- esphome/components/he60r/cover.py +4 -5
- esphome/components/heatpumpir/climate.py +3 -6
- esphome/components/hitachi_ac344/climate.py +2 -9
- esphome/components/hitachi_ac424/climate.py +2 -9
- esphome/components/hlw8012/hlw8012.cpp +1 -1
- esphome/components/hm3301/hm3301.h +1 -1
- esphome/components/hte501/sensor.py +6 -6
- esphome/components/http_request/__init__.py +39 -6
- esphome/components/http_request/http_request.cpp +20 -0
- esphome/components/http_request/http_request.h +57 -15
- esphome/components/http_request/http_request_arduino.cpp +22 -6
- esphome/components/http_request/http_request_arduino.h +4 -3
- esphome/components/http_request/http_request_host.cpp +141 -0
- esphome/components/http_request/http_request_host.h +37 -0
- esphome/components/http_request/http_request_idf.cpp +35 -3
- esphome/components/http_request/http_request_idf.h +10 -3
- esphome/components/http_request/httplib.h +9691 -0
- esphome/components/http_request/update/__init__.py +11 -8
- esphome/components/hyt271/sensor.py +6 -6
- esphome/components/i2c/i2c.h +4 -0
- esphome/components/i2c/i2c_bus_esp_idf.cpp +1 -1
- esphome/components/i2s_audio/__init__.py +131 -22
- esphome/components/i2s_audio/i2s_audio.h +44 -4
- esphome/components/i2s_audio/media_player/__init__.py +19 -9
- esphome/components/i2s_audio/microphone/__init__.py +63 -5
- esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +351 -61
- esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +40 -6
- esphome/components/i2s_audio/speaker/__init__.py +31 -5
- esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +155 -19
- esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +17 -4
- esphome/components/ili9xxx/ili9xxx_init.h +1 -1
- esphome/components/image/__init__.py +37 -17
- esphome/components/image/image.cpp +25 -8
- esphome/components/internal_temperature/internal_temperature.cpp +6 -4
- esphome/components/key_collector/__init__.py +35 -0
- esphome/components/key_collector/key_collector.cpp +8 -0
- esphome/components/key_collector/key_collector.h +10 -0
- esphome/components/kuntze/kuntze.cpp +2 -1
- esphome/components/ld2410/ld2410.h +1 -1
- esphome/components/ld2450/ld2450.h +1 -1
- esphome/components/light/__init__.py +57 -0
- esphome/components/lock/__init__.py +51 -4
- esphome/components/lock/automation.h +2 -13
- esphome/components/logger/__init__.py +22 -0
- esphome/components/logger/logger.cpp +154 -103
- esphome/components/logger/logger.h +211 -36
- esphome/components/logger/task_log_buffer.cpp +138 -0
- esphome/components/logger/task_log_buffer.h +69 -0
- esphome/components/lvgl/__init__.py +13 -5
- esphome/components/lvgl/automation.py +50 -1
- esphome/components/lvgl/defines.py +0 -1
- esphome/components/lvgl/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/matrix_keypad/matrix_keypad.cpp +2 -1
- esphome/components/max7219digit/max7219digit.cpp +28 -27
- esphome/components/mdns/__init__.py +11 -5
- esphome/components/mdns/mdns_component.cpp +11 -5
- esphome/components/mdns/mdns_component.h +3 -2
- esphome/components/mdns/mdns_esp32.cpp +4 -3
- esphome/components/mdns/mdns_esp8266.cpp +4 -2
- esphome/components/mdns/mdns_libretiny.cpp +4 -2
- esphome/components/mdns/mdns_rp2040.cpp +4 -2
- esphome/components/media_player/__init__.py +40 -6
- esphome/components/mhz19/sensor.py +11 -7
- esphome/components/micro_wake_word/__init__.py +99 -31
- esphome/components/micro_wake_word/automation.h +54 -0
- esphome/components/micro_wake_word/micro_wake_word.cpp +331 -319
- esphome/components/micro_wake_word/micro_wake_word.h +58 -105
- esphome/components/micro_wake_word/preprocessor_settings.h +19 -2
- esphome/components/micro_wake_word/streaming_model.cpp +158 -41
- esphome/components/micro_wake_word/streaming_model.h +85 -13
- esphome/components/microphone/__init__.py +139 -9
- esphome/components/microphone/automation.h +14 -2
- esphome/components/microphone/microphone.cpp +21 -0
- esphome/components/microphone/microphone.h +14 -5
- esphome/components/microphone/microphone_source.cpp +95 -0
- esphome/components/microphone/microphone_source.h +80 -0
- esphome/components/mics_4514/sensor.py +25 -14
- esphome/components/midea/climate.py +3 -4
- esphome/components/midea_ir/climate.py +3 -5
- esphome/components/mipi_spi/__init__.py +15 -0
- esphome/components/mipi_spi/display.py +474 -0
- esphome/components/mipi_spi/mipi_spi.cpp +481 -0
- esphome/components/mipi_spi/mipi_spi.h +171 -0
- esphome/components/mipi_spi/models/__init__.py +65 -0
- esphome/components/mipi_spi/models/amoled.py +72 -0
- esphome/components/mipi_spi/models/commands.py +82 -0
- esphome/components/mipi_spi/models/cyd.py +10 -0
- esphome/components/mipi_spi/models/ili.py +749 -0
- esphome/components/mipi_spi/models/jc.py +260 -0
- esphome/components/mipi_spi/models/lanbon.py +15 -0
- esphome/components/mipi_spi/models/lilygo.py +60 -0
- esphome/components/mipi_spi/models/waveshare.py +139 -0
- esphome/components/mitsubishi/climate.py +2 -5
- esphome/components/mitsubishi/mitsubishi.cpp +9 -9
- esphome/components/mixer/speaker/mixer_speaker.cpp +12 -22
- esphome/components/mixer/speaker/mixer_speaker.h +1 -3
- esphome/components/mlx90393/sensor.py +5 -0
- esphome/components/mlx90393/sensor_mlx90393.cpp +195 -13
- esphome/components/mlx90393/sensor_mlx90393.h +21 -4
- esphome/components/modbus/modbus.cpp +2 -1
- esphome/components/mqtt/__init__.py +1 -1
- esphome/components/mqtt/mqtt_client.cpp +6 -2
- esphome/components/mqtt/mqtt_const.h +4 -0
- esphome/components/mqtt/mqtt_fan.cpp +39 -0
- esphome/components/mqtt/mqtt_fan.h +2 -0
- esphome/components/ms5611/sensor.py +6 -6
- esphome/components/ms8607/sensor.py +3 -3
- esphome/components/network/__init__.py +1 -1
- esphome/components/nextion/base_component.py +17 -16
- esphome/components/nextion/display.py +11 -2
- esphome/components/nextion/nextion.cpp +39 -1
- esphome/components/nextion/nextion.h +50 -0
- esphome/components/noblex/climate.py +2 -9
- esphome/components/number/__init__.py +12 -9
- esphome/components/one_wire/one_wire_bus.cpp +14 -10
- esphome/components/one_wire/one_wire_bus.h +14 -8
- esphome/components/online_image/bmp_image.cpp +48 -11
- esphome/components/online_image/bmp_image.h +2 -0
- esphome/components/opentherm/binary_sensor/__init__.py +2 -4
- esphome/components/opentherm/number/__init__.py +11 -20
- esphome/components/opentherm/sensor/__init__.py +3 -3
- esphome/components/opentherm/switch/__init__.py +3 -5
- esphome/components/output/lock/__init__.py +11 -9
- esphome/components/packages/__init__.py +33 -31
- esphome/components/packet_transport/__init__.py +201 -0
- esphome/components/packet_transport/binary_sensor.py +19 -0
- esphome/components/packet_transport/packet_transport.cpp +534 -0
- esphome/components/packet_transport/packet_transport.h +154 -0
- esphome/components/packet_transport/sensor.py +19 -0
- esphome/components/pca9685/pca9685_output.cpp +2 -1
- esphome/components/pid/climate.py +2 -4
- esphome/components/pm2005/__init__.py +1 -0
- esphome/components/pm2005/pm2005.cpp +123 -0
- esphome/components/pm2005/pm2005.h +46 -0
- esphome/components/pm2005/sensor.py +86 -0
- esphome/components/pmsa003i/pmsa003i.cpp +43 -16
- esphome/components/pmsa003i/pmsa003i.h +25 -25
- esphome/components/pmsx003/pmsx003.cpp +195 -230
- esphome/components/pmsx003/pmsx003.h +51 -33
- esphome/components/pmsx003/sensor.py +21 -11
- esphome/components/pn7150/pn7150.h +2 -2
- esphome/components/pn7160/pn7160.h +2 -2
- esphome/components/prometheus/prometheus_handler.cpp +174 -0
- esphome/components/prometheus/prometheus_handler.h +17 -0
- esphome/components/psram/__init__.py +7 -5
- esphome/components/pulse_meter/pulse_meter_sensor.cpp +32 -12
- esphome/components/pulse_meter/pulse_meter_sensor.h +5 -5
- esphome/components/pzem004t/pzem004t.cpp +2 -1
- esphome/components/qspi_dbi/__init__.py +0 -1
- esphome/components/qspi_dbi/display.py +2 -1
- esphome/components/qspi_dbi/models.py +1 -2
- esphome/components/remote_base/__init__.py +91 -0
- esphome/components/remote_base/beo4_protocol.cpp +153 -0
- esphome/components/remote_base/beo4_protocol.h +43 -0
- esphome/components/remote_base/gobox_protocol.cpp +131 -0
- esphome/components/remote_base/gobox_protocol.h +54 -0
- esphome/components/remote_receiver/remote_receiver_esp32.cpp +16 -9
- esphome/components/resampler/speaker/resampler_speaker.cpp +12 -10
- esphome/components/resampler/speaker/resampler_speaker.h +1 -1
- esphome/components/rf_bridge/rf_bridge.cpp +2 -1
- esphome/components/scd30/sensor.py +2 -3
- esphome/components/scd4x/sensor.py +4 -5
- esphome/components/sdp3x/sensor.py +2 -1
- esphome/components/sds011/sds011.cpp +2 -1
- esphome/components/select/__init__.py +19 -20
- esphome/components/sen5x/sen5x.cpp +55 -36
- esphome/components/sen5x/sensor.py +1 -1
- esphome/components/senseair/sensor.py +3 -3
- esphome/components/sensor/__init__.py +158 -14
- esphome/components/sensor/filter.cpp +23 -0
- esphome/components/sensor/filter.h +22 -0
- esphome/components/sgp30/sensor.py +14 -16
- esphome/components/sgp4x/sensor.py +1 -1
- esphome/components/sht4x/sht4x.cpp +43 -22
- esphome/components/sht4x/sht4x.h +1 -1
- esphome/components/shtcx/sensor.py +6 -6
- esphome/components/slow_pwm/slow_pwm_output.cpp +2 -1
- esphome/components/sml/text_sensor/__init__.py +4 -6
- esphome/components/sound_level/__init__.py +0 -0
- esphome/components/sound_level/sensor.py +97 -0
- esphome/components/sound_level/sound_level.cpp +194 -0
- esphome/components/sound_level/sound_level.h +73 -0
- esphome/components/speaker/media_player/__init__.py +4 -8
- esphome/components/speaker/media_player/speaker_media_player.cpp +0 -18
- esphome/components/speaker/media_player/speaker_media_player.h +0 -11
- esphome/components/speaker/speaker.h +4 -7
- esphome/components/speed/fan/__init__.py +17 -16
- esphome/components/spi/spi.h +11 -1
- esphome/components/sprinkler/__init__.py +18 -19
- esphome/components/sprinkler/sprinkler.cpp +6 -5
- esphome/components/switch/__init__.py +32 -42
- esphome/components/syslog/__init__.py +41 -0
- esphome/components/syslog/esphome_syslog.cpp +49 -0
- esphome/components/syslog/esphome_syslog.h +27 -0
- esphome/components/t6615/sensor.py +3 -3
- esphome/components/t6615/t6615.cpp +2 -1
- esphome/components/tca9555/tca9555.cpp +11 -6
- esphome/components/tcl112/climate.py +2 -9
- esphome/components/template/alarm_control_panel/__init__.py +7 -6
- esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +21 -17
- esphome/components/template/alarm_control_panel/template_alarm_control_panel.h +2 -1
- esphome/components/template/cover/__init__.py +27 -21
- esphome/components/template/fan/__init__.py +14 -12
- esphome/components/template/lock/__init__.py +20 -25
- esphome/components/template/lock/automation.h +18 -0
- esphome/components/template/text/__init__.py +4 -3
- esphome/components/template/valve/__init__.py +32 -21
- esphome/components/template/valve/automation.h +24 -0
- esphome/components/text/__init__.py +32 -1
- esphome/components/text_sensor/__init__.py +24 -29
- esphome/components/thermostat/climate.py +5 -5
- esphome/components/time_based/cover.py +17 -16
- esphome/components/time_based/time_based_cover.cpp +2 -1
- esphome/components/tm1638/switch/__init__.py +10 -7
- esphome/components/tormatic/cover.py +4 -5
- esphome/components/toshiba/climate.py +3 -5
- esphome/components/touchscreen/touchscreen.cpp +3 -1
- esphome/components/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/uart/switch/uart_switch.cpp +2 -1
- esphome/components/udp/__init__.py +126 -128
- esphome/components/udp/automation.h +40 -0
- esphome/components/udp/binary_sensor.py +3 -25
- esphome/components/udp/packet_transport/__init__.py +29 -0
- esphome/components/udp/packet_transport/udp_transport.cpp +36 -0
- esphome/components/udp/packet_transport/udp_transport.h +28 -0
- esphome/components/udp/sensor.py +3 -25
- esphome/components/udp/udp_component.cpp +26 -470
- esphome/components/udp/udp_component.h +21 -128
- esphome/components/update/__init__.py +31 -1
- esphome/components/uponor_smatrix/climate/__init__.py +4 -9
- esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp +2 -1
- esphome/components/uponor_smatrix/uponor_smatrix.cpp +2 -1
- esphome/components/uptime/text_sensor/__init__.py +47 -7
- esphome/components/uptime/text_sensor/uptime_text_sensor.cpp +12 -7
- esphome/components/uptime/text_sensor/uptime_text_sensor.h +19 -0
- esphome/components/valve/__init__.py +34 -3
- esphome/components/valve/automation.h +1 -19
- esphome/components/vl53l0x/sensor.py +11 -0
- esphome/components/vl53l0x/vl53l0x_sensor.cpp +5 -1
- esphome/components/vl53l0x/vl53l0x_sensor.h +2 -1
- esphome/components/voice_assistant/__init__.py +36 -10
- esphome/components/voice_assistant/voice_assistant.cpp +170 -144
- esphome/components/voice_assistant/voice_assistant.h +26 -25
- esphome/components/waveshare_epaper/display.py +6 -0
- esphome/components/waveshare_epaper/waveshare_epaper.cpp +439 -37
- esphome/components/waveshare_epaper/waveshare_epaper.h +60 -11
- esphome/components/weikai/weikai.cpp +0 -52
- esphome/components/whirlpool/climate.py +3 -5
- esphome/components/whynter/climate.py +3 -5
- esphome/components/xpt2046/touchscreen/xpt2046.cpp +1 -1
- esphome/components/yashima/climate.py +6 -6
- esphome/components/zhlt01/climate.py +2 -7
- esphome/config.py +13 -13
- esphome/config_validation.py +38 -58
- esphome/const.py +15 -1
- esphome/core/__init__.py +2 -0
- esphome/core/application.cpp +27 -10
- esphome/core/application.h +9 -1
- esphome/core/automation.h +4 -3
- esphome/core/component.cpp +28 -7
- esphome/core/component.h +10 -1
- esphome/core/defines.h +23 -17
- esphome/core/doxygen.h +13 -0
- esphome/core/macros.h +4 -0
- esphome/core/scheduler.cpp +7 -1
- esphome/cpp_generator.py +6 -2
- esphome/dashboard/web_server.py +3 -3
- esphome/helpers.py +39 -0
- esphome/loader.py +4 -0
- esphome/log.py +15 -19
- esphome/mqtt.py +23 -10
- esphome/platformio_api.py +1 -1
- esphome/schema_extractors.py +0 -1
- esphome/voluptuous_schema.py +3 -1
- esphome/vscode.py +15 -0
- esphome/wizard.py +47 -37
- esphome/zeroconf.py +7 -3
- {esphome-2025.4.1.dist-info → esphome-2025.5.0.dist-info}/METADATA +10 -11
- {esphome-2025.4.1.dist-info → esphome-2025.5.0.dist-info}/RECORD +456 -396
- {esphome-2025.4.1.dist-info → esphome-2025.5.0.dist-info}/WHEEL +1 -1
- esphome/components/esp32_ble/const_esp32c6.h +0 -74
- {esphome-2025.4.1.dist-info → esphome-2025.5.0.dist-info}/entry_points.txt +0 -0
- {esphome-2025.4.1.dist-info → esphome-2025.5.0.dist-info}/licenses/LICENSE +0 -0
- {esphome-2025.4.1.dist-info → esphome-2025.5.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,534 @@
|
|
1
|
+
#include "esphome/core/log.h"
|
2
|
+
#include "esphome/core/application.h"
|
3
|
+
#include "packet_transport.h"
|
4
|
+
|
5
|
+
#include "esphome/components/xxtea/xxtea.h"
|
6
|
+
|
7
|
+
namespace esphome {
|
8
|
+
namespace packet_transport {
|
9
|
+
/**
|
10
|
+
* Structure of a data packet; everything is little-endian
|
11
|
+
*
|
12
|
+
* --- In clear text ---
|
13
|
+
* MAGIC_NUMBER: 16 bits
|
14
|
+
* host name length: 1 byte
|
15
|
+
* host name: (length) bytes
|
16
|
+
* padding: 0 or more null bytes to a 4 byte boundary
|
17
|
+
*
|
18
|
+
* --- Encrypted (if key set) ----
|
19
|
+
* DATA_KEY: 1 byte: OR ROLLING_CODE_KEY:
|
20
|
+
* Rolling code (if enabled): 8 bytes
|
21
|
+
* Ping keys: if any
|
22
|
+
* repeat:
|
23
|
+
* PING_KEY: 1 byte
|
24
|
+
* ping code: 4 bytes
|
25
|
+
* Sensors:
|
26
|
+
* repeat:
|
27
|
+
* SENSOR_KEY: 1 byte
|
28
|
+
* float value: 4 bytes
|
29
|
+
* name length: 1 byte
|
30
|
+
* name
|
31
|
+
* Binary Sensors:
|
32
|
+
* repeat:
|
33
|
+
* BINARY_SENSOR_KEY: 1 byte
|
34
|
+
* bool value: 1 bytes
|
35
|
+
* name length: 1 byte
|
36
|
+
* name
|
37
|
+
*
|
38
|
+
* Padded to a 4 byte boundary with nulls
|
39
|
+
*
|
40
|
+
* Structure of a ping request packet:
|
41
|
+
* --- In clear text ---
|
42
|
+
* MAGIC_PING: 16 bits
|
43
|
+
* host name length: 1 byte
|
44
|
+
* host name: (length) bytes
|
45
|
+
* Ping key (4 bytes)
|
46
|
+
*
|
47
|
+
*/
|
48
|
+
static const char *const TAG = "packet_transport";
|
49
|
+
|
50
|
+
static size_t round4(size_t value) { return (value + 3) & ~3; }
|
51
|
+
|
52
|
+
union FuData {
|
53
|
+
uint32_t u32;
|
54
|
+
float f32;
|
55
|
+
};
|
56
|
+
|
57
|
+
static const uint16_t MAGIC_NUMBER = 0x4553;
|
58
|
+
static const uint16_t MAGIC_PING = 0x5048;
|
59
|
+
static const uint32_t PREF_HASH = 0x45535043;
|
60
|
+
enum DataKey {
|
61
|
+
ZERO_FILL_KEY,
|
62
|
+
DATA_KEY,
|
63
|
+
SENSOR_KEY,
|
64
|
+
BINARY_SENSOR_KEY,
|
65
|
+
PING_KEY,
|
66
|
+
ROLLING_CODE_KEY,
|
67
|
+
};
|
68
|
+
|
69
|
+
enum DecodeResult {
|
70
|
+
DECODE_OK,
|
71
|
+
DECODE_UNMATCHED,
|
72
|
+
DECODE_ERROR,
|
73
|
+
DECODE_EMPTY,
|
74
|
+
};
|
75
|
+
|
76
|
+
static const size_t MAX_PING_KEYS = 4;
|
77
|
+
|
78
|
+
static inline void add(std::vector<uint8_t> &vec, uint32_t data) {
|
79
|
+
vec.push_back(data & 0xFF);
|
80
|
+
vec.push_back((data >> 8) & 0xFF);
|
81
|
+
vec.push_back((data >> 16) & 0xFF);
|
82
|
+
vec.push_back((data >> 24) & 0xFF);
|
83
|
+
}
|
84
|
+
|
85
|
+
class PacketDecoder {
|
86
|
+
public:
|
87
|
+
PacketDecoder(const uint8_t *buffer, size_t len) : buffer_(buffer), len_(len) {}
|
88
|
+
|
89
|
+
DecodeResult decode_string(char *data, size_t maxlen) {
|
90
|
+
if (this->position_ == this->len_)
|
91
|
+
return DECODE_EMPTY;
|
92
|
+
auto len = this->buffer_[this->position_];
|
93
|
+
if (len == 0 || this->position_ + 1 + len > this->len_ || len >= maxlen)
|
94
|
+
return DECODE_ERROR;
|
95
|
+
this->position_++;
|
96
|
+
memcpy(data, this->buffer_ + this->position_, len);
|
97
|
+
data[len] = 0;
|
98
|
+
this->position_ += len;
|
99
|
+
return DECODE_OK;
|
100
|
+
}
|
101
|
+
|
102
|
+
template<typename T> DecodeResult get(T &data) {
|
103
|
+
if (this->position_ + sizeof(T) > this->len_)
|
104
|
+
return DECODE_ERROR;
|
105
|
+
T value = 0;
|
106
|
+
for (size_t i = 0; i != sizeof(T); ++i) {
|
107
|
+
value += this->buffer_[this->position_++] << (i * 8);
|
108
|
+
}
|
109
|
+
data = value;
|
110
|
+
return DECODE_OK;
|
111
|
+
}
|
112
|
+
|
113
|
+
template<typename T> DecodeResult decode(uint8_t key, T &data) {
|
114
|
+
if (this->position_ == this->len_)
|
115
|
+
return DECODE_EMPTY;
|
116
|
+
if (this->buffer_[this->position_] != key)
|
117
|
+
return DECODE_UNMATCHED;
|
118
|
+
if (this->position_ + 1 + sizeof(T) > this->len_)
|
119
|
+
return DECODE_ERROR;
|
120
|
+
this->position_++;
|
121
|
+
T value = 0;
|
122
|
+
for (size_t i = 0; i != sizeof(T); ++i) {
|
123
|
+
value += this->buffer_[this->position_++] << (i * 8);
|
124
|
+
}
|
125
|
+
data = value;
|
126
|
+
return DECODE_OK;
|
127
|
+
}
|
128
|
+
|
129
|
+
template<typename T> DecodeResult decode(uint8_t key, char *buf, size_t buflen, T &data) {
|
130
|
+
if (this->position_ == this->len_)
|
131
|
+
return DECODE_EMPTY;
|
132
|
+
if (this->buffer_[this->position_] != key)
|
133
|
+
return DECODE_UNMATCHED;
|
134
|
+
this->position_++;
|
135
|
+
T value = 0;
|
136
|
+
for (size_t i = 0; i != sizeof(T); ++i) {
|
137
|
+
value += this->buffer_[this->position_++] << (i * 8);
|
138
|
+
}
|
139
|
+
data = value;
|
140
|
+
return this->decode_string(buf, buflen);
|
141
|
+
}
|
142
|
+
|
143
|
+
DecodeResult decode(uint8_t key) {
|
144
|
+
if (this->position_ == this->len_)
|
145
|
+
return DECODE_EMPTY;
|
146
|
+
if (this->buffer_[this->position_] != key)
|
147
|
+
return DECODE_UNMATCHED;
|
148
|
+
this->position_++;
|
149
|
+
return DECODE_OK;
|
150
|
+
}
|
151
|
+
|
152
|
+
size_t get_remaining_size() const { return this->len_ - this->position_; }
|
153
|
+
|
154
|
+
// align the pointer to the given byte boundary
|
155
|
+
bool bump_to(size_t boundary) {
|
156
|
+
auto newpos = this->position_;
|
157
|
+
auto offset = this->position_ % boundary;
|
158
|
+
if (offset != 0) {
|
159
|
+
newpos += boundary - offset;
|
160
|
+
}
|
161
|
+
if (newpos >= this->len_)
|
162
|
+
return false;
|
163
|
+
this->position_ = newpos;
|
164
|
+
return true;
|
165
|
+
}
|
166
|
+
|
167
|
+
bool decrypt(const uint32_t *key) {
|
168
|
+
if (this->get_remaining_size() % 4 != 0) {
|
169
|
+
return false;
|
170
|
+
}
|
171
|
+
xxtea::decrypt((uint32_t *) (this->buffer_ + this->position_), this->get_remaining_size() / 4, key);
|
172
|
+
return true;
|
173
|
+
}
|
174
|
+
|
175
|
+
protected:
|
176
|
+
const uint8_t *buffer_;
|
177
|
+
size_t len_;
|
178
|
+
size_t position_{};
|
179
|
+
};
|
180
|
+
|
181
|
+
static inline void add(std::vector<uint8_t> &vec, uint8_t data) { vec.push_back(data); }
|
182
|
+
static inline void add(std::vector<uint8_t> &vec, uint16_t data) {
|
183
|
+
vec.push_back((uint8_t) data);
|
184
|
+
vec.push_back((uint8_t) (data >> 8));
|
185
|
+
}
|
186
|
+
static inline void add(std::vector<uint8_t> &vec, DataKey data) { vec.push_back(data); }
|
187
|
+
static void add(std::vector<uint8_t> &vec, const char *str) {
|
188
|
+
auto len = strlen(str);
|
189
|
+
vec.push_back(len);
|
190
|
+
for (size_t i = 0; i != len; i++) {
|
191
|
+
vec.push_back(*str++);
|
192
|
+
}
|
193
|
+
}
|
194
|
+
|
195
|
+
void PacketTransport::setup() {
|
196
|
+
this->name_ = App.get_name().c_str();
|
197
|
+
if (strlen(this->name_) > 255) {
|
198
|
+
this->mark_failed();
|
199
|
+
this->status_set_error("Device name exceeds 255 chars");
|
200
|
+
return;
|
201
|
+
}
|
202
|
+
this->resend_ping_key_ = this->ping_pong_enable_;
|
203
|
+
this->pref_ = global_preferences->make_preference<uint32_t>(PREF_HASH, true);
|
204
|
+
if (this->rolling_code_enable_) {
|
205
|
+
// restore the upper 32 bits of the rolling code, increment and save.
|
206
|
+
this->pref_.load(&this->rolling_code_[1]);
|
207
|
+
this->rolling_code_[1]++;
|
208
|
+
this->pref_.save(&this->rolling_code_[1]);
|
209
|
+
// must make sure it's saved immediately
|
210
|
+
global_preferences->sync();
|
211
|
+
this->ping_key_ = random_uint32();
|
212
|
+
ESP_LOGV(TAG, "Rolling code incremented, upper part now %u", (unsigned) this->rolling_code_[1]);
|
213
|
+
}
|
214
|
+
#ifdef USE_SENSOR
|
215
|
+
for (auto &sensor : this->sensors_) {
|
216
|
+
sensor.sensor->add_on_state_callback([this, &sensor](float x) {
|
217
|
+
this->updated_ = true;
|
218
|
+
sensor.updated = true;
|
219
|
+
});
|
220
|
+
}
|
221
|
+
#endif
|
222
|
+
#ifdef USE_BINARY_SENSOR
|
223
|
+
for (auto &sensor : this->binary_sensors_) {
|
224
|
+
sensor.sensor->add_on_state_callback([this, &sensor](bool value) {
|
225
|
+
this->updated_ = true;
|
226
|
+
sensor.updated = true;
|
227
|
+
});
|
228
|
+
}
|
229
|
+
#endif
|
230
|
+
// initialise the header. This is invariant.
|
231
|
+
add(this->header_, MAGIC_NUMBER);
|
232
|
+
add(this->header_, this->name_);
|
233
|
+
// pad to a multiple of 4 bytes
|
234
|
+
while (this->header_.size() & 0x3)
|
235
|
+
this->header_.push_back(0);
|
236
|
+
}
|
237
|
+
|
238
|
+
void PacketTransport::init_data_() {
|
239
|
+
this->data_.clear();
|
240
|
+
if (this->rolling_code_enable_) {
|
241
|
+
add(this->data_, ROLLING_CODE_KEY);
|
242
|
+
add(this->data_, this->rolling_code_[0]);
|
243
|
+
add(this->data_, this->rolling_code_[1]);
|
244
|
+
this->increment_code_();
|
245
|
+
} else {
|
246
|
+
add(this->data_, DATA_KEY);
|
247
|
+
}
|
248
|
+
for (auto pkey : this->ping_keys_) {
|
249
|
+
add(this->data_, PING_KEY);
|
250
|
+
add(this->data_, pkey.second);
|
251
|
+
}
|
252
|
+
}
|
253
|
+
|
254
|
+
void PacketTransport::flush_() {
|
255
|
+
if (!this->should_send() || this->data_.empty())
|
256
|
+
return;
|
257
|
+
auto header_len = round4(this->header_.size());
|
258
|
+
auto len = round4(data_.size());
|
259
|
+
auto encode_buffer = std::vector<uint8_t>(round4(header_len + len));
|
260
|
+
memcpy(encode_buffer.data(), this->header_.data(), this->header_.size());
|
261
|
+
memcpy(encode_buffer.data() + header_len, this->data_.data(), this->data_.size());
|
262
|
+
if (this->is_encrypted_()) {
|
263
|
+
xxtea::encrypt((uint32_t *) (encode_buffer.data() + header_len), len / 4,
|
264
|
+
(uint32_t *) this->encryption_key_.data());
|
265
|
+
}
|
266
|
+
this->send_packet(encode_buffer);
|
267
|
+
}
|
268
|
+
|
269
|
+
void PacketTransport::add_binary_data_(uint8_t key, const char *id, bool data) {
|
270
|
+
auto len = 1 + 1 + 1 + strlen(id);
|
271
|
+
if (len + this->header_.size() + this->data_.size() > this->get_max_packet_size()) {
|
272
|
+
this->flush_();
|
273
|
+
}
|
274
|
+
add(this->data_, key);
|
275
|
+
add(this->data_, (uint8_t) data);
|
276
|
+
add(this->data_, id);
|
277
|
+
}
|
278
|
+
void PacketTransport::add_data_(uint8_t key, const char *id, float data) {
|
279
|
+
FuData udata{.f32 = data};
|
280
|
+
this->add_data_(key, id, udata.u32);
|
281
|
+
}
|
282
|
+
|
283
|
+
void PacketTransport::add_data_(uint8_t key, const char *id, uint32_t data) {
|
284
|
+
auto len = 4 + 1 + 1 + strlen(id);
|
285
|
+
if (len + this->header_.size() + this->data_.size() > this->get_max_packet_size()) {
|
286
|
+
this->flush_();
|
287
|
+
}
|
288
|
+
add(this->data_, key);
|
289
|
+
add(this->data_, data);
|
290
|
+
add(this->data_, id);
|
291
|
+
}
|
292
|
+
void PacketTransport::send_data_(bool all) {
|
293
|
+
if (!this->should_send())
|
294
|
+
return;
|
295
|
+
this->init_data_();
|
296
|
+
#ifdef USE_SENSOR
|
297
|
+
for (auto &sensor : this->sensors_) {
|
298
|
+
if (all || sensor.updated) {
|
299
|
+
sensor.updated = false;
|
300
|
+
this->add_data_(SENSOR_KEY, sensor.id, sensor.sensor->get_state());
|
301
|
+
}
|
302
|
+
}
|
303
|
+
#endif
|
304
|
+
#ifdef USE_BINARY_SENSOR
|
305
|
+
for (auto &sensor : this->binary_sensors_) {
|
306
|
+
if (all || sensor.updated) {
|
307
|
+
sensor.updated = false;
|
308
|
+
this->add_binary_data_(BINARY_SENSOR_KEY, sensor.id, sensor.sensor->state);
|
309
|
+
}
|
310
|
+
}
|
311
|
+
#endif
|
312
|
+
this->flush_();
|
313
|
+
this->updated_ = false;
|
314
|
+
}
|
315
|
+
|
316
|
+
void PacketTransport::update() {
|
317
|
+
auto now = millis() / 1000;
|
318
|
+
if (this->last_key_time_ + this->ping_pong_recyle_time_ < now) {
|
319
|
+
this->resend_ping_key_ = this->ping_pong_enable_;
|
320
|
+
this->last_key_time_ = now;
|
321
|
+
}
|
322
|
+
}
|
323
|
+
|
324
|
+
void PacketTransport::add_key_(const char *name, uint32_t key) {
|
325
|
+
if (!this->is_encrypted_())
|
326
|
+
return;
|
327
|
+
if (this->ping_keys_.count(name) == 0 && this->ping_keys_.size() == MAX_PING_KEYS) {
|
328
|
+
ESP_LOGW(TAG, "Ping key from %s discarded", name);
|
329
|
+
return;
|
330
|
+
}
|
331
|
+
this->ping_keys_[name] = key;
|
332
|
+
this->updated_ = true;
|
333
|
+
ESP_LOGV(TAG, "Ping key from %s now %X", name, (unsigned) key);
|
334
|
+
}
|
335
|
+
|
336
|
+
static bool process_rolling_code(Provider &provider, PacketDecoder &decoder) {
|
337
|
+
uint32_t code0, code1;
|
338
|
+
if (decoder.get(code0) != DECODE_OK || decoder.get(code1) != DECODE_OK) {
|
339
|
+
ESP_LOGW(TAG, "Rolling code requires 8 bytes");
|
340
|
+
return false;
|
341
|
+
}
|
342
|
+
if (code1 < provider.last_code[1] || (code1 == provider.last_code[1] && code0 <= provider.last_code[0])) {
|
343
|
+
ESP_LOGW(TAG, "Rolling code for %s %08lX:%08lX is old", provider.name, (unsigned long) code1,
|
344
|
+
(unsigned long) code0);
|
345
|
+
return false;
|
346
|
+
}
|
347
|
+
provider.last_code[0] = code0;
|
348
|
+
provider.last_code[1] = code1;
|
349
|
+
ESP_LOGV(TAG, "Saw new rolling code for %s %08lX:%08lX", provider.name, (unsigned long) code1, (unsigned long) code0);
|
350
|
+
return true;
|
351
|
+
}
|
352
|
+
|
353
|
+
/**
|
354
|
+
* Process a received packet
|
355
|
+
*/
|
356
|
+
void PacketTransport::process_(const std::vector<uint8_t> &data) {
|
357
|
+
auto ping_key_seen = !this->ping_pong_enable_;
|
358
|
+
PacketDecoder decoder((data.data()), data.size());
|
359
|
+
char namebuf[256]{};
|
360
|
+
uint8_t byte;
|
361
|
+
FuData rdata{};
|
362
|
+
uint16_t magic;
|
363
|
+
if (decoder.get(magic) != DECODE_OK) {
|
364
|
+
ESP_LOGD(TAG, "Short buffer");
|
365
|
+
return;
|
366
|
+
}
|
367
|
+
if (magic != MAGIC_NUMBER && magic != MAGIC_PING) {
|
368
|
+
ESP_LOGV(TAG, "Bad magic %X", magic);
|
369
|
+
return;
|
370
|
+
}
|
371
|
+
|
372
|
+
if (decoder.decode_string(namebuf, sizeof namebuf) != DECODE_OK) {
|
373
|
+
ESP_LOGV(TAG, "Bad hostname length");
|
374
|
+
return;
|
375
|
+
}
|
376
|
+
if (strcmp(this->name_, namebuf) == 0) {
|
377
|
+
ESP_LOGVV(TAG, "Ignoring our own data");
|
378
|
+
return;
|
379
|
+
}
|
380
|
+
if (magic == MAGIC_PING) {
|
381
|
+
uint32_t key;
|
382
|
+
if (decoder.get(key) != DECODE_OK) {
|
383
|
+
ESP_LOGW(TAG, "Bad ping request");
|
384
|
+
return;
|
385
|
+
}
|
386
|
+
this->add_key_(namebuf, key);
|
387
|
+
ESP_LOGV(TAG, "Updated ping key for %s to %08X", namebuf, (unsigned) key);
|
388
|
+
return;
|
389
|
+
}
|
390
|
+
|
391
|
+
if (this->providers_.count(namebuf) == 0) {
|
392
|
+
ESP_LOGVV(TAG, "Unknown hostname %s", namebuf);
|
393
|
+
return;
|
394
|
+
}
|
395
|
+
ESP_LOGV(TAG, "Found hostname %s", namebuf);
|
396
|
+
|
397
|
+
#ifdef USE_SENSOR
|
398
|
+
auto &sensors = this->remote_sensors_[namebuf];
|
399
|
+
#endif
|
400
|
+
#ifdef USE_BINARY_SENSOR
|
401
|
+
auto &binary_sensors = this->remote_binary_sensors_[namebuf];
|
402
|
+
#endif
|
403
|
+
|
404
|
+
if (!decoder.bump_to(4)) {
|
405
|
+
ESP_LOGW(TAG, "Bad packet length %zu", data.size());
|
406
|
+
}
|
407
|
+
auto len = decoder.get_remaining_size();
|
408
|
+
if (round4(len) != len) {
|
409
|
+
ESP_LOGW(TAG, "Bad payload length %zu", len);
|
410
|
+
return;
|
411
|
+
}
|
412
|
+
|
413
|
+
auto &provider = this->providers_[namebuf];
|
414
|
+
// if encryption not used with this host, ping check is pointless since it would be easily spoofed.
|
415
|
+
if (provider.encryption_key.empty())
|
416
|
+
ping_key_seen = true;
|
417
|
+
|
418
|
+
if (!provider.encryption_key.empty()) {
|
419
|
+
decoder.decrypt((const uint32_t *) provider.encryption_key.data());
|
420
|
+
}
|
421
|
+
if (decoder.get(byte) != DECODE_OK) {
|
422
|
+
ESP_LOGV(TAG, "No key byte");
|
423
|
+
return;
|
424
|
+
}
|
425
|
+
|
426
|
+
if (byte == ROLLING_CODE_KEY) {
|
427
|
+
if (!process_rolling_code(provider, decoder))
|
428
|
+
return;
|
429
|
+
} else if (byte != DATA_KEY) {
|
430
|
+
ESP_LOGV(TAG, "Expected rolling_key or data_key, got %X", byte);
|
431
|
+
return;
|
432
|
+
}
|
433
|
+
uint32_t key;
|
434
|
+
while (decoder.get_remaining_size() != 0) {
|
435
|
+
if (decoder.decode(ZERO_FILL_KEY) == DECODE_OK)
|
436
|
+
continue;
|
437
|
+
if (decoder.decode(PING_KEY, key) == DECODE_OK) {
|
438
|
+
if (key == this->ping_key_) {
|
439
|
+
ping_key_seen = true;
|
440
|
+
ESP_LOGV(TAG, "Found good ping key %X", (unsigned) key);
|
441
|
+
} else {
|
442
|
+
ESP_LOGV(TAG, "Unknown ping key %X", (unsigned) key);
|
443
|
+
}
|
444
|
+
continue;
|
445
|
+
}
|
446
|
+
if (!ping_key_seen) {
|
447
|
+
ESP_LOGW(TAG, "Ping key not seen");
|
448
|
+
this->resend_ping_key_ = true;
|
449
|
+
break;
|
450
|
+
}
|
451
|
+
if (decoder.decode(BINARY_SENSOR_KEY, namebuf, sizeof(namebuf), byte) == DECODE_OK) {
|
452
|
+
ESP_LOGV(TAG, "Got binary sensor %s %d", namebuf, byte);
|
453
|
+
#ifdef USE_BINARY_SENSOR
|
454
|
+
if (binary_sensors.count(namebuf) != 0)
|
455
|
+
binary_sensors[namebuf]->publish_state(byte != 0);
|
456
|
+
#endif
|
457
|
+
continue;
|
458
|
+
}
|
459
|
+
if (decoder.decode(SENSOR_KEY, namebuf, sizeof(namebuf), rdata.u32) == DECODE_OK) {
|
460
|
+
ESP_LOGV(TAG, "Got sensor %s %f", namebuf, rdata.f32);
|
461
|
+
#ifdef USE_SENSOR
|
462
|
+
if (sensors.count(namebuf) != 0)
|
463
|
+
sensors[namebuf]->publish_state(rdata.f32);
|
464
|
+
#endif
|
465
|
+
continue;
|
466
|
+
}
|
467
|
+
if (decoder.get(byte) == DECODE_OK) {
|
468
|
+
ESP_LOGW(TAG, "Unknown key %X", byte);
|
469
|
+
ESP_LOGD(TAG, "Buffer pos: %zu contents: %s", data.size() - decoder.get_remaining_size(),
|
470
|
+
format_hex_pretty(data).c_str());
|
471
|
+
}
|
472
|
+
break;
|
473
|
+
}
|
474
|
+
}
|
475
|
+
|
476
|
+
void PacketTransport::dump_config() {
|
477
|
+
ESP_LOGCONFIG(TAG, "Packet Transport:");
|
478
|
+
ESP_LOGCONFIG(TAG, " Platform: %s", this->platform_name_);
|
479
|
+
ESP_LOGCONFIG(TAG, " Encrypted: %s", YESNO(this->is_encrypted_()));
|
480
|
+
ESP_LOGCONFIG(TAG, " Ping-pong: %s", YESNO(this->ping_pong_enable_));
|
481
|
+
#ifdef USE_SENSOR
|
482
|
+
for (auto sensor : this->sensors_)
|
483
|
+
ESP_LOGCONFIG(TAG, " Sensor: %s", sensor.id);
|
484
|
+
#endif
|
485
|
+
#ifdef USE_BINARY_SENSOR
|
486
|
+
for (auto sensor : this->binary_sensors_)
|
487
|
+
ESP_LOGCONFIG(TAG, " Binary Sensor: %s", sensor.id);
|
488
|
+
#endif
|
489
|
+
for (const auto &host : this->providers_) {
|
490
|
+
ESP_LOGCONFIG(TAG, " Remote host: %s", host.first.c_str());
|
491
|
+
ESP_LOGCONFIG(TAG, " Encrypted: %s", YESNO(!host.second.encryption_key.empty()));
|
492
|
+
#ifdef USE_SENSOR
|
493
|
+
for (const auto &sensor : this->remote_sensors_[host.first.c_str()])
|
494
|
+
ESP_LOGCONFIG(TAG, " Sensor: %s", sensor.first.c_str());
|
495
|
+
#endif
|
496
|
+
#ifdef USE_BINARY_SENSOR
|
497
|
+
for (const auto &sensor : this->remote_binary_sensors_[host.first.c_str()])
|
498
|
+
ESP_LOGCONFIG(TAG, " Binary Sensor: %s", sensor.first.c_str());
|
499
|
+
#endif
|
500
|
+
}
|
501
|
+
}
|
502
|
+
void PacketTransport::increment_code_() {
|
503
|
+
if (this->rolling_code_enable_) {
|
504
|
+
if (++this->rolling_code_[0] == 0) {
|
505
|
+
this->rolling_code_[1]++;
|
506
|
+
this->pref_.save(&this->rolling_code_[1]);
|
507
|
+
// must make sure it's saved immediately
|
508
|
+
global_preferences->sync();
|
509
|
+
}
|
510
|
+
}
|
511
|
+
}
|
512
|
+
|
513
|
+
void PacketTransport::loop() {
|
514
|
+
if (this->resend_ping_key_)
|
515
|
+
this->send_ping_pong_request_();
|
516
|
+
if (this->updated_) {
|
517
|
+
this->send_data_(this->resend_data_);
|
518
|
+
}
|
519
|
+
}
|
520
|
+
|
521
|
+
void PacketTransport::send_ping_pong_request_() {
|
522
|
+
if (!this->ping_pong_enable_ || !this->should_send())
|
523
|
+
return;
|
524
|
+
this->ping_key_ = random_uint32();
|
525
|
+
this->ping_header_.clear();
|
526
|
+
add(this->ping_header_, MAGIC_PING);
|
527
|
+
add(this->ping_header_, this->name_);
|
528
|
+
add(this->ping_header_, this->ping_key_);
|
529
|
+
this->send_packet(this->ping_header_);
|
530
|
+
this->resend_ping_key_ = false;
|
531
|
+
ESP_LOGV(TAG, "Sent new ping request %08X", (unsigned) this->ping_key_);
|
532
|
+
}
|
533
|
+
} // namespace packet_transport
|
534
|
+
} // namespace esphome
|
@@ -0,0 +1,154 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include "esphome/core/component.h"
|
4
|
+
#include "esphome/core/preferences.h"
|
5
|
+
#ifdef USE_SENSOR
|
6
|
+
#include "esphome/components/sensor/sensor.h"
|
7
|
+
#endif
|
8
|
+
#ifdef USE_BINARY_SENSOR
|
9
|
+
#include "esphome/components/binary_sensor/binary_sensor.h"
|
10
|
+
#endif
|
11
|
+
#
|
12
|
+
#include <vector>
|
13
|
+
#include <map>
|
14
|
+
|
15
|
+
/**
|
16
|
+
* Providing packet encoding functions for exchanging data with a remote host.
|
17
|
+
*
|
18
|
+
* A transport is required to send the data; this is provided by a child class.
|
19
|
+
* The child class should implement the virtual functions send_packet_ and get_max_packet_size_.
|
20
|
+
* On receipt of a data packet, it should call `this->process_()` with the data.
|
21
|
+
*/
|
22
|
+
|
23
|
+
namespace esphome {
|
24
|
+
namespace packet_transport {
|
25
|
+
|
26
|
+
struct Provider {
|
27
|
+
std::vector<uint8_t> encryption_key;
|
28
|
+
const char *name;
|
29
|
+
uint32_t last_code[2];
|
30
|
+
};
|
31
|
+
|
32
|
+
#ifdef USE_SENSOR
|
33
|
+
struct Sensor {
|
34
|
+
sensor::Sensor *sensor;
|
35
|
+
const char *id;
|
36
|
+
bool updated;
|
37
|
+
};
|
38
|
+
#endif
|
39
|
+
#ifdef USE_BINARY_SENSOR
|
40
|
+
struct BinarySensor {
|
41
|
+
binary_sensor::BinarySensor *sensor;
|
42
|
+
const char *id;
|
43
|
+
bool updated;
|
44
|
+
};
|
45
|
+
#endif
|
46
|
+
|
47
|
+
class PacketTransport : public PollingComponent {
|
48
|
+
public:
|
49
|
+
void setup() override;
|
50
|
+
void loop() override;
|
51
|
+
void update() override;
|
52
|
+
void dump_config() override;
|
53
|
+
|
54
|
+
#ifdef USE_SENSOR
|
55
|
+
void add_sensor(const char *id, sensor::Sensor *sensor) {
|
56
|
+
Sensor st{sensor, id, true};
|
57
|
+
this->sensors_.push_back(st);
|
58
|
+
}
|
59
|
+
void add_remote_sensor(const char *hostname, const char *remote_id, sensor::Sensor *sensor) {
|
60
|
+
this->add_provider(hostname);
|
61
|
+
this->remote_sensors_[hostname][remote_id] = sensor;
|
62
|
+
}
|
63
|
+
#endif
|
64
|
+
#ifdef USE_BINARY_SENSOR
|
65
|
+
void add_binary_sensor(const char *id, binary_sensor::BinarySensor *sensor) {
|
66
|
+
BinarySensor st{sensor, id, true};
|
67
|
+
this->binary_sensors_.push_back(st);
|
68
|
+
}
|
69
|
+
|
70
|
+
void add_remote_binary_sensor(const char *hostname, const char *remote_id, binary_sensor::BinarySensor *sensor) {
|
71
|
+
this->add_provider(hostname);
|
72
|
+
this->remote_binary_sensors_[hostname][remote_id] = sensor;
|
73
|
+
}
|
74
|
+
#endif
|
75
|
+
|
76
|
+
void add_provider(const char *hostname) {
|
77
|
+
if (this->providers_.count(hostname) == 0) {
|
78
|
+
Provider provider;
|
79
|
+
provider.encryption_key = std::vector<uint8_t>{};
|
80
|
+
provider.last_code[0] = 0;
|
81
|
+
provider.last_code[1] = 0;
|
82
|
+
provider.name = hostname;
|
83
|
+
this->providers_[hostname] = provider;
|
84
|
+
#ifdef USE_SENSOR
|
85
|
+
this->remote_sensors_[hostname] = std::map<std::string, sensor::Sensor *>();
|
86
|
+
#endif
|
87
|
+
#ifdef USE_BINARY_SENSOR
|
88
|
+
this->remote_binary_sensors_[hostname] = std::map<std::string, binary_sensor::BinarySensor *>();
|
89
|
+
#endif
|
90
|
+
}
|
91
|
+
}
|
92
|
+
|
93
|
+
void set_encryption_key(std::vector<uint8_t> key) { this->encryption_key_ = std::move(key); }
|
94
|
+
void set_rolling_code_enable(bool enable) { this->rolling_code_enable_ = enable; }
|
95
|
+
void set_ping_pong_enable(bool enable) { this->ping_pong_enable_ = enable; }
|
96
|
+
void set_ping_pong_recycle_time(uint32_t recycle_time) { this->ping_pong_recyle_time_ = recycle_time; }
|
97
|
+
void set_provider_encryption(const char *name, std::vector<uint8_t> key) {
|
98
|
+
this->providers_[name].encryption_key = std::move(key);
|
99
|
+
}
|
100
|
+
void set_platform_name(const char *name) { this->platform_name_ = name; }
|
101
|
+
|
102
|
+
protected:
|
103
|
+
// child classes must implement this
|
104
|
+
virtual void send_packet(const std::vector<uint8_t> &buf) const = 0;
|
105
|
+
virtual size_t get_max_packet_size() = 0;
|
106
|
+
virtual bool should_send() { return true; }
|
107
|
+
|
108
|
+
// to be called by child classes when a data packet is received.
|
109
|
+
void process_(const std::vector<uint8_t> &data);
|
110
|
+
void send_data_(bool all);
|
111
|
+
void flush_();
|
112
|
+
void add_data_(uint8_t key, const char *id, float data);
|
113
|
+
void add_data_(uint8_t key, const char *id, uint32_t data);
|
114
|
+
void increment_code_();
|
115
|
+
void add_binary_data_(uint8_t key, const char *id, bool data);
|
116
|
+
void init_data_();
|
117
|
+
|
118
|
+
bool updated_{};
|
119
|
+
uint32_t ping_key_{};
|
120
|
+
uint32_t rolling_code_[2]{};
|
121
|
+
bool rolling_code_enable_{};
|
122
|
+
bool ping_pong_enable_{};
|
123
|
+
uint32_t ping_pong_recyle_time_{};
|
124
|
+
uint32_t last_key_time_{};
|
125
|
+
bool resend_ping_key_{};
|
126
|
+
bool resend_data_{};
|
127
|
+
const char *name_{};
|
128
|
+
ESPPreferenceObject pref_{};
|
129
|
+
|
130
|
+
std::vector<uint8_t> encryption_key_{};
|
131
|
+
|
132
|
+
#ifdef USE_SENSOR
|
133
|
+
std::vector<Sensor> sensors_{};
|
134
|
+
std::map<std::string, std::map<std::string, sensor::Sensor *>> remote_sensors_{};
|
135
|
+
#endif
|
136
|
+
#ifdef USE_BINARY_SENSOR
|
137
|
+
std::vector<BinarySensor> binary_sensors_{};
|
138
|
+
std::map<std::string, std::map<std::string, binary_sensor::BinarySensor *>> remote_binary_sensors_{};
|
139
|
+
#endif
|
140
|
+
|
141
|
+
std::map<std::string, Provider> providers_{};
|
142
|
+
std::vector<uint8_t> ping_header_{};
|
143
|
+
std::vector<uint8_t> header_{};
|
144
|
+
std::vector<uint8_t> data_{};
|
145
|
+
std::map<const char *, uint32_t> ping_keys_{};
|
146
|
+
const char *platform_name_{""};
|
147
|
+
void add_key_(const char *name, uint32_t key);
|
148
|
+
void send_ping_pong_request_();
|
149
|
+
|
150
|
+
inline bool is_encrypted_() { return !this->encryption_key_.empty(); }
|
151
|
+
};
|
152
|
+
|
153
|
+
} // namespace packet_transport
|
154
|
+
} // namespace esphome
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import esphome.codegen as cg
|
2
|
+
from esphome.components.sensor import new_sensor, sensor_schema
|
3
|
+
from esphome.const import CONF_ID
|
4
|
+
|
5
|
+
from . import (
|
6
|
+
CONF_PROVIDER,
|
7
|
+
CONF_REMOTE_ID,
|
8
|
+
CONF_TRANSPORT_ID,
|
9
|
+
packet_transport_sensor_schema,
|
10
|
+
)
|
11
|
+
|
12
|
+
CONFIG_SCHEMA = packet_transport_sensor_schema(sensor_schema())
|
13
|
+
|
14
|
+
|
15
|
+
async def to_code(config):
|
16
|
+
var = await new_sensor(config)
|
17
|
+
comp = await cg.get_variable(config[CONF_TRANSPORT_ID])
|
18
|
+
remote_id = str(config.get(CONF_REMOTE_ID) or config.get(CONF_ID))
|
19
|
+
cg.add(comp.add_remote_sensor(config[CONF_PROVIDER], remote_id, var))
|