esphome 2025.8.4__py3-none-any.whl → 2025.9.0b1__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 +36 -42
- esphome/components/absolute_humidity/absolute_humidity.cpp +3 -5
- esphome/components/adc/adc_sensor_esp32.cpp +29 -6
- esphome/components/ags10/ags10.cpp +3 -18
- esphome/components/ags10/ags10.h +2 -12
- esphome/components/aht10/aht10.cpp +3 -3
- esphome/components/airthings_ble/__init__.py +2 -2
- esphome/components/alarm_control_panel/__init__.py +2 -2
- esphome/components/am2315c/am2315c.cpp +1 -17
- esphome/components/am2315c/am2315c.h +2 -3
- esphome/components/api/__init__.py +2 -2
- esphome/components/api/api_connection.cpp +34 -23
- esphome/components/api/api_connection.h +20 -39
- esphome/components/api/api_frame_helper.cpp +25 -25
- esphome/components/api/api_frame_helper.h +3 -3
- esphome/components/api/api_frame_helper_noise.cpp +75 -40
- esphome/components/api/api_frame_helper_noise.h +3 -7
- esphome/components/api/api_frame_helper_plaintext.cpp +17 -4
- esphome/components/api/api_frame_helper_plaintext.h +1 -4
- esphome/components/api/api_pb2.cpp +20 -2
- esphome/components/api/api_pb2.h +146 -141
- esphome/components/api/api_pb2_dump.cpp +12 -1
- esphome/components/api/proto.cpp +33 -37
- esphome/components/async_tcp/__init__.py +2 -2
- esphome/components/atm90e26/sensor.py +2 -0
- esphome/components/atm90e32/sensor.py +4 -2
- esphome/components/audio_adc/__init__.py +2 -2
- esphome/components/audio_dac/__init__.py +2 -2
- esphome/components/axs15231/touchscreen/axs15231_touchscreen.cpp +1 -1
- esphome/components/bedjet/bedjet_hub.cpp +1 -1
- esphome/components/binary_sensor/__init__.py +2 -2
- esphome/components/binary_sensor/binary_sensor.cpp +13 -0
- esphome/components/binary_sensor/binary_sensor.h +4 -7
- esphome/components/bl0940/__init__.py +6 -1
- esphome/components/bl0940/bl0940.cpp +178 -41
- esphome/components/bl0940/bl0940.h +121 -76
- esphome/components/bl0940/button/__init__.py +27 -0
- esphome/components/bl0940/button/calibration_reset_button.cpp +20 -0
- esphome/components/bl0940/button/calibration_reset_button.h +19 -0
- esphome/components/bl0940/number/__init__.py +94 -0
- esphome/components/bl0940/number/calibration_number.cpp +29 -0
- esphome/components/bl0940/number/calibration_number.h +26 -0
- esphome/components/bl0940/sensor.py +151 -2
- esphome/components/bl0942/bl0942.cpp +1 -1
- esphome/components/ble_client/output/__init__.py +4 -4
- esphome/components/bluetooth_proxy/__init__.py +1 -1
- esphome/components/bluetooth_proxy/bluetooth_connection.h +1 -1
- esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +15 -7
- esphome/components/bluetooth_proxy/bluetooth_proxy.h +3 -2
- esphome/components/button/__init__.py +2 -2
- esphome/components/button/button.cpp +13 -0
- esphome/components/button/button.h +4 -7
- esphome/components/camera/buffer.h +18 -0
- esphome/components/camera/buffer_impl.cpp +20 -0
- esphome/components/camera/buffer_impl.h +26 -0
- esphome/components/camera/camera.h +43 -0
- esphome/components/camera/encoder.h +69 -0
- esphome/components/camera_encoder/__init__.py +62 -0
- esphome/components/camera_encoder/encoder_buffer_impl.cpp +23 -0
- esphome/components/camera_encoder/encoder_buffer_impl.h +25 -0
- esphome/components/camera_encoder/esp32_camera_jpeg_encoder.cpp +82 -0
- esphome/components/camera_encoder/esp32_camera_jpeg_encoder.h +39 -0
- esphome/components/captive_portal/__init__.py +2 -2
- esphome/components/captive_portal/captive_portal.cpp +35 -12
- esphome/components/captive_portal/captive_portal.h +3 -3
- esphome/components/ccs811/ccs811.cpp +3 -3
- esphome/components/climate/__init__.py +2 -2
- esphome/components/climate/climate.cpp +1 -1
- esphome/components/cover/__init__.py +5 -5
- esphome/components/cover/cover.cpp +1 -1
- esphome/components/cover/cover.h +2 -2
- esphome/components/dallas_temp/dallas_temp.cpp +2 -2
- esphome/components/datetime/__init__.py +2 -2
- esphome/components/datetime/date_entity.h +2 -2
- esphome/components/datetime/datetime_entity.h +2 -2
- esphome/components/datetime/time_entity.h +2 -2
- esphome/components/debug/debug_esp32.cpp +1 -1
- esphome/components/display/__init__.py +4 -4
- esphome/components/duty_time/duty_time_sensor.cpp +1 -1
- esphome/components/esp32/__init__.py +0 -5
- esphome/components/esp32/gpio.cpp +27 -23
- esphome/components/esp32/gpio.h +26 -11
- esphome/components/esp32/preferences.cpp +8 -4
- esphome/components/esp32_ble/__init__.py +7 -2
- esphome/components/esp32_ble_client/ble_client_base.cpp +7 -3
- esphome/components/esp32_ble_tracker/__init__.py +2 -2
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +9 -44
- esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +2 -14
- esphome/components/esp8266/__init__.py +2 -2
- esphome/components/esp8266/core.cpp +2 -2
- esphome/components/esp8266/gpio.py +4 -4
- esphome/components/esp8266/preferences.cpp +30 -28
- esphome/components/esphome/ota/__init__.py +2 -2
- esphome/components/esphome/ota/ota_esphome.cpp +21 -19
- esphome/components/esphome/ota/ota_esphome.h +6 -5
- esphome/components/ethernet/__init__.py +7 -2
- esphome/components/ethernet/ethernet_component.cpp +1 -1
- esphome/components/event/__init__.py +2 -2
- esphome/components/event/event.h +4 -4
- esphome/components/fan/__init__.py +2 -2
- esphome/components/fan/fan.cpp +2 -1
- esphome/components/gdk101/gdk101.cpp +4 -4
- esphome/components/globals/__init__.py +2 -2
- esphome/components/gpio/binary_sensor/gpio_binary_sensor.cpp +19 -18
- esphome/components/gpio_expander/cached_gpio.h +36 -16
- esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.cpp +5 -5
- esphome/components/gt911/touchscreen/gt911_touchscreen.cpp +1 -1
- esphome/components/haier/haier_base.cpp +1 -1
- esphome/components/haier/hon_climate.cpp +1 -1
- esphome/components/hlw8012/hlw8012.cpp +5 -5
- esphome/components/honeywellabp2_i2c/honeywellabp2.cpp +4 -4
- esphome/components/host/preferences.h +3 -2
- esphome/components/hte501/hte501.cpp +3 -21
- esphome/components/hte501/hte501.h +2 -3
- esphome/components/http_request/ota/__init__.py +2 -2
- esphome/components/i2c/__init__.py +2 -2
- esphome/components/i2c/i2c.cpp +13 -9
- esphome/components/i2c/i2c_bus.h +36 -6
- esphome/components/i2s_audio/__init__.py +8 -2
- esphome/components/i2s_audio/media_player/__init__.py +1 -1
- esphome/components/i2s_audio/microphone/__init__.py +1 -1
- esphome/components/i2s_audio/speaker/__init__.py +1 -1
- esphome/components/inkplate/__init__.py +1 -0
- esphome/components/inkplate/const.py +105 -0
- esphome/components/inkplate/display.py +238 -0
- esphome/components/{inkplate6 → inkplate}/inkplate.cpp +156 -74
- esphome/components/{inkplate6 → inkplate}/inkplate.h +28 -68
- esphome/components/inkplate6/__init__.py +0 -1
- esphome/components/inkplate6/display.py +2 -211
- esphome/components/integration/integration_sensor.cpp +1 -1
- esphome/components/json/__init__.py +2 -2
- esphome/components/lc709203f/lc709203f.cpp +4 -17
- esphome/components/lc709203f/lc709203f.h +2 -3
- esphome/components/ld2420/text_sensor/{text_sensor.cpp → ld2420_text_sensor.cpp} +1 -1
- esphome/components/ld2450/ld2450.cpp +1 -1
- esphome/components/libretiny/preferences.cpp +13 -5
- esphome/components/light/__init__.py +2 -2
- esphome/components/light/addressable_light_effect.h +7 -0
- esphome/components/light/base_light_effects.h +8 -0
- esphome/components/light/light_call.cpp +22 -20
- esphome/components/light/light_effect.cpp +36 -0
- esphome/components/light/light_effect.h +14 -0
- esphome/components/light/light_json_schema.cpp +9 -1
- esphome/components/light/light_state.cpp +2 -2
- esphome/components/light/light_state.h +38 -0
- esphome/components/lock/__init__.py +2 -2
- esphome/components/lock/lock.h +2 -2
- esphome/components/logger/__init__.py +2 -2
- esphome/components/logger/logger.cpp +25 -4
- esphome/components/logger/logger.h +1 -1
- esphome/components/logger/logger_esp32.cpp +16 -8
- esphome/components/logger/logger_esp8266.cpp +11 -3
- esphome/components/logger/logger_libretiny.cpp +13 -3
- esphome/components/logger/logger_rp2040.cpp +14 -3
- esphome/components/logger/logger_zephyr.cpp +15 -4
- esphome/components/lvgl/defines.py +1 -0
- esphome/components/lvgl/hello_world.py +96 -33
- esphome/components/lvgl/number/lvgl_number.h +1 -1
- esphome/components/lvgl/select/lvgl_select.h +1 -1
- esphome/components/lvgl/widgets/__init__.py +0 -1
- esphome/components/lvgl/widgets/spinbox.py +20 -11
- esphome/components/m5stack_8angle/binary_sensor/m5stack_8angle_binary_sensor.cpp +1 -1
- esphome/components/m5stack_8angle/sensor/m5stack_8angle_sensor.cpp +1 -1
- esphome/components/mapping/__init__.py +13 -5
- esphome/components/mapping/mapping.h +69 -0
- esphome/components/max17043/max17043.cpp +2 -2
- esphome/components/mcp23016/__init__.py +1 -0
- esphome/components/mcp23016/mcp23016.cpp +20 -5
- esphome/components/mcp23016/mcp23016.h +10 -4
- esphome/components/mcp23x08_base/mcp23x08_base.cpp +1 -1
- esphome/components/mcp23x17_base/mcp23x17_base.cpp +2 -2
- esphome/components/mdns/__init__.py +2 -2
- esphome/components/mdns/mdns_component.cpp +145 -54
- esphome/components/media_player/__init__.py +2 -2
- esphome/components/micro_wake_word/__init__.py +2 -2
- esphome/components/microphone/__init__.py +2 -2
- esphome/components/mipi/__init__.py +77 -33
- esphome/components/mipi_rgb/__init__.py +2 -0
- esphome/components/mipi_rgb/display.py +321 -0
- esphome/components/mipi_rgb/mipi_rgb.cpp +388 -0
- esphome/components/mipi_rgb/mipi_rgb.h +127 -0
- esphome/components/mipi_rgb/models/guition.py +24 -0
- esphome/components/mipi_rgb/models/lilygo.py +228 -0
- esphome/components/mipi_rgb/models/rpi.py +9 -0
- esphome/components/mipi_rgb/models/st7701s.py +214 -0
- esphome/components/mipi_rgb/models/waveshare.py +64 -0
- esphome/components/mipi_spi/models/jc.py +229 -0
- esphome/components/mlx90614/mlx90614.cpp +1 -16
- esphome/components/mlx90614/mlx90614.h +0 -1
- esphome/components/mqtt/__init__.py +2 -2
- esphome/components/mqtt/mqtt_sensor.cpp +7 -2
- esphome/components/ms5611/ms5611.cpp +7 -6
- esphome/components/network/__init__.py +2 -2
- esphome/components/nextion/nextion_upload.cpp +4 -1
- esphome/components/nrf52/__init__.py +49 -6
- esphome/components/nrf52/const.py +1 -0
- esphome/components/nrf52/dfu.cpp +51 -0
- esphome/components/nrf52/dfu.h +24 -0
- esphome/components/ntc/ntc.cpp +1 -1
- esphome/components/number/__init__.py +2 -2
- esphome/components/number/automation.cpp +1 -1
- esphome/components/number/number.cpp +21 -0
- esphome/components/number/number.h +4 -13
- esphome/components/opentherm/hub.h +6 -6
- esphome/components/opentherm/number/{number.cpp → opentherm_number.cpp} +2 -2
- esphome/components/opentherm/output/{output.cpp → opentherm_output.cpp} +1 -1
- esphome/components/opentherm/switch/{switch.cpp → opentherm_switch.cpp} +1 -1
- esphome/components/ota/__init__.py +2 -2
- esphome/components/pca6416a/__init__.py +1 -0
- esphome/components/pca6416a/pca6416a.cpp +20 -5
- esphome/components/pca6416a/pca6416a.h +12 -5
- esphome/components/pca9554/__init__.py +2 -1
- esphome/components/pca9554/pca9554.cpp +12 -18
- esphome/components/pca9554/pca9554.h +10 -9
- esphome/components/pcf8574/__init__.py +1 -0
- esphome/components/pcf8574/pcf8574.cpp +14 -5
- esphome/components/pcf8574/pcf8574.h +13 -6
- esphome/components/pi4ioe5v6408/pi4ioe5v6408.cpp +7 -7
- esphome/components/pipsolar/__init__.py +3 -3
- esphome/components/pipsolar/output/__init__.py +4 -4
- esphome/components/pulse_width/pulse_width.cpp +2 -2
- esphome/components/qmp6988/qmp6988.cpp +81 -126
- esphome/components/qmp6988/qmp6988.h +31 -37
- esphome/components/radon_eye_ble/__init__.py +2 -2
- esphome/components/remote_base/__init__.py +6 -8
- esphome/components/rotary_encoder/rotary_encoder.cpp +1 -1
- esphome/components/rp2040/__init__.py +2 -2
- esphome/components/runtime_stats/runtime_stats.cpp +10 -23
- esphome/components/runtime_stats/runtime_stats.h +4 -10
- esphome/components/safe_mode/__init__.py +2 -2
- esphome/components/safe_mode/safe_mode.cpp +33 -31
- esphome/components/script/script.cpp +6 -0
- esphome/components/script/script.h +19 -5
- esphome/components/sdm_meter/sensor.py +3 -1
- esphome/components/select/__init__.py +2 -2
- esphome/components/select/select.h +2 -2
- esphome/components/sen5x/sen5x.cpp +57 -55
- esphome/components/sen5x/sen5x.h +21 -15
- esphome/components/sen5x/sensor.py +67 -44
- esphome/components/sensirion_common/i2c_sensirion.cpp +18 -47
- esphome/components/sensirion_common/i2c_sensirion.h +39 -55
- esphome/components/sensor/__init__.py +2 -2
- esphome/components/sensor/automation.h +1 -1
- esphome/components/sensor/sensor.cpp +34 -6
- esphome/components/sensor/sensor.h +4 -21
- esphome/components/sgp30/sgp30.cpp +34 -35
- esphome/components/sgp30/sgp30.h +11 -10
- esphome/components/sgp4x/sgp4x.cpp +2 -2
- esphome/components/shelly_dimmer/light.py +7 -7
- esphome/components/sht4x/sht4x.cpp +1 -1
- esphome/components/sntp/sntp_component.cpp +36 -9
- esphome/components/sntp/sntp_component.h +7 -0
- esphome/components/sound_level/sound_level.cpp +1 -1
- esphome/components/speaker/__init__.py +2 -2
- esphome/components/speaker/media_player/__init__.py +2 -2
- esphome/components/speaker/media_player/speaker_media_player.cpp +1 -1
- esphome/components/spi/__init__.py +2 -2
- esphome/components/sprinkler/sprinkler.cpp +1 -1
- esphome/components/sps30/sps30.cpp +18 -23
- esphome/components/sps30/sps30.h +3 -3
- esphome/components/status_led/__init__.py +2 -2
- esphome/components/stepper/__init__.py +2 -2
- esphome/components/switch/__init__.py +2 -2
- esphome/components/switch/switch.cpp +5 -5
- esphome/components/sx1509/__init__.py +1 -1
- esphome/components/sx1509/sx1509.cpp +12 -7
- esphome/components/sx1509/sx1509.h +11 -4
- esphome/components/tca9555/tca9555.cpp +5 -5
- esphome/components/tee501/tee501.cpp +2 -21
- esphome/components/tee501/tee501.h +2 -4
- esphome/components/template/alarm_control_panel/template_alarm_control_panel.cpp +1 -1
- esphome/components/template/datetime/template_date.cpp +1 -1
- esphome/components/template/datetime/template_datetime.cpp +2 -2
- esphome/components/template/datetime/template_time.cpp +1 -1
- esphome/components/template/number/template_number.cpp +1 -1
- esphome/components/template/select/template_select.cpp +1 -1
- esphome/components/template/text/template_text.cpp +1 -1
- esphome/components/text/__init__.py +2 -2
- esphome/components/text/text.h +2 -2
- esphome/components/text_sensor/__init__.py +2 -2
- esphome/components/text_sensor/text_sensor.h +4 -4
- esphome/components/thermostat/climate.py +11 -7
- esphome/components/thermostat/thermostat_climate.cpp +237 -206
- esphome/components/thermostat/thermostat_climate.h +52 -41
- esphome/components/time/__init__.py +2 -2
- esphome/components/tmp1075/tmp1075.cpp +1 -1
- esphome/components/total_daily_energy/total_daily_energy.cpp +1 -1
- esphome/components/touchscreen/__init__.py +2 -2
- esphome/components/tuya/number/tuya_number.cpp +1 -1
- esphome/components/udp/udp_component.cpp +3 -3
- esphome/components/ufire_ec/ufire_ec.cpp +4 -4
- esphome/components/ufire_ise/ufire_ise.cpp +4 -4
- esphome/components/update/__init__.py +2 -2
- esphome/components/usb_uart/usb_uart.cpp +1 -1
- esphome/components/valve/__init__.py +5 -5
- esphome/components/valve/valve.cpp +1 -1
- esphome/components/valve/valve.h +2 -2
- esphome/components/wake_on_lan/wake_on_lan.cpp +2 -2
- esphome/components/waveshare_epaper/waveshare_213v3.cpp +1 -1
- esphome/components/web_server/__init__.py +2 -2
- esphome/components/web_server/ota/__init__.py +2 -2
- esphome/components/web_server/ota/ota_web_server.cpp +11 -0
- esphome/components/web_server/web_server.cpp +58 -12
- esphome/components/web_server_base/__init__.py +2 -2
- esphome/components/wifi/__init__.py +5 -5
- esphome/components/wifi/wifi_component.cpp +3 -3
- esphome/components/wifi/wifi_component_esp_idf.cpp +2 -0
- esphome/config_validation.py +2 -2
- esphome/const.py +2 -1
- esphome/core/__init__.py +1 -0
- esphome/core/application.cpp +89 -51
- esphome/core/application.h +1 -0
- esphome/core/component.cpp +41 -19
- esphome/core/component.h +17 -13
- esphome/core/config.py +7 -7
- esphome/core/defines.h +4 -0
- esphome/core/entity_base.cpp +22 -8
- esphome/core/entity_base.h +43 -0
- esphome/core/helpers.cpp +26 -13
- esphome/core/helpers.h +4 -3
- esphome/core/ring_buffer.cpp +6 -2
- esphome/core/ring_buffer.h +2 -1
- esphome/core/scheduler.cpp +175 -94
- esphome/core/scheduler.h +66 -35
- esphome/core/time.cpp +6 -20
- esphome/coroutine.py +80 -3
- esphome/cpp_generator.py +13 -0
- esphome/cpp_helpers.py +2 -2
- esphome/dashboard/web_server.py +67 -10
- esphome/espota2.py +13 -6
- esphome/helpers.py +68 -83
- esphome/resolver.py +67 -0
- esphome/util.py +9 -6
- esphome/wizard.py +39 -26
- {esphome-2025.8.4.dist-info → esphome-2025.9.0b1.dist-info}/METADATA +9 -9
- {esphome-2025.8.4.dist-info → esphome-2025.9.0b1.dist-info}/RECORD +344 -313
- /esphome/components/ld2420/text_sensor/{text_sensor.h → ld2420_text_sensor.h} +0 -0
- /esphome/components/opentherm/number/{number.h → opentherm_number.h} +0 -0
- /esphome/components/opentherm/output/{output.h → opentherm_output.h} +0 -0
- /esphome/components/opentherm/switch/{switch.h → opentherm_switch.h} +0 -0
- {esphome-2025.8.4.dist-info → esphome-2025.9.0b1.dist-info}/WHEEL +0 -0
- {esphome-2025.8.4.dist-info → esphome-2025.9.0b1.dist-info}/entry_points.txt +0 -0
- {esphome-2025.8.4.dist-info → esphome-2025.9.0b1.dist-info}/licenses/LICENSE +0 -0
- {esphome-2025.8.4.dist-info → esphome-2025.9.0b1.dist-info}/top_level.txt +0 -0
esphome/helpers.py
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import codecs
|
2
4
|
from contextlib import suppress
|
3
5
|
import ipaddress
|
@@ -11,6 +13,18 @@ from urllib.parse import urlparse
|
|
11
13
|
|
12
14
|
from esphome.const import __version__ as ESPHOME_VERSION
|
13
15
|
|
16
|
+
# Type aliases for socket address information
|
17
|
+
AddrInfo = tuple[
|
18
|
+
int, # family (AF_INET, AF_INET6, etc.)
|
19
|
+
int, # type (SOCK_STREAM, SOCK_DGRAM, etc.)
|
20
|
+
int, # proto (IPPROTO_TCP, etc.)
|
21
|
+
str, # canonname
|
22
|
+
tuple[str, int] | tuple[str, int, int, int], # sockaddr (IPv4 or IPv6)
|
23
|
+
]
|
24
|
+
IPv4SockAddr = tuple[str, int] # (host, port)
|
25
|
+
IPv6SockAddr = tuple[str, int, int, int] # (host, port, flowinfo, scope_id)
|
26
|
+
SockAddr = IPv4SockAddr | IPv6SockAddr
|
27
|
+
|
14
28
|
_LOGGER = logging.getLogger(__name__)
|
15
29
|
|
16
30
|
IS_MACOS = platform.system() == "Darwin"
|
@@ -147,32 +161,7 @@ def is_ip_address(host):
|
|
147
161
|
return False
|
148
162
|
|
149
163
|
|
150
|
-
def
|
151
|
-
from esphome.core import EsphomeError
|
152
|
-
from esphome.zeroconf import EsphomeZeroconf
|
153
|
-
|
154
|
-
try:
|
155
|
-
zc = EsphomeZeroconf()
|
156
|
-
except Exception as err:
|
157
|
-
raise EsphomeError(
|
158
|
-
"Cannot start mDNS sockets, is this a docker container without "
|
159
|
-
"host network mode?"
|
160
|
-
) from err
|
161
|
-
try:
|
162
|
-
info = zc.resolve_host(f"{host}.")
|
163
|
-
except Exception as err:
|
164
|
-
raise EsphomeError(f"Error resolving mDNS hostname: {err}") from err
|
165
|
-
finally:
|
166
|
-
zc.close()
|
167
|
-
if info is None:
|
168
|
-
raise EsphomeError(
|
169
|
-
"Error resolving address with mDNS: Did not respond. "
|
170
|
-
"Maybe the device is offline."
|
171
|
-
)
|
172
|
-
return info
|
173
|
-
|
174
|
-
|
175
|
-
def addr_preference_(res):
|
164
|
+
def addr_preference_(res: AddrInfo) -> int:
|
176
165
|
# Trivial alternative to RFC6724 sorting. Put sane IPv6 first, then
|
177
166
|
# Legacy IP, then IPv6 link-local addresses without an actual link.
|
178
167
|
sa = res[4]
|
@@ -184,66 +173,70 @@ def addr_preference_(res):
|
|
184
173
|
return 1
|
185
174
|
|
186
175
|
|
187
|
-
def resolve_ip_address(host, port):
|
176
|
+
def resolve_ip_address(host: str | list[str], port: int) -> list[AddrInfo]:
|
188
177
|
import socket
|
189
178
|
|
190
|
-
from esphome.core import EsphomeError
|
191
|
-
|
192
179
|
# There are five cases here. The host argument could be one of:
|
193
180
|
# • a *list* of IP addresses discovered by MQTT,
|
194
181
|
# • a single IP address specified by the user,
|
195
182
|
# • a .local hostname to be resolved by mDNS,
|
196
183
|
# • a normal hostname to be resolved in DNS, or
|
197
184
|
# • A URL from which we should extract the hostname.
|
198
|
-
|
199
|
-
|
200
|
-
# string form which need to be converted to a 5-tuple to be used
|
201
|
-
# for the socket connection attempt. The easiest way to construct
|
202
|
-
# those is to pass the IP address string to getaddrinfo(). Which,
|
203
|
-
# coincidentally, is how we do hostname lookups in the other cases
|
204
|
-
# too. So first build a list which contains either IP addresses or
|
205
|
-
# a single hostname, then call getaddrinfo() on each element of
|
206
|
-
# that list.
|
207
|
-
|
208
|
-
errs = []
|
185
|
+
|
186
|
+
hosts: list[str]
|
209
187
|
if isinstance(host, list):
|
210
|
-
|
211
|
-
elif is_ip_address(host):
|
212
|
-
addr_list = [host]
|
188
|
+
hosts = host
|
213
189
|
else:
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
190
|
+
if not is_ip_address(host):
|
191
|
+
url = urlparse(host)
|
192
|
+
if url.scheme != "":
|
193
|
+
host = url.hostname
|
194
|
+
hosts = [host]
|
195
|
+
|
196
|
+
res: list[AddrInfo] = []
|
197
|
+
if all(is_ip_address(h) for h in hosts):
|
198
|
+
# Fast path: all are IP addresses, use socket.getaddrinfo with AI_NUMERICHOST
|
199
|
+
for addr in hosts:
|
220
200
|
try:
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
#
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
201
|
+
res += socket.getaddrinfo(
|
202
|
+
addr, port, proto=socket.IPPROTO_TCP, flags=socket.AI_NUMERICHOST
|
203
|
+
)
|
204
|
+
except OSError:
|
205
|
+
_LOGGER.debug("Failed to parse IP address '%s'", addr)
|
206
|
+
# Sort by preference
|
207
|
+
res.sort(key=addr_preference_)
|
208
|
+
return res
|
209
|
+
|
210
|
+
from esphome.resolver import AsyncResolver
|
211
|
+
|
212
|
+
resolver = AsyncResolver(hosts, port)
|
213
|
+
addr_infos = resolver.resolve()
|
214
|
+
# Convert aioesphomeapi AddrInfo to our format
|
215
|
+
for addr_info in addr_infos:
|
216
|
+
sockaddr = addr_info.sockaddr
|
217
|
+
if addr_info.family == socket.AF_INET6:
|
218
|
+
# IPv6
|
219
|
+
sockaddr_tuple = (
|
220
|
+
sockaddr.address,
|
221
|
+
sockaddr.port,
|
222
|
+
sockaddr.flowinfo,
|
223
|
+
sockaddr.scope_id,
|
224
|
+
)
|
225
|
+
else:
|
226
|
+
# IPv4
|
227
|
+
sockaddr_tuple = (sockaddr.address, sockaddr.port)
|
228
|
+
|
229
|
+
res.append(
|
230
|
+
(
|
231
|
+
addr_info.family,
|
232
|
+
addr_info.type,
|
233
|
+
addr_info.proto,
|
234
|
+
"", # canonname
|
235
|
+
sockaddr_tuple,
|
236
|
+
)
|
237
|
+
)
|
244
238
|
|
245
|
-
#
|
246
|
-
# the link. Put those last in the list to be attempted.
|
239
|
+
# Sort by preference
|
247
240
|
res.sort(key=addr_preference_)
|
248
241
|
return res
|
249
242
|
|
@@ -262,15 +255,7 @@ def sort_ip_addresses(address_list: list[str]) -> list[str]:
|
|
262
255
|
|
263
256
|
# First "resolve" all the IP addresses to getaddrinfo() tuples of the form
|
264
257
|
# (family, type, proto, canonname, sockaddr)
|
265
|
-
res: list[
|
266
|
-
tuple[
|
267
|
-
int,
|
268
|
-
int,
|
269
|
-
int,
|
270
|
-
str | None,
|
271
|
-
tuple[str, int] | tuple[str, int, int, int],
|
272
|
-
]
|
273
|
-
] = []
|
258
|
+
res: list[AddrInfo] = []
|
274
259
|
for addr in address_list:
|
275
260
|
# This should always work as these are supposed to be IP addresses
|
276
261
|
try:
|
esphome/resolver.py
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
"""DNS resolver for ESPHome using aioesphomeapi."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
import asyncio
|
6
|
+
import threading
|
7
|
+
|
8
|
+
from aioesphomeapi.core import ResolveAPIError, ResolveTimeoutAPIError
|
9
|
+
import aioesphomeapi.host_resolver as hr
|
10
|
+
|
11
|
+
from esphome.core import EsphomeError
|
12
|
+
|
13
|
+
RESOLVE_TIMEOUT = 10.0 # seconds
|
14
|
+
|
15
|
+
|
16
|
+
class AsyncResolver(threading.Thread):
|
17
|
+
"""Resolver using aioesphomeapi that runs in a thread for faster results.
|
18
|
+
|
19
|
+
This resolver uses aioesphomeapi's async_resolve_host to handle DNS resolution,
|
20
|
+
including proper .local domain fallback. Running in a thread allows us to get
|
21
|
+
the result immediately without waiting for asyncio.run() to complete its
|
22
|
+
cleanup cycle, which can take significant time.
|
23
|
+
"""
|
24
|
+
|
25
|
+
def __init__(self, hosts: list[str], port: int) -> None:
|
26
|
+
"""Initialize the resolver."""
|
27
|
+
super().__init__(daemon=True)
|
28
|
+
self.hosts = hosts
|
29
|
+
self.port = port
|
30
|
+
self.result: list[hr.AddrInfo] | None = None
|
31
|
+
self.exception: Exception | None = None
|
32
|
+
self.event = threading.Event()
|
33
|
+
|
34
|
+
async def _resolve(self) -> None:
|
35
|
+
"""Resolve hostnames to IP addresses."""
|
36
|
+
try:
|
37
|
+
self.result = await hr.async_resolve_host(
|
38
|
+
self.hosts, self.port, timeout=RESOLVE_TIMEOUT
|
39
|
+
)
|
40
|
+
except Exception as e: # pylint: disable=broad-except
|
41
|
+
# We need to catch all exceptions to ensure the event is set
|
42
|
+
# Otherwise the thread could hang forever
|
43
|
+
self.exception = e
|
44
|
+
finally:
|
45
|
+
self.event.set()
|
46
|
+
|
47
|
+
def run(self) -> None:
|
48
|
+
"""Run the DNS resolution."""
|
49
|
+
asyncio.run(self._resolve())
|
50
|
+
|
51
|
+
def resolve(self) -> list[hr.AddrInfo]:
|
52
|
+
"""Start the thread and wait for the result."""
|
53
|
+
self.start()
|
54
|
+
|
55
|
+
if not self.event.wait(
|
56
|
+
timeout=RESOLVE_TIMEOUT + 1.0
|
57
|
+
): # Give it 1 second more than the resolver timeout
|
58
|
+
raise EsphomeError("Timeout resolving IP address")
|
59
|
+
|
60
|
+
if exc := self.exception:
|
61
|
+
if isinstance(exc, ResolveTimeoutAPIError):
|
62
|
+
raise EsphomeError(f"Timeout resolving IP address: {exc}") from exc
|
63
|
+
if isinstance(exc, ResolveAPIError):
|
64
|
+
raise EsphomeError(f"Error resolving IP address: {exc}") from exc
|
65
|
+
raise exc
|
66
|
+
|
67
|
+
return self.result
|
esphome/util.py
CHANGED
@@ -272,12 +272,15 @@ class OrderedDict(collections.OrderedDict):
|
|
272
272
|
return dict(self).__repr__()
|
273
273
|
|
274
274
|
|
275
|
-
def list_yaml_files(
|
276
|
-
files =
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
275
|
+
def list_yaml_files(configs: list[str]) -> list[str]:
|
276
|
+
files: list[str] = []
|
277
|
+
for config in configs:
|
278
|
+
if os.path.isfile(config):
|
279
|
+
files.append(config)
|
280
|
+
else:
|
281
|
+
files.extend(os.path.join(config, p) for p in os.listdir(config))
|
282
|
+
files = filter_yaml_files(files)
|
283
|
+
return sorted(files)
|
281
284
|
|
282
285
|
|
283
286
|
def filter_yaml_files(files: list[str]) -> list[str]:
|
esphome/wizard.py
CHANGED
@@ -189,32 +189,45 @@ def wizard_write(path, **kwargs):
|
|
189
189
|
from esphome.components.rtl87xx import boards as rtl87xx_boards
|
190
190
|
|
191
191
|
name = kwargs["name"]
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
192
|
+
if kwargs["type"] == "empty":
|
193
|
+
file_text = ""
|
194
|
+
# Will be updated later after editing the file
|
195
|
+
hardware = "UNKNOWN"
|
196
|
+
elif kwargs["type"] == "upload":
|
197
|
+
file_text = kwargs["file_text"]
|
198
|
+
hardware = "UNKNOWN"
|
199
|
+
else: # "basic"
|
200
|
+
board = kwargs["board"]
|
201
|
+
|
202
|
+
for key in ("ssid", "psk", "password", "ota_password"):
|
203
|
+
if key in kwargs:
|
204
|
+
kwargs[key] = sanitize_double_quotes(kwargs[key])
|
205
|
+
if "platform" not in kwargs:
|
206
|
+
if board in esp8266_boards.BOARDS:
|
207
|
+
platform = "ESP8266"
|
208
|
+
elif board in esp32_boards.BOARDS:
|
209
|
+
platform = "ESP32"
|
210
|
+
elif board in rp2040_boards.BOARDS:
|
211
|
+
platform = "RP2040"
|
212
|
+
elif board in bk72xx_boards.BOARDS:
|
213
|
+
platform = "BK72XX"
|
214
|
+
elif board in ln882x_boards.BOARDS:
|
215
|
+
platform = "LN882X"
|
216
|
+
elif board in rtl87xx_boards.BOARDS:
|
217
|
+
platform = "RTL87XX"
|
218
|
+
else:
|
219
|
+
safe_print(color(AnsiFore.RED, f'The board "{board}" is unknown.'))
|
220
|
+
return False
|
221
|
+
kwargs["platform"] = platform
|
222
|
+
hardware = kwargs["platform"]
|
223
|
+
file_text = wizard_file(**kwargs)
|
224
|
+
|
225
|
+
# Check if file already exists to prevent overwriting
|
226
|
+
if os.path.exists(path) and os.path.isfile(path):
|
227
|
+
safe_print(color(AnsiFore.RED, f'The file "{path}" already exists.'))
|
228
|
+
return False
|
229
|
+
|
230
|
+
write_file(path, file_text)
|
218
231
|
storage = StorageJSON.from_wizard(name, name, f"{name}.local", hardware)
|
219
232
|
storage_path = ext_storage_path(os.path.basename(path))
|
220
233
|
storage.save(storage_path)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: esphome
|
3
|
-
Version: 2025.
|
3
|
+
Version: 2025.9.0b1
|
4
4
|
Summary: ESPHome is a system to configure your microcontrollers by simple yet powerful configuration files and control them remotely through Home Automation systems.
|
5
5
|
Author-email: The ESPHome Authors <esphome@openhomefoundation.org>
|
6
6
|
License: MIT
|
@@ -36,11 +36,11 @@ Requires-Dist: pyserial==3.5
|
|
36
36
|
Requires-Dist: platformio==6.1.18
|
37
37
|
Requires-Dist: esptool==5.0.2
|
38
38
|
Requires-Dist: click==8.1.7
|
39
|
-
Requires-Dist: esphome-dashboard==
|
40
|
-
Requires-Dist: aioesphomeapi==
|
41
|
-
Requires-Dist: zeroconf==0.147.
|
39
|
+
Requires-Dist: esphome-dashboard==20250904.0
|
40
|
+
Requires-Dist: aioesphomeapi==40.1.0
|
41
|
+
Requires-Dist: zeroconf==0.147.2
|
42
42
|
Requires-Dist: puremagic==1.30
|
43
|
-
Requires-Dist: ruamel.yaml==0.18.
|
43
|
+
Requires-Dist: ruamel.yaml==0.18.15
|
44
44
|
Requires-Dist: esphome-glyphsets==0.2.0
|
45
45
|
Requires-Dist: pillow==10.4.0
|
46
46
|
Requires-Dist: cairosvg==2.8.2
|
@@ -56,12 +56,12 @@ Requires-Dist: yamllint==1.37.1; extra == "dev"
|
|
56
56
|
Provides-Extra: test
|
57
57
|
Requires-Dist: pylint==3.3.8; extra == "test"
|
58
58
|
Requires-Dist: flake8==7.3.0; extra == "test"
|
59
|
-
Requires-Dist: ruff==0.12.
|
59
|
+
Requires-Dist: ruff==0.12.12; extra == "test"
|
60
60
|
Requires-Dist: pyupgrade==3.20.0; extra == "test"
|
61
61
|
Requires-Dist: pre-commit; extra == "test"
|
62
|
-
Requires-Dist: pytest==8.4.
|
63
|
-
Requires-Dist: pytest-cov==
|
64
|
-
Requires-Dist: pytest-mock==3.
|
62
|
+
Requires-Dist: pytest==8.4.2; extra == "test"
|
63
|
+
Requires-Dist: pytest-cov==7.0.0; extra == "test"
|
64
|
+
Requires-Dist: pytest-mock==3.15.0; extra == "test"
|
65
65
|
Requires-Dist: pytest-asyncio==1.1.0; extra == "test"
|
66
66
|
Requires-Dist: pytest-xdist==3.8.0; extra == "test"
|
67
67
|
Requires-Dist: asyncmock==0.4.2; extra == "test"
|