esphome 2025.10.0b4__py3-none-any.whl → 2025.10.2__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.
Potentially problematic release.
This version of esphome might be problematic. Click here for more details.
- esphome/__main__.py +87 -35
- esphome/components/bme680_bsec/__init__.py +1 -1
- esphome/components/bme68x_bsec2/__init__.py +1 -1
- esphome/components/datetime/datetime_base.h +0 -2
- esphome/components/display/display.cpp +5 -2
- esphome/components/esp32/__init__.py +1 -0
- esphome/components/esp32/core.cpp +11 -0
- esphome/components/esphome/ota/__init__.py +4 -2
- esphome/components/htu21d/htu21d.cpp +2 -2
- esphome/components/mipi/__init__.py +17 -14
- esphome/components/mipi_spi/display.py +17 -11
- esphome/components/mipi_spi/mipi_spi.h +25 -19
- esphome/components/mipi_spi/models/waveshare.py +10 -0
- esphome/components/scd4x/sensor.py +1 -1
- esphome/components/substitutions/__init__.py +5 -5
- esphome/components/wifi/__init__.py +2 -2
- esphome/config.py +4 -5
- esphome/config_helpers.py +25 -1
- esphome/config_validation.py +14 -0
- esphome/const.py +2 -1
- esphome/core/__init__.py +4 -0
- esphome/dashboard/settings.py +10 -1
- esphome/dashboard/web_server.py +2 -1
- esphome/espota2.py +5 -5
- {esphome-2025.10.0b4.dist-info → esphome-2025.10.2.dist-info}/METADATA +1 -1
- {esphome-2025.10.0b4.dist-info → esphome-2025.10.2.dist-info}/RECORD +30 -30
- {esphome-2025.10.0b4.dist-info → esphome-2025.10.2.dist-info}/WHEEL +0 -0
- {esphome-2025.10.0b4.dist-info → esphome-2025.10.2.dist-info}/entry_points.txt +0 -0
- {esphome-2025.10.0b4.dist-info → esphome-2025.10.2.dist-info}/licenses/LICENSE +0 -0
- {esphome-2025.10.0b4.dist-info → esphome-2025.10.2.dist-info}/top_level.txt +0 -0
esphome/__main__.py
CHANGED
|
@@ -117,6 +117,17 @@ class Purpose(StrEnum):
|
|
|
117
117
|
LOGGING = "logging"
|
|
118
118
|
|
|
119
119
|
|
|
120
|
+
class PortType(StrEnum):
|
|
121
|
+
SERIAL = "SERIAL"
|
|
122
|
+
NETWORK = "NETWORK"
|
|
123
|
+
MQTT = "MQTT"
|
|
124
|
+
MQTTIP = "MQTTIP"
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
# Magic MQTT port types that require special handling
|
|
128
|
+
_MQTT_PORT_TYPES = frozenset({PortType.MQTT, PortType.MQTTIP})
|
|
129
|
+
|
|
130
|
+
|
|
120
131
|
def _resolve_with_cache(address: str, purpose: Purpose) -> list[str]:
|
|
121
132
|
"""Resolve an address using cache if available, otherwise return the address itself."""
|
|
122
133
|
if CORE.address_cache and (cached := CORE.address_cache.get_addresses(address)):
|
|
@@ -174,7 +185,9 @@ def choose_upload_log_host(
|
|
|
174
185
|
else:
|
|
175
186
|
resolved.append(device)
|
|
176
187
|
if not resolved:
|
|
177
|
-
|
|
188
|
+
raise EsphomeError(
|
|
189
|
+
f"All specified devices {defaults} could not be resolved. Is the device connected to the network?"
|
|
190
|
+
)
|
|
178
191
|
return resolved
|
|
179
192
|
|
|
180
193
|
# No devices specified, show interactive chooser
|
|
@@ -280,16 +293,67 @@ def mqtt_get_ip(config: ConfigType, username: str, password: str, client_id: str
|
|
|
280
293
|
return mqtt.get_esphome_device_ip(config, username, password, client_id)
|
|
281
294
|
|
|
282
295
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
296
|
+
def _resolve_network_devices(
|
|
297
|
+
devices: list[str], config: ConfigType, args: ArgsProtocol
|
|
298
|
+
) -> list[str]:
|
|
299
|
+
"""Resolve device list, converting MQTT magic strings to actual IP addresses.
|
|
300
|
+
|
|
301
|
+
This function filters the devices list to:
|
|
302
|
+
- Replace MQTT/MQTTIP magic strings with actual IP addresses via MQTT lookup
|
|
303
|
+
- Deduplicate addresses while preserving order
|
|
304
|
+
- Only resolve MQTT once even if multiple MQTT strings are present
|
|
305
|
+
- If MQTT resolution fails, log a warning and continue with other devices
|
|
306
|
+
|
|
307
|
+
Args:
|
|
308
|
+
devices: List of device identifiers (IPs, hostnames, or magic strings)
|
|
309
|
+
config: ESPHome configuration
|
|
310
|
+
args: Command-line arguments containing MQTT credentials
|
|
311
|
+
|
|
312
|
+
Returns:
|
|
313
|
+
List of network addresses suitable for connection attempts
|
|
314
|
+
"""
|
|
315
|
+
network_devices: list[str] = []
|
|
316
|
+
mqtt_resolved: bool = False
|
|
317
|
+
|
|
318
|
+
for device in devices:
|
|
319
|
+
port_type = get_port_type(device)
|
|
320
|
+
if port_type in _MQTT_PORT_TYPES:
|
|
321
|
+
# Only resolve MQTT once, even if multiple MQTT entries
|
|
322
|
+
if not mqtt_resolved:
|
|
323
|
+
try:
|
|
324
|
+
mqtt_ips = mqtt_get_ip(
|
|
325
|
+
config, args.username, args.password, args.client_id
|
|
326
|
+
)
|
|
327
|
+
network_devices.extend(mqtt_ips)
|
|
328
|
+
except EsphomeError as err:
|
|
329
|
+
_LOGGER.warning(
|
|
330
|
+
"MQTT IP discovery failed (%s), will try other devices if available",
|
|
331
|
+
err,
|
|
332
|
+
)
|
|
333
|
+
mqtt_resolved = True
|
|
334
|
+
elif device not in network_devices:
|
|
335
|
+
# Regular network address or IP - add if not already present
|
|
336
|
+
network_devices.append(device)
|
|
337
|
+
|
|
338
|
+
return network_devices
|
|
287
339
|
|
|
288
340
|
|
|
289
|
-
def get_port_type(port: str) ->
|
|
341
|
+
def get_port_type(port: str) -> PortType:
|
|
342
|
+
"""Determine the type of port/device identifier.
|
|
343
|
+
|
|
344
|
+
Returns:
|
|
345
|
+
PortType.SERIAL for serial ports (/dev/ttyUSB0, COM1, etc.)
|
|
346
|
+
PortType.MQTT for MQTT logging
|
|
347
|
+
PortType.MQTTIP for MQTT IP lookup
|
|
348
|
+
PortType.NETWORK for IP addresses, hostnames, or mDNS names
|
|
349
|
+
"""
|
|
290
350
|
if port.startswith("/") or port.startswith("COM"):
|
|
291
|
-
return
|
|
292
|
-
|
|
351
|
+
return PortType.SERIAL
|
|
352
|
+
if port == "MQTT":
|
|
353
|
+
return PortType.MQTT
|
|
354
|
+
if port == "MQTTIP":
|
|
355
|
+
return PortType.MQTTIP
|
|
356
|
+
return PortType.NETWORK
|
|
293
357
|
|
|
294
358
|
|
|
295
359
|
def run_miniterm(config: ConfigType, port: str, args) -> int:
|
|
@@ -489,7 +553,7 @@ def upload_using_platformio(config: ConfigType, port: str):
|
|
|
489
553
|
|
|
490
554
|
|
|
491
555
|
def check_permissions(port: str):
|
|
492
|
-
if os.name == "posix" and get_port_type(port) ==
|
|
556
|
+
if os.name == "posix" and get_port_type(port) == PortType.SERIAL:
|
|
493
557
|
# Check if we can open selected serial port
|
|
494
558
|
if not os.access(port, os.F_OK):
|
|
495
559
|
raise EsphomeError(
|
|
@@ -517,7 +581,7 @@ def upload_program(
|
|
|
517
581
|
except AttributeError:
|
|
518
582
|
pass
|
|
519
583
|
|
|
520
|
-
if get_port_type(host) ==
|
|
584
|
+
if get_port_type(host) == PortType.SERIAL:
|
|
521
585
|
check_permissions(host)
|
|
522
586
|
|
|
523
587
|
exit_code = 1
|
|
@@ -544,17 +608,16 @@ def upload_program(
|
|
|
544
608
|
from esphome import espota2
|
|
545
609
|
|
|
546
610
|
remote_port = int(ota_conf[CONF_PORT])
|
|
547
|
-
password = ota_conf.get(CONF_PASSWORD
|
|
611
|
+
password = ota_conf.get(CONF_PASSWORD)
|
|
548
612
|
if getattr(args, "file", None) is not None:
|
|
549
613
|
binary = Path(args.file)
|
|
550
614
|
else:
|
|
551
615
|
binary = CORE.firmware_bin
|
|
552
616
|
|
|
553
|
-
# MQTT
|
|
554
|
-
|
|
555
|
-
devices = mqtt_get_ip(config, args.username, args.password, args.client_id)
|
|
617
|
+
# Resolve MQTT magic strings to actual IP addresses
|
|
618
|
+
network_devices = _resolve_network_devices(devices, config, args)
|
|
556
619
|
|
|
557
|
-
return espota2.run_ota(
|
|
620
|
+
return espota2.run_ota(network_devices, remote_port, password, binary)
|
|
558
621
|
|
|
559
622
|
|
|
560
623
|
def show_logs(config: ConfigType, args: ArgsProtocol, devices: list[str]) -> int | None:
|
|
@@ -569,33 +632,22 @@ def show_logs(config: ConfigType, args: ArgsProtocol, devices: list[str]) -> int
|
|
|
569
632
|
raise EsphomeError("Logger is not configured!")
|
|
570
633
|
|
|
571
634
|
port = devices[0]
|
|
635
|
+
port_type = get_port_type(port)
|
|
572
636
|
|
|
573
|
-
if
|
|
637
|
+
if port_type == PortType.SERIAL:
|
|
574
638
|
check_permissions(port)
|
|
575
639
|
return run_miniterm(config, port, args)
|
|
576
640
|
|
|
577
|
-
port_type = get_port_type(port)
|
|
578
|
-
|
|
579
641
|
# Check if we should use API for logging
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
# The resolve_ip_address() function in helpers.py handles all types
|
|
586
|
-
addresses_to_use = devices
|
|
587
|
-
elif port_type in ("MQTT", "MQTTIP") and has_mqtt_ip_lookup():
|
|
588
|
-
# Use MQTT IP lookup for MQTT/MQTTIP types
|
|
589
|
-
addresses_to_use = mqtt_get_ip(
|
|
590
|
-
config, args.username, args.password, args.client_id
|
|
591
|
-
)
|
|
592
|
-
|
|
593
|
-
if addresses_to_use is not None:
|
|
594
|
-
from esphome.components.api.client import run_logs
|
|
642
|
+
# Resolve MQTT magic strings to actual IP addresses
|
|
643
|
+
if has_api() and (
|
|
644
|
+
network_devices := _resolve_network_devices(devices, config, args)
|
|
645
|
+
):
|
|
646
|
+
from esphome.components.api.client import run_logs
|
|
595
647
|
|
|
596
|
-
|
|
648
|
+
return run_logs(config, network_devices)
|
|
597
649
|
|
|
598
|
-
if port_type in (
|
|
650
|
+
if port_type in (PortType.NETWORK, PortType.MQTT) and has_mqtt_logging():
|
|
599
651
|
from esphome import mqtt
|
|
600
652
|
|
|
601
653
|
return mqtt.show_logs(
|
|
@@ -41,7 +41,7 @@ CONFIG_SCHEMA = cv.All(
|
|
|
41
41
|
cv.Schema(
|
|
42
42
|
{
|
|
43
43
|
cv.GenerateID(): cv.declare_id(BME680BSECComponent),
|
|
44
|
-
cv.Optional(CONF_TEMPERATURE_OFFSET, default=0): cv.
|
|
44
|
+
cv.Optional(CONF_TEMPERATURE_OFFSET, default=0): cv.temperature_delta,
|
|
45
45
|
cv.Optional(CONF_IAQ_MODE, default="STATIC"): cv.enum(
|
|
46
46
|
IAQ_MODE_OPTIONS, upper=True
|
|
47
47
|
),
|
|
@@ -139,7 +139,7 @@ CONFIG_SCHEMA_BASE = (
|
|
|
139
139
|
cv.Optional(CONF_SUPPLY_VOLTAGE, default="3.3V"): cv.enum(
|
|
140
140
|
VOLTAGE_OPTIONS, upper=True
|
|
141
141
|
),
|
|
142
|
-
cv.Optional(CONF_TEMPERATURE_OFFSET, default=0): cv.
|
|
142
|
+
cv.Optional(CONF_TEMPERATURE_OFFSET, default=0): cv.temperature_delta,
|
|
143
143
|
cv.Optional(
|
|
144
144
|
CONF_STATE_SAVE_INTERVAL, default="6hours"
|
|
145
145
|
): cv.positive_time_period_minutes,
|
|
@@ -30,14 +30,12 @@ class DateTimeBase : public EntityBase {
|
|
|
30
30
|
#endif
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
#ifdef USE_TIME
|
|
34
33
|
class DateTimeStateTrigger : public Trigger<ESPTime> {
|
|
35
34
|
public:
|
|
36
35
|
explicit DateTimeStateTrigger(DateTimeBase *parent) {
|
|
37
36
|
parent->add_on_state_callback([this, parent]() { this->trigger(parent->state_as_esptime()); });
|
|
38
37
|
}
|
|
39
38
|
};
|
|
40
|
-
#endif
|
|
41
39
|
|
|
42
40
|
} // namespace datetime
|
|
43
41
|
} // namespace esphome
|
|
@@ -775,7 +775,7 @@ void Display::test_card() {
|
|
|
775
775
|
int shift_y = (h - image_h) / 2;
|
|
776
776
|
int line_w = (image_w - 6) / 6;
|
|
777
777
|
int image_c = image_w / 2;
|
|
778
|
-
for (auto i = 0; i
|
|
778
|
+
for (auto i = 0; i != image_h; i++) {
|
|
779
779
|
int c = esp_scale(i, image_h);
|
|
780
780
|
this->horizontal_line(shift_x + 0, shift_y + i, line_w, r.fade_to_white(c));
|
|
781
781
|
this->horizontal_line(shift_x + line_w, shift_y + i, line_w, r.fade_to_black(c)); //
|
|
@@ -809,8 +809,11 @@ void Display::test_card() {
|
|
|
809
809
|
}
|
|
810
810
|
}
|
|
811
811
|
}
|
|
812
|
-
this->rectangle(0, 0, w, h, Color(127, 0, 127));
|
|
813
812
|
this->filled_rectangle(0, 0, 10, 10, Color(255, 0, 255));
|
|
813
|
+
this->filled_rectangle(w - 10, 0, 10, 10, Color(255, 0, 255));
|
|
814
|
+
this->filled_rectangle(0, h - 10, 10, 10, Color(255, 0, 255));
|
|
815
|
+
this->filled_rectangle(w - 10, h - 10, 10, 10, Color(255, 0, 255));
|
|
816
|
+
this->rectangle(0, 0, w, h, Color(255, 255, 255));
|
|
814
817
|
this->stop_poller();
|
|
815
818
|
}
|
|
816
819
|
|
|
@@ -790,6 +790,7 @@ async def to_code(config):
|
|
|
790
790
|
add_idf_sdkconfig_option("CONFIG_AUTOSTART_ARDUINO", True)
|
|
791
791
|
add_idf_sdkconfig_option("CONFIG_MBEDTLS_PSK_MODES", True)
|
|
792
792
|
add_idf_sdkconfig_option("CONFIG_MBEDTLS_CERTIFICATE_BUNDLE", True)
|
|
793
|
+
add_idf_sdkconfig_option("CONFIG_ESP_PHY_REDUCE_TX_POWER", True)
|
|
793
794
|
|
|
794
795
|
cg.add_build_flag("-Wno-nonnull-compare")
|
|
795
796
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
#include <freertos/FreeRTOS.h>
|
|
7
7
|
#include <freertos/task.h>
|
|
8
8
|
#include <esp_idf_version.h>
|
|
9
|
+
#include <esp_ota_ops.h>
|
|
9
10
|
#include <esp_task_wdt.h>
|
|
10
11
|
#include <esp_timer.h>
|
|
11
12
|
#include <soc/rtc.h>
|
|
@@ -52,6 +53,16 @@ void arch_init() {
|
|
|
52
53
|
disableCore1WDT();
|
|
53
54
|
#endif
|
|
54
55
|
#endif
|
|
56
|
+
|
|
57
|
+
// If the bootloader was compiled with CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE the current
|
|
58
|
+
// partition will get rolled back unless it is marked as valid.
|
|
59
|
+
esp_ota_img_states_t state;
|
|
60
|
+
const esp_partition_t *running = esp_ota_get_running_partition();
|
|
61
|
+
if (esp_ota_get_state_partition(running, &state) == ESP_OK) {
|
|
62
|
+
if (state == ESP_OTA_IMG_PENDING_VERIFY) {
|
|
63
|
+
esp_ota_mark_app_valid_cancel_rollback();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
55
66
|
}
|
|
56
67
|
void IRAM_ATTR HOT arch_feed_wdt() { esp_task_wdt_reset(); }
|
|
57
68
|
|
|
@@ -19,6 +19,7 @@ from esphome.const import (
|
|
|
19
19
|
from esphome.core import CORE, coroutine_with_priority
|
|
20
20
|
from esphome.coroutine import CoroPriority
|
|
21
21
|
import esphome.final_validate as fv
|
|
22
|
+
from esphome.types import ConfigType
|
|
22
23
|
|
|
23
24
|
_LOGGER = logging.getLogger(__name__)
|
|
24
25
|
|
|
@@ -136,11 +137,12 @@ FINAL_VALIDATE_SCHEMA = ota_esphome_final_validate
|
|
|
136
137
|
|
|
137
138
|
|
|
138
139
|
@coroutine_with_priority(CoroPriority.OTA_UPDATES)
|
|
139
|
-
async def to_code(config):
|
|
140
|
+
async def to_code(config: ConfigType) -> None:
|
|
140
141
|
var = cg.new_Pvariable(config[CONF_ID])
|
|
141
142
|
cg.add(var.set_port(config[CONF_PORT]))
|
|
142
143
|
|
|
143
|
-
|
|
144
|
+
# Password could be set to an empty string and we can assume that means no password
|
|
145
|
+
if config.get(CONF_PASSWORD):
|
|
144
146
|
cg.add(var.set_auth_password(config[CONF_PASSWORD]))
|
|
145
147
|
cg.add_define("USE_OTA_PASSWORD")
|
|
146
148
|
# Only include hash algorithms when password is configured
|
|
@@ -9,8 +9,8 @@ static const char *const TAG = "htu21d";
|
|
|
9
9
|
|
|
10
10
|
static const uint8_t HTU21D_ADDRESS = 0x40;
|
|
11
11
|
static const uint8_t HTU21D_REGISTER_RESET = 0xFE;
|
|
12
|
-
static const uint8_t HTU21D_REGISTER_TEMPERATURE =
|
|
13
|
-
static const uint8_t HTU21D_REGISTER_HUMIDITY =
|
|
12
|
+
static const uint8_t HTU21D_REGISTER_TEMPERATURE = 0xF3;
|
|
13
|
+
static const uint8_t HTU21D_REGISTER_HUMIDITY = 0xF5;
|
|
14
14
|
static const uint8_t HTU21D_WRITERHT_REG_CMD = 0xE6; /**< Write RH/T User Register 1 */
|
|
15
15
|
static const uint8_t HTU21D_REGISTER_STATUS = 0xE7;
|
|
16
16
|
static const uint8_t HTU21D_WRITEHEATER_REG_CMD = 0x51; /**< Write Heater Control Register */
|
|
@@ -11,6 +11,7 @@ from esphome.const import (
|
|
|
11
11
|
CONF_BRIGHTNESS,
|
|
12
12
|
CONF_COLOR_ORDER,
|
|
13
13
|
CONF_DIMENSIONS,
|
|
14
|
+
CONF_DISABLED,
|
|
14
15
|
CONF_HEIGHT,
|
|
15
16
|
CONF_INIT_SEQUENCE,
|
|
16
17
|
CONF_INVERT_COLORS,
|
|
@@ -301,6 +302,8 @@ class DriverChip:
|
|
|
301
302
|
Check if a rotation can be implemented in hardware using the MADCTL register.
|
|
302
303
|
A rotation of 180 is always possible if x and y mirroring are supported, 90 and 270 are possible if the model supports swapping X and Y.
|
|
303
304
|
"""
|
|
305
|
+
if config.get(CONF_TRANSFORM) == CONF_DISABLED:
|
|
306
|
+
return False
|
|
304
307
|
transforms = self.transforms
|
|
305
308
|
rotation = config.get(CONF_ROTATION, 0)
|
|
306
309
|
if rotation == 0 or not transforms:
|
|
@@ -358,26 +361,26 @@ class DriverChip:
|
|
|
358
361
|
CONF_SWAP_XY: self.get_default(CONF_SWAP_XY),
|
|
359
362
|
},
|
|
360
363
|
)
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
364
|
+
if not isinstance(transform, dict):
|
|
365
|
+
# Presumably disabled
|
|
366
|
+
return {
|
|
367
|
+
CONF_MIRROR_X: False,
|
|
368
|
+
CONF_MIRROR_Y: False,
|
|
369
|
+
CONF_SWAP_XY: False,
|
|
370
|
+
CONF_TRANSFORM: False,
|
|
371
|
+
}
|
|
369
372
|
# Can we use the MADCTL register to set the rotation?
|
|
370
373
|
if can_transform and CONF_TRANSFORM not in config:
|
|
371
374
|
rotation = config[CONF_ROTATION]
|
|
372
375
|
if rotation == 180:
|
|
373
|
-
transform[CONF_MIRROR_X] = not
|
|
374
|
-
transform[CONF_MIRROR_Y] = not
|
|
376
|
+
transform[CONF_MIRROR_X] = not transform[CONF_MIRROR_X]
|
|
377
|
+
transform[CONF_MIRROR_Y] = not transform[CONF_MIRROR_Y]
|
|
375
378
|
elif rotation == 90:
|
|
376
|
-
transform[CONF_SWAP_XY] = not
|
|
377
|
-
transform[CONF_MIRROR_X] = not
|
|
379
|
+
transform[CONF_SWAP_XY] = not transform[CONF_SWAP_XY]
|
|
380
|
+
transform[CONF_MIRROR_X] = not transform[CONF_MIRROR_X]
|
|
378
381
|
else:
|
|
379
|
-
transform[CONF_SWAP_XY] = not
|
|
380
|
-
transform[CONF_MIRROR_Y] = not
|
|
382
|
+
transform[CONF_SWAP_XY] = not transform[CONF_SWAP_XY]
|
|
383
|
+
transform[CONF_MIRROR_Y] = not transform[CONF_MIRROR_Y]
|
|
381
384
|
transform[CONF_TRANSFORM] = True
|
|
382
385
|
return transform
|
|
383
386
|
|
|
@@ -37,6 +37,7 @@ from esphome.const import (
|
|
|
37
37
|
CONF_DATA_RATE,
|
|
38
38
|
CONF_DC_PIN,
|
|
39
39
|
CONF_DIMENSIONS,
|
|
40
|
+
CONF_DISABLED,
|
|
40
41
|
CONF_ENABLE_PIN,
|
|
41
42
|
CONF_ID,
|
|
42
43
|
CONF_INIT_SEQUENCE,
|
|
@@ -146,12 +147,15 @@ def swap_xy_schema(model):
|
|
|
146
147
|
def model_schema(config):
|
|
147
148
|
model = MODELS[config[CONF_MODEL]]
|
|
148
149
|
bus_mode = config[CONF_BUS_MODE]
|
|
149
|
-
transform = cv.
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
150
|
+
transform = cv.Any(
|
|
151
|
+
cv.Schema(
|
|
152
|
+
{
|
|
153
|
+
cv.Required(CONF_MIRROR_X): cv.boolean,
|
|
154
|
+
cv.Required(CONF_MIRROR_Y): cv.boolean,
|
|
155
|
+
**swap_xy_schema(model),
|
|
156
|
+
}
|
|
157
|
+
),
|
|
158
|
+
cv.one_of(CONF_DISABLED, lower=True),
|
|
155
159
|
)
|
|
156
160
|
# CUSTOM model will need to provide a custom init sequence
|
|
157
161
|
iseqconf = (
|
|
@@ -160,7 +164,11 @@ def model_schema(config):
|
|
|
160
164
|
else cv.Optional(CONF_INIT_SEQUENCE)
|
|
161
165
|
)
|
|
162
166
|
# Dimensions are optional if the model has a default width and the x-y transform is not overridden
|
|
163
|
-
|
|
167
|
+
transform_config = config.get(CONF_TRANSFORM, {})
|
|
168
|
+
is_swapped = (
|
|
169
|
+
isinstance(transform_config, dict)
|
|
170
|
+
and transform_config.get(CONF_SWAP_XY, False) is True
|
|
171
|
+
)
|
|
164
172
|
cv_dimensions = (
|
|
165
173
|
cv.Optional if model.get_default(CONF_WIDTH) and not is_swapped else cv.Required
|
|
166
174
|
)
|
|
@@ -192,9 +200,7 @@ def model_schema(config):
|
|
|
192
200
|
.extend(
|
|
193
201
|
{
|
|
194
202
|
cv.GenerateID(): cv.declare_id(MipiSpi),
|
|
195
|
-
cv_dimensions(CONF_DIMENSIONS): dimension_schema(
|
|
196
|
-
model.get_default(CONF_DRAW_ROUNDING, 1)
|
|
197
|
-
),
|
|
203
|
+
cv_dimensions(CONF_DIMENSIONS): dimension_schema(1),
|
|
198
204
|
model.option(CONF_ENABLE_PIN, cv.UNDEFINED): cv.ensure_list(
|
|
199
205
|
pins.gpio_output_pin_schema
|
|
200
206
|
),
|
|
@@ -400,6 +406,7 @@ def get_instance(config):
|
|
|
400
406
|
offset_height,
|
|
401
407
|
DISPLAY_ROTATIONS[rotation],
|
|
402
408
|
frac,
|
|
409
|
+
config[CONF_DRAW_ROUNDING],
|
|
403
410
|
]
|
|
404
411
|
)
|
|
405
412
|
return MipiSpiBuffer, templateargs
|
|
@@ -431,7 +438,6 @@ async def to_code(config):
|
|
|
431
438
|
else:
|
|
432
439
|
config[CONF_ROTATION] = 0
|
|
433
440
|
cg.add(var.set_model(config[CONF_MODEL]))
|
|
434
|
-
cg.add(var.set_draw_rounding(config[CONF_DRAW_ROUNDING]))
|
|
435
441
|
if enable_pin := config.get(CONF_ENABLE_PIN):
|
|
436
442
|
enable = [await cg.gpio_pin_expression(pin) for pin in enable_pin]
|
|
437
443
|
cg.add(var.set_enable_pins(enable))
|
|
@@ -38,7 +38,7 @@ static constexpr uint8_t MADCTL_BGR = 0x08; // Bit 3 Blue-Green-Red pixel ord
|
|
|
38
38
|
static constexpr uint8_t MADCTL_XFLIP = 0x02; // Mirror the display horizontally
|
|
39
39
|
static constexpr uint8_t MADCTL_YFLIP = 0x01; // Mirror the display vertically
|
|
40
40
|
|
|
41
|
-
static
|
|
41
|
+
static constexpr uint8_t DELAY_FLAG = 0xFF;
|
|
42
42
|
// store a 16 bit value in a buffer, big endian.
|
|
43
43
|
static inline void put16_be(uint8_t *buf, uint16_t value) {
|
|
44
44
|
buf[0] = value >> 8;
|
|
@@ -79,7 +79,7 @@ class MipiSpi : public display::Display,
|
|
|
79
79
|
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING,
|
|
80
80
|
spi::DATA_RATE_1MHZ> {
|
|
81
81
|
public:
|
|
82
|
-
MipiSpi()
|
|
82
|
+
MipiSpi() = default;
|
|
83
83
|
void update() override { this->stop_poller(); }
|
|
84
84
|
void draw_pixel_at(int x, int y, Color color) override {}
|
|
85
85
|
void set_model(const char *model) { this->model_ = model; }
|
|
@@ -99,7 +99,6 @@ class MipiSpi : public display::Display,
|
|
|
99
99
|
int get_width_internal() override { return WIDTH; }
|
|
100
100
|
int get_height_internal() override { return HEIGHT; }
|
|
101
101
|
void set_init_sequence(const std::vector<uint8_t> &sequence) { this->init_sequence_ = sequence; }
|
|
102
|
-
void set_draw_rounding(unsigned rounding) { this->draw_rounding_ = rounding; }
|
|
103
102
|
|
|
104
103
|
// reset the display, and write the init sequence
|
|
105
104
|
void setup() override {
|
|
@@ -326,6 +325,7 @@ class MipiSpi : public display::Display,
|
|
|
326
325
|
|
|
327
326
|
/**
|
|
328
327
|
* Writes a buffer to the display.
|
|
328
|
+
* @param ptr The pointer to the pixel data
|
|
329
329
|
* @param w Width of each line in bytes
|
|
330
330
|
* @param h Height of the buffer in rows
|
|
331
331
|
* @param pad Padding in bytes after each line
|
|
@@ -424,7 +424,6 @@ class MipiSpi : public display::Display,
|
|
|
424
424
|
|
|
425
425
|
// other properties set by configuration
|
|
426
426
|
bool invert_colors_{};
|
|
427
|
-
unsigned draw_rounding_{2};
|
|
428
427
|
optional<uint8_t> brightness_{};
|
|
429
428
|
const char *model_{"Unknown"};
|
|
430
429
|
std::vector<uint8_t> init_sequence_{};
|
|
@@ -444,12 +443,20 @@ class MipiSpi : public display::Display,
|
|
|
444
443
|
* @tparam OFFSET_WIDTH The x-offset of the display in pixels
|
|
445
444
|
* @tparam OFFSET_HEIGHT The y-offset of the display in pixels
|
|
446
445
|
* @tparam FRACTION The fraction of the display size to use for the buffer (e.g. 4 means a 1/4 buffer).
|
|
446
|
+
* @tparam ROUNDING The alignment requirement for drawing operations (e.g. 2 means that x coordinates must be even)
|
|
447
447
|
*/
|
|
448
448
|
template<typename BUFFERTYPE, PixelMode BUFFERPIXEL, bool IS_BIG_ENDIAN, PixelMode DISPLAYPIXEL, BusType BUS_TYPE,
|
|
449
|
-
|
|
449
|
+
uint16_t WIDTH, uint16_t HEIGHT, int OFFSET_WIDTH, int OFFSET_HEIGHT, display::DisplayRotation ROTATION,
|
|
450
|
+
int FRACTION, unsigned ROUNDING>
|
|
450
451
|
class MipiSpiBuffer : public MipiSpi<BUFFERTYPE, BUFFERPIXEL, IS_BIG_ENDIAN, DISPLAYPIXEL, BUS_TYPE, WIDTH, HEIGHT,
|
|
451
452
|
OFFSET_WIDTH, OFFSET_HEIGHT> {
|
|
452
453
|
public:
|
|
454
|
+
// these values define the buffer size needed to write in accordance with the chip pixel alignment
|
|
455
|
+
// requirements. If the required rounding does not divide the width and height, we round up to the next multiple and
|
|
456
|
+
// ignore the extra columns and rows when drawing, but use them to write to the display.
|
|
457
|
+
static constexpr unsigned BUFFER_WIDTH = (WIDTH + ROUNDING - 1) / ROUNDING * ROUNDING;
|
|
458
|
+
static constexpr unsigned BUFFER_HEIGHT = (HEIGHT + ROUNDING - 1) / ROUNDING * ROUNDING;
|
|
459
|
+
|
|
453
460
|
MipiSpiBuffer() { this->rotation_ = ROTATION; }
|
|
454
461
|
|
|
455
462
|
void dump_config() override {
|
|
@@ -461,15 +468,15 @@ class MipiSpiBuffer : public MipiSpi<BUFFERTYPE, BUFFERPIXEL, IS_BIG_ENDIAN, DIS
|
|
|
461
468
|
" Buffer fraction: 1/%d\n"
|
|
462
469
|
" Buffer bytes: %zu\n"
|
|
463
470
|
" Draw rounding: %u",
|
|
464
|
-
this->rotation_, BUFFERPIXEL * 8, FRACTION,
|
|
465
|
-
|
|
471
|
+
this->rotation_, BUFFERPIXEL * 8, FRACTION,
|
|
472
|
+
sizeof(BUFFERTYPE) * BUFFER_WIDTH * BUFFER_HEIGHT / FRACTION, ROUNDING);
|
|
466
473
|
}
|
|
467
474
|
|
|
468
475
|
void setup() override {
|
|
469
476
|
MipiSpi<BUFFERTYPE, BUFFERPIXEL, IS_BIG_ENDIAN, DISPLAYPIXEL, BUS_TYPE, WIDTH, HEIGHT, OFFSET_WIDTH,
|
|
470
477
|
OFFSET_HEIGHT>::setup();
|
|
471
478
|
RAMAllocator<BUFFERTYPE> allocator{};
|
|
472
|
-
this->buffer_ = allocator.allocate(
|
|
479
|
+
this->buffer_ = allocator.allocate(BUFFER_WIDTH * BUFFER_HEIGHT / FRACTION);
|
|
473
480
|
if (this->buffer_ == nullptr) {
|
|
474
481
|
this->mark_failed("Buffer allocation failed");
|
|
475
482
|
}
|
|
@@ -508,15 +515,14 @@ class MipiSpiBuffer : public MipiSpi<BUFFERTYPE, BUFFERPIXEL, IS_BIG_ENDIAN, DIS
|
|
|
508
515
|
esph_log_v(TAG, "x_low %d, y_low %d, x_high %d, y_high %d", this->x_low_, this->y_low_, this->x_high_,
|
|
509
516
|
this->y_high_);
|
|
510
517
|
// Some chips require that the drawing window be aligned on certain boundaries
|
|
511
|
-
|
|
512
|
-
this->
|
|
513
|
-
this->
|
|
514
|
-
this->
|
|
515
|
-
this->y_high_ = (this->y_high_ + dr) / dr * dr - 1;
|
|
518
|
+
this->x_low_ = this->x_low_ / ROUNDING * ROUNDING;
|
|
519
|
+
this->y_low_ = this->y_low_ / ROUNDING * ROUNDING;
|
|
520
|
+
this->x_high_ = (this->x_high_ + ROUNDING) / ROUNDING * ROUNDING - 1;
|
|
521
|
+
this->y_high_ = (this->y_high_ + ROUNDING) / ROUNDING * ROUNDING - 1;
|
|
516
522
|
int w = this->x_high_ - this->x_low_ + 1;
|
|
517
523
|
int h = this->y_high_ - this->y_low_ + 1;
|
|
518
524
|
this->write_to_display_(this->x_low_, this->y_low_, w, h, this->buffer_, this->x_low_,
|
|
519
|
-
this->y_low_ - this->start_line_,
|
|
525
|
+
this->y_low_ - this->start_line_, BUFFER_WIDTH - w);
|
|
520
526
|
// invalidate watermarks
|
|
521
527
|
this->x_low_ = WIDTH;
|
|
522
528
|
this->y_low_ = HEIGHT;
|
|
@@ -536,10 +542,10 @@ class MipiSpiBuffer : public MipiSpi<BUFFERTYPE, BUFFERPIXEL, IS_BIG_ENDIAN, DIS
|
|
|
536
542
|
void draw_pixel_at(int x, int y, Color color) override {
|
|
537
543
|
if (!this->get_clipping().inside(x, y))
|
|
538
544
|
return;
|
|
539
|
-
|
|
545
|
+
rotate_coordinates(x, y);
|
|
540
546
|
if (x < 0 || x >= WIDTH || y < this->start_line_ || y >= this->end_line_)
|
|
541
547
|
return;
|
|
542
|
-
this->buffer_[(y - this->start_line_) *
|
|
548
|
+
this->buffer_[(y - this->start_line_) * BUFFER_WIDTH + x] = convert_color(color);
|
|
543
549
|
if (x < this->x_low_) {
|
|
544
550
|
this->x_low_ = x;
|
|
545
551
|
}
|
|
@@ -560,7 +566,7 @@ class MipiSpiBuffer : public MipiSpi<BUFFERTYPE, BUFFERPIXEL, IS_BIG_ENDIAN, DIS
|
|
|
560
566
|
this->y_low_ = this->start_line_;
|
|
561
567
|
this->x_high_ = WIDTH - 1;
|
|
562
568
|
this->y_high_ = this->end_line_ - 1;
|
|
563
|
-
std::fill_n(this->buffer_, HEIGHT *
|
|
569
|
+
std::fill_n(this->buffer_, HEIGHT * BUFFER_WIDTH / FRACTION, convert_color(color));
|
|
564
570
|
}
|
|
565
571
|
|
|
566
572
|
int get_width() override {
|
|
@@ -577,7 +583,7 @@ class MipiSpiBuffer : public MipiSpi<BUFFERTYPE, BUFFERPIXEL, IS_BIG_ENDIAN, DIS
|
|
|
577
583
|
|
|
578
584
|
protected:
|
|
579
585
|
// Rotate the coordinates to match the display orientation.
|
|
580
|
-
void
|
|
586
|
+
static void rotate_coordinates(int &x, int &y) {
|
|
581
587
|
if constexpr (ROTATION == display::DISPLAY_ROTATION_180_DEGREES) {
|
|
582
588
|
x = WIDTH - x - 1;
|
|
583
589
|
y = HEIGHT - y - 1;
|
|
@@ -593,7 +599,7 @@ class MipiSpiBuffer : public MipiSpi<BUFFERTYPE, BUFFERPIXEL, IS_BIG_ENDIAN, DIS
|
|
|
593
599
|
}
|
|
594
600
|
|
|
595
601
|
// Convert a color to the buffer pixel format.
|
|
596
|
-
BUFFERTYPE
|
|
602
|
+
static BUFFERTYPE convert_color(const Color &color) {
|
|
597
603
|
if constexpr (BUFFERPIXEL == PIXEL_MODE_8) {
|
|
598
604
|
return (color.red & 0xE0) | (color.g & 0xE0) >> 3 | color.b >> 6;
|
|
599
605
|
} else if constexpr (BUFFERPIXEL == PIXEL_MODE_16) {
|
|
@@ -3,6 +3,7 @@ import esphome.config_validation as cv
|
|
|
3
3
|
|
|
4
4
|
from .amoled import CO5300
|
|
5
5
|
from .ili import ILI9488_A
|
|
6
|
+
from .jc import AXS15231
|
|
6
7
|
|
|
7
8
|
DriverChip(
|
|
8
9
|
"WAVESHARE-4-TFT",
|
|
@@ -152,3 +153,12 @@ CO5300.extend(
|
|
|
152
153
|
cs_pin=12,
|
|
153
154
|
reset_pin=39,
|
|
154
155
|
)
|
|
156
|
+
|
|
157
|
+
AXS15231.extend(
|
|
158
|
+
"WAVESHARE-ESP32-S3-TOUCH-LCD-3.49",
|
|
159
|
+
width=172,
|
|
160
|
+
height=640,
|
|
161
|
+
data_rate="80MHz",
|
|
162
|
+
cs_pin=9,
|
|
163
|
+
reset_pin=21,
|
|
164
|
+
)
|
|
@@ -81,7 +81,7 @@ CONFIG_SCHEMA = (
|
|
|
81
81
|
cv.int_range(min=0, max=0xFFFF, max_included=False),
|
|
82
82
|
),
|
|
83
83
|
cv.Optional(CONF_AMBIENT_PRESSURE_COMPENSATION): cv.pressure,
|
|
84
|
-
cv.Optional(CONF_TEMPERATURE_OFFSET, default="4°C"): cv.
|
|
84
|
+
cv.Optional(CONF_TEMPERATURE_OFFSET, default="4°C"): cv.temperature_delta,
|
|
85
85
|
cv.Optional(CONF_AMBIENT_PRESSURE_COMPENSATION_SOURCE): cv.use_id(
|
|
86
86
|
sensor.Sensor
|
|
87
87
|
),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
|
|
3
3
|
from esphome import core
|
|
4
|
-
from esphome.config_helpers import Extend, Remove, merge_config
|
|
4
|
+
from esphome.config_helpers import Extend, Remove, merge_config, merge_dicts_ordered
|
|
5
5
|
import esphome.config_validation as cv
|
|
6
6
|
from esphome.const import CONF_SUBSTITUTIONS, VALID_SUBSTITUTIONS_CHARACTERS
|
|
7
7
|
from esphome.yaml_util import ESPHomeDataBase, ESPLiteralValue, make_data_base
|
|
@@ -170,10 +170,10 @@ def do_substitution_pass(config, command_line_substitutions, ignore_missing=Fals
|
|
|
170
170
|
return
|
|
171
171
|
|
|
172
172
|
# Merge substitutions in config, overriding with substitutions coming from command line:
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
173
|
+
# Use merge_dicts_ordered to preserve OrderedDict type for move_to_end()
|
|
174
|
+
substitutions = merge_dicts_ordered(
|
|
175
|
+
config.get(CONF_SUBSTITUTIONS, {}), command_line_substitutions or {}
|
|
176
|
+
)
|
|
177
177
|
with cv.prepend_path("substitutions"):
|
|
178
178
|
if not isinstance(substitutions, dict):
|
|
179
179
|
raise cv.Invalid(
|
|
@@ -402,8 +402,8 @@ async def to_code(config):
|
|
|
402
402
|
add_idf_sdkconfig_option("CONFIG_LWIP_DHCPS", False)
|
|
403
403
|
|
|
404
404
|
# Disable Enterprise WiFi support if no EAP is configured
|
|
405
|
-
if CORE.is_esp32
|
|
406
|
-
add_idf_sdkconfig_option("CONFIG_ESP_WIFI_ENTERPRISE_SUPPORT",
|
|
405
|
+
if CORE.is_esp32:
|
|
406
|
+
add_idf_sdkconfig_option("CONFIG_ESP_WIFI_ENTERPRISE_SUPPORT", has_eap)
|
|
407
407
|
|
|
408
408
|
cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT]))
|
|
409
409
|
cg.add(var.set_power_save_mode(config[CONF_POWER_SAVE_MODE]))
|
esphome/config.py
CHANGED
|
@@ -12,7 +12,7 @@ from typing import Any
|
|
|
12
12
|
import voluptuous as vol
|
|
13
13
|
|
|
14
14
|
from esphome import core, loader, pins, yaml_util
|
|
15
|
-
from esphome.config_helpers import Extend, Remove
|
|
15
|
+
from esphome.config_helpers import Extend, Remove, merge_dicts_ordered
|
|
16
16
|
import esphome.config_validation as cv
|
|
17
17
|
from esphome.const import (
|
|
18
18
|
CONF_ESPHOME,
|
|
@@ -922,10 +922,9 @@ def validate_config(
|
|
|
922
922
|
if CONF_SUBSTITUTIONS in config or command_line_substitutions:
|
|
923
923
|
from esphome.components import substitutions
|
|
924
924
|
|
|
925
|
-
result[CONF_SUBSTITUTIONS] =
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
}
|
|
925
|
+
result[CONF_SUBSTITUTIONS] = merge_dicts_ordered(
|
|
926
|
+
config.get(CONF_SUBSTITUTIONS) or {}, command_line_substitutions
|
|
927
|
+
)
|
|
929
928
|
result.add_output_path([CONF_SUBSTITUTIONS], CONF_SUBSTITUTIONS)
|
|
930
929
|
try:
|
|
931
930
|
substitutions.do_substitution_pass(config, command_line_substitutions)
|
esphome/config_helpers.py
CHANGED
|
@@ -10,6 +10,7 @@ from esphome.const import (
|
|
|
10
10
|
PlatformFramework,
|
|
11
11
|
)
|
|
12
12
|
from esphome.core import CORE
|
|
13
|
+
from esphome.util import OrderedDict
|
|
13
14
|
|
|
14
15
|
# Pre-build lookup map from (platform, framework) tuples to PlatformFramework enum
|
|
15
16
|
_PLATFORM_FRAMEWORK_LOOKUP = {
|
|
@@ -17,6 +18,25 @@ _PLATFORM_FRAMEWORK_LOOKUP = {
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
|
|
21
|
+
def merge_dicts_ordered(*dicts: dict) -> OrderedDict:
|
|
22
|
+
"""Merge multiple dicts into an OrderedDict, preserving key order.
|
|
23
|
+
|
|
24
|
+
This is a helper to ensure that dictionary merging preserves OrderedDict type,
|
|
25
|
+
which is important for operations like move_to_end().
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
*dicts: Variable number of dictionaries to merge (later dicts override earlier ones)
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
OrderedDict with merged contents
|
|
32
|
+
"""
|
|
33
|
+
result = OrderedDict()
|
|
34
|
+
for d in dicts:
|
|
35
|
+
if d:
|
|
36
|
+
result.update(d)
|
|
37
|
+
return result
|
|
38
|
+
|
|
39
|
+
|
|
20
40
|
class Extend:
|
|
21
41
|
def __init__(self, value):
|
|
22
42
|
self.value = value
|
|
@@ -60,7 +80,11 @@ def merge_config(full_old, full_new):
|
|
|
60
80
|
if isinstance(new, dict):
|
|
61
81
|
if not isinstance(old, dict):
|
|
62
82
|
return new
|
|
63
|
-
|
|
83
|
+
# Preserve OrderedDict type by copying to OrderedDict if either input is OrderedDict
|
|
84
|
+
if isinstance(old, OrderedDict) or isinstance(new, OrderedDict):
|
|
85
|
+
res = OrderedDict(old)
|
|
86
|
+
else:
|
|
87
|
+
res = old.copy()
|
|
64
88
|
for k, v in new.items():
|
|
65
89
|
if isinstance(v, Remove) and k in old:
|
|
66
90
|
del res[k]
|
esphome/config_validation.py
CHANGED
|
@@ -244,6 +244,20 @@ RESERVED_IDS = [
|
|
|
244
244
|
"uart0",
|
|
245
245
|
"uart1",
|
|
246
246
|
"uart2",
|
|
247
|
+
# ESP32 ROM functions
|
|
248
|
+
"crc16_be",
|
|
249
|
+
"crc16_le",
|
|
250
|
+
"crc32_be",
|
|
251
|
+
"crc32_le",
|
|
252
|
+
"crc8_be",
|
|
253
|
+
"crc8_le",
|
|
254
|
+
"dbg_state",
|
|
255
|
+
"debug_timer",
|
|
256
|
+
"one_bits",
|
|
257
|
+
"recv_packet",
|
|
258
|
+
"send_packet",
|
|
259
|
+
"check_pos",
|
|
260
|
+
"software_reset",
|
|
247
261
|
]
|
|
248
262
|
|
|
249
263
|
|
esphome/const.py
CHANGED
|
@@ -4,7 +4,7 @@ from enum import Enum
|
|
|
4
4
|
|
|
5
5
|
from esphome.enum import StrEnum
|
|
6
6
|
|
|
7
|
-
__version__ = "2025.10.
|
|
7
|
+
__version__ = "2025.10.2"
|
|
8
8
|
|
|
9
9
|
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
|
|
10
10
|
VALID_SUBSTITUTIONS_CHARACTERS = (
|
|
@@ -696,6 +696,7 @@ CONF_OPEN_DRAIN = "open_drain"
|
|
|
696
696
|
CONF_OPEN_DRAIN_INTERRUPT = "open_drain_interrupt"
|
|
697
697
|
CONF_OPEN_DURATION = "open_duration"
|
|
698
698
|
CONF_OPEN_ENDSTOP = "open_endstop"
|
|
699
|
+
CONF_OPENTHREAD = "openthread"
|
|
699
700
|
CONF_OPERATION = "operation"
|
|
700
701
|
CONF_OPTIMISTIC = "optimistic"
|
|
701
702
|
CONF_OPTION = "option"
|
esphome/core/__init__.py
CHANGED
|
@@ -11,6 +11,7 @@ from esphome.const import (
|
|
|
11
11
|
CONF_COMMENT,
|
|
12
12
|
CONF_ESPHOME,
|
|
13
13
|
CONF_ETHERNET,
|
|
14
|
+
CONF_OPENTHREAD,
|
|
14
15
|
CONF_PORT,
|
|
15
16
|
CONF_USE_ADDRESS,
|
|
16
17
|
CONF_WEB_SERVER,
|
|
@@ -641,6 +642,9 @@ class EsphomeCore:
|
|
|
641
642
|
if CONF_ETHERNET in self.config:
|
|
642
643
|
return self.config[CONF_ETHERNET][CONF_USE_ADDRESS]
|
|
643
644
|
|
|
645
|
+
if CONF_OPENTHREAD in self.config:
|
|
646
|
+
return f"{self.name}.local"
|
|
647
|
+
|
|
644
648
|
return None
|
|
645
649
|
|
|
646
650
|
@property
|
esphome/dashboard/settings.py
CHANGED
|
@@ -10,6 +10,10 @@ from esphome.helpers import get_bool_env
|
|
|
10
10
|
|
|
11
11
|
from .util.password import password_hash
|
|
12
12
|
|
|
13
|
+
# Sentinel file name used for CORE.config_path when dashboard initializes.
|
|
14
|
+
# This ensures .parent returns the config directory instead of root.
|
|
15
|
+
_DASHBOARD_SENTINEL_FILE = "___DASHBOARD_SENTINEL___.yaml"
|
|
16
|
+
|
|
13
17
|
|
|
14
18
|
class DashboardSettings:
|
|
15
19
|
"""Settings for the dashboard."""
|
|
@@ -48,7 +52,12 @@ class DashboardSettings:
|
|
|
48
52
|
self.config_dir = Path(args.configuration)
|
|
49
53
|
self.absolute_config_dir = self.config_dir.resolve()
|
|
50
54
|
self.verbose = args.verbose
|
|
51
|
-
|
|
55
|
+
# Set to a sentinel file so .parent gives us the config directory.
|
|
56
|
+
# Previously this was `os.path.join(self.config_dir, ".")` which worked because
|
|
57
|
+
# os.path.dirname("/config/.") returns "/config", but Path("/config/.").parent
|
|
58
|
+
# normalizes to Path("/config") first, then .parent returns Path("/"), breaking
|
|
59
|
+
# secret resolution. Using a sentinel file ensures .parent gives the correct directory.
|
|
60
|
+
CORE.config_path = self.config_dir / _DASHBOARD_SENTINEL_FILE
|
|
52
61
|
|
|
53
62
|
@property
|
|
54
63
|
def relative_url(self) -> str:
|
esphome/dashboard/web_server.py
CHANGED
|
@@ -1058,7 +1058,8 @@ class DownloadBinaryRequestHandler(BaseHandler):
|
|
|
1058
1058
|
"download",
|
|
1059
1059
|
f"{storage_json.name}-{file_name}",
|
|
1060
1060
|
)
|
|
1061
|
-
|
|
1061
|
+
|
|
1062
|
+
path = storage_json.firmware_bin_path.parent.joinpath(file_name)
|
|
1062
1063
|
|
|
1063
1064
|
if not path.is_file():
|
|
1064
1065
|
args = ["esphome", "idedata", settings.rel_path(configuration)]
|
esphome/espota2.py
CHANGED
|
@@ -242,7 +242,7 @@ def send_check(
|
|
|
242
242
|
|
|
243
243
|
|
|
244
244
|
def perform_ota(
|
|
245
|
-
sock: socket.socket, password: str, file_handle: io.IOBase, filename: Path
|
|
245
|
+
sock: socket.socket, password: str | None, file_handle: io.IOBase, filename: Path
|
|
246
246
|
) -> None:
|
|
247
247
|
file_contents = file_handle.read()
|
|
248
248
|
file_size = len(file_contents)
|
|
@@ -278,13 +278,13 @@ def perform_ota(
|
|
|
278
278
|
|
|
279
279
|
def perform_auth(
|
|
280
280
|
sock: socket.socket,
|
|
281
|
-
password: str,
|
|
281
|
+
password: str | None,
|
|
282
282
|
hash_func: Callable[..., Any],
|
|
283
283
|
nonce_size: int,
|
|
284
284
|
hash_name: str,
|
|
285
285
|
) -> None:
|
|
286
286
|
"""Perform challenge-response authentication using specified hash algorithm."""
|
|
287
|
-
if
|
|
287
|
+
if password is None:
|
|
288
288
|
raise OTAError("ESP requests password, but no password given!")
|
|
289
289
|
|
|
290
290
|
nonce_bytes = receive_exactly(
|
|
@@ -385,7 +385,7 @@ def perform_ota(
|
|
|
385
385
|
|
|
386
386
|
|
|
387
387
|
def run_ota_impl_(
|
|
388
|
-
remote_host: str | list[str], remote_port: int, password: str, filename: Path
|
|
388
|
+
remote_host: str | list[str], remote_port: int, password: str | None, filename: Path
|
|
389
389
|
) -> tuple[int, str | None]:
|
|
390
390
|
from esphome.core import CORE
|
|
391
391
|
|
|
@@ -436,7 +436,7 @@ def run_ota_impl_(
|
|
|
436
436
|
|
|
437
437
|
|
|
438
438
|
def run_ota(
|
|
439
|
-
remote_host: str | list[str], remote_port: int, password: str, filename: Path
|
|
439
|
+
remote_host: str | list[str], remote_port: int, password: str | None, filename: Path
|
|
440
440
|
) -> tuple[int, str | None]:
|
|
441
441
|
try:
|
|
442
442
|
return run_ota_impl_(remote_host, remote_port, password, filename)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: esphome
|
|
3
|
-
Version: 2025.10.
|
|
3
|
+
Version: 2025.10.2
|
|
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-Expression: MIT
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
esphome/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
esphome/__main__.py,sha256=
|
|
2
|
+
esphome/__main__.py,sha256=mZntGYZogDYs6njugstrFFe7RZx2TlQv-2AmUOCdE3I,43943
|
|
3
3
|
esphome/address_cache.py,sha256=mXLDjOZ6NZebwRXSldZLnpPn51jfXeReRx0YdQ6oxOE,4622
|
|
4
4
|
esphome/automation.py,sha256=Gd_VHPDp5a1GYVNgwrzsCMuzHobmv49gcWdlV3y646g,17696
|
|
5
5
|
esphome/codegen.py,sha256=91Q9SVbTlgH_gFU4_sbVi6YEAiOjLuAGHG6qqV3lLHo,1939
|
|
6
|
-
esphome/config.py,sha256=
|
|
7
|
-
esphome/config_helpers.py,sha256=
|
|
8
|
-
esphome/config_validation.py,sha256=
|
|
9
|
-
esphome/const.py,sha256=
|
|
6
|
+
esphome/config.py,sha256=fu7G01comMPAfWd-ZZzUyIqO6cuLb6F44Sxv2LIFCUc,45156
|
|
7
|
+
esphome/config_helpers.py,sha256=P-tlafqFIQuCxdWERlk38MXQcB9apHcR68-DgeUco5s,6207
|
|
8
|
+
esphome/config_validation.py,sha256=r1llmDF0ePekWWblPwLd0Cnrbxwn0W1BBZubpBtYuEc,65057
|
|
9
|
+
esphome/const.py,sha256=299M2xhdejxA6mx0fM5n8_uRX4G_Kzeo12dRUwfy7zc,44487
|
|
10
10
|
esphome/coroutine.py,sha256=JFBDo8ngY46zhxvjk5aQ5MO_-qrqKDfKQTglEIqHrYY,11946
|
|
11
11
|
esphome/cpp_generator.py,sha256=DhHZT5s-Pbx5MJ762N_hFOYFo4Q9c0wJQG4p7UU_1lc,31901
|
|
12
12
|
esphome/cpp_helpers.py,sha256=r-hrUp7luke-ByuK2Z0TCzIvn4tTswMUkAzlVoKntiI,4039
|
|
13
13
|
esphome/cpp_types.py,sha256=UkmRmwPDShPw9HauiaFuNb6eZ6RsIy2YNQXUy4_UzQ8,1960
|
|
14
14
|
esphome/enum.py,sha256=rlQFVVxyBt5Iw7OL0o9F8D5LGgw23tbvi-KYjzP0QUQ,597
|
|
15
|
-
esphome/espota2.py,sha256=
|
|
15
|
+
esphome/espota2.py,sha256=WB9STR5RuoeKEa9ELYu9q2Ax8IqIXjoNw6SPydhOGvg,15219
|
|
16
16
|
esphome/external_files.py,sha256=BHNQqKGkY-AWZ7hEVPoB4mxgHQi-pYYHkrUayoXTa8M,3398
|
|
17
17
|
esphome/final_validate.py,sha256=EPhbU4xyEL3POdSY7oygyAIVvkeYM7qH-6eZWb76DFE,1898
|
|
18
18
|
esphome/git.py,sha256=GClrvAo4VsAhKRRDbqSv5nivtGZThEfbTCDxgq6uJyI,6615
|
|
@@ -418,12 +418,12 @@ esphome/components/bme680/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
|
|
|
418
418
|
esphome/components/bme680/bme680.cpp,sha256=WrRH-u_NJitaM5mFZkanPTVOjzyZ4EIq3rlf9aa1Oxc,16989
|
|
419
419
|
esphome/components/bme680/bme680.h,sha256=zdTFcpzNb3nSPy7q9V3iSJ5uBsmoZgJkzxU3Fcwf_BY,4678
|
|
420
420
|
esphome/components/bme680/sensor.py,sha256=fjZOcC0AymBF4f3vdgd-WfTfkqL0QrCOnaXdnGvGolU,5801
|
|
421
|
-
esphome/components/bme680_bsec/__init__.py,sha256=
|
|
421
|
+
esphome/components/bme680_bsec/__init__.py,sha256=gv6WRGGwdPw6-F0iVde6ahKAwt3g8n9QKbPqnVWCkeE,3068
|
|
422
422
|
esphome/components/bme680_bsec/bme680_bsec.cpp,sha256=I3YR9oQF_jP-9_FAG-BMhBZi_hE0mB_Ym8u0ZN6o9Mc,22630
|
|
423
423
|
esphome/components/bme680_bsec/bme680_bsec.h,sha256=aUGZXwSiu0wOlt13_mxhy0yKxToAInX2rnhZ_UQc7TM,5907
|
|
424
424
|
esphome/components/bme680_bsec/sensor.py,sha256=Y4efGwK7zDHbS8ET7o-bF__A86OJsZt2Ypl4sRtqKLE,4114
|
|
425
425
|
esphome/components/bme680_bsec/text_sensor.py,sha256=h2WQ_GjAOTsPqhCXwlfa-WmfOsUVWBcLKU5iO0fDEFw,921
|
|
426
|
-
esphome/components/bme68x_bsec2/__init__.py,sha256=
|
|
426
|
+
esphome/components/bme68x_bsec2/__init__.py,sha256=j0KAh7MifXz7E9PWuW1G5pSMN8AdsdrwzD1ZWKjlOtM,6240
|
|
427
427
|
esphome/components/bme68x_bsec2/bme68x_bsec2.cpp,sha256=LRIAb86G4WL2orOYPsKmOaeehPgQHwjP7yXR2DH8kjQ,20493
|
|
428
428
|
esphome/components/bme68x_bsec2/bme68x_bsec2.h,sha256=EVck6JgQOYydwRfO9Vvv5YgY4CDiMMwHzOXozJZZsNA,5415
|
|
429
429
|
esphome/components/bme68x_bsec2/sensor.py,sha256=AM-oEOcJuOmu55lkYZ7SOrbqx7wUYd4ku8s_DxK1cR8,4336
|
|
@@ -673,7 +673,7 @@ esphome/components/dashboard_import/dashboard_import.h,sha256=gPLB3_vwG3ma-j-VHe
|
|
|
673
673
|
esphome/components/datetime/__init__.py,sha256=_NjXvuxRyOQvLmf5E5A9B2e3PF8ZWmRTtw98dM10GGc,8911
|
|
674
674
|
esphome/components/datetime/date_entity.cpp,sha256=IbEWpZwT6bqNOd6WRu8_Jm6RH9j2oXznydiZ_xRPg7w,3700
|
|
675
675
|
esphome/components/datetime/date_entity.h,sha256=vg5e6YYGMaRQw0lz8c-xt4Sji6Tk2rzDwp5Q4g70kq0,2592
|
|
676
|
-
esphome/components/datetime/datetime_base.h,sha256=
|
|
676
|
+
esphome/components/datetime/datetime_base.h,sha256=6exbxBZDJOcziQBQfuI4Kjxk2m6XUXO2yJbnZYCJgeM,1028
|
|
677
677
|
esphome/components/datetime/datetime_entity.cpp,sha256=gWxzAJFSnkxWw1xYdPo-KDYvjZxMs-IHT8UAN8q476A,7758
|
|
678
678
|
esphome/components/datetime/datetime_entity.h,sha256=C7wW-0U2GV6FJWSBpP5vNKJtI3pK3jJxjGggHG5kGgA,3750
|
|
679
679
|
esphome/components/datetime/time_entity.cpp,sha256=R7bZ569BcA8qxhl0cfhEyh2uyxLdIrfbcWCvM4wHjd0,4411
|
|
@@ -739,7 +739,7 @@ esphome/components/dht12/dht12.cpp,sha256=WPNKJRSDsnSqKU7K6bbEY4jy1ZhSqij2W_3KUE
|
|
|
739
739
|
esphome/components/dht12/dht12.h,sha256=zHdQNIDC7nPMblvyEkTfXMeGCLmYRB7ZL_SXuB2PEWY,788
|
|
740
740
|
esphome/components/dht12/sensor.py,sha256=HhqMkXz9OSY2Maq0axZtx34Tf-IjXI0JGthWm11iFFk,1684
|
|
741
741
|
esphome/components/display/__init__.py,sha256=I1ZgORqGqAZpsUbaSOrf5X7S3ZC6galzFElwihDzygs,7247
|
|
742
|
-
esphome/components/display/display.cpp,sha256=
|
|
742
|
+
esphome/components/display/display.cpp,sha256=KreekNmu5YVuWHrE8_eC8g5kE69qK6hciq7U1qhcPlU,32657
|
|
743
743
|
esphome/components/display/display.h,sha256=l3APZ4ToQBLK1En501QmnToy1huwAFy2oaBQhOc_3Ow,32357
|
|
744
744
|
esphome/components/display/display_buffer.cpp,sha256=BRLcAJlr2oM-z-XzV2Y2QwO1gK3RohVoY0PP4aQF6bc,1842
|
|
745
745
|
esphome/components/display/display_buffer.h,sha256=RkFqe72PZtSKR60mWlb79FDqVQQFsefcEOdOMwIjuGc,809
|
|
@@ -863,10 +863,10 @@ esphome/components/es8388/select/adc_input_mic_select.cpp,sha256=xBlweJHP_BHSvwP
|
|
|
863
863
|
esphome/components/es8388/select/adc_input_mic_select.h,sha256=1Wf54OGjnrj9vFe7WuIU10qraRAJX7HM1qEcCTTSlvc,336
|
|
864
864
|
esphome/components/es8388/select/dac_output_select.cpp,sha256=FDOC7NHBaR34BqzzsPrmzBIzY_4o_YkF3_wSs_A9LC8,302
|
|
865
865
|
esphome/components/es8388/select/dac_output_select.h,sha256=u00z0jHqKmqsaKht3b3k7aGtYudU0cCJRTOw1Zh-mbw,334
|
|
866
|
-
esphome/components/esp32/__init__.py,sha256=
|
|
866
|
+
esphome/components/esp32/__init__.py,sha256=OIvKIuakzmwF5a0VUqvI1vFhZMD_RwVk7kjgueZaDUE,39115
|
|
867
867
|
esphome/components/esp32/boards.py,sha256=O2f122xZLngeY1pBbyjThMd1rLfrda1332rZF68n2KM,58436
|
|
868
868
|
esphome/components/esp32/const.py,sha256=3sPibOKzbGgtSUIn7PfTQJRyob6x1vUtxagKCGS9CyY,1130
|
|
869
|
-
esphome/components/esp32/core.cpp,sha256=
|
|
869
|
+
esphome/components/esp32/core.cpp,sha256=wgkilw_-s3YRT-b0CBSaPNj5r3Ey77ZEE0qd63B8Drs,3197
|
|
870
870
|
esphome/components/esp32/gpio.cpp,sha256=_lXijDd4c6cB2HI8vIUtrmKodoGZ43AOSRgdOkS3pIg,6870
|
|
871
871
|
esphome/components/esp32/gpio.h,sha256=uMa21XBEGZedxBP9EsXhf815BuOSTr7md4Ww6cFHcws,2314
|
|
872
872
|
esphome/components/esp32/gpio.py,sha256=gQFf7rA1DB-TRgu72ADY11LBBmKhWWFRTuQwxGgdgSc,7760
|
|
@@ -972,7 +972,7 @@ esphome/components/esp8266_pwm/output.py,sha256=s5sMTbATthPAJCJyTwvIBYQAoEcffAGx
|
|
|
972
972
|
esphome/components/esp_ldo/__init__.py,sha256=bBEaDXgqzFtcRSOcUttuA8VGJnayYjLU-zhvzGw8UiU,2892
|
|
973
973
|
esphome/components/esp_ldo/esp_ldo.cpp,sha256=cAnIUUnO9EGLEx2y0cZ7eT5eVeollwTxFLGQUdZ9sP8,1506
|
|
974
974
|
esphome/components/esp_ldo/esp_ldo.h,sha256=M5pLF6Wv1_CasrOHDG9e6UIYD-7KNVbZtXk0uAt61Bc,1130
|
|
975
|
-
esphome/components/esphome/ota/__init__.py,sha256=
|
|
975
|
+
esphome/components/esphome/ota/__init__.py,sha256=Xlvz7YBZNlWJ1O3vKr7ILCpDa49VXJh62Dx0E9c33U4,5721
|
|
976
976
|
esphome/components/esphome/ota/ota_esphome.cpp,sha256=ZVLd4igvi_U4jhWCILVa3oS7a5hFkLRHb_1koQH6JJ8,26475
|
|
977
977
|
esphome/components/esphome/ota/ota_esphome.h,sha256=W31T8JqyBBTSCLmqaLrL6PRDRrx0lAJaesEG-48Ugf8,3411
|
|
978
978
|
esphome/components/espnow/__init__.py,sha256=ZMz0wyAmx4VW8dewtRhvSu3-TiCMbjVswF8_PvgxcvA,9780
|
|
@@ -1286,7 +1286,7 @@ esphome/components/http_request/update/__init__.py,sha256=cIoR3Gw0Bc2ZKoc-1LfW1g
|
|
|
1286
1286
|
esphome/components/http_request/update/http_request_update.cpp,sha256=ly_2n4pE751OPPhYpXARBKswhr8krxpW5LPTCTkVLB4,7517
|
|
1287
1287
|
esphome/components/http_request/update/http_request_update.h,sha256=8OP3PwsEAUsqJfkNOwOHUJl7HJdgknHBBOiAbAiBPXU,1255
|
|
1288
1288
|
esphome/components/htu21d/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
1289
|
-
esphome/components/htu21d/htu21d.cpp,sha256=
|
|
1289
|
+
esphome/components/htu21d/htu21d.cpp,sha256=WP0_Wvtm7BAhb8iwTsBnQS39joxtUkzPqqrk-9ERCEI,4560
|
|
1290
1290
|
esphome/components/htu21d/htu21d.h,sha256=zbuxEFgwjRWYp1x1mW1AX0_B1ZfV5BeNX2Ce56xV1Lc,1894
|
|
1291
1291
|
esphome/components/htu21d/sensor.py,sha256=yetZH4qlaDqyFU9Mo3B7ByqB5kNpVeqrQuqJDM0Ifeg,3839
|
|
1292
1292
|
esphome/components/htu31d/__init__.py,sha256=OVxEVhYcUY-P_ZwdHRIQ_gjZCxGPi12jHy7xuix1z-A,36
|
|
@@ -1931,7 +1931,7 @@ esphome/components/midea_ir/climate.py,sha256=SIGjpEuW2nJbXh_SjKz1Tt5zkqqC65bsZp
|
|
|
1931
1931
|
esphome/components/midea_ir/midea_data.h,sha256=XOHDD0CzimykTCRGWqgQGIhDgmd0bXL1AjWscswO5OE,2783
|
|
1932
1932
|
esphome/components/midea_ir/midea_ir.cpp,sha256=5RJ1Igf_SCFOj2OwrS3n-_aAVfQPVNjiVFqi7cHwyJE,6666
|
|
1933
1933
|
esphome/components/midea_ir/midea_ir.h,sha256=BNVdX5xjpFy-q0OTgBubUpFA_GQgUeK3Vx7rahOT-nM,1518
|
|
1934
|
-
esphome/components/mipi/__init__.py,sha256=
|
|
1934
|
+
esphome/components/mipi/__init__.py,sha256=3i5xcABD4vGA4tNj0tY7-JdgqCQsH_H8uLs--ooQ-2M,15651
|
|
1935
1935
|
esphome/components/mipi_dsi/__init__.py,sha256=0xobDY4ImBcQod3BD1spYTLRg5gBGGJiuPx8R4KYApY,111
|
|
1936
1936
|
esphome/components/mipi_dsi/display.py,sha256=5atj0wGqzBTuM9CvbLjHB9mCcyGiYe3xhMVSJC5fxog,8060
|
|
1937
1937
|
esphome/components/mipi_dsi/mipi_dsi.cpp,sha256=WrZxI9snPhBfM2S2E53vmUltnW5uLWUwkT5fkuEMvhM,14254
|
|
@@ -1950,9 +1950,9 @@ esphome/components/mipi_rgb/models/rpi.py,sha256=TsfdCw1R34KkuWjdcpr86QVlAWKcnE3
|
|
|
1950
1950
|
esphome/components/mipi_rgb/models/st7701s.py,sha256=AOOwVcOb7kOc7L_sb59-7edyIuEbW-z4x6c5o_tW_Q8,8395
|
|
1951
1951
|
esphome/components/mipi_rgb/models/waveshare.py,sha256=iEYTdtiuGCgL4zdsdljSVrwUZZgmS8vpHwX8UzbyERE,1618
|
|
1952
1952
|
esphome/components/mipi_spi/__init__.py,sha256=XJwpaWKr4Ldgbuja4FdSVVpj-0q1Ef67Qmdg-SkTR7o,102
|
|
1953
|
-
esphome/components/mipi_spi/display.py,sha256=
|
|
1953
|
+
esphome/components/mipi_spi/display.py,sha256=dgB4_xwhYy5iPeA1vzTNfKZZG7QruX6A0aHVrpK-m_k,15883
|
|
1954
1954
|
esphome/components/mipi_spi/mipi_spi.cpp,sha256=atqJGYm2bPQOfMrgzjfCgjxrryanTXOieAqANXj4bgU,142
|
|
1955
|
-
esphome/components/mipi_spi/mipi_spi.h,sha256=
|
|
1955
|
+
esphome/components/mipi_spi/mipi_spi.h,sha256=Aey2I9bJJjtq6FVsVa6DZio_KC1Z8n8j4hVE8A9Aiwc,24007
|
|
1956
1956
|
esphome/components/mipi_spi/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
1957
1957
|
esphome/components/mipi_spi/models/adafruit.py,sha256=Izz_5WeYuxbDQv-5Ixtm_hrdggT1drVLbTafV4TsJbM,482
|
|
1958
1958
|
esphome/components/mipi_spi/models/amoled.py,sha256=Ac1L4Bf-ZZLTdgGYcqyj3XnQer04RcBXP7UjHi_ZsKM,1978
|
|
@@ -1961,7 +1961,7 @@ esphome/components/mipi_spi/models/ili.py,sha256=cM0_-S116aHZLjenHcPXZ1jrNROWfbN
|
|
|
1961
1961
|
esphome/components/mipi_spi/models/jc.py,sha256=3tBxkMWL6vvEBHj16ivGRp9qZmuQk5tAP-kTlRCBxOk,10319
|
|
1962
1962
|
esphome/components/mipi_spi/models/lanbon.py,sha256=q40EYluGZlDGRYhckwW8Co_HcoWTJd7-NwgQgGQXrYM,214
|
|
1963
1963
|
esphome/components/mipi_spi/models/lilygo.py,sha256=i6yRxJSu_oP8DaOY2Cs-A3xHR0RSm8a2obsSn5B0TW0,1035
|
|
1964
|
-
esphome/components/mipi_spi/models/waveshare.py,sha256=
|
|
1964
|
+
esphome/components/mipi_spi/models/waveshare.py,sha256=07VlEGlEFgdDHjiDCoV9GigdX7BoRxIVF3sHUZwLwDg,2716
|
|
1965
1965
|
esphome/components/mitsubishi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
1966
1966
|
esphome/components/mitsubishi/climate.py,sha256=rHoFA-9tLNKL7zkPiqJ3R764qLXHRc9V4_mRTPkmsSo,2669
|
|
1967
1967
|
esphome/components/mitsubishi/mitsubishi.cpp,sha256=ZZu-3RQrZ8J7x1V0hvKevJfAMejgLNwo97qio4Wl-74,14228
|
|
@@ -2680,7 +2680,7 @@ esphome/components/scd4x/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
|
|
|
2680
2680
|
esphome/components/scd4x/automation.h,sha256=bGkGwV0wHimPe_0oug111GAsCJyahpr1LRoW0n1F81E,715
|
|
2681
2681
|
esphome/components/scd4x/scd4x.cpp,sha256=fgXtz-GTE4A0gFG4XJerYuAxSdXsuM1tPDTEW5uHlRY,11834
|
|
2682
2682
|
esphome/components/scd4x/scd4x.h,sha256=ZCCgczDXJ4ocxEMKeAN9RFaMcZTWls4E-tIru5DCIcU,2216
|
|
2683
|
-
esphome/components/scd4x/sensor.py,sha256=
|
|
2683
|
+
esphome/components/scd4x/sensor.py,sha256=p2C1A80yBw42C69jUtimAPdVBz3E5CjvvGY2nWf8mU8,5629
|
|
2684
2684
|
esphome/components/script/__init__.py,sha256=N4B0qNFRls5Z4LfHQLDgeCZ24RVD8mIhjA6Er-SBXCk,7771
|
|
2685
2685
|
esphome/components/script/script.cpp,sha256=7-ucdkKqP25FdHWkrEWa70oo85si2zKB1lbY1QKZXmM,531
|
|
2686
2686
|
esphome/components/script/script.h,sha256=Pc-CfDOklDggE2RIurKoLBxxeyM5LltZO636STEplwI,8584
|
|
@@ -3063,7 +3063,7 @@ esphome/components/sts3x/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
|
|
|
3063
3063
|
esphome/components/sts3x/sensor.py,sha256=L3KzexM-oB7ZYLkyfuL2tGcr374xbMEtgh4qCC-O6RA,921
|
|
3064
3064
|
esphome/components/sts3x/sts3x.cpp,sha256=pH6D7ZzXFhLtULwyutm8bp9bnDLNFyI3rf6U-WBQhzI,2041
|
|
3065
3065
|
esphome/components/sts3x/sts3x.h,sha256=MDc_-wFonrh4r4FKwm4OYT5nZiTDZKO0-gmadE-cvjA,610
|
|
3066
|
-
esphome/components/substitutions/__init__.py,sha256=
|
|
3066
|
+
esphome/components/substitutions/__init__.py,sha256=LcQ6zPL7gMTZRZ89xsb65tiLCVd6Tj_Isw6pvZXzQbY,7370
|
|
3067
3067
|
esphome/components/substitutions/jinja.py,sha256=1YQGJMbgl9bq02dT42wbnqXHo7JB_oQZxMozT9srCFw,4953
|
|
3068
3068
|
esphome/components/sun/__init__.py,sha256=TgaqeswSU1ZNtnrHGB0OAsodLEWwf7_DpU9OglsYDrw,5334
|
|
3069
3069
|
esphome/components/sun/sun.cpp,sha256=YEe2SsF4OEmK27NQRZ0tYBv_n5vnJd-6HA45GDTxqa8,11874
|
|
@@ -3546,7 +3546,7 @@ esphome/components/whynter/whynter.h,sha256=S9Psg1t7acdsWyaEJvTwwEnXwXcBgnGXlhVL
|
|
|
3546
3546
|
esphome/components/wiegand/__init__.py,sha256=omQlVAU2D_tLx1sO8Mr_lBfAlhi9mrPsJJUrM1gihFk,2633
|
|
3547
3547
|
esphome/components/wiegand/wiegand.cpp,sha256=K7XAflWGKzb4_qp32dnVHglnV3dH_OvLtPvhp7b9IhM,3816
|
|
3548
3548
|
esphome/components/wiegand/wiegand.h,sha256=gyg5szEK0okoeLBQR284k84xy-ln19kNIkeOTe-CX-Y,1658
|
|
3549
|
-
esphome/components/wifi/__init__.py,sha256=
|
|
3549
|
+
esphome/components/wifi/__init__.py,sha256=InmIG0vlAUfokONZNIzNwbCKTk6kiJywdO3xLmwsEUs,18207
|
|
3550
3550
|
esphome/components/wifi/wifi_component.cpp,sha256=V9VYiF2EMmqW4Ebv8dPqp_WRvvWI6iu2LN7O3TCRbuw,31917
|
|
3551
3551
|
esphome/components/wifi/wifi_component.h,sha256=4GBdRPXS-aMTk0lZK3V4puV6Pk7Y2oDVWxg0UtamQd8,15876
|
|
3552
3552
|
esphome/components/wifi/wifi_component_esp8266.cpp,sha256=bQD5vukfO4OoyBeAhZxvae-tQbvb6vIQRLXaR4JSeKc,27507
|
|
@@ -3723,7 +3723,7 @@ esphome/components/zyaura/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
|
|
|
3723
3723
|
esphome/components/zyaura/sensor.py,sha256=cSmO4ozYdi4ZD7NK4lmYjswWVmJoDvEruU1HhGrZSE0,2476
|
|
3724
3724
|
esphome/components/zyaura/zyaura.cpp,sha256=F7WM8XAZ5MYuCD3eERm2_IA-O7sP1i-A-yF5TV4PqX8,3679
|
|
3725
3725
|
esphome/components/zyaura/zyaura.h,sha256=7O3EGFIopUEfBp3A5sBC0l4zx2mesRxDWa_MBdGtPKQ,2307
|
|
3726
|
-
esphome/core/__init__.py,sha256=
|
|
3726
|
+
esphome/core/__init__.py,sha256=a2o-xr9YWut3tF8oZZOY43pl3gdb-EhLfckgel3ZjoQ,30571
|
|
3727
3727
|
esphome/core/application.cpp,sha256=3cpbiUjvUf4W4qmDOmXMkWUVtMz5BmnOkgj3eylC1wo,22814
|
|
3728
3728
|
esphome/core/application.h,sha256=Enfg00Tc0egbU3-WvoNGqTDy4TZk12tYwMS5Laf4_Y8,20439
|
|
3729
3729
|
esphome/core/area.h,sha256=mtFxmuv8fC2hArcey3cZ0F6hMXSO2QpFZ9Xv49z4rvk,393
|
|
@@ -3775,8 +3775,8 @@ esphome/dashboard/dashboard.py,sha256=4k32z98tQXj01FKHfXePLlP_zch-piHSxdbhjdgf3a
|
|
|
3775
3775
|
esphome/dashboard/dns.py,sha256=zmePZ5i_qTMbjku-7sOFSRHLBH_BuI4wiu2nLhIlm9c,1999
|
|
3776
3776
|
esphome/dashboard/entries.py,sha256=L-o0Wvo4moS-W-OGViZ3lOUTWyR_Sm1V6P_RrbApqqw,15607
|
|
3777
3777
|
esphome/dashboard/models.py,sha256=YKr-M-PY1IO-_mtxL2sw5hWbFGXVsBQXq0-mLiyJXsc,2181
|
|
3778
|
-
esphome/dashboard/settings.py,sha256=
|
|
3779
|
-
esphome/dashboard/web_server.py,sha256=
|
|
3778
|
+
esphome/dashboard/settings.py,sha256=CI9WO--Xntt1Nyyknbj61vVfWFYGA7QUFfwoR0UOHCg,3495
|
|
3779
|
+
esphome/dashboard/web_server.py,sha256=xQ3aTnQNqpEK02Vi4YE0-ipuEp-mT9EGA8Z-bln2-ks,56665
|
|
3780
3780
|
esphome/dashboard/status/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3781
3781
|
esphome/dashboard/status/mdns.py,sha256=2cxZsnIxDSsaaEiNQ_jIxW-LdvEigFMerfPTr2zy31g,6621
|
|
3782
3782
|
esphome/dashboard/status/mqtt.py,sha256=2QOq1vgwJCHW5uL_hqmi_R5fX5OTeTJUHx7c0pMLQKE,2578
|
|
@@ -3786,9 +3786,9 @@ esphome/dashboard/util/itertools.py,sha256=8eLrWEWmICLtXNxkKdYPQV0c_N4GEz8m9Npnb
|
|
|
3786
3786
|
esphome/dashboard/util/password.py,sha256=cQz3b9B-ijTe7zS6BeCW0hc3pWv6JjC78jmnycYYAh8,321
|
|
3787
3787
|
esphome/dashboard/util/subprocess.py,sha256=T8EW6dbU4LPd2DG1dRrdh8li71tt6J1isn411poMhkk,1022
|
|
3788
3788
|
esphome/dashboard/util/text.py,sha256=wwFtORlvHjsYkqb68IT-772LHAhWxT4OtnkIcPICQB0,317
|
|
3789
|
-
esphome-2025.10.
|
|
3790
|
-
esphome-2025.10.
|
|
3791
|
-
esphome-2025.10.
|
|
3792
|
-
esphome-2025.10.
|
|
3793
|
-
esphome-2025.10.
|
|
3794
|
-
esphome-2025.10.
|
|
3789
|
+
esphome-2025.10.2.dist-info/licenses/LICENSE,sha256=HzEjkBInJe44L4WvAOPfhPJJDNj6YbnqFyvGWRzArGM,36664
|
|
3790
|
+
esphome-2025.10.2.dist-info/METADATA,sha256=eY6T-qd00Ac2b8uC53Z5H5DldOlH4cSwuPVaVYizt5g,3633
|
|
3791
|
+
esphome-2025.10.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
3792
|
+
esphome-2025.10.2.dist-info/entry_points.txt,sha256=mIxVNuWtbYzeEcaWCl-AQ-97aBOWbnYBAK8nbF6P4M0,50
|
|
3793
|
+
esphome-2025.10.2.dist-info/top_level.txt,sha256=0GSXEW3cnITpgG3qnsSMz0qoqJHAFyfw7Y8MVtEf1Yk,8
|
|
3794
|
+
esphome-2025.10.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|