esphome 2025.9.3__py3-none-any.whl → 2025.10.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 +94 -31
- esphome/address_cache.py +142 -0
- esphome/automation.py +130 -32
- esphome/build_gen/platformio.py +1 -3
- esphome/codegen.py +1 -0
- esphome/components/animation/animation.cpp +2 -2
- esphome/components/api/__init__.py +166 -3
- esphome/components/api/api_connection.cpp +84 -41
- esphome/components/api/api_connection.h +22 -16
- esphome/components/api/api_frame_helper.cpp +33 -19
- esphome/components/api/api_frame_helper.h +19 -4
- esphome/components/api/api_frame_helper_noise.cpp +41 -53
- esphome/components/api/api_frame_helper_noise.h +1 -1
- esphome/components/api/api_frame_helper_plaintext.cpp +22 -31
- esphome/components/api/api_frame_helper_plaintext.h +1 -1
- esphome/components/api/api_pb2.cpp +189 -15
- esphome/components/api/api_pb2.h +132 -20
- esphome/components/api/api_pb2_dump.cpp +97 -9
- esphome/components/api/api_pb2_service.cpp +118 -160
- esphome/components/api/api_pb2_service.h +31 -3
- esphome/components/api/api_server.cpp +68 -10
- esphome/components/api/api_server.h +32 -4
- esphome/components/api/custom_api_device.h +8 -8
- esphome/components/api/homeassistant_service.h +123 -6
- esphome/components/api/proto.h +6 -2
- esphome/components/api/user_services.h +2 -2
- esphome/components/as7341/sensor.py +1 -1
- esphome/components/audio/__init__.py +1 -1
- esphome/components/audio/audio.cpp +1 -1
- esphome/components/audio/audio_decoder.cpp +9 -9
- esphome/components/bl0906/bl0906.cpp +2 -2
- esphome/components/bl0942/bl0942.cpp +2 -2
- esphome/components/ble_client/__init__.py +1 -1
- esphome/components/bluetooth_proxy/__init__.py +4 -30
- esphome/components/bluetooth_proxy/bluetooth_connection.cpp +11 -4
- esphome/components/bluetooth_proxy/bluetooth_connection.h +2 -2
- esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +2 -2
- esphome/components/camera_encoder/__init__.py +2 -4
- esphome/components/camera_encoder/esp32_camera_jpeg_encoder.cpp +4 -2
- esphome/components/camera_encoder/esp32_camera_jpeg_encoder.h +3 -1
- esphome/components/canbus/canbus.cpp +7 -5
- esphome/components/canbus/canbus.h +7 -7
- esphome/components/captive_portal/__init__.py +18 -1
- esphome/components/captive_portal/captive_portal.cpp +40 -46
- esphome/components/captive_portal/captive_portal.h +20 -22
- esphome/components/captive_portal/dns_server_esp32_idf.cpp +205 -0
- esphome/components/captive_portal/dns_server_esp32_idf.h +27 -0
- esphome/components/ccs811/ccs811.cpp +1 -1
- esphome/components/climate/climate.cpp +10 -7
- esphome/components/cm1106/cm1106.cpp +1 -1
- esphome/components/copy/lock/copy_lock.cpp +1 -1
- esphome/components/cover/cover.cpp +1 -0
- esphome/components/daikin_arc/daikin_arc.cpp +19 -12
- esphome/components/dashboard_import/dashboard_import.cpp +1 -1
- esphome/components/dashboard_import/dashboard_import.h +1 -1
- esphome/components/deep_sleep/__init__.py +9 -2
- esphome/components/deep_sleep/deep_sleep_component.h +11 -9
- esphome/components/deep_sleep/deep_sleep_esp32.cpp +51 -27
- esphome/components/ektf2232/touchscreen/__init__.py +8 -5
- esphome/components/ektf2232/touchscreen/ektf2232.cpp +4 -4
- esphome/components/ektf2232/touchscreen/ektf2232.h +2 -2
- esphome/components/epaper_spi/__init__.py +1 -0
- esphome/components/epaper_spi/display.py +80 -0
- esphome/components/epaper_spi/epaper_spi.cpp +227 -0
- esphome/components/epaper_spi/epaper_spi.h +93 -0
- esphome/components/epaper_spi/epaper_spi_model_7p3in_spectra_e6.cpp +42 -0
- esphome/components/epaper_spi/epaper_spi_model_7p3in_spectra_e6.h +45 -0
- esphome/components/epaper_spi/epaper_spi_spectra_e6.cpp +135 -0
- esphome/components/epaper_spi/epaper_spi_spectra_e6.h +23 -0
- esphome/components/es7210/es7210.cpp +3 -3
- esphome/components/esp32/__init__.py +256 -340
- esphome/components/esp32/boards.py +81 -0
- esphome/components/esp32/preferences.cpp +23 -17
- esphome/components/esp32_ble/__init__.py +167 -44
- esphome/components/esp32_ble/ble.cpp +47 -3
- esphome/components/esp32_ble/ble.h +18 -0
- esphome/components/esp32_ble/ble_advertising.cpp +7 -3
- esphome/components/esp32_ble/ble_advertising.h +4 -0
- esphome/components/esp32_ble/ble_uuid.cpp +16 -42
- esphome/components/esp32_ble_beacon/__init__.py +3 -4
- esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp +0 -4
- esphome/components/esp32_ble_client/ble_client_base.cpp +14 -12
- esphome/components/esp32_ble_server/__init__.py +28 -14
- esphome/components/esp32_ble_server/ble_characteristic.cpp +67 -57
- esphome/components/esp32_ble_server/ble_characteristic.h +27 -16
- esphome/components/esp32_ble_server/ble_descriptor.cpp +4 -3
- esphome/components/esp32_ble_server/ble_descriptor.h +13 -9
- esphome/components/esp32_ble_server/ble_server.cpp +59 -24
- esphome/components/esp32_ble_server/ble_server.h +38 -20
- esphome/components/esp32_ble_server/ble_server_automations.cpp +49 -33
- esphome/components/esp32_ble_server/ble_server_automations.h +39 -24
- esphome/components/esp32_ble_tracker/__init__.py +25 -80
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +2 -8
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +0 -3
- esphome/components/esp32_camera/__init__.py +1 -3
- esphome/components/esp32_can/esp32_can.cpp +22 -4
- esphome/components/esp32_can/esp32_can.h +3 -0
- esphome/components/esp32_hosted/__init__.py +2 -1
- esphome/components/esp32_improv/esp32_improv_component.cpp +135 -65
- esphome/components/esp32_improv/esp32_improv_component.h +7 -1
- esphome/components/esp32_rmt_led_strip/led_strip.cpp +1 -1
- esphome/components/esp8266/__init__.py +3 -3
- esphome/components/esphome/ota/__init__.py +21 -2
- esphome/components/esphome/ota/ota_esphome.cpp +456 -146
- esphome/components/esphome/ota/ota_esphome.h +49 -2
- esphome/components/ethernet/__init__.py +39 -22
- esphome/components/ethernet/ethernet_component.cpp +28 -5
- esphome/components/ethernet/ethernet_component.h +5 -1
- esphome/components/external_components/__init__.py +8 -6
- esphome/components/fingerprint_grow/fingerprint_grow.cpp +1 -1
- esphome/components/fingerprint_grow/fingerprint_grow.h +2 -1
- esphome/components/font/__init__.py +5 -5
- esphome/components/graph/graph.cpp +1 -1
- esphome/components/graphical_display_menu/graphical_display_menu.cpp +3 -2
- esphome/components/haier/hon_climate.cpp +2 -2
- esphome/components/haier/hon_climate.h +1 -1
- esphome/components/hdc1080/hdc1080.cpp +42 -34
- esphome/components/hdc1080/hdc1080.h +1 -3
- esphome/components/homeassistant/number/homeassistant_number.cpp +2 -2
- esphome/components/homeassistant/switch/homeassistant_switch.cpp +2 -2
- esphome/components/http_request/__init__.py +3 -3
- esphome/components/htu21d/htu21d.cpp +13 -18
- esphome/components/htu21d/htu21d.h +1 -1
- esphome/components/i2s_audio/__init__.py +1 -2
- esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +1 -1
- esphome/components/ili9xxx/ili9xxx_display.cpp +2 -2
- esphome/components/improv_serial/improv_serial_component.cpp +12 -15
- esphome/components/improv_serial/improv_serial_component.h +6 -8
- esphome/components/json/json_util.cpp +42 -44
- esphome/components/json/json_util.h +57 -0
- esphome/components/kamstrup_kmp/kamstrup_kmp.cpp +2 -2
- esphome/components/key_collector/key_collector.h +4 -4
- esphome/components/libretiny/__init__.py +6 -6
- esphome/components/libretiny/preferences.cpp +23 -16
- esphome/components/light/light_call.cpp +98 -120
- esphome/components/light/light_call.h +17 -7
- esphome/components/lm75b/__init__.py +0 -0
- esphome/components/lm75b/lm75b.cpp +39 -0
- esphome/components/lm75b/lm75b.h +19 -0
- esphome/components/lm75b/sensor.py +34 -0
- esphome/components/lock/lock.h +12 -6
- esphome/components/logger/__init__.py +15 -27
- esphome/components/logger/logger.cpp +10 -20
- esphome/components/logger/logger.h +105 -62
- esphome/components/logger/logger_esp32.cpp +0 -48
- esphome/components/logger/logger_zephyr.cpp +2 -3
- esphome/components/logger/select/logger_level_select.cpp +6 -7
- esphome/components/logger/select/logger_level_select.h +7 -0
- esphome/components/ltr501/ltr501.cpp +7 -6
- esphome/components/ltr_als_ps/ltr_als_ps.cpp +7 -6
- esphome/components/matrix_keypad/matrix_keypad.h +4 -4
- esphome/components/max7219digit/max7219digit.cpp +1 -1
- esphome/components/mcp23xxx_base/mcp23xxx_base.h +3 -3
- esphome/components/mcp2515/mcp2515.cpp +31 -3
- esphome/components/mcp2515/mcp2515_defs.h +3 -1
- esphome/components/md5/md5.cpp +0 -26
- esphome/components/md5/md5.h +10 -20
- esphome/components/mdns/__init__.py +93 -19
- esphome/components/mdns/mdns_component.cpp +57 -94
- esphome/components/mdns/mdns_component.h +35 -11
- esphome/components/mdns/mdns_esp32.cpp +7 -13
- esphome/components/mdns/mdns_esp8266.cpp +7 -7
- esphome/components/mdns/mdns_libretiny.cpp +3 -4
- esphome/components/mdns/mdns_rp2040.cpp +3 -4
- esphome/components/mipi/__init__.py +1 -5
- esphome/components/mipi_spi/display.py +24 -8
- esphome/components/mipi_spi/mipi_spi.h +3 -3
- esphome/components/mixer/speaker/mixer_speaker.cpp +3 -3
- esphome/components/mmc5603/mmc5603.cpp +3 -3
- esphome/components/modbus/modbus.cpp +27 -13
- esphome/components/modbus/modbus.h +5 -3
- esphome/components/modbus/modbus_definitions.h +86 -0
- esphome/components/modbus_controller/__init__.py +29 -1
- esphome/components/modbus_controller/const.py +4 -0
- esphome/components/modbus_controller/modbus_controller.cpp +38 -13
- esphome/components/modbus_controller/modbus_controller.h +18 -29
- esphome/components/mpr121/mpr121.cpp +41 -42
- esphome/components/mpr121/mpr121.h +0 -1
- esphome/components/nau7802/nau7802.cpp +2 -2
- esphome/components/network/__init__.py +7 -3
- esphome/components/nextion/display.py +4 -4
- esphome/components/nextion/nextion.cpp +8 -8
- esphome/components/number/__init__.py +2 -0
- esphome/components/number/number_call.cpp +23 -12
- esphome/components/number/number_call.h +5 -0
- esphome/components/online_image/bmp_image.cpp +2 -1
- esphome/components/online_image/jpeg_image.cpp +4 -2
- esphome/components/opentherm/opentherm.cpp +5 -5
- esphome/components/opentherm/opentherm.h +3 -3
- esphome/components/openthread/openthread.cpp +11 -10
- esphome/components/openthread/openthread.h +0 -1
- esphome/components/ota/ota_backend.h +1 -0
- esphome/components/packages/__init__.py +10 -8
- esphome/components/packet_transport/packet_transport.cpp +2 -0
- esphome/components/pid/pid_controller.cpp +1 -1
- esphome/components/prometheus/prometheus_handler.cpp +239 -239
- esphome/components/psram/__init__.py +30 -28
- esphome/components/qmc5883l/qmc5883l.cpp +15 -0
- esphome/components/qmc5883l/qmc5883l.h +3 -0
- esphome/components/qmc5883l/sensor.py +31 -12
- esphome/components/remote_base/gobox_protocol.cpp +3 -3
- esphome/components/remote_receiver/__init__.py +14 -2
- esphome/components/remote_receiver/{remote_receiver_esp8266.cpp → remote_receiver.cpp} +2 -2
- esphome/components/remote_receiver/remote_receiver.h +4 -0
- esphome/components/remote_receiver/remote_receiver_esp32.cpp +18 -1
- esphome/components/remote_transmitter/__init__.py +2 -2
- esphome/components/remote_transmitter/remote_transmitter.cpp +103 -0
- esphome/components/rp2040/__init__.py +11 -11
- esphome/components/rtttl/rtttl.cpp +2 -2
- esphome/components/scd30/sensor.py +1 -1
- esphome/components/script/__init__.py +1 -1
- esphome/components/script/script.h +7 -7
- esphome/components/select/select.cpp +5 -4
- esphome/components/select/select_call.cpp +1 -1
- esphome/components/sensirion_common/i2c_sensirion.cpp +2 -1
- esphome/components/sensor/__init__.py +2 -0
- esphome/components/sha256/__init__.py +22 -0
- esphome/components/sha256/sha256.cpp +116 -0
- esphome/components/sha256/sha256.h +60 -0
- esphome/components/socket/lwip_raw_tcp_impl.cpp +34 -6
- esphome/components/sonoff_d1/sonoff_d1.cpp +1 -1
- esphome/components/spi/__init__.py +0 -3
- esphome/components/split_buffer/__init__.py +5 -0
- esphome/components/split_buffer/split_buffer.cpp +133 -0
- esphome/components/split_buffer/split_buffer.h +40 -0
- esphome/components/sps30/sps30.cpp +14 -10
- esphome/components/sps30/sps30.h +2 -0
- esphome/components/st7567_i2c/st7567_i2c.cpp +3 -1
- esphome/components/st7789v/st7789v.cpp +3 -2
- esphome/components/statsd/statsd.cpp +1 -1
- esphome/components/substitutions/__init__.py +3 -1
- esphome/components/substitutions/jinja.py +13 -3
- esphome/components/sx126x/__init__.py +16 -0
- esphome/components/sx126x/sx126x.cpp +15 -1
- esphome/components/sx126x/sx126x.h +9 -1
- esphome/components/sx126x/sx126x_reg.h +2 -0
- esphome/components/text_sensor/text_sensor.cpp +16 -0
- esphome/components/text_sensor/text_sensor.h +3 -10
- esphome/components/tormatic/tormatic_cover.cpp +1 -1
- esphome/components/tuya/select/tuya_select.cpp +1 -1
- esphome/components/tuya/tuya.cpp +29 -4
- esphome/components/uart/__init__.py +37 -27
- esphome/components/uart/uart.h +6 -0
- esphome/components/uart/uart_component.cpp +8 -0
- esphome/components/uart/uart_component.h +28 -0
- esphome/components/uart/uart_component_esp_idf.cpp +64 -10
- esphome/components/uart/uart_component_esp_idf.h +5 -2
- esphome/components/uponor_smatrix/climate/uponor_smatrix_climate.cpp +1 -1
- esphome/components/uponor_smatrix/sensor/uponor_smatrix_sensor.cpp +1 -1
- esphome/components/uponor_smatrix/uponor_smatrix.cpp +3 -3
- esphome/components/usb_host/__init__.py +12 -2
- esphome/components/usb_host/usb_host.h +89 -14
- esphome/components/usb_host/usb_host_client.cpp +157 -22
- esphome/components/usb_host/usb_host_component.cpp +1 -1
- esphome/components/usb_uart/__init__.py +0 -1
- esphome/components/usb_uart/ch34x.cpp +4 -4
- esphome/components/usb_uart/cp210x.cpp +3 -3
- esphome/components/usb_uart/usb_uart.cpp +88 -32
- esphome/components/usb_uart/usb_uart.h +30 -6
- esphome/components/valve/valve.cpp +1 -0
- esphome/components/veml7700/veml7700.cpp +7 -6
- esphome/components/version/version_text_sensor.cpp +2 -1
- esphome/components/voice_assistant/voice_assistant.cpp +3 -2
- esphome/components/waveshare_epaper/waveshare_epaper.cpp +4 -4
- esphome/components/web_server/list_entities.cpp +3 -4
- esphome/components/web_server/list_entities.h +8 -10
- esphome/components/web_server/ota/__init__.py +1 -1
- esphome/components/web_server/ota/ota_web_server.cpp +9 -3
- esphome/components/web_server/web_server.cpp +509 -404
- esphome/components/web_server/web_server.h +5 -6
- esphome/components/web_server/web_server_v1.cpp +21 -19
- esphome/components/web_server_base/__init__.py +5 -2
- esphome/components/web_server_base/web_server_base.h +27 -7
- esphome/components/web_server_idf/__init__.py +1 -1
- esphome/components/web_server_idf/multipart.cpp +2 -2
- esphome/components/web_server_idf/multipart.h +2 -2
- esphome/components/web_server_idf/utils.cpp +2 -2
- esphome/components/web_server_idf/utils.h +2 -2
- esphome/components/web_server_idf/web_server_idf.cpp +118 -26
- esphome/components/web_server_idf/web_server_idf.h +12 -10
- esphome/components/wifi/__init__.py +13 -11
- esphome/components/wifi/wifi_component.cpp +74 -56
- esphome/components/wifi/wifi_component.h +4 -4
- esphome/components/wifi/wifi_component_esp8266.cpp +1 -1
- esphome/components/wifi/wifi_component_esp_idf.cpp +24 -4
- esphome/components/wireguard/__init__.py +1 -1
- esphome/components/wts01/__init__.py +0 -0
- esphome/components/wts01/sensor.py +41 -0
- esphome/components/wts01/wts01.cpp +91 -0
- esphome/components/wts01/wts01.h +27 -0
- esphome/components/zephyr/__init__.py +5 -5
- esphome/components/zwave_proxy/__init__.py +43 -0
- esphome/components/zwave_proxy/zwave_proxy.cpp +346 -0
- esphome/components/zwave_proxy/zwave_proxy.h +93 -0
- esphome/config.py +79 -24
- esphome/config_validation.py +13 -15
- esphome/const.py +9 -2
- esphome/core/__init__.py +33 -22
- esphome/core/component.cpp +28 -18
- esphome/core/component_iterator.h +2 -1
- esphome/core/config.py +15 -15
- esphome/core/defines.h +21 -0
- esphome/core/entity_helpers.py +9 -6
- esphome/core/hash_base.h +56 -0
- esphome/core/helpers.cpp +19 -3
- esphome/core/helpers.h +26 -0
- esphome/core/scheduler.cpp +5 -21
- esphome/core/scheduler.h +19 -8
- esphome/core/string_ref.h +1 -1
- esphome/core/time.cpp +5 -5
- esphome/cpp_generator.py +4 -29
- esphome/dashboard/const.py +21 -4
- esphome/dashboard/core.py +10 -8
- esphome/dashboard/dns.py +15 -0
- esphome/dashboard/entries.py +15 -21
- esphome/dashboard/models.py +76 -0
- esphome/dashboard/settings.py +7 -7
- esphome/dashboard/status/mdns.py +46 -2
- esphome/dashboard/web_server.py +367 -93
- esphome/espota2.py +112 -32
- esphome/external_files.py +6 -7
- esphome/git.py +8 -0
- esphome/helpers.py +124 -77
- esphome/loader.py +8 -9
- esphome/pins.py +2 -2
- esphome/platformio_api.py +56 -18
- esphome/storage_json.py +26 -21
- esphome/types.py +30 -2
- esphome/util.py +32 -16
- esphome/vscode.py +8 -8
- esphome/wizard.py +10 -10
- esphome/writer.py +50 -15
- esphome/yaml_util.py +37 -31
- esphome/zeroconf.py +12 -3
- {esphome-2025.9.3.dist-info → esphome-2025.10.0b2.dist-info}/METADATA +12 -12
- {esphome-2025.9.3.dist-info → esphome-2025.10.0b2.dist-info}/RECORD +340 -320
- esphome/components/event_emitter/__init__.py +0 -5
- esphome/components/event_emitter/event_emitter.cpp +0 -14
- esphome/components/event_emitter/event_emitter.h +0 -63
- esphome/components/remote_receiver/remote_receiver_libretiny.cpp +0 -125
- esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +0 -107
- esphome/components/remote_transmitter/remote_transmitter_libretiny.cpp +0 -110
- esphome/components/uart/uart_component_esp32_arduino.cpp +0 -214
- esphome/components/uart/uart_component_esp32_arduino.h +0 -60
- esphome/components/wifi/wifi_component_esp32_arduino.cpp +0 -860
- esphome/core/string_ref.cpp +0 -12
- esphome/dashboard/util/file.py +0 -63
- {esphome-2025.9.3.dist-info → esphome-2025.10.0b2.dist-info}/WHEEL +0 -0
- {esphome-2025.9.3.dist-info → esphome-2025.10.0b2.dist-info}/entry_points.txt +0 -0
- {esphome-2025.9.3.dist-info → esphome-2025.10.0b2.dist-info}/licenses/LICENSE +0 -0
- {esphome-2025.9.3.dist-info → esphome-2025.10.0b2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,346 @@
|
|
1
|
+
#include "zwave_proxy.h"
|
2
|
+
#include "esphome/components/api/api_server.h"
|
3
|
+
#include "esphome/core/application.h"
|
4
|
+
#include "esphome/core/helpers.h"
|
5
|
+
#include "esphome/core/log.h"
|
6
|
+
#include "esphome/core/util.h"
|
7
|
+
|
8
|
+
namespace esphome {
|
9
|
+
namespace zwave_proxy {
|
10
|
+
|
11
|
+
static const char *const TAG = "zwave_proxy";
|
12
|
+
|
13
|
+
static constexpr uint8_t ZWAVE_COMMAND_GET_NETWORK_IDS = 0x20;
|
14
|
+
// GET_NETWORK_IDS response: [SOF][LENGTH][TYPE][CMD][HOME_ID(4)][NODE_ID][...]
|
15
|
+
static constexpr uint8_t ZWAVE_COMMAND_TYPE_RESPONSE = 0x01; // Response type field value
|
16
|
+
static constexpr uint8_t ZWAVE_MIN_GET_NETWORK_IDS_LENGTH = 9; // TYPE + CMD + HOME_ID(4) + NODE_ID + checksum
|
17
|
+
static constexpr uint32_t HOME_ID_TIMEOUT_MS = 100; // Timeout for waiting for home ID during setup
|
18
|
+
|
19
|
+
static uint8_t calculate_frame_checksum(const uint8_t *data, uint8_t length) {
|
20
|
+
// Calculate Z-Wave frame checksum
|
21
|
+
// XOR all bytes between SOF and checksum position (exclusive)
|
22
|
+
// Initial value is 0xFF per Z-Wave protocol specification
|
23
|
+
uint8_t checksum = 0xFF;
|
24
|
+
for (uint8_t i = 1; i < length - 1; i++) {
|
25
|
+
checksum ^= data[i];
|
26
|
+
}
|
27
|
+
return checksum;
|
28
|
+
}
|
29
|
+
|
30
|
+
ZWaveProxy::ZWaveProxy() { global_zwave_proxy = this; }
|
31
|
+
|
32
|
+
void ZWaveProxy::setup() {
|
33
|
+
this->setup_time_ = App.get_loop_component_start_time();
|
34
|
+
this->send_simple_command_(ZWAVE_COMMAND_GET_NETWORK_IDS);
|
35
|
+
}
|
36
|
+
|
37
|
+
float ZWaveProxy::get_setup_priority() const {
|
38
|
+
// Set up before API so home ID is ready when API starts
|
39
|
+
return setup_priority::BEFORE_CONNECTION;
|
40
|
+
}
|
41
|
+
|
42
|
+
bool ZWaveProxy::can_proceed() {
|
43
|
+
// If we already have the home ID, we can proceed
|
44
|
+
if (this->home_id_ready_) {
|
45
|
+
return true;
|
46
|
+
}
|
47
|
+
|
48
|
+
// Handle any pending responses
|
49
|
+
if (this->response_handler_()) {
|
50
|
+
ESP_LOGV(TAG, "Handled response during setup");
|
51
|
+
}
|
52
|
+
|
53
|
+
// Process UART data to check for home ID
|
54
|
+
this->process_uart_();
|
55
|
+
|
56
|
+
// Check if we got the home ID after processing
|
57
|
+
if (this->home_id_ready_) {
|
58
|
+
return true;
|
59
|
+
}
|
60
|
+
|
61
|
+
// Wait up to HOME_ID_TIMEOUT_MS for home ID response
|
62
|
+
const uint32_t now = App.get_loop_component_start_time();
|
63
|
+
if (now - this->setup_time_ > HOME_ID_TIMEOUT_MS) {
|
64
|
+
ESP_LOGW(TAG, "Timeout reading Home ID during setup");
|
65
|
+
return true; // Proceed anyway after timeout
|
66
|
+
}
|
67
|
+
|
68
|
+
return false; // Keep waiting
|
69
|
+
}
|
70
|
+
|
71
|
+
void ZWaveProxy::loop() {
|
72
|
+
if (this->response_handler_()) {
|
73
|
+
ESP_LOGV(TAG, "Handled late response");
|
74
|
+
}
|
75
|
+
if (this->api_connection_ != nullptr && (!this->api_connection_->is_connection_setup() || !api_is_connected())) {
|
76
|
+
ESP_LOGW(TAG, "Subscriber disconnected");
|
77
|
+
this->api_connection_ = nullptr; // Unsubscribe if disconnected
|
78
|
+
}
|
79
|
+
|
80
|
+
this->process_uart_();
|
81
|
+
this->status_clear_warning();
|
82
|
+
}
|
83
|
+
|
84
|
+
void ZWaveProxy::process_uart_() {
|
85
|
+
while (this->available()) {
|
86
|
+
uint8_t byte;
|
87
|
+
if (!this->read_byte(&byte)) {
|
88
|
+
this->status_set_warning("UART read failed");
|
89
|
+
return;
|
90
|
+
}
|
91
|
+
if (this->parse_byte_(byte)) {
|
92
|
+
// Check if this is a GET_NETWORK_IDS response frame
|
93
|
+
// Frame format: [SOF][LENGTH][TYPE][CMD][HOME_ID(4)][NODE_ID][...]
|
94
|
+
// We verify:
|
95
|
+
// - buffer_[0]: Start of frame marker (0x01)
|
96
|
+
// - buffer_[1]: Length field must be >= 9 to contain all required data
|
97
|
+
// - buffer_[2]: Command type (0x01 for response)
|
98
|
+
// - buffer_[3]: Command ID (0x20 for GET_NETWORK_IDS)
|
99
|
+
if (this->buffer_[3] == ZWAVE_COMMAND_GET_NETWORK_IDS && this->buffer_[2] == ZWAVE_COMMAND_TYPE_RESPONSE &&
|
100
|
+
this->buffer_[1] >= ZWAVE_MIN_GET_NETWORK_IDS_LENGTH && this->buffer_[0] == ZWAVE_FRAME_TYPE_START) {
|
101
|
+
// Store the 4-byte Home ID, which starts at offset 4, and notify connected clients if it changed
|
102
|
+
// The frame parser has already validated the checksum and ensured all bytes are present
|
103
|
+
if (this->set_home_id(&this->buffer_[4])) {
|
104
|
+
this->send_homeid_changed_msg_();
|
105
|
+
}
|
106
|
+
}
|
107
|
+
ESP_LOGV(TAG, "Sending to client: %s", YESNO(this->api_connection_ != nullptr));
|
108
|
+
if (this->api_connection_ != nullptr) {
|
109
|
+
// Zero-copy: point directly to our buffer
|
110
|
+
this->outgoing_proto_msg_.data = this->buffer_.data();
|
111
|
+
if (this->in_bootloader_) {
|
112
|
+
this->outgoing_proto_msg_.data_len = this->buffer_index_;
|
113
|
+
} else {
|
114
|
+
// If this is a data frame, use frame length indicator + 2 (for SoF + checksum), else assume 1 for ACK/NAK/CAN
|
115
|
+
this->outgoing_proto_msg_.data_len = this->buffer_[0] == ZWAVE_FRAME_TYPE_START ? this->buffer_[1] + 2 : 1;
|
116
|
+
}
|
117
|
+
this->api_connection_->send_message(this->outgoing_proto_msg_, api::ZWaveProxyFrame::MESSAGE_TYPE);
|
118
|
+
}
|
119
|
+
}
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
123
|
+
void ZWaveProxy::dump_config() {
|
124
|
+
ESP_LOGCONFIG(TAG,
|
125
|
+
"Z-Wave Proxy:\n"
|
126
|
+
" Home ID: %s",
|
127
|
+
format_hex_pretty(this->home_id_.data(), this->home_id_.size(), ':', false).c_str());
|
128
|
+
}
|
129
|
+
|
130
|
+
void ZWaveProxy::api_connection_authenticated(api::APIConnection *conn) {
|
131
|
+
if (this->home_id_ready_) {
|
132
|
+
// If a client just authenticated & HomeID is ready, send the current HomeID
|
133
|
+
this->send_homeid_changed_msg_(conn);
|
134
|
+
}
|
135
|
+
}
|
136
|
+
|
137
|
+
void ZWaveProxy::zwave_proxy_request(api::APIConnection *api_connection, api::enums::ZWaveProxyRequestType type) {
|
138
|
+
switch (type) {
|
139
|
+
case api::enums::ZWAVE_PROXY_REQUEST_TYPE_SUBSCRIBE:
|
140
|
+
if (this->api_connection_ != nullptr) {
|
141
|
+
ESP_LOGE(TAG, "Only one API subscription is allowed at a time");
|
142
|
+
return;
|
143
|
+
}
|
144
|
+
this->api_connection_ = api_connection;
|
145
|
+
ESP_LOGV(TAG, "API connection is now subscribed");
|
146
|
+
break;
|
147
|
+
case api::enums::ZWAVE_PROXY_REQUEST_TYPE_UNSUBSCRIBE:
|
148
|
+
if (this->api_connection_ != api_connection) {
|
149
|
+
ESP_LOGV(TAG, "API connection is not subscribed");
|
150
|
+
return;
|
151
|
+
}
|
152
|
+
this->api_connection_ = nullptr;
|
153
|
+
break;
|
154
|
+
default:
|
155
|
+
ESP_LOGW(TAG, "Unknown request type: %d", type);
|
156
|
+
break;
|
157
|
+
}
|
158
|
+
}
|
159
|
+
|
160
|
+
bool ZWaveProxy::set_home_id(const uint8_t *new_home_id) {
|
161
|
+
if (std::memcmp(this->home_id_.data(), new_home_id, this->home_id_.size()) == 0) {
|
162
|
+
ESP_LOGV(TAG, "Home ID unchanged");
|
163
|
+
return false; // No change
|
164
|
+
}
|
165
|
+
std::memcpy(this->home_id_.data(), new_home_id, this->home_id_.size());
|
166
|
+
ESP_LOGI(TAG, "Home ID: %s", format_hex_pretty(this->home_id_.data(), this->home_id_.size(), ':', false).c_str());
|
167
|
+
this->home_id_ready_ = true;
|
168
|
+
return true; // Home ID was changed
|
169
|
+
}
|
170
|
+
|
171
|
+
void ZWaveProxy::send_frame(const uint8_t *data, size_t length) {
|
172
|
+
if (length == 1 && data[0] == this->last_response_) {
|
173
|
+
ESP_LOGV(TAG, "Skipping sending duplicate response: 0x%02X", data[0]);
|
174
|
+
return;
|
175
|
+
}
|
176
|
+
ESP_LOGVV(TAG, "Sending: %s", format_hex_pretty(data, length).c_str());
|
177
|
+
this->write_array(data, length);
|
178
|
+
}
|
179
|
+
|
180
|
+
void ZWaveProxy::send_homeid_changed_msg_(api::APIConnection *conn) {
|
181
|
+
api::ZWaveProxyRequest msg;
|
182
|
+
msg.type = api::enums::ZWAVE_PROXY_REQUEST_TYPE_HOME_ID_CHANGE;
|
183
|
+
msg.data = this->home_id_.data();
|
184
|
+
msg.data_len = this->home_id_.size();
|
185
|
+
if (conn != nullptr) {
|
186
|
+
// Send to specific connection
|
187
|
+
conn->send_message(msg, api::ZWaveProxyRequest::MESSAGE_TYPE);
|
188
|
+
} else if (api::global_api_server != nullptr) {
|
189
|
+
// We could add code to manage a second subscription type, but, since this message is
|
190
|
+
// very infrequent and small, we simply send it to all clients
|
191
|
+
api::global_api_server->on_zwave_proxy_request(msg);
|
192
|
+
}
|
193
|
+
}
|
194
|
+
|
195
|
+
void ZWaveProxy::send_simple_command_(const uint8_t command_id) {
|
196
|
+
// Send a simple Z-Wave command with no parameters
|
197
|
+
// Frame format: [SOF][LENGTH][TYPE][CMD][CHECKSUM]
|
198
|
+
// Where LENGTH=0x03 (3 bytes: TYPE + CMD + CHECKSUM)
|
199
|
+
uint8_t cmd[] = {0x01, 0x03, 0x00, command_id, 0x00};
|
200
|
+
cmd[4] = calculate_frame_checksum(cmd, sizeof(cmd));
|
201
|
+
this->send_frame(cmd, sizeof(cmd));
|
202
|
+
}
|
203
|
+
|
204
|
+
bool ZWaveProxy::parse_byte_(uint8_t byte) {
|
205
|
+
bool frame_completed = false;
|
206
|
+
// Basic parsing logic for received frames
|
207
|
+
switch (this->parsing_state_) {
|
208
|
+
case ZWAVE_PARSING_STATE_WAIT_START:
|
209
|
+
this->parse_start_(byte);
|
210
|
+
break;
|
211
|
+
case ZWAVE_PARSING_STATE_WAIT_LENGTH:
|
212
|
+
if (!byte) {
|
213
|
+
ESP_LOGW(TAG, "Invalid LENGTH: %u", byte);
|
214
|
+
this->parsing_state_ = ZWAVE_PARSING_STATE_SEND_NAK;
|
215
|
+
return false;
|
216
|
+
}
|
217
|
+
ESP_LOGVV(TAG, "Received LENGTH: %u", byte);
|
218
|
+
this->end_frame_after_ = this->buffer_index_ + byte;
|
219
|
+
ESP_LOGVV(TAG, "Calculated EOF: %u", this->end_frame_after_);
|
220
|
+
this->buffer_[this->buffer_index_++] = byte;
|
221
|
+
this->parsing_state_ = ZWAVE_PARSING_STATE_WAIT_TYPE;
|
222
|
+
break;
|
223
|
+
case ZWAVE_PARSING_STATE_WAIT_TYPE:
|
224
|
+
this->buffer_[this->buffer_index_++] = byte;
|
225
|
+
ESP_LOGVV(TAG, "Received TYPE: 0x%02X", byte);
|
226
|
+
this->parsing_state_ = ZWAVE_PARSING_STATE_WAIT_COMMAND_ID;
|
227
|
+
break;
|
228
|
+
case ZWAVE_PARSING_STATE_WAIT_COMMAND_ID:
|
229
|
+
this->buffer_[this->buffer_index_++] = byte;
|
230
|
+
ESP_LOGVV(TAG, "Received COMMAND ID: 0x%02X", byte);
|
231
|
+
this->parsing_state_ = ZWAVE_PARSING_STATE_WAIT_PAYLOAD;
|
232
|
+
break;
|
233
|
+
case ZWAVE_PARSING_STATE_WAIT_PAYLOAD:
|
234
|
+
this->buffer_[this->buffer_index_++] = byte;
|
235
|
+
ESP_LOGVV(TAG, "Received PAYLOAD: 0x%02X", byte);
|
236
|
+
if (this->buffer_index_ >= this->end_frame_after_) {
|
237
|
+
this->parsing_state_ = ZWAVE_PARSING_STATE_WAIT_CHECKSUM;
|
238
|
+
}
|
239
|
+
break;
|
240
|
+
case ZWAVE_PARSING_STATE_WAIT_CHECKSUM: {
|
241
|
+
this->buffer_[this->buffer_index_++] = byte;
|
242
|
+
auto checksum = calculate_frame_checksum(this->buffer_.data(), this->buffer_index_);
|
243
|
+
ESP_LOGVV(TAG, "CHECKSUM Received: 0x%02X - Calculated: 0x%02X", byte, checksum);
|
244
|
+
if (checksum != byte) {
|
245
|
+
ESP_LOGW(TAG, "Bad checksum: expected 0x%02X, got 0x%02X", checksum, byte);
|
246
|
+
this->parsing_state_ = ZWAVE_PARSING_STATE_SEND_NAK;
|
247
|
+
} else {
|
248
|
+
this->parsing_state_ = ZWAVE_PARSING_STATE_SEND_ACK;
|
249
|
+
ESP_LOGVV(TAG, "Received frame: %s", format_hex_pretty(this->buffer_.data(), this->buffer_index_).c_str());
|
250
|
+
frame_completed = true;
|
251
|
+
}
|
252
|
+
this->response_handler_();
|
253
|
+
break;
|
254
|
+
}
|
255
|
+
case ZWAVE_PARSING_STATE_READ_BL_MENU:
|
256
|
+
this->buffer_[this->buffer_index_++] = byte;
|
257
|
+
if (!byte) {
|
258
|
+
this->parsing_state_ = ZWAVE_PARSING_STATE_WAIT_START;
|
259
|
+
frame_completed = true;
|
260
|
+
}
|
261
|
+
break;
|
262
|
+
case ZWAVE_PARSING_STATE_SEND_ACK:
|
263
|
+
case ZWAVE_PARSING_STATE_SEND_NAK:
|
264
|
+
break; // Should not happen, handled in loop()
|
265
|
+
default:
|
266
|
+
ESP_LOGW(TAG, "Bad parsing state; resetting");
|
267
|
+
this->parsing_state_ = ZWAVE_PARSING_STATE_WAIT_START;
|
268
|
+
break;
|
269
|
+
}
|
270
|
+
return frame_completed;
|
271
|
+
}
|
272
|
+
|
273
|
+
void ZWaveProxy::parse_start_(uint8_t byte) {
|
274
|
+
this->buffer_index_ = 0;
|
275
|
+
this->parsing_state_ = ZWAVE_PARSING_STATE_WAIT_START;
|
276
|
+
switch (byte) {
|
277
|
+
case ZWAVE_FRAME_TYPE_START:
|
278
|
+
ESP_LOGVV(TAG, "Received START");
|
279
|
+
if (this->in_bootloader_) {
|
280
|
+
ESP_LOGD(TAG, "Exited bootloader mode");
|
281
|
+
this->in_bootloader_ = false;
|
282
|
+
}
|
283
|
+
this->buffer_[this->buffer_index_++] = byte;
|
284
|
+
this->parsing_state_ = ZWAVE_PARSING_STATE_WAIT_LENGTH;
|
285
|
+
return;
|
286
|
+
case ZWAVE_FRAME_TYPE_BL_MENU:
|
287
|
+
ESP_LOGVV(TAG, "Received BL_MENU");
|
288
|
+
if (!this->in_bootloader_) {
|
289
|
+
ESP_LOGD(TAG, "Entered bootloader mode");
|
290
|
+
this->in_bootloader_ = true;
|
291
|
+
}
|
292
|
+
this->buffer_[this->buffer_index_++] = byte;
|
293
|
+
this->parsing_state_ = ZWAVE_PARSING_STATE_READ_BL_MENU;
|
294
|
+
return;
|
295
|
+
case ZWAVE_FRAME_TYPE_BL_BEGIN_UPLOAD:
|
296
|
+
ESP_LOGVV(TAG, "Received BL_BEGIN_UPLOAD");
|
297
|
+
break;
|
298
|
+
case ZWAVE_FRAME_TYPE_ACK:
|
299
|
+
ESP_LOGVV(TAG, "Received ACK");
|
300
|
+
break;
|
301
|
+
case ZWAVE_FRAME_TYPE_NAK:
|
302
|
+
ESP_LOGW(TAG, "Received NAK");
|
303
|
+
break;
|
304
|
+
case ZWAVE_FRAME_TYPE_CAN:
|
305
|
+
ESP_LOGW(TAG, "Received CAN");
|
306
|
+
break;
|
307
|
+
default:
|
308
|
+
ESP_LOGW(TAG, "Unrecognized START: 0x%02X", byte);
|
309
|
+
return;
|
310
|
+
}
|
311
|
+
// Forward response (ACK/NAK/CAN) back to client for processing
|
312
|
+
if (this->api_connection_ != nullptr) {
|
313
|
+
// Store single byte in buffer and point to it
|
314
|
+
this->buffer_[0] = byte;
|
315
|
+
this->outgoing_proto_msg_.data = this->buffer_.data();
|
316
|
+
this->outgoing_proto_msg_.data_len = 1;
|
317
|
+
this->api_connection_->send_message(this->outgoing_proto_msg_, api::ZWaveProxyFrame::MESSAGE_TYPE);
|
318
|
+
}
|
319
|
+
}
|
320
|
+
|
321
|
+
bool ZWaveProxy::response_handler_() {
|
322
|
+
switch (this->parsing_state_) {
|
323
|
+
case ZWAVE_PARSING_STATE_SEND_ACK:
|
324
|
+
this->last_response_ = ZWAVE_FRAME_TYPE_ACK;
|
325
|
+
break;
|
326
|
+
case ZWAVE_PARSING_STATE_SEND_CAN:
|
327
|
+
this->last_response_ = ZWAVE_FRAME_TYPE_CAN;
|
328
|
+
break;
|
329
|
+
case ZWAVE_PARSING_STATE_SEND_NAK:
|
330
|
+
this->last_response_ = ZWAVE_FRAME_TYPE_NAK;
|
331
|
+
break;
|
332
|
+
default:
|
333
|
+
return false; // No response handled
|
334
|
+
}
|
335
|
+
|
336
|
+
ESP_LOGVV(TAG, "Sending %s (0x%02X)", this->last_response_ == ZWAVE_FRAME_TYPE_ACK ? "ACK" : "NAK/CAN",
|
337
|
+
this->last_response_);
|
338
|
+
this->write_byte(this->last_response_);
|
339
|
+
this->parsing_state_ = ZWAVE_PARSING_STATE_WAIT_START;
|
340
|
+
return true;
|
341
|
+
}
|
342
|
+
|
343
|
+
ZWaveProxy *global_zwave_proxy = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
344
|
+
|
345
|
+
} // namespace zwave_proxy
|
346
|
+
} // namespace esphome
|
@@ -0,0 +1,93 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include "esphome/components/api/api_connection.h"
|
4
|
+
#include "esphome/components/api/api_pb2.h"
|
5
|
+
#include "esphome/core/component.h"
|
6
|
+
#include "esphome/core/helpers.h"
|
7
|
+
#include "esphome/components/uart/uart.h"
|
8
|
+
|
9
|
+
#include <array>
|
10
|
+
|
11
|
+
namespace esphome {
|
12
|
+
namespace zwave_proxy {
|
13
|
+
|
14
|
+
static constexpr size_t MAX_ZWAVE_FRAME_SIZE = 257; // Maximum Z-Wave frame size
|
15
|
+
|
16
|
+
enum ZWaveResponseTypes : uint8_t {
|
17
|
+
ZWAVE_FRAME_TYPE_ACK = 0x06,
|
18
|
+
ZWAVE_FRAME_TYPE_CAN = 0x18,
|
19
|
+
ZWAVE_FRAME_TYPE_NAK = 0x15,
|
20
|
+
ZWAVE_FRAME_TYPE_START = 0x01,
|
21
|
+
ZWAVE_FRAME_TYPE_BL_MENU = 0x0D,
|
22
|
+
ZWAVE_FRAME_TYPE_BL_BEGIN_UPLOAD = 0x43,
|
23
|
+
};
|
24
|
+
|
25
|
+
enum ZWaveParsingState : uint8_t {
|
26
|
+
ZWAVE_PARSING_STATE_WAIT_START,
|
27
|
+
ZWAVE_PARSING_STATE_WAIT_LENGTH,
|
28
|
+
ZWAVE_PARSING_STATE_WAIT_TYPE,
|
29
|
+
ZWAVE_PARSING_STATE_WAIT_COMMAND_ID,
|
30
|
+
ZWAVE_PARSING_STATE_WAIT_PAYLOAD,
|
31
|
+
ZWAVE_PARSING_STATE_WAIT_CHECKSUM,
|
32
|
+
ZWAVE_PARSING_STATE_SEND_ACK,
|
33
|
+
ZWAVE_PARSING_STATE_SEND_CAN,
|
34
|
+
ZWAVE_PARSING_STATE_SEND_NAK,
|
35
|
+
ZWAVE_PARSING_STATE_READ_BL_MENU,
|
36
|
+
};
|
37
|
+
|
38
|
+
enum ZWaveProxyFeature : uint32_t {
|
39
|
+
FEATURE_ZWAVE_PROXY_ENABLED = 1 << 0,
|
40
|
+
};
|
41
|
+
|
42
|
+
class ZWaveProxy : public uart::UARTDevice, public Component {
|
43
|
+
public:
|
44
|
+
ZWaveProxy();
|
45
|
+
|
46
|
+
void setup() override;
|
47
|
+
void loop() override;
|
48
|
+
void dump_config() override;
|
49
|
+
float get_setup_priority() const override;
|
50
|
+
bool can_proceed() override;
|
51
|
+
|
52
|
+
void api_connection_authenticated(api::APIConnection *conn);
|
53
|
+
void zwave_proxy_request(api::APIConnection *api_connection, api::enums::ZWaveProxyRequestType type);
|
54
|
+
api::APIConnection *get_api_connection() { return this->api_connection_; }
|
55
|
+
|
56
|
+
uint32_t get_feature_flags() const { return ZWaveProxyFeature::FEATURE_ZWAVE_PROXY_ENABLED; }
|
57
|
+
uint32_t get_home_id() {
|
58
|
+
return encode_uint32(this->home_id_[0], this->home_id_[1], this->home_id_[2], this->home_id_[3]);
|
59
|
+
}
|
60
|
+
bool set_home_id(const uint8_t *new_home_id); // Store a new home ID. Returns true if it changed.
|
61
|
+
|
62
|
+
void send_frame(const uint8_t *data, size_t length);
|
63
|
+
|
64
|
+
protected:
|
65
|
+
void send_homeid_changed_msg_(api::APIConnection *conn = nullptr);
|
66
|
+
void send_simple_command_(uint8_t command_id);
|
67
|
+
bool parse_byte_(uint8_t byte); // Returns true if frame parsing was completed (a frame is ready in the buffer)
|
68
|
+
void parse_start_(uint8_t byte);
|
69
|
+
bool response_handler_();
|
70
|
+
void process_uart_(); // Process all available UART data
|
71
|
+
|
72
|
+
// Pre-allocated message - always ready to send
|
73
|
+
api::ZWaveProxyFrame outgoing_proto_msg_;
|
74
|
+
std::array<uint8_t, MAX_ZWAVE_FRAME_SIZE> buffer_; // Fixed buffer for incoming data
|
75
|
+
std::array<uint8_t, 4> home_id_{0, 0, 0, 0}; // Fixed buffer for home ID
|
76
|
+
|
77
|
+
// Pointers and 32-bit values (aligned together)
|
78
|
+
api::APIConnection *api_connection_{nullptr}; // Current subscribed client
|
79
|
+
uint32_t setup_time_{0}; // Time when setup() was called
|
80
|
+
|
81
|
+
// 8-bit values (grouped together to minimize padding)
|
82
|
+
uint8_t buffer_index_{0}; // Index for populating the data buffer
|
83
|
+
uint8_t end_frame_after_{0}; // Payload reception ends after this index
|
84
|
+
uint8_t last_response_{0}; // Last response type sent
|
85
|
+
ZWaveParsingState parsing_state_{ZWAVE_PARSING_STATE_WAIT_START};
|
86
|
+
bool in_bootloader_{false}; // True if the device is detected to be in bootloader mode
|
87
|
+
bool home_id_ready_{false}; // True when home ID has been received from Z-Wave module
|
88
|
+
};
|
89
|
+
|
90
|
+
extern ZWaveProxy *global_zwave_proxy; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
91
|
+
|
92
|
+
} // namespace zwave_proxy
|
93
|
+
} // namespace esphome
|
esphome/config.py
CHANGED
@@ -32,7 +32,7 @@ from esphome.log import AnsiFore, color
|
|
32
32
|
from esphome.types import ConfigFragmentType, ConfigType
|
33
33
|
from esphome.util import OrderedDict, safe_print
|
34
34
|
from esphome.voluptuous_schema import ExtraKeysInvalid
|
35
|
-
from esphome.yaml_util import
|
35
|
+
from esphome.yaml_util import ESPHomeDataBase, ESPLiteralValue, is_secret
|
36
36
|
|
37
37
|
_LOGGER = logging.getLogger(__name__)
|
38
38
|
|
@@ -67,6 +67,31 @@ ConfigPath = list[str | int]
|
|
67
67
|
path_context = contextvars.ContextVar("Config path")
|
68
68
|
|
69
69
|
|
70
|
+
def _add_auto_load_steps(result: Config, loads: list[str]) -> None:
|
71
|
+
"""Add AutoLoadValidationStep for each component in loads that isn't already loaded."""
|
72
|
+
for load in loads:
|
73
|
+
if load not in result:
|
74
|
+
result.add_validation_step(AutoLoadValidationStep(load))
|
75
|
+
|
76
|
+
|
77
|
+
def _process_auto_load(
|
78
|
+
result: Config, platform: ComponentManifest, path: ConfigPath
|
79
|
+
) -> None:
|
80
|
+
# Process platform's AUTO_LOAD
|
81
|
+
auto_load = platform.auto_load
|
82
|
+
if isinstance(auto_load, list):
|
83
|
+
_add_auto_load_steps(result, auto_load)
|
84
|
+
elif callable(auto_load):
|
85
|
+
import inspect
|
86
|
+
|
87
|
+
if inspect.signature(auto_load).parameters:
|
88
|
+
result.add_validation_step(
|
89
|
+
AddDynamicAutoLoadsValidationStep(path, platform)
|
90
|
+
)
|
91
|
+
else:
|
92
|
+
_add_auto_load_steps(result, auto_load())
|
93
|
+
|
94
|
+
|
70
95
|
def _process_platform_config(
|
71
96
|
result: Config,
|
72
97
|
component_name: str,
|
@@ -91,9 +116,7 @@ def _process_platform_config(
|
|
91
116
|
CORE.loaded_platforms.add(f"{component_name}/{platform_name}")
|
92
117
|
|
93
118
|
# Process platform's AUTO_LOAD
|
94
|
-
|
95
|
-
if load not in result:
|
96
|
-
result.add_validation_step(AutoLoadValidationStep(load))
|
119
|
+
_process_auto_load(result, platform, path)
|
97
120
|
|
98
121
|
# Add validation steps for the platform
|
99
122
|
p_domain = f"{component_name}.{platform_name}"
|
@@ -306,7 +329,7 @@ def recursive_check_replaceme(value):
|
|
306
329
|
return cv.Schema([recursive_check_replaceme])(value)
|
307
330
|
if isinstance(value, dict):
|
308
331
|
return cv.Schema({cv.valid: recursive_check_replaceme})(value)
|
309
|
-
if isinstance(value,
|
332
|
+
if isinstance(value, ESPLiteralValue):
|
310
333
|
pass
|
311
334
|
if isinstance(value, str) and value == "REPLACEME":
|
312
335
|
raise cv.Invalid(
|
@@ -314,7 +337,7 @@ def recursive_check_replaceme(value):
|
|
314
337
|
"Please make sure you have replaced all fields from the sample "
|
315
338
|
"configuration.\n"
|
316
339
|
"If you want to use the literal REPLACEME string, "
|
317
|
-
'please use "!
|
340
|
+
'please use "!literal REPLACEME"'
|
318
341
|
)
|
319
342
|
return value
|
320
343
|
|
@@ -382,11 +405,15 @@ class LoadValidationStep(ConfigValidationStep):
|
|
382
405
|
result.add_str_error(f"Component not found: {self.domain}", path)
|
383
406
|
return
|
384
407
|
CORE.loaded_integrations.add(self.domain)
|
408
|
+
# For platform components, normalize conf before creating MetadataValidationStep
|
409
|
+
if component.is_platform_component:
|
410
|
+
if not self.conf:
|
411
|
+
result[self.domain] = self.conf = []
|
412
|
+
elif not isinstance(self.conf, list):
|
413
|
+
result[self.domain] = self.conf = [self.conf]
|
385
414
|
|
386
415
|
# Process AUTO_LOAD
|
387
|
-
|
388
|
-
if load not in result:
|
389
|
-
result.add_validation_step(AutoLoadValidationStep(load))
|
416
|
+
_process_auto_load(result, component, path)
|
390
417
|
|
391
418
|
result.add_validation_step(
|
392
419
|
MetadataValidationStep([self.domain], self.domain, self.conf, component)
|
@@ -399,12 +426,6 @@ class LoadValidationStep(ConfigValidationStep):
|
|
399
426
|
# Remove this is as an output path
|
400
427
|
result.remove_output_path([self.domain], self.domain)
|
401
428
|
|
402
|
-
# Ensure conf is a list
|
403
|
-
if not self.conf:
|
404
|
-
result[self.domain] = self.conf = []
|
405
|
-
elif not isinstance(self.conf, list):
|
406
|
-
result[self.domain] = self.conf = [self.conf]
|
407
|
-
|
408
429
|
for i, p_config in enumerate(self.conf):
|
409
430
|
path = [self.domain, i]
|
410
431
|
# Construct temporary unknown output path
|
@@ -618,6 +639,34 @@ class MetadataValidationStep(ConfigValidationStep):
|
|
618
639
|
result.add_validation_step(FinalValidateValidationStep(self.path, self.comp))
|
619
640
|
|
620
641
|
|
642
|
+
class AddDynamicAutoLoadsValidationStep(ConfigValidationStep):
|
643
|
+
"""Add dynamic auto loads step.
|
644
|
+
|
645
|
+
This step is used to auto-load components where one component can alter its
|
646
|
+
AUTO_LOAD based on its configuration.
|
647
|
+
"""
|
648
|
+
|
649
|
+
# Has to happen after normal schema is validated and before final schema validation
|
650
|
+
priority = -5.0
|
651
|
+
|
652
|
+
def __init__(self, path: ConfigPath, comp: ComponentManifest) -> None:
|
653
|
+
self.path = path
|
654
|
+
self.comp = comp
|
655
|
+
|
656
|
+
def run(self, result: Config) -> None:
|
657
|
+
if result.errors:
|
658
|
+
# If result already has errors, skip this step
|
659
|
+
return
|
660
|
+
|
661
|
+
conf = result.get_nested_item(self.path)
|
662
|
+
with result.catch_error(self.path):
|
663
|
+
auto_load = self.comp.auto_load
|
664
|
+
if not callable(auto_load):
|
665
|
+
return
|
666
|
+
loads = auto_load(conf)
|
667
|
+
_add_auto_load_steps(result, loads)
|
668
|
+
|
669
|
+
|
621
670
|
class SchemaValidationStep(ConfigValidationStep):
|
622
671
|
"""Schema validation step.
|
623
672
|
|
@@ -846,7 +895,9 @@ class PinUseValidationCheck(ConfigValidationStep):
|
|
846
895
|
|
847
896
|
|
848
897
|
def validate_config(
|
849
|
-
config: dict[str, Any],
|
898
|
+
config: dict[str, Any],
|
899
|
+
command_line_substitutions: dict[str, Any],
|
900
|
+
skip_external_update: bool = False,
|
850
901
|
) -> Config:
|
851
902
|
result = Config()
|
852
903
|
|
@@ -859,7 +910,7 @@ def validate_config(
|
|
859
910
|
|
860
911
|
result.add_output_path([CONF_PACKAGES], CONF_PACKAGES)
|
861
912
|
try:
|
862
|
-
config = do_packages_pass(config)
|
913
|
+
config = do_packages_pass(config, skip_update=skip_external_update)
|
863
914
|
except vol.Invalid as err:
|
864
915
|
result.update(config)
|
865
916
|
result.add_error(err)
|
@@ -896,7 +947,7 @@ def validate_config(
|
|
896
947
|
|
897
948
|
result.add_output_path([CONF_EXTERNAL_COMPONENTS], CONF_EXTERNAL_COMPONENTS)
|
898
949
|
try:
|
899
|
-
do_external_components_pass(config)
|
950
|
+
do_external_components_pass(config, skip_update=skip_external_update)
|
900
951
|
except vol.Invalid as err:
|
901
952
|
result.update(config)
|
902
953
|
result.add_error(err)
|
@@ -1020,7 +1071,9 @@ class InvalidYAMLError(EsphomeError):
|
|
1020
1071
|
self.base_exc = base_exc
|
1021
1072
|
|
1022
1073
|
|
1023
|
-
def _load_config(
|
1074
|
+
def _load_config(
|
1075
|
+
command_line_substitutions: dict[str, Any], skip_external_update: bool = False
|
1076
|
+
) -> Config:
|
1024
1077
|
"""Load the configuration file."""
|
1025
1078
|
try:
|
1026
1079
|
config = yaml_util.load_yaml(CORE.config_path)
|
@@ -1028,7 +1081,7 @@ def _load_config(command_line_substitutions: dict[str, Any]) -> Config:
|
|
1028
1081
|
raise InvalidYAMLError(e) from e
|
1029
1082
|
|
1030
1083
|
try:
|
1031
|
-
return validate_config(config, command_line_substitutions)
|
1084
|
+
return validate_config(config, command_line_substitutions, skip_external_update)
|
1032
1085
|
except EsphomeError:
|
1033
1086
|
raise
|
1034
1087
|
except Exception:
|
@@ -1036,9 +1089,11 @@ def _load_config(command_line_substitutions: dict[str, Any]) -> Config:
|
|
1036
1089
|
raise
|
1037
1090
|
|
1038
1091
|
|
1039
|
-
def load_config(
|
1092
|
+
def load_config(
|
1093
|
+
command_line_substitutions: dict[str, Any], skip_external_update: bool = False
|
1094
|
+
) -> Config:
|
1040
1095
|
try:
|
1041
|
-
return _load_config(command_line_substitutions)
|
1096
|
+
return _load_config(command_line_substitutions, skip_external_update)
|
1042
1097
|
except vol.Invalid as err:
|
1043
1098
|
raise EsphomeError(f"Error while parsing config: {err}") from err
|
1044
1099
|
|
@@ -1178,10 +1233,10 @@ def strip_default_ids(config):
|
|
1178
1233
|
return config
|
1179
1234
|
|
1180
1235
|
|
1181
|
-
def read_config(command_line_substitutions):
|
1236
|
+
def read_config(command_line_substitutions, skip_external_update=False):
|
1182
1237
|
_LOGGER.info("Reading configuration %s...", CORE.config_path)
|
1183
1238
|
try:
|
1184
|
-
res = load_config(command_line_substitutions)
|
1239
|
+
res = load_config(command_line_substitutions, skip_external_update)
|
1185
1240
|
except EsphomeError as err:
|
1186
1241
|
_LOGGER.error("Error while reading config: %s", err)
|
1187
1242
|
return None
|