esphome 2025.10.0b4__py3-none-any.whl → 2025.10.1__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 +84 -34
- esphome/components/display/display.cpp +5 -2
- esphome/components/esphome/ota/__init__.py +4 -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/substitutions/__init__.py +5 -5
- esphome/components/wifi/__init__.py +2 -2
- esphome/config.py +4 -5
- esphome/config_helpers.py +25 -1
- esphome/const.py +1 -1
- esphome/espota2.py +5 -5
- {esphome-2025.10.0b4.dist-info → esphome-2025.10.1.dist-info}/METADATA +1 -1
- {esphome-2025.10.0b4.dist-info → esphome-2025.10.1.dist-info}/RECORD +19 -19
- {esphome-2025.10.0b4.dist-info → esphome-2025.10.1.dist-info}/WHEEL +0 -0
- {esphome-2025.10.0b4.dist-info → esphome-2025.10.1.dist-info}/entry_points.txt +0 -0
- {esphome-2025.10.0b4.dist-info → esphome-2025.10.1.dist-info}/licenses/LICENSE +0 -0
- {esphome-2025.10.0b4.dist-info → esphome-2025.10.1.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)):
|
|
@@ -280,16 +291,67 @@ def mqtt_get_ip(config: ConfigType, username: str, password: str, client_id: str
|
|
|
280
291
|
return mqtt.get_esphome_device_ip(config, username, password, client_id)
|
|
281
292
|
|
|
282
293
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
294
|
+
def _resolve_network_devices(
|
|
295
|
+
devices: list[str], config: ConfigType, args: ArgsProtocol
|
|
296
|
+
) -> list[str]:
|
|
297
|
+
"""Resolve device list, converting MQTT magic strings to actual IP addresses.
|
|
298
|
+
|
|
299
|
+
This function filters the devices list to:
|
|
300
|
+
- Replace MQTT/MQTTIP magic strings with actual IP addresses via MQTT lookup
|
|
301
|
+
- Deduplicate addresses while preserving order
|
|
302
|
+
- Only resolve MQTT once even if multiple MQTT strings are present
|
|
303
|
+
- If MQTT resolution fails, log a warning and continue with other devices
|
|
304
|
+
|
|
305
|
+
Args:
|
|
306
|
+
devices: List of device identifiers (IPs, hostnames, or magic strings)
|
|
307
|
+
config: ESPHome configuration
|
|
308
|
+
args: Command-line arguments containing MQTT credentials
|
|
309
|
+
|
|
310
|
+
Returns:
|
|
311
|
+
List of network addresses suitable for connection attempts
|
|
312
|
+
"""
|
|
313
|
+
network_devices: list[str] = []
|
|
314
|
+
mqtt_resolved: bool = False
|
|
315
|
+
|
|
316
|
+
for device in devices:
|
|
317
|
+
port_type = get_port_type(device)
|
|
318
|
+
if port_type in _MQTT_PORT_TYPES:
|
|
319
|
+
# Only resolve MQTT once, even if multiple MQTT entries
|
|
320
|
+
if not mqtt_resolved:
|
|
321
|
+
try:
|
|
322
|
+
mqtt_ips = mqtt_get_ip(
|
|
323
|
+
config, args.username, args.password, args.client_id
|
|
324
|
+
)
|
|
325
|
+
network_devices.extend(mqtt_ips)
|
|
326
|
+
except EsphomeError as err:
|
|
327
|
+
_LOGGER.warning(
|
|
328
|
+
"MQTT IP discovery failed (%s), will try other devices if available",
|
|
329
|
+
err,
|
|
330
|
+
)
|
|
331
|
+
mqtt_resolved = True
|
|
332
|
+
elif device not in network_devices:
|
|
333
|
+
# Regular network address or IP - add if not already present
|
|
334
|
+
network_devices.append(device)
|
|
287
335
|
|
|
336
|
+
return network_devices
|
|
288
337
|
|
|
289
|
-
|
|
338
|
+
|
|
339
|
+
def get_port_type(port: str) -> PortType:
|
|
340
|
+
"""Determine the type of port/device identifier.
|
|
341
|
+
|
|
342
|
+
Returns:
|
|
343
|
+
PortType.SERIAL for serial ports (/dev/ttyUSB0, COM1, etc.)
|
|
344
|
+
PortType.MQTT for MQTT logging
|
|
345
|
+
PortType.MQTTIP for MQTT IP lookup
|
|
346
|
+
PortType.NETWORK for IP addresses, hostnames, or mDNS names
|
|
347
|
+
"""
|
|
290
348
|
if port.startswith("/") or port.startswith("COM"):
|
|
291
|
-
return
|
|
292
|
-
|
|
349
|
+
return PortType.SERIAL
|
|
350
|
+
if port == "MQTT":
|
|
351
|
+
return PortType.MQTT
|
|
352
|
+
if port == "MQTTIP":
|
|
353
|
+
return PortType.MQTTIP
|
|
354
|
+
return PortType.NETWORK
|
|
293
355
|
|
|
294
356
|
|
|
295
357
|
def run_miniterm(config: ConfigType, port: str, args) -> int:
|
|
@@ -489,7 +551,7 @@ def upload_using_platformio(config: ConfigType, port: str):
|
|
|
489
551
|
|
|
490
552
|
|
|
491
553
|
def check_permissions(port: str):
|
|
492
|
-
if os.name == "posix" and get_port_type(port) ==
|
|
554
|
+
if os.name == "posix" and get_port_type(port) == PortType.SERIAL:
|
|
493
555
|
# Check if we can open selected serial port
|
|
494
556
|
if not os.access(port, os.F_OK):
|
|
495
557
|
raise EsphomeError(
|
|
@@ -517,7 +579,7 @@ def upload_program(
|
|
|
517
579
|
except AttributeError:
|
|
518
580
|
pass
|
|
519
581
|
|
|
520
|
-
if get_port_type(host) ==
|
|
582
|
+
if get_port_type(host) == PortType.SERIAL:
|
|
521
583
|
check_permissions(host)
|
|
522
584
|
|
|
523
585
|
exit_code = 1
|
|
@@ -544,17 +606,16 @@ def upload_program(
|
|
|
544
606
|
from esphome import espota2
|
|
545
607
|
|
|
546
608
|
remote_port = int(ota_conf[CONF_PORT])
|
|
547
|
-
password = ota_conf.get(CONF_PASSWORD
|
|
609
|
+
password = ota_conf.get(CONF_PASSWORD)
|
|
548
610
|
if getattr(args, "file", None) is not None:
|
|
549
611
|
binary = Path(args.file)
|
|
550
612
|
else:
|
|
551
613
|
binary = CORE.firmware_bin
|
|
552
614
|
|
|
553
|
-
# MQTT
|
|
554
|
-
|
|
555
|
-
devices = mqtt_get_ip(config, args.username, args.password, args.client_id)
|
|
615
|
+
# Resolve MQTT magic strings to actual IP addresses
|
|
616
|
+
network_devices = _resolve_network_devices(devices, config, args)
|
|
556
617
|
|
|
557
|
-
return espota2.run_ota(
|
|
618
|
+
return espota2.run_ota(network_devices, remote_port, password, binary)
|
|
558
619
|
|
|
559
620
|
|
|
560
621
|
def show_logs(config: ConfigType, args: ArgsProtocol, devices: list[str]) -> int | None:
|
|
@@ -569,33 +630,22 @@ def show_logs(config: ConfigType, args: ArgsProtocol, devices: list[str]) -> int
|
|
|
569
630
|
raise EsphomeError("Logger is not configured!")
|
|
570
631
|
|
|
571
632
|
port = devices[0]
|
|
633
|
+
port_type = get_port_type(port)
|
|
572
634
|
|
|
573
|
-
if
|
|
635
|
+
if port_type == PortType.SERIAL:
|
|
574
636
|
check_permissions(port)
|
|
575
637
|
return run_miniterm(config, port, args)
|
|
576
638
|
|
|
577
|
-
port_type = get_port_type(port)
|
|
578
|
-
|
|
579
639
|
# 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
|
|
640
|
+
# Resolve MQTT magic strings to actual IP addresses
|
|
641
|
+
if has_api() and (
|
|
642
|
+
network_devices := _resolve_network_devices(devices, config, args)
|
|
643
|
+
):
|
|
644
|
+
from esphome.components.api.client import run_logs
|
|
595
645
|
|
|
596
|
-
|
|
646
|
+
return run_logs(config, network_devices)
|
|
597
647
|
|
|
598
|
-
if port_type in (
|
|
648
|
+
if port_type in (PortType.NETWORK, PortType.MQTT) and has_mqtt_logging():
|
|
599
649
|
from esphome import mqtt
|
|
600
650
|
|
|
601
651
|
return mqtt.show_logs(
|
|
@@ -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
|
|
|
@@ -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
|
|
@@ -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
|
+
)
|
|
@@ -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/const.py
CHANGED
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.1
|
|
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=CyF97d_1fVXAe_S71fgwljmwX5egquqVxt0-qQZeoRU,43870
|
|
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=
|
|
6
|
+
esphome/config.py,sha256=fu7G01comMPAfWd-ZZzUyIqO6cuLb6F44Sxv2LIFCUc,45156
|
|
7
|
+
esphome/config_helpers.py,sha256=P-tlafqFIQuCxdWERlk38MXQcB9apHcR68-DgeUco5s,6207
|
|
8
8
|
esphome/config_validation.py,sha256=WOIf3s3CIhWiAFJ7NdpmMzLfCNv89J67pvZXHmiBz5I,64808
|
|
9
|
-
esphome/const.py,sha256=
|
|
9
|
+
esphome/const.py,sha256=n64w5wIHdaCrpC_RSrNmb3C1to0xC-cRsEEnmOUax6s,44456
|
|
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
|
|
@@ -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
|
|
@@ -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
|
|
@@ -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
|
|
@@ -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
|
|
@@ -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.1.dist-info/licenses/LICENSE,sha256=HzEjkBInJe44L4WvAOPfhPJJDNj6YbnqFyvGWRzArGM,36664
|
|
3790
|
+
esphome-2025.10.1.dist-info/METADATA,sha256=686fJT3AL6uuhrCF4LZWVu1c5XTclV41e4HMbttq4IY,3633
|
|
3791
|
+
esphome-2025.10.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
3792
|
+
esphome-2025.10.1.dist-info/entry_points.txt,sha256=mIxVNuWtbYzeEcaWCl-AQ-97aBOWbnYBAK8nbF6P4M0,50
|
|
3793
|
+
esphome-2025.10.1.dist-info/top_level.txt,sha256=0GSXEW3cnITpgG3qnsSMz0qoqJHAFyfw7Y8MVtEf1Yk,8
|
|
3794
|
+
esphome-2025.10.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|