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
esphome/core/helpers.h
CHANGED
@@ -45,6 +45,9 @@
|
|
45
45
|
|
46
46
|
namespace esphome {
|
47
47
|
|
48
|
+
// Forward declaration to avoid circular dependency with string_ref.h
|
49
|
+
class StringRef;
|
50
|
+
|
48
51
|
/// @name STL backports
|
49
52
|
///@{
|
50
53
|
|
@@ -82,6 +85,16 @@ template<typename T> constexpr T byteswap(T n) {
|
|
82
85
|
return m;
|
83
86
|
}
|
84
87
|
template<> constexpr uint8_t byteswap(uint8_t n) { return n; }
|
88
|
+
#ifdef USE_LIBRETINY
|
89
|
+
// LibreTiny's Beken framework redefines __builtin_bswap functions as non-constexpr
|
90
|
+
template<> inline uint16_t byteswap(uint16_t n) { return __builtin_bswap16(n); }
|
91
|
+
template<> inline uint32_t byteswap(uint32_t n) { return __builtin_bswap32(n); }
|
92
|
+
template<> inline uint64_t byteswap(uint64_t n) { return __builtin_bswap64(n); }
|
93
|
+
template<> inline int8_t byteswap(int8_t n) { return n; }
|
94
|
+
template<> inline int16_t byteswap(int16_t n) { return __builtin_bswap16(n); }
|
95
|
+
template<> inline int32_t byteswap(int32_t n) { return __builtin_bswap32(n); }
|
96
|
+
template<> inline int64_t byteswap(int64_t n) { return __builtin_bswap64(n); }
|
97
|
+
#else
|
85
98
|
template<> constexpr uint16_t byteswap(uint16_t n) { return __builtin_bswap16(n); }
|
86
99
|
template<> constexpr uint32_t byteswap(uint32_t n) { return __builtin_bswap32(n); }
|
87
100
|
template<> constexpr uint64_t byteswap(uint64_t n) { return __builtin_bswap64(n); }
|
@@ -89,6 +102,7 @@ template<> constexpr int8_t byteswap(int8_t n) { return n; }
|
|
89
102
|
template<> constexpr int16_t byteswap(int16_t n) { return __builtin_bswap16(n); }
|
90
103
|
template<> constexpr int32_t byteswap(int32_t n) { return __builtin_bswap32(n); }
|
91
104
|
template<> constexpr int64_t byteswap(int64_t n) { return __builtin_bswap64(n); }
|
105
|
+
#endif
|
92
106
|
|
93
107
|
///@}
|
94
108
|
|
@@ -116,6 +130,16 @@ template<typename T, size_t N> class StaticVector {
|
|
116
130
|
}
|
117
131
|
}
|
118
132
|
|
133
|
+
// Return reference to next element and increment count (with bounds checking)
|
134
|
+
T &emplace_next() {
|
135
|
+
if (count_ >= N) {
|
136
|
+
// Should never happen with proper size calculation
|
137
|
+
// Return reference to last element to avoid crash
|
138
|
+
return data_[N - 1];
|
139
|
+
}
|
140
|
+
return data_[count_++];
|
141
|
+
}
|
142
|
+
|
119
143
|
size_t size() const { return count_; }
|
120
144
|
bool empty() const { return count_ == 0; }
|
121
145
|
|
@@ -589,6 +613,8 @@ ParseOnOffState parse_on_off(const char *str, const char *on = nullptr, const ch
|
|
589
613
|
|
590
614
|
/// Create a string from a value and an accuracy in decimals.
|
591
615
|
std::string value_accuracy_to_string(float value, int8_t accuracy_decimals);
|
616
|
+
/// Create a string from a value, an accuracy in decimals, and a unit of measurement.
|
617
|
+
std::string value_accuracy_with_uom_to_string(float value, int8_t accuracy_decimals, StringRef unit_of_measurement);
|
592
618
|
|
593
619
|
/// Derive accuracy in decimals from an increment step.
|
594
620
|
int8_t step_to_accuracy_decimals(float step);
|
esphome/core/scheduler.cpp
CHANGED
@@ -118,7 +118,6 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type
|
|
118
118
|
item->type = type;
|
119
119
|
item->callback = std::move(func);
|
120
120
|
// Initialize remove to false (though it should already be from constructor)
|
121
|
-
// Not using mark_item_removed_ helper since we're setting to false, not true
|
122
121
|
#ifdef ESPHOME_THREAD_MULTI_ATOMICS
|
123
122
|
item->remove.store(false, std::memory_order_relaxed);
|
124
123
|
#else
|
@@ -600,12 +599,7 @@ bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_c
|
|
600
599
|
#ifndef ESPHOME_THREAD_SINGLE
|
601
600
|
// Mark items in defer queue as cancelled (they'll be skipped when processed)
|
602
601
|
if (type == SchedulerItem::TIMEOUT) {
|
603
|
-
|
604
|
-
if (this->matches_item_(item, component, name_cstr, type, match_retry)) {
|
605
|
-
this->mark_item_removed_(item.get());
|
606
|
-
total_cancelled++;
|
607
|
-
}
|
608
|
-
}
|
602
|
+
total_cancelled += this->mark_matching_items_removed_(this->defer_queue_, component, name_cstr, type, match_retry);
|
609
603
|
}
|
610
604
|
#endif /* not ESPHOME_THREAD_SINGLE */
|
611
605
|
|
@@ -620,23 +614,13 @@ bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_c
|
|
620
614
|
total_cancelled++;
|
621
615
|
}
|
622
616
|
// For other items in heap, we can only mark for removal (can't remove from middle of heap)
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
total_cancelled++;
|
627
|
-
this->to_remove_++; // Track removals for heap items
|
628
|
-
}
|
629
|
-
}
|
617
|
+
size_t heap_cancelled = this->mark_matching_items_removed_(this->items_, component, name_cstr, type, match_retry);
|
618
|
+
total_cancelled += heap_cancelled;
|
619
|
+
this->to_remove_ += heap_cancelled; // Track removals for heap items
|
630
620
|
}
|
631
621
|
|
632
622
|
// Cancel items in to_add_
|
633
|
-
|
634
|
-
if (this->matches_item_(item, component, name_cstr, type, match_retry)) {
|
635
|
-
this->mark_item_removed_(item.get());
|
636
|
-
total_cancelled++;
|
637
|
-
// Don't track removals for to_add_ items
|
638
|
-
}
|
639
|
-
}
|
623
|
+
total_cancelled += this->mark_matching_items_removed_(this->to_add_, component, name_cstr, type, match_retry);
|
640
624
|
|
641
625
|
return total_cancelled > 0;
|
642
626
|
}
|
esphome/core/scheduler.h
CHANGED
@@ -280,19 +280,30 @@ class Scheduler {
|
|
280
280
|
#endif
|
281
281
|
}
|
282
282
|
|
283
|
-
// Helper to mark
|
283
|
+
// Helper to mark matching items in a container as removed
|
284
|
+
// Returns the number of items marked for removal
|
284
285
|
// For ESPHOME_THREAD_MULTI_NO_ATOMICS platforms, the caller must hold the scheduler lock before calling this
|
285
286
|
// function.
|
286
|
-
|
287
|
+
template<typename Container>
|
288
|
+
size_t mark_matching_items_removed_(Container &container, Component *component, const char *name_cstr,
|
289
|
+
SchedulerItem::Type type, bool match_retry) {
|
290
|
+
size_t count = 0;
|
291
|
+
for (auto &item : container) {
|
292
|
+
if (this->matches_item_(item, component, name_cstr, type, match_retry)) {
|
293
|
+
// Mark item for removal (platform-specific)
|
287
294
|
#ifdef ESPHOME_THREAD_MULTI_ATOMICS
|
288
|
-
|
289
|
-
|
295
|
+
// Multi-threaded with atomics: use atomic store
|
296
|
+
item->remove.store(true, std::memory_order_release);
|
290
297
|
#else
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
298
|
+
// Single-threaded (ESPHOME_THREAD_SINGLE) or
|
299
|
+
// multi-threaded without atomics (ESPHOME_THREAD_MULTI_NO_ATOMICS): direct write
|
300
|
+
// For ESPHOME_THREAD_MULTI_NO_ATOMICS, caller MUST hold lock!
|
301
|
+
item->remove = true;
|
295
302
|
#endif
|
303
|
+
count++;
|
304
|
+
}
|
305
|
+
}
|
306
|
+
return count;
|
296
307
|
}
|
297
308
|
|
298
309
|
// Template helper to check if any item in a container matches our criteria
|
esphome/core/string_ref.h
CHANGED
@@ -130,7 +130,7 @@ inline std::string operator+(const StringRef &lhs, const char *rhs) {
|
|
130
130
|
|
131
131
|
#ifdef USE_JSON
|
132
132
|
// NOLINTNEXTLINE(readability-identifier-naming)
|
133
|
-
void convertToJson(const StringRef &src, JsonVariant dst);
|
133
|
+
inline void convertToJson(const StringRef &src, JsonVariant dst) { dst.set(src.c_str()); }
|
134
134
|
#endif // USE_JSON
|
135
135
|
|
136
136
|
} // namespace esphome
|
esphome/core/time.cpp
CHANGED
@@ -77,7 +77,7 @@ bool ESPTime::strptime(const std::string &time_to_parse, ESPTime &esp_time) {
|
|
77
77
|
&hour, // NOLINT
|
78
78
|
&minute, // NOLINT
|
79
79
|
&second, &num) == 6 && // NOLINT
|
80
|
-
num == time_to_parse.size()) {
|
80
|
+
num == static_cast<int>(time_to_parse.size())) {
|
81
81
|
esp_time.year = year;
|
82
82
|
esp_time.month = month;
|
83
83
|
esp_time.day_of_month = day;
|
@@ -87,7 +87,7 @@ bool ESPTime::strptime(const std::string &time_to_parse, ESPTime &esp_time) {
|
|
87
87
|
} else if (sscanf(time_to_parse.c_str(), "%04hu-%02hhu-%02hhu %02hhu:%02hhu %n", &year, &month, &day, // NOLINT
|
88
88
|
&hour, // NOLINT
|
89
89
|
&minute, &num) == 5 && // NOLINT
|
90
|
-
num == time_to_parse.size()) {
|
90
|
+
num == static_cast<int>(time_to_parse.size())) {
|
91
91
|
esp_time.year = year;
|
92
92
|
esp_time.month = month;
|
93
93
|
esp_time.day_of_month = day;
|
@@ -95,17 +95,17 @@ bool ESPTime::strptime(const std::string &time_to_parse, ESPTime &esp_time) {
|
|
95
95
|
esp_time.minute = minute;
|
96
96
|
esp_time.second = 0;
|
97
97
|
} else if (sscanf(time_to_parse.c_str(), "%02hhu:%02hhu:%02hhu %n", &hour, &minute, &second, &num) == 3 && // NOLINT
|
98
|
-
num == time_to_parse.size()) {
|
98
|
+
num == static_cast<int>(time_to_parse.size())) {
|
99
99
|
esp_time.hour = hour;
|
100
100
|
esp_time.minute = minute;
|
101
101
|
esp_time.second = second;
|
102
102
|
} else if (sscanf(time_to_parse.c_str(), "%02hhu:%02hhu %n", &hour, &minute, &num) == 2 && // NOLINT
|
103
|
-
num == time_to_parse.size()) {
|
103
|
+
num == static_cast<int>(time_to_parse.size())) {
|
104
104
|
esp_time.hour = hour;
|
105
105
|
esp_time.minute = minute;
|
106
106
|
esp_time.second = 0;
|
107
107
|
} else if (sscanf(time_to_parse.c_str(), "%04hu-%02hhu-%02hhu %n", &year, &month, &day, &num) == 3 && // NOLINT
|
108
|
-
num == time_to_parse.size()) {
|
108
|
+
num == static_cast<int>(time_to_parse.size())) {
|
109
109
|
esp_time.year = year;
|
110
110
|
esp_time.month = month;
|
111
111
|
esp_time.day_of_month = day;
|
esphome/cpp_generator.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import abc
|
2
|
-
from collections.abc import Callable
|
2
|
+
from collections.abc import Callable
|
3
3
|
import inspect
|
4
4
|
import math
|
5
5
|
import re
|
@@ -13,7 +13,6 @@ from esphome.core import (
|
|
13
13
|
HexInt,
|
14
14
|
Lambda,
|
15
15
|
Library,
|
16
|
-
TimePeriod,
|
17
16
|
TimePeriodMicroseconds,
|
18
17
|
TimePeriodMilliseconds,
|
19
18
|
TimePeriodMinutes,
|
@@ -21,35 +20,11 @@ from esphome.core import (
|
|
21
20
|
TimePeriodSeconds,
|
22
21
|
)
|
23
22
|
from esphome.helpers import cpp_string_escape, indent_all_but_first_and_last
|
23
|
+
from esphome.types import Expression, SafeExpType, TemplateArgsType
|
24
24
|
from esphome.util import OrderedDict
|
25
25
|
from esphome.yaml_util import ESPHomeDataBase
|
26
26
|
|
27
27
|
|
28
|
-
class Expression(abc.ABC):
|
29
|
-
__slots__ = ()
|
30
|
-
|
31
|
-
@abc.abstractmethod
|
32
|
-
def __str__(self):
|
33
|
-
"""
|
34
|
-
Convert expression into C++ code
|
35
|
-
"""
|
36
|
-
|
37
|
-
|
38
|
-
SafeExpType = (
|
39
|
-
Expression
|
40
|
-
| bool
|
41
|
-
| str
|
42
|
-
| str
|
43
|
-
| int
|
44
|
-
| float
|
45
|
-
| TimePeriod
|
46
|
-
| type[bool]
|
47
|
-
| type[int]
|
48
|
-
| type[float]
|
49
|
-
| Sequence[Any]
|
50
|
-
)
|
51
|
-
|
52
|
-
|
53
28
|
class RawExpression(Expression):
|
54
29
|
__slots__ = ("text",)
|
55
30
|
|
@@ -575,7 +550,7 @@ def Pvariable(id_: ID, rhs: SafeExpType, type_: "MockObj" = None) -> "MockObj":
|
|
575
550
|
return obj
|
576
551
|
|
577
552
|
|
578
|
-
def new_Pvariable(id_: ID, *args: SafeExpType) ->
|
553
|
+
def new_Pvariable(id_: ID, *args: SafeExpType) -> "MockObj":
|
579
554
|
"""Declare a new pointer variable in the code generation by calling it's constructor
|
580
555
|
with the given arguments.
|
581
556
|
|
@@ -681,7 +656,7 @@ async def get_variable_with_full_id(id_: ID) -> tuple[ID, "MockObj"]:
|
|
681
656
|
|
682
657
|
async def process_lambda(
|
683
658
|
value: Lambda,
|
684
|
-
parameters:
|
659
|
+
parameters: TemplateArgsType,
|
685
660
|
capture: str = "=",
|
686
661
|
return_type: SafeExpType = None,
|
687
662
|
) -> LambdaExpression | None:
|
esphome/dashboard/const.py
CHANGED
@@ -1,9 +1,26 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
from esphome.enum import StrEnum
|
4
|
+
|
5
|
+
|
6
|
+
class DashboardEvent(StrEnum):
|
7
|
+
"""Dashboard WebSocket event types."""
|
8
|
+
|
9
|
+
# Server -> Client events (backend sends to frontend)
|
10
|
+
ENTRY_ADDED = "entry_added"
|
11
|
+
ENTRY_REMOVED = "entry_removed"
|
12
|
+
ENTRY_UPDATED = "entry_updated"
|
13
|
+
ENTRY_STATE_CHANGED = "entry_state_changed"
|
14
|
+
IMPORTABLE_DEVICE_ADDED = "importable_device_added"
|
15
|
+
IMPORTABLE_DEVICE_REMOVED = "importable_device_removed"
|
16
|
+
INITIAL_STATE = "initial_state" # Sent on WebSocket connection
|
17
|
+
PONG = "pong" # Response to client ping
|
18
|
+
|
19
|
+
# Client -> Server events (frontend sends to backend)
|
20
|
+
PING = "ping" # WebSocket keepalive from client
|
21
|
+
REFRESH = "refresh" # Force backend to poll for changes
|
22
|
+
|
23
|
+
|
7
24
|
MAX_EXECUTOR_WORKERS = 48
|
8
25
|
|
9
26
|
|
esphome/dashboard/core.py
CHANGED
@@ -7,13 +7,13 @@ from dataclasses import dataclass
|
|
7
7
|
from functools import partial
|
8
8
|
import json
|
9
9
|
import logging
|
10
|
-
from pathlib import Path
|
11
10
|
import threading
|
12
11
|
from typing import Any
|
13
12
|
|
14
13
|
from esphome.storage_json import ignored_devices_storage_path
|
15
14
|
|
16
15
|
from ..zeroconf import DiscoveredImport
|
16
|
+
from .const import DashboardEvent
|
17
17
|
from .dns import DNSCache
|
18
18
|
from .entries import DashboardEntries
|
19
19
|
from .settings import DashboardSettings
|
@@ -31,7 +31,7 @@ MDNS_BOOTSTRAP_TIME = 7.5
|
|
31
31
|
class Event:
|
32
32
|
"""Dashboard Event."""
|
33
33
|
|
34
|
-
event_type:
|
34
|
+
event_type: DashboardEvent
|
35
35
|
data: dict[str, Any]
|
36
36
|
|
37
37
|
|
@@ -40,22 +40,24 @@ class EventBus:
|
|
40
40
|
|
41
41
|
def __init__(self) -> None:
|
42
42
|
"""Initialize the Dashboard event bus."""
|
43
|
-
self._listeners: dict[
|
43
|
+
self._listeners: dict[DashboardEvent, set[Callable[[Event], None]]] = {}
|
44
44
|
|
45
45
|
def async_add_listener(
|
46
|
-
self, event_type:
|
46
|
+
self, event_type: DashboardEvent, listener: Callable[[Event], None]
|
47
47
|
) -> Callable[[], None]:
|
48
48
|
"""Add a listener to the event bus."""
|
49
49
|
self._listeners.setdefault(event_type, set()).add(listener)
|
50
50
|
return partial(self._async_remove_listener, event_type, listener)
|
51
51
|
|
52
52
|
def _async_remove_listener(
|
53
|
-
self, event_type:
|
53
|
+
self, event_type: DashboardEvent, listener: Callable[[Event], None]
|
54
54
|
) -> None:
|
55
55
|
"""Remove a listener from the event bus."""
|
56
56
|
self._listeners[event_type].discard(listener)
|
57
57
|
|
58
|
-
def async_fire(
|
58
|
+
def async_fire(
|
59
|
+
self, event_type: DashboardEvent, event_data: dict[str, Any]
|
60
|
+
) -> None:
|
59
61
|
"""Fire an event."""
|
60
62
|
event = Event(event_type, event_data)
|
61
63
|
|
@@ -108,7 +110,7 @@ class ESPHomeDashboard:
|
|
108
110
|
await self.loop.run_in_executor(None, self.load_ignored_devices)
|
109
111
|
|
110
112
|
def load_ignored_devices(self) -> None:
|
111
|
-
storage_path =
|
113
|
+
storage_path = ignored_devices_storage_path()
|
112
114
|
try:
|
113
115
|
with storage_path.open("r", encoding="utf-8") as f_handle:
|
114
116
|
data = json.load(f_handle)
|
@@ -117,7 +119,7 @@ class ESPHomeDashboard:
|
|
117
119
|
pass
|
118
120
|
|
119
121
|
def save_ignored_devices(self) -> None:
|
120
|
-
storage_path =
|
122
|
+
storage_path = ignored_devices_storage_path()
|
121
123
|
with storage_path.open("w", encoding="utf-8") as f_handle:
|
122
124
|
json.dump(
|
123
125
|
{"ignored_devices": sorted(self.ignored_devices)}, indent=2, fp=f_handle
|
esphome/dashboard/dns.py
CHANGED
@@ -28,6 +28,21 @@ class DNSCache:
|
|
28
28
|
self._cache: dict[str, tuple[float, list[str] | Exception]] = {}
|
29
29
|
self._ttl = ttl
|
30
30
|
|
31
|
+
def get_cached_addresses(
|
32
|
+
self, hostname: str, now_monotonic: float
|
33
|
+
) -> list[str] | None:
|
34
|
+
"""Get cached addresses without triggering resolution.
|
35
|
+
|
36
|
+
Returns None if not in cache, list of addresses if found.
|
37
|
+
"""
|
38
|
+
# Normalize hostname for consistent lookups
|
39
|
+
normalized = hostname.rstrip(".").lower()
|
40
|
+
if expire_time_addresses := self._cache.get(normalized):
|
41
|
+
expire_time, addresses = expire_time_addresses
|
42
|
+
if expire_time > now_monotonic and not isinstance(addresses, Exception):
|
43
|
+
return addresses
|
44
|
+
return None
|
45
|
+
|
31
46
|
async def async_resolve(
|
32
47
|
self, hostname: str, now_monotonic: float
|
33
48
|
) -> list[str] | Exception:
|
esphome/dashboard/entries.py
CHANGED
@@ -5,20 +5,14 @@ from collections import defaultdict
|
|
5
5
|
from dataclasses import dataclass
|
6
6
|
from functools import lru_cache
|
7
7
|
import logging
|
8
|
-
import
|
8
|
+
from pathlib import Path
|
9
9
|
from typing import TYPE_CHECKING, Any
|
10
10
|
|
11
11
|
from esphome import const, util
|
12
12
|
from esphome.enum import StrEnum
|
13
13
|
from esphome.storage_json import StorageJSON, ext_storage_path
|
14
14
|
|
15
|
-
from .const import
|
16
|
-
DASHBOARD_COMMAND,
|
17
|
-
EVENT_ENTRY_ADDED,
|
18
|
-
EVENT_ENTRY_REMOVED,
|
19
|
-
EVENT_ENTRY_STATE_CHANGED,
|
20
|
-
EVENT_ENTRY_UPDATED,
|
21
|
-
)
|
15
|
+
from .const import DASHBOARD_COMMAND, DashboardEvent
|
22
16
|
from .util.subprocess import async_run_system_command
|
23
17
|
|
24
18
|
if TYPE_CHECKING:
|
@@ -102,12 +96,12 @@ class DashboardEntries:
|
|
102
96
|
# "path/to/file.yaml": DashboardEntry,
|
103
97
|
# ...
|
104
98
|
# }
|
105
|
-
self._entries: dict[
|
99
|
+
self._entries: dict[Path, DashboardEntry] = {}
|
106
100
|
self._loaded_entries = False
|
107
101
|
self._update_lock = asyncio.Lock()
|
108
102
|
self._name_to_entry: dict[str, set[DashboardEntry]] = defaultdict(set)
|
109
103
|
|
110
|
-
def get(self, path:
|
104
|
+
def get(self, path: Path) -> DashboardEntry | None:
|
111
105
|
"""Get an entry by path."""
|
112
106
|
return self._entries.get(path)
|
113
107
|
|
@@ -192,7 +186,7 @@ class DashboardEntries:
|
|
192
186
|
return
|
193
187
|
entry.state = state
|
194
188
|
self._dashboard.bus.async_fire(
|
195
|
-
|
189
|
+
DashboardEvent.ENTRY_STATE_CHANGED, {"entry": entry, "state": state}
|
196
190
|
)
|
197
191
|
|
198
192
|
async def async_request_update_entries(self) -> None:
|
@@ -260,22 +254,22 @@ class DashboardEntries:
|
|
260
254
|
for entry in added:
|
261
255
|
entries[entry.path] = entry
|
262
256
|
name_to_entry[entry.name].add(entry)
|
263
|
-
bus.async_fire(
|
257
|
+
bus.async_fire(DashboardEvent.ENTRY_ADDED, {"entry": entry})
|
264
258
|
|
265
259
|
for entry in removed:
|
266
260
|
del entries[entry.path]
|
267
261
|
name_to_entry[entry.name].discard(entry)
|
268
|
-
bus.async_fire(
|
262
|
+
bus.async_fire(DashboardEvent.ENTRY_REMOVED, {"entry": entry})
|
269
263
|
|
270
264
|
for entry in updated:
|
271
265
|
if (original_name := original_names[entry]) != (current_name := entry.name):
|
272
266
|
name_to_entry[original_name].discard(entry)
|
273
267
|
name_to_entry[current_name].add(entry)
|
274
|
-
bus.async_fire(
|
268
|
+
bus.async_fire(DashboardEvent.ENTRY_UPDATED, {"entry": entry})
|
275
269
|
|
276
|
-
def _get_path_to_cache_key(self) -> dict[
|
270
|
+
def _get_path_to_cache_key(self) -> dict[Path, DashboardCacheKeyType]:
|
277
271
|
"""Return a dict of path to cache key."""
|
278
|
-
path_to_cache_key: dict[
|
272
|
+
path_to_cache_key: dict[Path, DashboardCacheKeyType] = {}
|
279
273
|
#
|
280
274
|
# The cache key is (inode, device, mtime, size)
|
281
275
|
# which allows us to avoid locking since it ensures
|
@@ -287,12 +281,12 @@ class DashboardEntries:
|
|
287
281
|
for file in util.list_yaml_files([self._config_dir]):
|
288
282
|
try:
|
289
283
|
# Prefer the json storage path if it exists
|
290
|
-
stat =
|
284
|
+
stat = ext_storage_path(file.name).stat()
|
291
285
|
except OSError:
|
292
286
|
try:
|
293
287
|
# Fallback to the yaml file if the storage
|
294
288
|
# file does not exist or could not be generated
|
295
|
-
stat =
|
289
|
+
stat = file.stat()
|
296
290
|
except OSError:
|
297
291
|
# File was deleted, ignore
|
298
292
|
continue
|
@@ -329,10 +323,10 @@ class DashboardEntry:
|
|
329
323
|
"_to_dict",
|
330
324
|
)
|
331
325
|
|
332
|
-
def __init__(self, path:
|
326
|
+
def __init__(self, path: Path, cache_key: DashboardCacheKeyType) -> None:
|
333
327
|
"""Initialize the DashboardEntry."""
|
334
328
|
self.path = path
|
335
|
-
self.filename: str =
|
329
|
+
self.filename: str = path.name
|
336
330
|
self._storage_path = ext_storage_path(self.filename)
|
337
331
|
self.cache_key = cache_key
|
338
332
|
self.storage: StorageJSON | None = None
|
@@ -365,7 +359,7 @@ class DashboardEntry:
|
|
365
359
|
"loaded_integrations": sorted(self.loaded_integrations),
|
366
360
|
"deployed_version": self.update_old,
|
367
361
|
"current_version": self.update_new,
|
368
|
-
"path": self.path,
|
362
|
+
"path": str(self.path),
|
369
363
|
"comment": self.comment,
|
370
364
|
"address": self.address,
|
371
365
|
"web_port": self.web_port,
|
@@ -0,0 +1,76 @@
|
|
1
|
+
"""Data models and builders for the dashboard."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
from typing import TYPE_CHECKING, TypedDict
|
6
|
+
|
7
|
+
if TYPE_CHECKING:
|
8
|
+
from esphome.zeroconf import DiscoveredImport
|
9
|
+
|
10
|
+
from .core import ESPHomeDashboard
|
11
|
+
from .entries import DashboardEntry
|
12
|
+
|
13
|
+
|
14
|
+
class ImportableDeviceDict(TypedDict):
|
15
|
+
"""Dictionary representation of an importable device."""
|
16
|
+
|
17
|
+
name: str
|
18
|
+
friendly_name: str | None
|
19
|
+
package_import_url: str
|
20
|
+
project_name: str
|
21
|
+
project_version: str
|
22
|
+
network: str
|
23
|
+
ignored: bool
|
24
|
+
|
25
|
+
|
26
|
+
class ConfiguredDeviceDict(TypedDict, total=False):
|
27
|
+
"""Dictionary representation of a configured device."""
|
28
|
+
|
29
|
+
name: str
|
30
|
+
friendly_name: str | None
|
31
|
+
configuration: str
|
32
|
+
loaded_integrations: list[str] | None
|
33
|
+
deployed_version: str | None
|
34
|
+
current_version: str | None
|
35
|
+
path: str
|
36
|
+
comment: str | None
|
37
|
+
address: str | None
|
38
|
+
web_port: int | None
|
39
|
+
target_platform: str | None
|
40
|
+
|
41
|
+
|
42
|
+
class DeviceListResponse(TypedDict):
|
43
|
+
"""Response for device list API."""
|
44
|
+
|
45
|
+
configured: list[ConfiguredDeviceDict]
|
46
|
+
importable: list[ImportableDeviceDict]
|
47
|
+
|
48
|
+
|
49
|
+
def build_importable_device_dict(
|
50
|
+
dashboard: ESPHomeDashboard, discovered: DiscoveredImport
|
51
|
+
) -> ImportableDeviceDict:
|
52
|
+
"""Build the importable device dictionary."""
|
53
|
+
return ImportableDeviceDict(
|
54
|
+
name=discovered.device_name,
|
55
|
+
friendly_name=discovered.friendly_name,
|
56
|
+
package_import_url=discovered.package_import_url,
|
57
|
+
project_name=discovered.project_name,
|
58
|
+
project_version=discovered.project_version,
|
59
|
+
network=discovered.network,
|
60
|
+
ignored=discovered.device_name in dashboard.ignored_devices,
|
61
|
+
)
|
62
|
+
|
63
|
+
|
64
|
+
def build_device_list_response(
|
65
|
+
dashboard: ESPHomeDashboard, entries: list[DashboardEntry]
|
66
|
+
) -> DeviceListResponse:
|
67
|
+
"""Build the device list response data."""
|
68
|
+
configured = {entry.name for entry in entries}
|
69
|
+
return DeviceListResponse(
|
70
|
+
configured=[entry.to_dict() for entry in entries],
|
71
|
+
importable=[
|
72
|
+
build_importable_device_dict(dashboard, res)
|
73
|
+
for res in dashboard.import_result.values()
|
74
|
+
if res.device_name not in configured
|
75
|
+
],
|
76
|
+
)
|
esphome/dashboard/settings.py
CHANGED
@@ -27,7 +27,7 @@ class DashboardSettings:
|
|
27
27
|
|
28
28
|
def __init__(self) -> None:
|
29
29
|
"""Initialize the dashboard settings."""
|
30
|
-
self.config_dir:
|
30
|
+
self.config_dir: Path = None
|
31
31
|
self.password_hash: str = ""
|
32
32
|
self.username: str = ""
|
33
33
|
self.using_password: bool = False
|
@@ -45,10 +45,10 @@ class DashboardSettings:
|
|
45
45
|
self.using_password = bool(password)
|
46
46
|
if self.using_password:
|
47
47
|
self.password_hash = password_hash(password)
|
48
|
-
self.config_dir = args.configuration
|
49
|
-
self.absolute_config_dir =
|
48
|
+
self.config_dir = Path(args.configuration)
|
49
|
+
self.absolute_config_dir = self.config_dir.resolve()
|
50
50
|
self.verbose = args.verbose
|
51
|
-
CORE.config_path =
|
51
|
+
CORE.config_path = self.config_dir / "."
|
52
52
|
|
53
53
|
@property
|
54
54
|
def relative_url(self) -> str:
|
@@ -81,9 +81,9 @@ class DashboardSettings:
|
|
81
81
|
# Compare password in constant running time (to prevent timing attacks)
|
82
82
|
return hmac.compare_digest(self.password_hash, password_hash(password))
|
83
83
|
|
84
|
-
def rel_path(self, *args: Any) ->
|
84
|
+
def rel_path(self, *args: Any) -> Path:
|
85
85
|
"""Return a path relative to the ESPHome config folder."""
|
86
|
-
joined_path =
|
86
|
+
joined_path = self.config_dir / Path(*args)
|
87
87
|
# Raises ValueError if not relative to ESPHome config folder
|
88
|
-
|
88
|
+
joined_path.resolve().relative_to(self.absolute_config_dir)
|
89
89
|
return joined_path
|