esphome 2025.10.0b3__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 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
- _PORT_TO_PORT_TYPE = {
284
- "MQTT": "MQTT",
285
- "MQTTIP": "MQTTIP",
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
- def get_port_type(port: str) -> str:
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 "SERIAL"
292
- return _PORT_TO_PORT_TYPE.get(port, "NETWORK")
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) == "SERIAL":
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) == "SERIAL":
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 address resolution
554
- if get_port_type(host) in ("MQTT", "MQTTIP"):
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(devices, remote_port, password, binary)
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 get_port_type(port) == "SERIAL":
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
- if has_api():
581
- addresses_to_use: list[str] | None = None
582
-
583
- if port_type == "NETWORK":
584
- # Network addresses (IPs, mDNS names, or regular DNS hostnames) can be used
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
- return run_logs(config, addresses_to_use)
646
+ return run_logs(config, network_devices)
597
647
 
598
- if port_type in ("NETWORK", "MQTT") and has_mqtt_logging():
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 <= image_h; 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
- if CONF_PASSWORD in config:
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
- # fill in defaults if not provided
362
- mirror_x = transform.get(CONF_MIRROR_X, self.get_default(CONF_MIRROR_X))
363
- mirror_y = transform.get(CONF_MIRROR_Y, self.get_default(CONF_MIRROR_Y))
364
- swap_xy = transform.get(CONF_SWAP_XY, self.get_default(CONF_SWAP_XY))
365
- transform[CONF_MIRROR_X] = mirror_x
366
- transform[CONF_MIRROR_Y] = mirror_y
367
- transform[CONF_SWAP_XY] = swap_xy
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 mirror_x
374
- transform[CONF_MIRROR_Y] = not mirror_y
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 swap_xy
377
- transform[CONF_MIRROR_X] = not mirror_x
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 swap_xy
380
- transform[CONF_MIRROR_Y] = not mirror_y
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
 
@@ -56,50 +56,41 @@ DriverChip(
56
56
  "WAVESHARE-P4-86-PANEL",
57
57
  height=720,
58
58
  width=720,
59
- hsync_back_porch=80,
59
+ hsync_back_porch=50,
60
60
  hsync_pulse_width=20,
61
- hsync_front_porch=80,
62
- vsync_back_porch=12,
61
+ hsync_front_porch=50,
62
+ vsync_back_porch=20,
63
63
  vsync_pulse_width=4,
64
- vsync_front_porch=30,
65
- pclk_frequency="46MHz",
66
- lane_bit_rate="1Gbps",
64
+ vsync_front_porch=20,
65
+ pclk_frequency="38MHz",
66
+ lane_bit_rate="480Mbps",
67
67
  swap_xy=cv.UNDEFINED,
68
68
  color_order="RGB",
69
69
  reset_pin=27,
70
70
  initsequence=[
71
71
  (0xB9, 0xF1, 0x12, 0x83),
72
- (
73
- 0xBA, 0x31, 0x81, 0x05, 0xF9, 0x0E, 0x0E, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x25, 0x00,
74
- 0x90, 0x0A, 0x00, 0x00, 0x01, 0x4F, 0x01, 0x00, 0x00, 0x37,
75
- ),
76
- (0xB8, 0x25, 0x22, 0xF0, 0x63),
77
- (0xBF, 0x02, 0x11, 0x00),
72
+ (0xB1, 0x00, 0x00, 0x00, 0xDA, 0x80),
73
+ (0xB2, 0x3C, 0x12, 0x30),
78
74
  (0xB3, 0x10, 0x10, 0x28, 0x28, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00),
79
- (0xC0, 0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x12, 0x70, 0x00),
80
- (0xBC, 0x46), (0xCC, 0x0B), (0xB4, 0x80), (0xB2, 0x3C, 0x12, 0x30),
81
- (0xE3, 0x07, 0x07, 0x0B, 0x0B, 0x03, 0x0B, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xC0, 0x10,),
82
- (0xC1, 0x36, 0x00, 0x32, 0x32, 0x77, 0xF1, 0xCC, 0xCC, 0x77, 0x77, 0x33, 0x33),
75
+ (0xB4, 0x80),
83
76
  (0xB5, 0x0A, 0x0A),
84
- (0xB6, 0xB2, 0xB2),
85
- (
86
- 0xE9, 0xC8, 0x10, 0x0A, 0x10, 0x0F, 0xA1, 0x80, 0x12, 0x31, 0x23, 0x47, 0x86, 0xA1, 0x80,
87
- 0x47, 0x08, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x48,
88
- 0x02, 0x8B, 0xAF, 0x46, 0x02, 0x88, 0x88, 0x88, 0x88, 0x88, 0x48, 0x13, 0x8B, 0xAF, 0x57,
89
- 0x13, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
90
- 0x00, 0x00, 0x00, 0x00,
91
- ),
92
- (
93
- 0xEA, 0x96, 0x12, 0x01, 0x01, 0x01, 0x78, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x31,
94
- 0x8B, 0xA8, 0x31, 0x75, 0x88, 0x88, 0x88, 0x88, 0x88, 0x4F, 0x20, 0x8B, 0xA8, 0x20, 0x64,
95
- 0x88, 0x88, 0x88, 0x88, 0x88, 0x23, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
96
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xA1, 0x80, 0x00, 0x00,
97
- 0x00, 0x00,
98
- ),
99
- (
100
- 0xE0, 0x00, 0x0A, 0x0F, 0x29, 0x3B, 0x3F, 0x42, 0x39, 0x06, 0x0D, 0x10, 0x13, 0x15, 0x14,
101
- 0x15, 0x10, 0x17, 0x00, 0x0A, 0x0F, 0x29, 0x3B, 0x3F, 0x42, 0x39, 0x06, 0x0D, 0x10, 0x13,
102
- 0x15, 0x14, 0x15, 0x10, 0x17,
103
- ),
77
+ (0xB6, 0x97, 0x97),
78
+ (0xB8, 0x26, 0x22, 0xF0, 0x13),
79
+ (0xBA, 0x31, 0x81, 0x0F, 0xF9, 0x0E, 0x06, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x25, 0x00, 0x90, 0x0A, 0x00, 0x00, 0x01, 0x4F, 0x01, 0x00, 0x00, 0x37),
80
+ (0xBC, 0x47),
81
+ (0xBF, 0x02, 0x11, 0x00),
82
+ (0xC0, 0x73, 0x73, 0x50, 0x50, 0x00, 0x00, 0x12, 0x70, 0x00),
83
+ (0xC1, 0x25, 0x00, 0x32, 0x32, 0x77, 0xE4, 0xFF, 0xFF, 0xCC, 0xCC, 0x77, 0x77),
84
+ (0xC6, 0x82, 0x00, 0xBF, 0xFF, 0x00, 0xFF),
85
+ (0xC7, 0xB8, 0x00, 0x0A, 0x10, 0x01, 0x09),
86
+ (0xC8, 0x10, 0x40, 0x1E, 0x02),
87
+ (0xCC, 0x0B),
88
+ (0xE0, 0x00, 0x0B, 0x10, 0x2C, 0x3D, 0x3F, 0x42, 0x3A, 0x07, 0x0D, 0x0F, 0x13, 0x15, 0x13, 0x14, 0x0F, 0x16, 0x00, 0x0B, 0x10, 0x2C, 0x3D, 0x3F, 0x42, 0x3A, 0x07, 0x0D, 0x0F, 0x13, 0x15, 0x13, 0x14, 0x0F, 0x16),
89
+ (0xE3, 0x07, 0x07, 0x0B, 0x0B, 0x0B, 0x0B, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xC0, 0x10),
90
+ (0xE9, 0xC8, 0x10, 0x0A, 0x00, 0x00, 0x80, 0x81, 0x12, 0x31, 0x23, 0x4F, 0x86, 0xA0, 0x00, 0x47, 0x08, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x98, 0x02, 0x8B, 0xAF, 0x46, 0x02, 0x88, 0x88, 0x88, 0x88, 0x88, 0x98, 0x13, 0x8B, 0xAF, 0x57, 0x13, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
91
+ (0xEA, 0x97, 0x0C, 0x09, 0x09, 0x09, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9F, 0x31, 0x8B, 0xA8, 0x31, 0x75, 0x88, 0x88, 0x88, 0x88, 0x88, 0x9F, 0x20, 0x8B, 0xA8, 0x20, 0x64, 0x88, 0x88, 0x88, 0x88, 0x88, 0x23, 0x00, 0x00, 0x02, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x80, 0x81, 0x00, 0x00, 0x00, 0x00),
92
+ (0xEF, 0xFF, 0xFF, 0x01),
93
+ (0x11, 0x00),
94
+ (0x29, 0x00),
104
95
  ],
105
96
  )
@@ -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.Schema(
150
- {
151
- cv.Required(CONF_MIRROR_X): cv.boolean,
152
- cv.Required(CONF_MIRROR_Y): cv.boolean,
153
- **swap_xy_schema(model),
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
- is_swapped = config.get(CONF_TRANSFORM, {}).get(CONF_SWAP_XY) is True
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 const uint8_t DELAY_FLAG = 0xFF;
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
- int WIDTH, int HEIGHT, int OFFSET_WIDTH, int OFFSET_HEIGHT, display::DisplayRotation ROTATION, int FRACTION>
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, sizeof(BUFFERTYPE) * WIDTH * HEIGHT / FRACTION,
465
- this->draw_rounding_);
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(WIDTH * HEIGHT / FRACTION);
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
- auto dr = this->draw_rounding_;
512
- this->x_low_ = this->x_low_ / dr * dr;
513
- this->y_low_ = this->y_low_ / dr * dr;
514
- this->x_high_ = (this->x_high_ + dr) / dr * dr - 1;
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_, WIDTH - w);
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
- rotate_coordinates_(x, y);
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_) * WIDTH + x] = convert_color_(color);
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 * WIDTH / FRACTION, convert_color_(color));
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 rotate_coordinates_(int &x, int &y) const {
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 convert_color_(Color &color) const {
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
- substitutions = {
174
- **config.get(CONF_SUBSTITUTIONS, {}),
175
- **(command_line_substitutions or {}),
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 and not has_eap:
406
- add_idf_sdkconfig_option("CONFIG_ESP_WIFI_ENTERPRISE_SUPPORT", False)
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
- **(config.get(CONF_SUBSTITUTIONS) or {}),
927
- **command_line_substitutions,
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
- res = old.copy()
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
@@ -4,7 +4,7 @@ from enum import Enum
4
4
 
5
5
  from esphome.enum import StrEnum
6
6
 
7
- __version__ = "2025.10.0b3"
7
+ __version__ = "2025.10.1"
8
8
 
9
9
  ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
10
10
  VALID_SUBSTITUTIONS_CHARACTERS = (
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 not password:
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.0b3
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
@@ -36,7 +36,7 @@ Requires-Dist: platformio==6.1.18
36
36
  Requires-Dist: esptool==5.1.0
37
37
  Requires-Dist: click==8.1.7
38
38
  Requires-Dist: esphome-dashboard==20251013.0
39
- Requires-Dist: aioesphomeapi==41.14.0
39
+ Requires-Dist: aioesphomeapi==41.16.1
40
40
  Requires-Dist: zeroconf==0.148.0
41
41
  Requires-Dist: puremagic==1.30
42
42
  Requires-Dist: ruamel.yaml==0.18.15
@@ -1,18 +1,18 @@
1
1
  esphome/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- esphome/__main__.py,sha256=ecT5QSWDElewwl7FsrwxD-nrpYN-6QQdEagcS4GHTvc,42029
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=yfwaqzZb--kLHyfhZxyTo5cXImtJrMC9Xs9lxurKNww,45135
7
- esphome/config_helpers.py,sha256=E0UO0khX9JW-br_NSsDlLH3VuE-YwMQ99_pzEQVupDc,5391
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=vnpMkgGPCDUfkGNiAM89CaFjzSh9it1Yi0DHaW70470,44458
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=Jv8uzFKy6uSoYfoEz8h7P6Kxr6DZiiMnMAK5Shpgb6g,15187
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=d9DDcwVhMySt9DPQftoaepi-179KevvltQdys8FjO5Q,32455
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=08Imm3tTvxePntmW7tujXWtYOnD9rVSxso2iOpIFZZs,5574
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=5Oub7Y-9rUnhxO9eKvv7Mv1pJ6tWTwYP3j_ESVHzWbo,15604
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
@@ -1939,7 +1939,7 @@ esphome/components/mipi_dsi/mipi_dsi.h,sha256=Iox028s4P0tV364GDMg5CV63bxPS4RQvtO
1939
1939
  esphome/components/mipi_dsi/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1940
1940
  esphome/components/mipi_dsi/models/guition.py,sha256=1CGC9Q22lVwU7JPmJvh8HdhU57C283wzKBnpEdRzmkc,1924
1941
1941
  esphome/components/mipi_dsi/models/m5stack.py,sha256=rCaDaIsODGbH-r8rgrWZPnqVr6zsXa2QB9drIaX5Ie8,3702
1942
- esphome/components/mipi_dsi/models/waveshare.py,sha256=-nEVsVrj9zLXzGwC0eLImjArCMSXZ_tPSfXMcwZu0rk,5941
1942
+ esphome/components/mipi_dsi/models/waveshare.py,sha256=-TbgXEXqsBr-GiW8JZWxNbBpSAPxh4YMbtim9cncIRA,6010
1943
1943
  esphome/components/mipi_rgb/__init__.py,sha256=qyDwXsLmE_stmzY22m9hVdNHKNPwiq3rNT7Cn-tVDHE,50
1944
1944
  esphome/components/mipi_rgb/display.py,sha256=JoY161r9utSKoaWdrmuuHzN_QBrKOVnLKC2j4rJyNlg,11450
1945
1945
  esphome/components/mipi_rgb/mipi_rgb.cpp,sha256=2ssWQw80GTgwSrPuwaI05FOdESE4f2a0OTsZ3wRloZ0,13390
@@ -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=IL37NgcnqCUM47DzyB3wsnHK3JXc57617HzYK-QEnQM,15756
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=CYAj2jAc-RjLzgzQ1qrc_loc9gD-X4bu6ZfDuBqk6sc,23352
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=mABbTYcPHvpMIEHvedsFSOMN4bz9s3gAEZB1PMXMihg,2544
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=uPKmegvlWe3oiNL9vFssEJzoyfnXin-8lZMTtM5w748,7268
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=3zMzYNVqHTmsYSGah-lnTePTnbKPr7mYDHds04iedng,18221
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.0b3.dist-info/licenses/LICENSE,sha256=HzEjkBInJe44L4WvAOPfhPJJDNj6YbnqFyvGWRzArGM,36664
3790
- esphome-2025.10.0b3.dist-info/METADATA,sha256=8ekM5N3F0xnpkd0GTgswnTQ8tGxZSEBAxi3u9KNBAjU,3635
3791
- esphome-2025.10.0b3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
3792
- esphome-2025.10.0b3.dist-info/entry_points.txt,sha256=mIxVNuWtbYzeEcaWCl-AQ-97aBOWbnYBAK8nbF6P4M0,50
3793
- esphome-2025.10.0b3.dist-info/top_level.txt,sha256=0GSXEW3cnITpgG3qnsSMz0qoqJHAFyfw7Y8MVtEf1Yk,8
3794
- esphome-2025.10.0b3.dist-info/RECORD,,
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,,