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 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
- _LOGGER.error("All specified devices: %s could not be resolved.", defaults)
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
- _PORT_TO_PORT_TYPE = {
284
- "MQTT": "MQTT",
285
- "MQTTIP": "MQTTIP",
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) -> 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 "SERIAL"
292
- return _PORT_TO_PORT_TYPE.get(port, "NETWORK")
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) == "SERIAL":
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) == "SERIAL":
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 address resolution
554
- if get_port_type(host) in ("MQTT", "MQTTIP"):
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(devices, remote_port, password, binary)
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 get_port_type(port) == "SERIAL":
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
- 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
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
- return run_logs(config, addresses_to_use)
648
+ return run_logs(config, network_devices)
597
649
 
598
- if port_type in ("NETWORK", "MQTT") and has_mqtt_logging():
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.temperature,
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.temperature,
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 <= 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
 
@@ -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
- 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
@@ -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 = 0xE3;
13
- static const uint8_t HTU21D_REGISTER_HUMIDITY = 0xE5;
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
- # 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
 
@@ -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
+ )
@@ -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.temperature,
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
- 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]
@@ -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.0b4"
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
@@ -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
- CORE.config_path = self.config_dir / "."
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:
@@ -1058,7 +1058,8 @@ class DownloadBinaryRequestHandler(BaseHandler):
1058
1058
  "download",
1059
1059
  f"{storage_json.name}-{file_name}",
1060
1060
  )
1061
- path = storage_json.firmware_bin_path.with_name(file_name)
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 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.0b4
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=ecT5QSWDElewwl7FsrwxD-nrpYN-6QQdEagcS4GHTvc,42029
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=yfwaqzZb--kLHyfhZxyTo5cXImtJrMC9Xs9lxurKNww,45135
7
- esphome/config_helpers.py,sha256=E0UO0khX9JW-br_NSsDlLH3VuE-YwMQ99_pzEQVupDc,5391
8
- esphome/config_validation.py,sha256=WOIf3s3CIhWiAFJ7NdpmMzLfCNv89J67pvZXHmiBz5I,64808
9
- esphome/const.py,sha256=6_VnE7Qy7S30q_3HrhTo6T5nB-bj9O1J_E5lQ6xNJMc,44458
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=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
@@ -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=FmtARbE-B7Dkb1FnOWQavaAdOggraJqrDIocHitQrmI,3062
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=OarBjxWaiAoR_xc6isNHHSaa6PkDXinQY4-Wur9BR-I,6234
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=t-65pTVsHCOk9zWNWkgLFC296DjHAZb5NvKCC_smWCw,1051
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=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
@@ -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=NNLUijHeU5ZSlf5AbS0Sk8oYQN5wk5-cbfnFwIRqttc,39042
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=7zSi6LBjWUV1qMNdCTs3R4LRLkg6Xnl8a7bsTGy-f8Q,2747
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=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
@@ -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=XAeGTYFgDnUDSGv65K7zTKQWwzLTKRbgPC5oQO_GAFM,4560
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=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
@@ -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
@@ -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=NYhoNVTz6inXvoY2wu6l8aWDEKpzOrGBax8lj6y5QnQ,5623
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=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
@@ -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=4TnnZeKAoV-EAiKVdRu5dkBWz1ettqW3RO4Z9VxxkhI,30466
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=17ia_N1BMGCFnUC5XHOOJhV4FUs8atPLDpnTVoWNHfM,2836
3779
- esphome/dashboard/web_server.py,sha256=TQGkuADbyI86IAGUOksBTOyCXZGyT0fGYVkGLMUpSdk,56658
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.0b4.dist-info/licenses/LICENSE,sha256=HzEjkBInJe44L4WvAOPfhPJJDNj6YbnqFyvGWRzArGM,36664
3790
- esphome-2025.10.0b4.dist-info/METADATA,sha256=humqhRXMIHXSE5FVUQEstMB7cvGKWMAZcEcbKXQlwn4,3635
3791
- esphome-2025.10.0b4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
3792
- esphome-2025.10.0b4.dist-info/entry_points.txt,sha256=mIxVNuWtbYzeEcaWCl-AQ-97aBOWbnYBAK8nbF6P4M0,50
3793
- esphome-2025.10.0b4.dist-info/top_level.txt,sha256=0GSXEW3cnITpgG3qnsSMz0qoqJHAFyfw7Y8MVtEf1Yk,8
3794
- esphome-2025.10.0b4.dist-info/RECORD,,
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,,