esphome 2025.7.2__py3-none-any.whl → 2025.7.4__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.

@@ -1,7 +1,7 @@
1
1
  import esphome.codegen as cg
2
2
  from esphome.components import esp32, i2c
3
3
  import esphome.config_validation as cv
4
- from esphome.const import CONF_ID, CONF_SAMPLE_RATE, CONF_TEMPERATURE_OFFSET
4
+ from esphome.const import CONF_ID, CONF_SAMPLE_RATE, CONF_TEMPERATURE_OFFSET, Framework
5
5
 
6
6
  CODEOWNERS = ["@trvrnrth"]
7
7
  DEPENDENCIES = ["i2c"]
@@ -56,7 +56,15 @@ CONFIG_SCHEMA = cv.All(
56
56
  ): cv.positive_time_period_minutes,
57
57
  }
58
58
  ).extend(i2c.i2c_device_schema(0x76)),
59
- cv.only_with_arduino,
59
+ cv.only_with_framework(
60
+ frameworks=Framework.ARDUINO,
61
+ suggestions={
62
+ Framework.ESP_IDF: (
63
+ "bme68x_bsec2_i2c",
64
+ "sensor/bme68x_bsec2",
65
+ )
66
+ },
67
+ ),
60
68
  cv.Any(
61
69
  cv.only_on_esp8266,
62
70
  cv.All(
@@ -16,6 +16,8 @@ namespace esp32_touch {
16
16
 
17
17
  static const char *const TAG = "esp32_touch";
18
18
 
19
+ static const uint32_t SETUP_MODE_THRESHOLD = 0xFFFF;
20
+
19
21
  void ESP32TouchComponent::setup() {
20
22
  // Create queue for touch events
21
23
  // Queue size calculation: children * 4 allows for burst scenarios where ISR
@@ -44,7 +46,11 @@ void ESP32TouchComponent::setup() {
44
46
 
45
47
  // Configure each touch pad
46
48
  for (auto *child : this->children_) {
47
- touch_pad_config(child->get_touch_pad(), child->get_threshold());
49
+ if (this->setup_mode_) {
50
+ touch_pad_config(child->get_touch_pad(), SETUP_MODE_THRESHOLD);
51
+ } else {
52
+ touch_pad_config(child->get_touch_pad(), child->get_threshold());
53
+ }
48
54
  }
49
55
 
50
56
  // Register ISR handler
@@ -114,8 +120,8 @@ void ESP32TouchComponent::loop() {
114
120
  child->publish_state(new_state);
115
121
  // Original ESP32: ISR only fires when touched, release is detected by timeout
116
122
  // Note: ESP32 v1 uses inverted logic - touched when value < threshold
117
- ESP_LOGV(TAG, "Touch Pad '%s' state: ON (value: %" PRIu32 " < threshold: %" PRIu32 ")",
118
- child->get_name().c_str(), event.value, child->get_threshold());
123
+ ESP_LOGV(TAG, "Touch Pad '%s' state: %s (value: %" PRIu32 " < threshold: %" PRIu32 ")",
124
+ child->get_name().c_str(), ONOFF(new_state), event.value, child->get_threshold());
119
125
  }
120
126
  break; // Exit inner loop after processing matching pad
121
127
  }
@@ -188,11 +194,6 @@ void IRAM_ATTR ESP32TouchComponent::touch_isr_handler(void *arg) {
188
194
  // as any pad remains touched. This allows us to detect both new touches and
189
195
  // continued touches, but releases must be detected by timeout in the main loop.
190
196
 
191
- // IMPORTANT: ESP32 v1 touch detection logic - INVERTED compared to v2!
192
- // ESP32 v1: Touch is detected when capacitance INCREASES, causing the measured value to DECREASE
193
- // Therefore: touched = (value < threshold)
194
- // This is opposite to ESP32-S2/S3 v2 where touched = (value > threshold)
195
-
196
197
  // Process all configured pads to check their current state
197
198
  // Note: ESP32 v1 doesn't tell us which specific pad triggered the interrupt,
198
199
  // so we must scan all configured pads to find which ones were touched
@@ -211,11 +212,16 @@ void IRAM_ATTR ESP32TouchComponent::touch_isr_handler(void *arg) {
211
212
  }
212
213
 
213
214
  // Skip pads that aren’t in the trigger mask
214
- bool is_touched = (mask >> pad) & 1;
215
- if (!is_touched) {
215
+ if (((mask >> pad) & 1) == 0) {
216
216
  continue;
217
217
  }
218
218
 
219
+ // IMPORTANT: ESP32 v1 touch detection logic - INVERTED compared to v2!
220
+ // ESP32 v1: Touch is detected when capacitance INCREASES, causing the measured value to DECREASE
221
+ // Therefore: touched = (value < threshold)
222
+ // This is opposite to ESP32-S2/S3 v2 where touched = (value > threshold)
223
+ bool is_touched = value < child->get_threshold();
224
+
219
225
  // Always send the current state - the main loop will filter for changes
220
226
  // We send both touched and untouched states because the ISR doesn't
221
227
  // track previous state (to keep ISR fast and simple)
@@ -2,7 +2,13 @@ from esphome import pins
2
2
  import esphome.codegen as cg
3
3
  from esphome.components import fastled_base
4
4
  import esphome.config_validation as cv
5
- from esphome.const import CONF_CHIPSET, CONF_NUM_LEDS, CONF_PIN, CONF_RGB_ORDER
5
+ from esphome.const import (
6
+ CONF_CHIPSET,
7
+ CONF_NUM_LEDS,
8
+ CONF_PIN,
9
+ CONF_RGB_ORDER,
10
+ Framework,
11
+ )
6
12
 
7
13
  AUTO_LOAD = ["fastled_base"]
8
14
 
@@ -48,13 +54,22 @@ CONFIG_SCHEMA = cv.All(
48
54
  cv.Required(CONF_PIN): pins.internal_gpio_output_pin_number,
49
55
  }
50
56
  ),
51
- _validate,
57
+ cv.only_with_framework(
58
+ frameworks=Framework.ARDUINO,
59
+ suggestions={
60
+ Framework.ESP_IDF: (
61
+ "esp32_rmt_led_strip",
62
+ "light/esp32_rmt_led_strip",
63
+ )
64
+ },
65
+ ),
52
66
  cv.require_framework_version(
53
67
  esp8266_arduino=cv.Version(2, 7, 4),
54
68
  esp32_arduino=cv.Version(99, 0, 0),
55
69
  max_version=True,
56
70
  extra_message="Please see note on documentation for FastLED",
57
71
  ),
72
+ _validate,
58
73
  )
59
74
 
60
75
 
@@ -9,6 +9,7 @@ from esphome.const import (
9
9
  CONF_DATA_RATE,
10
10
  CONF_NUM_LEDS,
11
11
  CONF_RGB_ORDER,
12
+ Framework,
12
13
  )
13
14
 
14
15
  AUTO_LOAD = ["fastled_base"]
@@ -33,6 +34,15 @@ CONFIG_SCHEMA = cv.All(
33
34
  cv.Optional(CONF_DATA_RATE): cv.frequency,
34
35
  }
35
36
  ),
37
+ cv.only_with_framework(
38
+ frameworks=Framework.ARDUINO,
39
+ suggestions={
40
+ Framework.ESP_IDF: (
41
+ "spi_led_strip",
42
+ "light/spi_led_strip",
43
+ )
44
+ },
45
+ ),
36
46
  cv.require_framework_version(
37
47
  esp8266_arduino=cv.Version(2, 7, 4),
38
48
  esp32_arduino=cv.Version(99, 0, 0),
@@ -4,7 +4,13 @@ from esphome import pins
4
4
  import esphome.codegen as cg
5
5
  from esphome.components import binary_sensor
6
6
  import esphome.config_validation as cv
7
- from esphome.const import CONF_ID, CONF_NAME, CONF_NUMBER, CONF_PIN
7
+ from esphome.const import (
8
+ CONF_ALLOW_OTHER_USES,
9
+ CONF_ID,
10
+ CONF_NAME,
11
+ CONF_NUMBER,
12
+ CONF_PIN,
13
+ )
8
14
  from esphome.core import CORE
9
15
 
10
16
  from .. import gpio_ns
@@ -76,6 +82,18 @@ async def to_code(config):
76
82
  )
77
83
  use_interrupt = False
78
84
 
85
+ # Check if pin is shared with other components (allow_other_uses)
86
+ # When a pin is shared, interrupts can interfere with other components
87
+ # (e.g., duty_cycle sensor) that need to monitor the pin's state changes
88
+ if use_interrupt and config[CONF_PIN].get(CONF_ALLOW_OTHER_USES, False):
89
+ _LOGGER.info(
90
+ "GPIO binary_sensor '%s': Disabling interrupts because pin %s is shared with other components. "
91
+ "The sensor will use polling mode for compatibility with other pin uses.",
92
+ config.get(CONF_NAME, config[CONF_ID]),
93
+ config[CONF_PIN][CONF_NUMBER],
94
+ )
95
+ use_interrupt = False
96
+
79
97
  cg.add(var.set_use_interrupt(use_interrupt))
80
98
  if use_interrupt:
81
99
  cg.add(var.set_interrupt_type(config[CONF_INTERRUPT_TYPE]))
@@ -8,6 +8,8 @@ namespace gt911 {
8
8
 
9
9
  static const char *const TAG = "gt911.touchscreen";
10
10
 
11
+ static const uint8_t PRIMARY_ADDRESS = 0x5D; // default I2C address for GT911
12
+ static const uint8_t SECONDARY_ADDRESS = 0x14; // secondary I2C address for GT911
11
13
  static const uint8_t GET_TOUCH_STATE[2] = {0x81, 0x4E};
12
14
  static const uint8_t CLEAR_TOUCH_STATE[3] = {0x81, 0x4E, 0x00};
13
15
  static const uint8_t GET_TOUCHES[2] = {0x81, 0x4F};
@@ -18,8 +20,7 @@ static const size_t MAX_BUTTONS = 4; // max number of buttons scanned
18
20
 
19
21
  #define ERROR_CHECK(err) \
20
22
  if ((err) != i2c::ERROR_OK) { \
21
- ESP_LOGE(TAG, "Failed to communicate!"); \
22
- this->status_set_warning(); \
23
+ this->status_set_warning("Communication failure"); \
23
24
  return; \
24
25
  }
25
26
 
@@ -30,31 +31,31 @@ void GT911Touchscreen::setup() {
30
31
  this->reset_pin_->setup();
31
32
  this->reset_pin_->digital_write(false);
32
33
  if (this->interrupt_pin_ != nullptr) {
33
- // The interrupt pin is used as an input during reset to select the I2C address.
34
+ // temporarily set the interrupt pin to output to control address selection
34
35
  this->interrupt_pin_->pin_mode(gpio::FLAG_OUTPUT);
35
- this->interrupt_pin_->setup();
36
36
  this->interrupt_pin_->digital_write(false);
37
37
  }
38
38
  delay(2);
39
39
  this->reset_pin_->digital_write(true);
40
40
  delay(50); // NOLINT
41
- if (this->interrupt_pin_ != nullptr) {
42
- this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT);
43
- this->interrupt_pin_->setup();
44
- }
41
+ }
42
+ if (this->interrupt_pin_ != nullptr) {
43
+ // set pre-configured input mode
44
+ this->interrupt_pin_->setup();
45
45
  }
46
46
 
47
47
  // check the configuration of the int line.
48
48
  uint8_t data[4];
49
- err = this->write(GET_SWITCHES, 2);
49
+ err = this->write(GET_SWITCHES, sizeof(GET_SWITCHES));
50
+ if (err != i2c::ERROR_OK && this->address_ == PRIMARY_ADDRESS) {
51
+ this->address_ = SECONDARY_ADDRESS;
52
+ err = this->write(GET_SWITCHES, sizeof(GET_SWITCHES));
53
+ }
50
54
  if (err == i2c::ERROR_OK) {
51
55
  err = this->read(data, 1);
52
56
  if (err == i2c::ERROR_OK) {
53
- ESP_LOGD(TAG, "Read from switches: 0x%02X", data[0]);
57
+ ESP_LOGD(TAG, "Read from switches at address 0x%02X: 0x%02X", this->address_, data[0]);
54
58
  if (this->interrupt_pin_ != nullptr) {
55
- // datasheet says NOT to use pullup/down on the int line.
56
- this->interrupt_pin_->pin_mode(gpio::FLAG_INPUT);
57
- this->interrupt_pin_->setup();
58
59
  this->attach_interrupt_(this->interrupt_pin_,
59
60
  (data[0] & 1) ? gpio::INTERRUPT_FALLING_EDGE : gpio::INTERRUPT_RISING_EDGE);
60
61
  }
@@ -63,7 +64,7 @@ void GT911Touchscreen::setup() {
63
64
  if (this->x_raw_max_ == 0 || this->y_raw_max_ == 0) {
64
65
  // no calibration? Attempt to read the max values from the touchscreen.
65
66
  if (err == i2c::ERROR_OK) {
66
- err = this->write(GET_MAX_VALUES, 2);
67
+ err = this->write(GET_MAX_VALUES, sizeof(GET_MAX_VALUES));
67
68
  if (err == i2c::ERROR_OK) {
68
69
  err = this->read(data, sizeof(data));
69
70
  if (err == i2c::ERROR_OK) {
@@ -75,15 +76,12 @@ void GT911Touchscreen::setup() {
75
76
  }
76
77
  }
77
78
  if (err != i2c::ERROR_OK) {
78
- ESP_LOGE(TAG, "Failed to read calibration values from touchscreen!");
79
- this->mark_failed();
79
+ this->mark_failed("Failed to read calibration");
80
80
  return;
81
81
  }
82
82
  }
83
83
  if (err != i2c::ERROR_OK) {
84
- ESP_LOGE(TAG, "Failed to communicate!");
85
- this->mark_failed();
86
- return;
84
+ this->mark_failed("Failed to communicate");
87
85
  }
88
86
 
89
87
  ESP_LOGCONFIG(TAG, "GT911 Touchscreen setup complete");
@@ -94,7 +92,7 @@ void GT911Touchscreen::update_touches() {
94
92
  uint8_t touch_state = 0;
95
93
  uint8_t data[MAX_TOUCHES + 1][8]; // 8 bytes each for each point, plus extra space for the key byte
96
94
 
97
- err = this->write(GET_TOUCH_STATE, sizeof(GET_TOUCH_STATE), false);
95
+ err = this->write(GET_TOUCH_STATE, sizeof(GET_TOUCH_STATE));
98
96
  ERROR_CHECK(err);
99
97
  err = this->read(&touch_state, 1);
100
98
  ERROR_CHECK(err);
@@ -106,7 +104,7 @@ void GT911Touchscreen::update_touches() {
106
104
  return;
107
105
  }
108
106
 
109
- err = this->write(GET_TOUCHES, sizeof(GET_TOUCHES), false);
107
+ err = this->write(GET_TOUCHES, sizeof(GET_TOUCHES));
110
108
  ERROR_CHECK(err);
111
109
  // num_of_touches is guaranteed to be 0..5. Also read the key data
112
110
  err = this->read(data[0], sizeof(data[0]) * num_of_touches + 1);
@@ -132,6 +130,7 @@ void GT911Touchscreen::dump_config() {
132
130
  ESP_LOGCONFIG(TAG, "GT911 Touchscreen:");
133
131
  LOG_I2C_DEVICE(this);
134
132
  LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_);
133
+ LOG_PIN(" Reset Pin: ", this->reset_pin_);
135
134
  }
136
135
 
137
136
  } // namespace gt911
@@ -477,10 +477,11 @@ void LD2450Component::handle_periodic_data_() {
477
477
  // X
478
478
  start = TARGET_X + index * 8;
479
479
  is_moving = false;
480
+ // tx is used for further calculations, so always needs to be populated
481
+ val = ld2450::decode_coordinate(this->buffer_data_[start], this->buffer_data_[start + 1]);
482
+ tx = val;
480
483
  sensor::Sensor *sx = this->move_x_sensors_[index];
481
484
  if (sx != nullptr) {
482
- val = ld2450::decode_coordinate(this->buffer_data_[start], this->buffer_data_[start + 1]);
483
- tx = val;
484
485
  if (this->cached_target_data_[index].x != val) {
485
486
  sx->publish_state(val);
486
487
  this->cached_target_data_[index].x = val;
@@ -488,10 +489,11 @@ void LD2450Component::handle_periodic_data_() {
488
489
  }
489
490
  // Y
490
491
  start = TARGET_Y + index * 8;
492
+ // ty is used for further calculations, so always needs to be populated
493
+ val = ld2450::decode_coordinate(this->buffer_data_[start], this->buffer_data_[start + 1]);
494
+ ty = val;
491
495
  sensor::Sensor *sy = this->move_y_sensors_[index];
492
496
  if (sy != nullptr) {
493
- val = ld2450::decode_coordinate(this->buffer_data_[start], this->buffer_data_[start + 1]);
494
- ty = val;
495
497
  if (this->cached_target_data_[index].y != val) {
496
498
  sy->publish_state(val);
497
499
  this->cached_target_data_[index].y = val;
@@ -400,6 +400,7 @@ CONF_LOGGER_LOG = "logger.log"
400
400
  LOGGER_LOG_ACTION_SCHEMA = cv.All(
401
401
  cv.maybe_simple_value(
402
402
  {
403
+ cv.GenerateID(CONF_LOGGER_ID): cv.use_id(Logger),
403
404
  cv.Required(CONF_FORMAT): cv.string,
404
405
  cv.Optional(CONF_ARGS, default=list): cv.ensure_list(cv.lambda_),
405
406
  cv.Optional(CONF_LEVEL, default="DEBUG"): cv.one_of(
@@ -15,6 +15,7 @@ from esphome.const import (
15
15
  CONF_PIN,
16
16
  CONF_TYPE,
17
17
  CONF_VARIANT,
18
+ Framework,
18
19
  )
19
20
  from esphome.core import CORE
20
21
 
@@ -162,7 +163,15 @@ def _validate_method(value):
162
163
 
163
164
 
164
165
  CONFIG_SCHEMA = cv.All(
165
- cv.only_with_arduino,
166
+ cv.only_with_framework(
167
+ frameworks=Framework.ARDUINO,
168
+ suggestions={
169
+ Framework.ESP_IDF: (
170
+ "esp32_rmt_led_strip",
171
+ "light/esp32_rmt_led_strip",
172
+ )
173
+ },
174
+ ),
166
175
  cv.require_framework_version(
167
176
  esp8266_arduino=cv.Version(2, 4, 0),
168
177
  esp32_arduino=cv.Version(0, 0, 0),
@@ -60,6 +60,20 @@ RemoteReceiverComponent = remote_receiver_ns.class_(
60
60
  )
61
61
 
62
62
 
63
+ def validate_config(config):
64
+ if CORE.is_esp32:
65
+ variant = esp32.get_esp32_variant()
66
+ if variant in (esp32.const.VARIANT_ESP32, esp32.const.VARIANT_ESP32S2):
67
+ max_idle = 65535
68
+ else:
69
+ max_idle = 32767
70
+ if CONF_CLOCK_RESOLUTION in config:
71
+ max_idle = int(max_idle * 1000000 / config[CONF_CLOCK_RESOLUTION])
72
+ if config[CONF_IDLE].total_microseconds > max_idle:
73
+ raise cv.Invalid(f"config 'idle' exceeds the maximum value of {max_idle}us")
74
+ return config
75
+
76
+
63
77
  def validate_tolerance(value):
64
78
  if isinstance(value, dict):
65
79
  return TOLERANCE_SCHEMA(value)
@@ -136,7 +150,9 @@ CONFIG_SCHEMA = remote_base.validate_triggers(
136
150
  cv.boolean,
137
151
  ),
138
152
  }
139
- ).extend(cv.COMPONENT_SCHEMA)
153
+ )
154
+ .extend(cv.COMPONENT_SCHEMA)
155
+ .add_extra(validate_config)
140
156
  )
141
157
 
142
158
 
@@ -86,10 +86,9 @@ void RemoteReceiverComponent::setup() {
86
86
 
87
87
  uint32_t event_size = sizeof(rmt_rx_done_event_data_t);
88
88
  uint32_t max_filter_ns = 255u * 1000 / (RMT_CLK_FREQ / 1000000);
89
- uint32_t max_idle_ns = 65535u * 1000;
90
89
  memset(&this->store_.config, 0, sizeof(this->store_.config));
91
90
  this->store_.config.signal_range_min_ns = std::min(this->filter_us_ * 1000, max_filter_ns);
92
- this->store_.config.signal_range_max_ns = std::min(this->idle_us_ * 1000, max_idle_ns);
91
+ this->store_.config.signal_range_max_ns = this->idle_us_ * 1000;
93
92
  this->store_.filter_symbols = this->filter_symbols_;
94
93
  this->store_.receive_size = this->receive_symbols_ * sizeof(rmt_symbol_word_t);
95
94
  this->store_.buffer_size = std::max((event_size + this->store_.receive_size) * 2, this->buffer_size_);
@@ -48,6 +48,9 @@ void Sdl::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *
48
48
  }
49
49
 
50
50
  void Sdl::draw_pixel_at(int x, int y, Color color) {
51
+ if (!this->get_clipping().inside(x, y))
52
+ return;
53
+
51
54
  SDL_Rect rect{x, y, 1, 1};
52
55
  auto data = (display::ColorUtil::color_to_565(color, display::COLOR_ORDER_RGB));
53
56
  SDL_UpdateTexture(this->texture_, &rect, &data, 2);
@@ -1,7 +1,7 @@
1
1
  import esphome.codegen as cg
2
2
  from esphome.components import fan
3
3
  import esphome.config_validation as cv
4
- from esphome.const import CONF_OUTPUT_ID, CONF_SPEED_COUNT, CONF_SWITCH_DATAPOINT
4
+ from esphome.const import CONF_ID, CONF_SPEED_COUNT, CONF_SWITCH_DATAPOINT
5
5
 
6
6
  from .. import CONF_TUYA_ID, Tuya, tuya_ns
7
7
 
@@ -14,9 +14,9 @@ CONF_DIRECTION_DATAPOINT = "direction_datapoint"
14
14
  TuyaFan = tuya_ns.class_("TuyaFan", cg.Component, fan.Fan)
15
15
 
16
16
  CONFIG_SCHEMA = cv.All(
17
- fan.FAN_SCHEMA.extend(
17
+ fan.fan_schema(TuyaFan)
18
+ .extend(
18
19
  {
19
- cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(TuyaFan),
20
20
  cv.GenerateID(CONF_TUYA_ID): cv.use_id(Tuya),
21
21
  cv.Optional(CONF_OSCILLATION_DATAPOINT): cv.uint8_t,
22
22
  cv.Optional(CONF_SPEED_DATAPOINT): cv.uint8_t,
@@ -24,7 +24,8 @@ CONFIG_SCHEMA = cv.All(
24
24
  cv.Optional(CONF_DIRECTION_DATAPOINT): cv.uint8_t,
25
25
  cv.Optional(CONF_SPEED_COUNT, default=3): cv.int_range(min=1, max=256),
26
26
  }
27
- ).extend(cv.COMPONENT_SCHEMA),
27
+ )
28
+ .extend(cv.COMPONENT_SCHEMA),
28
29
  cv.has_at_least_one_key(CONF_SPEED_DATAPOINT, CONF_SWITCH_DATAPOINT),
29
30
  )
30
31
 
@@ -32,7 +33,7 @@ CONFIG_SCHEMA = cv.All(
32
33
  async def to_code(config):
33
34
  parent = await cg.get_variable(config[CONF_TUYA_ID])
34
35
 
35
- var = cg.new_Pvariable(config[CONF_OUTPUT_ID], parent, config[CONF_SPEED_COUNT])
36
+ var = cg.new_Pvariable(config[CONF_ID], parent, config[CONF_SPEED_COUNT])
36
37
  await cg.register_component(var, config)
37
38
  await fan.register_fan(var, config)
38
39
 
@@ -76,7 +76,7 @@ void OTARequestHandler::report_ota_progress_(AsyncWebServerRequest *request) {
76
76
  percentage = (this->ota_read_length_ * 100.0f) / request->contentLength();
77
77
  ESP_LOGD(TAG, "OTA in progress: %0.1f%%", percentage);
78
78
  } else {
79
- ESP_LOGD(TAG, "OTA in progress: %u bytes read", this->ota_read_length_);
79
+ ESP_LOGD(TAG, "OTA in progress: %" PRIu32 " bytes read", this->ota_read_length_);
80
80
  }
81
81
  #ifdef USE_OTA_STATE_CALLBACK
82
82
  // Report progress - use call_deferred since we're in web server task
@@ -171,7 +171,7 @@ void OTARequestHandler::handleUpload(AsyncWebServerRequest *request, const Strin
171
171
 
172
172
  // Finalize
173
173
  if (final) {
174
- ESP_LOGD(TAG, "OTA final chunk: index=%zu, len=%zu, total_read=%u, contentLength=%zu", index, len,
174
+ ESP_LOGD(TAG, "OTA final chunk: index=%zu, len=%zu, total_read=%" PRIu32 ", contentLength=%zu", index, len,
175
175
  this->ota_read_length_, request->contentLength());
176
176
 
177
177
  // For Arduino framework, the Update library tracks expected size from firmware header
@@ -73,6 +73,7 @@ from esphome.const import (
73
73
  TYPE_GIT,
74
74
  TYPE_LOCAL,
75
75
  VALID_SUBSTITUTIONS_CHARACTERS,
76
+ Framework,
76
77
  __version__ as ESPHOME_VERSION,
77
78
  )
78
79
  from esphome.core import (
@@ -282,6 +283,38 @@ class FinalExternalInvalid(Invalid):
282
283
  """Represents an invalid value in the final validation phase where the path should not be prepended."""
283
284
 
284
285
 
286
+ @dataclass(frozen=True, order=True)
287
+ class Version:
288
+ major: int
289
+ minor: int
290
+ patch: int
291
+ extra: str = ""
292
+
293
+ def __str__(self):
294
+ return f"{self.major}.{self.minor}.{self.patch}"
295
+
296
+ @classmethod
297
+ def parse(cls, value: str) -> Version:
298
+ match = re.match(r"^(\d+).(\d+).(\d+)-?(\w*)$", value)
299
+ if match is None:
300
+ raise ValueError(f"Not a valid version number {value}")
301
+ major = int(match[1])
302
+ minor = int(match[2])
303
+ patch = int(match[3])
304
+ extra = match[4] or ""
305
+ return Version(major=major, minor=minor, patch=patch, extra=extra)
306
+
307
+ @property
308
+ def is_beta(self) -> bool:
309
+ """Check if this version is a beta version."""
310
+ return self.extra.startswith("b")
311
+
312
+ @property
313
+ def is_dev(self) -> bool:
314
+ """Check if this version is a development version."""
315
+ return self.extra.startswith("dev")
316
+
317
+
285
318
  def check_not_templatable(value):
286
319
  if isinstance(value, Lambda):
287
320
  raise Invalid("This option is not templatable!")
@@ -619,16 +652,35 @@ def only_on(platforms):
619
652
  return validator_
620
653
 
621
654
 
622
- def only_with_framework(frameworks):
655
+ def only_with_framework(
656
+ frameworks: Framework | str | list[Framework | str], suggestions=None
657
+ ):
623
658
  """Validate that this option can only be specified on the given frameworks."""
624
659
  if not isinstance(frameworks, list):
625
660
  frameworks = [frameworks]
626
661
 
662
+ frameworks = [Framework(framework) for framework in frameworks]
663
+
664
+ if suggestions is None:
665
+ suggestions = {}
666
+
667
+ version = Version.parse(ESPHOME_VERSION)
668
+ if version.is_beta:
669
+ docs_format = "https://beta.esphome.io/components/{path}"
670
+ elif version.is_dev:
671
+ docs_format = "https://next.esphome.io/components/{path}"
672
+ else:
673
+ docs_format = "https://esphome.io/components/{path}"
674
+
627
675
  def validator_(obj):
628
676
  if CORE.target_framework not in frameworks:
629
- raise Invalid(
630
- f"This feature is only available with frameworks {frameworks}"
631
- )
677
+ err_str = f"This feature is only available with framework(s) {', '.join([framework.value for framework in frameworks])}"
678
+ if suggestion := suggestions.get(CORE.target_framework, None):
679
+ (component, docs_path) = suggestion
680
+ err_str += f"\nPlease use '{component}'"
681
+ if docs_path:
682
+ err_str += f": {docs_format.format(path=docs_path)}"
683
+ raise Invalid(err_str)
632
684
  return obj
633
685
 
634
686
  return validator_
@@ -637,8 +689,8 @@ def only_with_framework(frameworks):
637
689
  only_on_esp32 = only_on(PLATFORM_ESP32)
638
690
  only_on_esp8266 = only_on(PLATFORM_ESP8266)
639
691
  only_on_rp2040 = only_on(PLATFORM_RP2040)
640
- only_with_arduino = only_with_framework("arduino")
641
- only_with_esp_idf = only_with_framework("esp-idf")
692
+ only_with_arduino = only_with_framework(Framework.ARDUINO)
693
+ only_with_esp_idf = only_with_framework(Framework.ESP_IDF)
642
694
 
643
695
 
644
696
  # Adapted from:
@@ -1965,26 +2017,6 @@ def source_refresh(value: str):
1965
2017
  return positive_time_period_seconds(value)
1966
2018
 
1967
2019
 
1968
- @dataclass(frozen=True, order=True)
1969
- class Version:
1970
- major: int
1971
- minor: int
1972
- patch: int
1973
-
1974
- def __str__(self):
1975
- return f"{self.major}.{self.minor}.{self.patch}"
1976
-
1977
- @classmethod
1978
- def parse(cls, value: str) -> Version:
1979
- match = re.match(r"^(\d+).(\d+).(\d+)-?\w*$", value)
1980
- if match is None:
1981
- raise ValueError(f"Not a valid version number {value}")
1982
- major = int(match[1])
1983
- minor = int(match[2])
1984
- patch = int(match[3])
1985
- return Version(major=major, minor=minor, patch=patch)
1986
-
1987
-
1988
2020
  def version_number(value):
1989
2021
  value = string_strict(value)
1990
2022
  try:
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.7.2"
7
+ __version__ = "2025.7.4"
8
8
 
9
9
  ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
10
10
  VALID_SUBSTITUTIONS_CHARACTERS = (
@@ -68,8 +68,11 @@ void Application::setup() {
68
68
 
69
69
  do {
70
70
  uint8_t new_app_state = STATUS_LED_WARNING;
71
- this->scheduler.call();
72
- this->feed_wdt();
71
+ uint32_t now = millis();
72
+
73
+ // Process pending loop enables to handle GPIO interrupts during setup
74
+ this->before_loop_tasks_(now);
75
+
73
76
  for (uint32_t j = 0; j <= i; j++) {
74
77
  // Update loop_component_start_time_ right before calling each component
75
78
  this->loop_component_start_time_ = millis();
@@ -78,6 +81,8 @@ void Application::setup() {
78
81
  this->app_state_ |= new_app_state;
79
82
  this->feed_wdt();
80
83
  }
84
+
85
+ this->after_loop_tasks_();
81
86
  this->app_state_ = new_app_state;
82
87
  yield();
83
88
  } while (!component->can_proceed());
@@ -94,30 +99,10 @@ void Application::setup() {
94
99
  void Application::loop() {
95
100
  uint8_t new_app_state = 0;
96
101
 
97
- this->scheduler.call();
98
-
99
102
  // Get the initial loop time at the start
100
103
  uint32_t last_op_end_time = millis();
101
104
 
102
- // Feed WDT with time
103
- this->feed_wdt(last_op_end_time);
104
-
105
- // Process any pending enable_loop requests from ISRs
106
- // This must be done before marking in_loop_ = true to avoid race conditions
107
- if (this->has_pending_enable_loop_requests_) {
108
- // Clear flag BEFORE processing to avoid race condition
109
- // If ISR sets it during processing, we'll catch it next loop iteration
110
- // This is safe because:
111
- // 1. Each component has its own pending_enable_loop_ flag that we check
112
- // 2. If we can't process a component (wrong state), enable_pending_loops_()
113
- // will set this flag back to true
114
- // 3. Any new ISR requests during processing will set the flag again
115
- this->has_pending_enable_loop_requests_ = false;
116
- this->enable_pending_loops_();
117
- }
118
-
119
- // Mark that we're in the loop for safe reentrant modifications
120
- this->in_loop_ = true;
105
+ this->before_loop_tasks_(last_op_end_time);
121
106
 
122
107
  for (this->current_loop_index_ = 0; this->current_loop_index_ < this->looping_components_active_end_;
123
108
  this->current_loop_index_++) {
@@ -138,7 +123,7 @@ void Application::loop() {
138
123
  this->feed_wdt(last_op_end_time);
139
124
  }
140
125
 
141
- this->in_loop_ = false;
126
+ this->after_loop_tasks_();
142
127
  this->app_state_ = new_app_state;
143
128
 
144
129
  // Use the last component's end time instead of calling millis() again
@@ -400,6 +385,36 @@ void Application::enable_pending_loops_() {
400
385
  }
401
386
  }
402
387
 
388
+ void Application::before_loop_tasks_(uint32_t loop_start_time) {
389
+ // Process scheduled tasks
390
+ this->scheduler.call();
391
+
392
+ // Feed the watchdog timer
393
+ this->feed_wdt(loop_start_time);
394
+
395
+ // Process any pending enable_loop requests from ISRs
396
+ // This must be done before marking in_loop_ = true to avoid race conditions
397
+ if (this->has_pending_enable_loop_requests_) {
398
+ // Clear flag BEFORE processing to avoid race condition
399
+ // If ISR sets it during processing, we'll catch it next loop iteration
400
+ // This is safe because:
401
+ // 1. Each component has its own pending_enable_loop_ flag that we check
402
+ // 2. If we can't process a component (wrong state), enable_pending_loops_()
403
+ // will set this flag back to true
404
+ // 3. Any new ISR requests during processing will set the flag again
405
+ this->has_pending_enable_loop_requests_ = false;
406
+ this->enable_pending_loops_();
407
+ }
408
+
409
+ // Mark that we're in the loop for safe reentrant modifications
410
+ this->in_loop_ = true;
411
+ }
412
+
413
+ void Application::after_loop_tasks_() {
414
+ // Clear the in_loop_ flag to indicate we're done processing components
415
+ this->in_loop_ = false;
416
+ }
417
+
403
418
  #ifdef USE_SOCKET_SELECT_SUPPORT
404
419
  bool Application::register_socket_fd(int fd) {
405
420
  // WARNING: This function is NOT thread-safe and must only be called from the main loop
@@ -504,6 +504,8 @@ class Application {
504
504
  void enable_component_loop_(Component *component);
505
505
  void enable_pending_loops_();
506
506
  void activate_looping_component_(uint16_t index);
507
+ void before_loop_tasks_(uint32_t loop_start_time);
508
+ void after_loop_tasks_();
507
509
 
508
510
  void feed_wdt_arch_();
509
511
 
esphome/core/helpers.h CHANGED
@@ -67,7 +67,10 @@ To bit_cast(const From &src) {
67
67
  return dst;
68
68
  }
69
69
  #endif
70
- using std::lerp;
70
+
71
+ // clang-format off
72
+ inline float lerp(float completion, float start, float end) = delete; // Please use std::lerp. Notice that it has different order on arguments!
73
+ // clang-format on
71
74
 
72
75
  // std::byteswap from C++23
73
76
  template<typename T> constexpr T byteswap(T n) {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: esphome
3
- Version: 2025.7.2
3
+ Version: 2025.7.4
4
4
  Summary: ESPHome is a system to configure your microcontrollers by simple yet powerful configuration files and control them remotely through Home Automation systems.
5
5
  Author-email: The ESPHome Authors <esphome@openhomefoundation.org>
6
6
  License: MIT
@@ -4,8 +4,8 @@ esphome/automation.py,sha256=9xmW3AmWDd2oKB7zF-UITYIiSci8ys8qiylK-rcU7Rg,15689
4
4
  esphome/codegen.py,sha256=H_WB4rj0uEowvlhEb31EjJQwutLQ5CQkJIsNgDK-wx8,1917
5
5
  esphome/config.py,sha256=b-Gh-DEx_pax0ZKqHTKb5gmMIvaQA71bJvE-15AI0JI,42211
6
6
  esphome/config_helpers.py,sha256=BpyuWRxj5edJGIW7VP4S59i4I8g8baSlWpNyu6nB_uM,5413
7
- esphome/config_validation.py,sha256=_SMAcS_AhMh0kaLki86hjPZp5b95culJZtQePqoL_fU,63712
8
- esphome/const.py,sha256=KwAeVg3hE8JEHR5Yp_X1tHFZCxXbepoWToDGgrgEhJE,43367
7
+ esphome/config_validation.py,sha256=QUNpomRbIhqTaSrZZXNFcTQdPibGPbYDNSo5sTIzqCI,64924
8
+ esphome/const.py,sha256=TiaRIL8HNMH-_mcJANWLuu8l8f5Mnv0Z59u7fw7TzZg,43367
9
9
  esphome/coroutine.py,sha256=HNBqqhaTbpvsOI19bTXltxJCMVtoeqZPe4qTf4CKkAc,9309
10
10
  esphome/cpp_generator.py,sha256=khmyuRIOc-ST9zIZjX7uOWLy9sSJhk4C2KexoBv51uk,31946
11
11
  esphome/cpp_helpers.py,sha256=P9FVGpid75_UcKxIf-sj7GbhWGQNRcBm_2XVF3r7NtU,3998
@@ -402,7 +402,7 @@ esphome/components/bme680/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
402
402
  esphome/components/bme680/bme680.cpp,sha256=R_OJihYGYxFUqjK_C8Kcdtf0jNKxEwEWdusQN5ByCkU,16994
403
403
  esphome/components/bme680/bme680.h,sha256=zdTFcpzNb3nSPy7q9V3iSJ5uBsmoZgJkzxU3Fcwf_BY,4678
404
404
  esphome/components/bme680/sensor.py,sha256=fjZOcC0AymBF4f3vdgd-WfTfkqL0QrCOnaXdnGvGolU,5801
405
- esphome/components/bme680_bsec/__init__.py,sha256=YZjUXomMLi9D-G3JxaEhrfQrSD-yTGU0mBk8HxGBdq0,2849
405
+ esphome/components/bme680_bsec/__init__.py,sha256=FmtARbE-B7Dkb1FnOWQavaAdOggraJqrDIocHitQrmI,3062
406
406
  esphome/components/bme680_bsec/bme680_bsec.cpp,sha256=ja1s94_Gh6HhhXhJiuT5f_rChHy87tgyHurmmMF1Tx8,22705
407
407
  esphome/components/bme680_bsec/bme680_bsec.h,sha256=aUGZXwSiu0wOlt13_mxhy0yKxToAInX2rnhZ_UQc7TM,5907
408
408
  esphome/components/bme680_bsec/sensor.py,sha256=Y4efGwK7zDHbS8ET7o-bF__A86OJsZt2Ypl4sRtqKLE,4114
@@ -915,7 +915,7 @@ esphome/components/esp32_touch/__init__.py,sha256=zcxxAkhMPOd--SZyngwDEW-Q9AYcWm
915
915
  esphome/components/esp32_touch/binary_sensor.py,sha256=JV0p5Ovn4E1EVj6L4JEmRJ--L-D7HWGK2PaQLE9hs3E,1165
916
916
  esphome/components/esp32_touch/esp32_touch.h,sha256=Br1-5Lh4U6xmvrM59iuPe5hR6cmO-LO5vSoyc49k9QY,9644
917
917
  esphome/components/esp32_touch/esp32_touch_common.cpp,sha256=engSTGARGHfLIwjceV2xrg0y0yX_gpb8MjoK2zd6KZQ,5703
918
- esphome/components/esp32_touch/esp32_touch_v1.cpp,sha256=VQdfo4rsDCVZ7kvG7IXFoatg97AM_ZECMNGB55I7xwQ,8266
918
+ esphome/components/esp32_touch/esp32_touch_v1.cpp,sha256=v1tGKlHazj2gJFEfQDPIJKR8Q90x_GmlA2DQo3nnteU,8492
919
919
  esphome/components/esp32_touch/esp32_touch_v2.cpp,sha256=SZPbpG3fOrJNMkzcJIYFubi4YcTspnVp58iO-_7rJdM,13475
920
920
  esphome/components/esp8266/__init__.py,sha256=-2xGbrwE5P4eTmQZRcoKwjBhtCVgnhdh3kEsrqYcGSI,9683
921
921
  esphome/components/esp8266/boards.py,sha256=p2btoDa36gfr9QsCOBZuME7Pi20Xf2LcQ_F-oeMFFA8,8647
@@ -987,9 +987,9 @@ esphome/components/fastled_base/__init__.py,sha256=5Re6yi_0HOCts-U-iiyNKvsQUEBzS
987
987
  esphome/components/fastled_base/fastled_light.cpp,sha256=MFLr1AV4mTxIQMwxTBg8RtbPuwex7y3nlHbulHdtdps,1369
988
988
  esphome/components/fastled_base/fastled_light.h,sha256=Xmv5WA6XuZNGClpiITMo6f73i8P0Z5YoYtlUVhQj2FI,8593
989
989
  esphome/components/fastled_clockless/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
990
- esphome/components/fastled_clockless/light.py,sha256=D-tQakBL9hRmdWNhInfD3YBDFCgRiLQcJYllT8j_Rfk,1677
990
+ esphome/components/fastled_clockless/light.py,sha256=M9x-UAPjmHuaFJR0U1jNgD69Q1FiWTtoRsosju7J8gU,1950
991
991
  esphome/components/fastled_spi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
992
- esphome/components/fastled_spi/light.py,sha256=2neqVimjN-_fUgunaiN10XxeKGKJ1GjSsRuME_kBpac,1867
992
+ esphome/components/fastled_spi/light.py,sha256=KjvKzYWQ82iPdgEXicjZO8VYaQQoHSJ_UQ6gi22fbPE,2107
993
993
  esphome/components/feedback/__init__.py,sha256=GjTE91yQyeMDCmGmxAgQwsB9ohUUw83JSCmIi2_sujg,25
994
994
  esphome/components/feedback/cover.py,sha256=NuS-WNv7RFctg3jrBjPm6moIlNGgtmXvspdD1_CPgsM,6324
995
995
  esphome/components/feedback/feedback_cover.cpp,sha256=bvZ-dgN3zh8Ivc2BvSPO4QKTQuVIFm_AiTUgTUIHnE8,16241
@@ -1044,7 +1044,7 @@ esphome/components/gp8403/output/__init__.py,sha256=3VX9AD0N0SRXdOfKJcMMgjCwA1-R
1044
1044
  esphome/components/gp8403/output/gp8403_output.cpp,sha256=FQPUa_ZMgLz7LNHy6N8sNUpnI2hwOIZTRrwWtjXrbGs,714
1045
1045
  esphome/components/gp8403/output/gp8403_output.h,sha256=wJd_-CtUSxw5ujhR21E1zCiB9hvpl3Ktt665D3iQLf4,598
1046
1046
  esphome/components/gpio/__init__.py,sha256=afIFpPG_fsom-8vYV1yRyvhSCFyASlAdraUCuztWQZ4,103
1047
- esphome/components/gpio/binary_sensor/__init__.py,sha256=Q0P2-6zpxSYg3MFW1vx5SeZju27C-kOc4G5nEtE8ugg,2893
1047
+ esphome/components/gpio/binary_sensor/__init__.py,sha256=1rCKRDCrbINcJN6m0gRYgyGTouuVpK-s_IV6cGgivUE,3594
1048
1048
  esphome/components/gpio/binary_sensor/gpio_binary_sensor.cpp,sha256=wVa5pNQMnUOO9RUovnf6G8MYQtKQoRSojA3MItHLnbg,3131
1049
1049
  esphome/components/gpio/binary_sensor/gpio_binary_sensor.h,sha256=ukwmyxJhiYXMczcT16zwdliTX5Brf8fdgDzid6l13wE,2000
1050
1050
  esphome/components/gpio/one_wire/__init__.py,sha256=oH6-6zy18pG_7iqzLegjh4AbjnbZHqBRZKHdHBI-828,714
@@ -1090,7 +1090,7 @@ esphome/components/gt911/binary_sensor/__init__.py,sha256=iLJRYUsSFiY0BeJMH0RQ1A
1090
1090
  esphome/components/gt911/binary_sensor/gt911_button.cpp,sha256=KACuxHcVbk3TZdSPy-8kO4j0LZZZmdAy4VWMicnaoLY,586
1091
1091
  esphome/components/gt911/binary_sensor/gt911_button.h,sha256=3QCm3g8Ca9VtsKKjWgc_Bre4Dc3RhXgaLt1Mq3iEKd8,714
1092
1092
  esphome/components/gt911/touchscreen/__init__.py,sha256=Hx69_ljCE1F-dP5BzEoQ7vIcOTy9_AsnDBb9FH3pCpU,1155
1093
- esphome/components/gt911/touchscreen/gt911_touchscreen.cpp,sha256=8Z2u1jfQM5Yu80gveiptlrpKuv_V5AaH1X4YW_ZMIio,4666
1093
+ esphome/components/gt911/touchscreen/gt911_touchscreen.cpp,sha256=AIfOlB5kudlaw-NIEsQOb_MDBzTOadf0q3kSNajtByI,4770
1094
1094
  esphome/components/gt911/touchscreen/gt911_touchscreen.h,sha256=q-ZvP6OEGk8TU9eNfWI5-Bj5lVz-DXkxEGEiSzYr2L0,1026
1095
1095
  esphome/components/haier/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1096
1096
  esphome/components/haier/automation.h,sha256=xHnMbqHWFwtibKxgqLraPeNlKLWv2_AT7VX0aMVYtr8,3688
@@ -1455,7 +1455,7 @@ esphome/components/ld2420/text_sensor/text_sensor.cpp,sha256=6rvT1dp3u9Fpmpm-3kH
1455
1455
  esphome/components/ld2420/text_sensor/text_sensor.h,sha256=aK91ri0NvHth3ya0zN1OeX81v1nqveoiJcOfqCpaAJI,672
1456
1456
  esphome/components/ld2450/__init__.py,sha256=n6KvEkMsoaR3DUix1a1MGq-ZyjAAIAKDZ0lYN7iKSpQ,1267
1457
1457
  esphome/components/ld2450/binary_sensor.py,sha256=SyIw9c-4JqNm8JKuzSKA9rVWaVCGsYAslV0VbhqJnoM,1755
1458
- esphome/components/ld2450/ld2450.cpp,sha256=NcLXN5TwNgA_Nxb-CORIR8lhgDZsSfJivVPGV9pjppc,35495
1458
+ esphome/components/ld2450/ld2450.cpp,sha256=YpWKOjueeC0dXrh4gJemnbnXBpIUv-UjLMx2uvGp70w,35639
1459
1459
  esphome/components/ld2450/ld2450.h,sha256=azHWHt9CdUL2R80km7_ahTjEc7OUgsIzDWq9apVrtCg,8292
1460
1460
  esphome/components/ld2450/sensor.py,sha256=91vupem8yF0nfMaLLkjaRsaarWxxNxKXLvT6WiaimDo,6568
1461
1461
  esphome/components/ld2450/text_sensor.py,sha256=msgqwIFvkSrewI7MP_KPKMWdMOEpPFOj0hhsfudUfho,2009
@@ -1550,7 +1550,7 @@ esphome/components/lock/__init__.py,sha256=z2ykcnNNmzRbri8sqwqQmbX0WtEkBV_Tazr1R
1550
1550
  esphome/components/lock/automation.h,sha256=7MU5AuJizydt7mKTr_uFsNleFI2jeBf7B_dNp3e8Fks,1771
1551
1551
  esphome/components/lock/lock.cpp,sha256=IyEt5xShAxMpmcn_GAPFv2lRCS-kr4MjjfExxfXuK-Q,3212
1552
1552
  esphome/components/lock/lock.h,sha256=p_t5cG-75MNGVM8zEyfKJlziSjeUiIbpYan3tlZG6W8,6042
1553
- esphome/components/logger/__init__.py,sha256=6nw19OHeHlqIoSGqqxgeg5FzWkbyFILQh_Il1PqUQTs,16203
1553
+ esphome/components/logger/__init__.py,sha256=3T2mSo2rmK87MINepmYAW6nF6kloLvLueb_dIPyXCJM,16265
1554
1554
  esphome/components/logger/logger.cpp,sha256=aIra5ZZ5OEAPuqDgxZd3zkyAf46Jchht6mx-mcv380U,11704
1555
1555
  esphome/components/logger/logger.h,sha256=Tkdz9qoSTiyx6Ebp9jBUnGMXyxG1iH6LTJoJfguTOaA,14460
1556
1556
  esphome/components/logger/logger_esp32.cpp,sha256=NJddudDpsMP1fdV0mel8zXd7VNgMxqRIfarLp3uJPe0,6220
@@ -2028,7 +2028,7 @@ esphome/components/nau7802/sensor.py,sha256=1_p7nQLcClL3l5FX3qZ627wf9cP8h8Yt4yl7
2028
2028
  esphome/components/neopixelbus/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2029
2029
  esphome/components/neopixelbus/_methods.py,sha256=zWtt2M2kG4V3iYlvEWZn-BGuTlmCnTvT2VYjiRn3NiY,14544
2030
2030
  esphome/components/neopixelbus/const.py,sha256=8dPnqN1skzSEnjjsr3H2Q3BJOEn-TyFB1PApCSSsYUw,824
2031
- esphome/components/neopixelbus/light.py,sha256=q7uRYgtA50hHESiR8PcFC-wa1k5SAoNo0xHzWplBw3I,7346
2031
+ esphome/components/neopixelbus/light.py,sha256=mGs_W1AuFwzw9_nft_vZdpeX3Z4fe0QNrm49lj15YRc,7572
2032
2032
  esphome/components/neopixelbus/neopixelbus_light.h,sha256=flA7U9l1Bfj7Vdii4wwi2ukVG_RO0mvL6kNaa_k3xBI,4680
2033
2033
  esphome/components/network/__init__.py,sha256=4SVVU4J6pPdGUY_Tz1zEZm4E1LzITStl1RMMMWR71A4,2218
2034
2034
  esphome/components/network/ip_address.h,sha256=tTltJn7qkuxr1_BhSTUYOBItZG-d3eBus04C5GEOS08,4552
@@ -2464,10 +2464,10 @@ esphome/components/remote_base/toshiba_ac_protocol.cpp,sha256=CQNONh4-M91jPDecR_
2464
2464
  esphome/components/remote_base/toshiba_ac_protocol.h,sha256=xkpVmBNtQ_uEbMHzwaJOle2KXBxa34Q-ABZfuCsMouA,1092
2465
2465
  esphome/components/remote_base/toto_protocol.cpp,sha256=GxWkvuGXo3ThBQ3Q7ogNRrMYWHxjxcmeWRZj0JxZ1eM,2601
2466
2466
  esphome/components/remote_base/toto_protocol.h,sha256=ZN4X6heapkxFeA0k5acAgRpGjzaB8SiKs2ABEvaDfns,1275
2467
- esphome/components/remote_receiver/__init__.py,sha256=m5bDLafsKQ0b4BY9i6ZMmUOKgVx2piaB1Tqtf-B4-7I,6120
2467
+ esphome/components/remote_receiver/__init__.py,sha256=0d8i1tuM-VQkJ8S6XMDHBuPqcgmdrUzxOOo37bk7SVA,6696
2468
2468
  esphome/components/remote_receiver/binary_sensor.py,sha256=AybkaXQkJpcxpJhDkbgupO9zIiVhBFYpxvjUHBQbs_w,291
2469
2469
  esphome/components/remote_receiver/remote_receiver.h,sha256=Rl2MH_ZclRL889h5Tiig15Yf_EX6KbsKcezuLSiKQlQ,2885
2470
- esphome/components/remote_receiver/remote_receiver_esp32.cpp,sha256=rcQrpkNgA-oc8Y9Ak8oGgzuu7GLcaNe2KRQ-kmk8m_g,9087
2470
+ esphome/components/remote_receiver/remote_receiver_esp32.cpp,sha256=tZlNUOJ24yoywp-ZDF4WptKp-RK6B6-b-0N2CLPvUS0,9024
2471
2471
  esphome/components/remote_receiver/remote_receiver_esp8266.cpp,sha256=CDLSICGZ360ewhn1rgVDaKzwR-TOFqqRGjYusElNT0g,4535
2472
2472
  esphome/components/remote_receiver/remote_receiver_libretiny.cpp,sha256=742G1k3RNETru9kbFHn5Ax3NE_OVt6EqeWWVDUNKj9Y,4490
2473
2473
  esphome/components/remote_transmitter/__init__.py,sha256=wE9ogHpnVTbBPLn9pZH5qhyYi_mYzRHd5pKJ96uvuUc,3867
@@ -2574,7 +2574,7 @@ esphome/components/script/script.h,sha256=0z4aoCBM2x2RLtpk0LNs3biHIS--rDzhfQjN-S
2574
2574
  esphome/components/sdl/__init__.py,sha256=4Nn7UhpMJ9oSbuLdyVEW7G9PlIey2v33SWRNVizt9Oc,30
2575
2575
  esphome/components/sdl/binary_sensor.py,sha256=0fXpK11HIO9fpRqnyhN-nshlmKCgqXGK1OBlInQjOL4,6528
2576
2576
  esphome/components/sdl/display.py,sha256=mVm2J93NP-a0_Z70b6Rz_305-jIO8Rt2Kcxnc3rp5tw,3385
2577
- esphome/components/sdl/sdl_esphome.cpp,sha256=o4fCN_x9l6NDo4uahGEt1iDtZ3yU5W_xV1YrohKYVy8,4032
2577
+ esphome/components/sdl/sdl_esphome.cpp,sha256=sc5r_zZuy_bg2iX50tZYRL_KyRZaeQfdWSGZVzolqK4,4087
2578
2578
  esphome/components/sdl/sdl_esphome.h,sha256=S_HSX0cQYI3oUWHVPPwv9dFVhbhTUYitnO5o5GNeX28,2373
2579
2579
  esphome/components/sdl/touchscreen/__init__.py,sha256=VhcFG1Vc_i4eIYHoV4_Nb3VlanUCr0r9RPAM5BMKr9Y,633
2580
2580
  esphome/components/sdl/touchscreen/sdl_touchscreen.h,sha256=dgw2wGuEvjX6zbPjqMOPLkOl-aVYVWxY7n7Gc_kAKm0,595
@@ -3229,7 +3229,7 @@ esphome/components/tuya/climate/tuya_climate.h,sha256=16nLDVoVwyd6fOtqq9SqRSQu17
3229
3229
  esphome/components/tuya/cover/__init__.py,sha256=zNsaJUYfibX_38oe9mQ-19UhXIWXNBOVbNWwr4bq3yo,2947
3230
3230
  esphome/components/tuya/cover/tuya_cover.cpp,sha256=0VKNLg0zDy7EPk7sRQC6aL1QjV7832tmubbPenbQW3Y,4369
3231
3231
  esphome/components/tuya/cover/tuya_cover.h,sha256=9S9_X7xuiusjDRfsXw_aV6_7i8P9WpMOvOg-dlyOrz8,1787
3232
- esphome/components/tuya/fan/__init__.py,sha256=nAcHS5WZtP3BeyxPBG5k8uHhJKDQnu_t88Vfwl_otas,1796
3232
+ esphome/components/tuya/fan/__init__.py,sha256=nHTmFEtNcASuOrBHeixTUpzvQYtYHsb0F_bRLSYutgk,1734
3233
3233
  esphome/components/tuya/fan/tuya_fan.cpp,sha256=KZTNjsQtSAIr4JXf2VKocxBANqORi7jaC5BsUt-gNWs,4330
3234
3234
  esphome/components/tuya/fan/tuya_fan.h,sha256=nYKtPdShhbQl05W83rAFS4CxKbsY2jhp5N11k1c2alk,1114
3235
3235
  esphome/components/tuya/light/__init__.py,sha256=mZj2c72EhHo8CHwkNFvn58jmj7mycIzHPRY5F5Th3VI,4858
@@ -3396,7 +3396,7 @@ esphome/components/web_server/web_server.cpp,sha256=bwc2jb7b4-7mtrztGpW9SMWseuh3
3396
3396
  esphome/components/web_server/web_server.h,sha256=AGCOdOaZe7Xe5nCL4rHhqWNg-uo5cc6nu84vQr4GdrQ,21558
3397
3397
  esphome/components/web_server/web_server_v1.cpp,sha256=ZnFV1J2YAzAT2mtR-eeVgG1TSVpy953EF1yVKYdTcTg,7409
3398
3398
  esphome/components/web_server/ota/__init__.py,sha256=w4Ufe8mN-5r0913AODCjl9LlCKMpWRTFMSCEmLgRzxE,1018
3399
- esphome/components/web_server/ota/ota_web_server.cpp,sha256=4c1BeJDlwT1YJCbnt4fYRzAVtJxDP3hvJkbYfetZX4E,8140
3399
+ esphome/components/web_server/ota/ota_web_server.cpp,sha256=q-GKNDmwjonXaBOndktCulkJfrCSpmCGqcreuJxtRJI,8158
3400
3400
  esphome/components/web_server/ota/ota_web_server.h,sha256=ZZQHTxb21gqukibGei-om00MxpBD4Qyy031PXBMmjT8,604
3401
3401
  esphome/components/web_server_base/__init__.py,sha256=uM9rrhE1Pu_yvwfrLLHheh1ip_9mL2JSZC5NrrBdrj0,1281
3402
3402
  esphome/components/web_server_base/web_server_base.cpp,sha256=plEcGgYKFku3cSlv7rNAPC6GMWkjIflie3yVERoHDNA,876
@@ -3593,8 +3593,8 @@ esphome/components/zyaura/sensor.py,sha256=cSmO4ozYdi4ZD7NK4lmYjswWVmJoDvEruU1Hh
3593
3593
  esphome/components/zyaura/zyaura.cpp,sha256=F7WM8XAZ5MYuCD3eERm2_IA-O7sP1i-A-yF5TV4PqX8,3679
3594
3594
  esphome/components/zyaura/zyaura.h,sha256=7O3EGFIopUEfBp3A5sBC0l4zx2mesRxDWa_MBdGtPKQ,2307
3595
3595
  esphome/core/__init__.py,sha256=uWhgtqaPGiMeMp6KMvRs60gvFG_4jabueLzKTMuVAHc,28445
3596
- esphome/core/application.cpp,sha256=Tsl2sCS3awRjrJL5A9g2syW7s8tRwl1RtoCUhZUrQsI,18762
3597
- esphome/core/application.h,sha256=upjEdRfEZWrH27s3PFYqmORG7Kd4SlbtexipPdYKwUk,21848
3596
+ esphome/core/application.cpp,sha256=wrjr4SVAWMarEo-cKJ_eUfarCrSrWzqoK2-oA74wPYU,19182
3597
+ esphome/core/application.h,sha256=psgw-_TDgZQLK00Kg5A9Y-B17Mkdn4WeMzmOkyX2e98,21929
3598
3598
  esphome/core/area.h,sha256=mtFxmuv8fC2hArcey3cZ0F6hMXSO2QpFZ9Xv49z4rvk,393
3599
3599
  esphome/core/automation.h,sha256=UgoI-ebaL5YJ_cyRB-3ijHQxzt4cTbTaWw4eRGoOwBI,8627
3600
3600
  esphome/core/base_automation.h,sha256=C-6op63m-ghuaUy-q515XAx7iKjoxBFbQeRLmRtQGSM,11230
@@ -3618,7 +3618,7 @@ esphome/core/event_pool.h,sha256=X8_-72rODgpG9P_dSjezkJjFaaFvXy0cV42o6X-Vv1Q,240
3618
3618
  esphome/core/gpio.h,sha256=kLkCnPxu4_1CsLR4BI_Baj1lDGoRIh8uubbwsIkJPIA,2575
3619
3619
  esphome/core/hal.h,sha256=Le0-vtdDylYCaE9i4yvrv5-Y5PB5xoL3PM2FfMJsIeA,970
3620
3620
  esphome/core/helpers.cpp,sha256=eyOYJWmMEcdX8dJ3RoIcl6MeOmc0C3cTPafZDTzQ4OM,20961
3621
- esphome/core/helpers.h,sha256=213N4rXgWCcuBB-aZDV9TW5UIYRzzGhU44YXWJz2Ys4,33961
3621
+ esphome/core/helpers.h,sha256=TX-xJuk8__wi4mGJPjNr8B9JngJMRju8MmAVhfg6xn0,34129
3622
3622
  esphome/core/lock_free_queue.h,sha256=j3wSEyxqkjBDnCwEQd4ARHDubjSrLPxMAzZvqdNN2co,4953
3623
3623
  esphome/core/log.cpp,sha256=cc6JIMRlIEk7lCQa6JFrL6bTBZ89xWNLwf26AFNzKC0,1625
3624
3624
  esphome/core/log.h,sha256=Fb0_ORK0q-WV0i49gzb8i9_C38RUe8VILIdbu1Bel5M,6627
@@ -3655,9 +3655,9 @@ esphome/dashboard/util/itertools.py,sha256=8eLrWEWmICLtXNxkKdYPQV0c_N4GEz8m9Npnb
3655
3655
  esphome/dashboard/util/password.py,sha256=cQz3b9B-ijTe7zS6BeCW0hc3pWv6JjC78jmnycYYAh8,321
3656
3656
  esphome/dashboard/util/subprocess.py,sha256=T8EW6dbU4LPd2DG1dRrdh8li71tt6J1isn411poMhkk,1022
3657
3657
  esphome/dashboard/util/text.py,sha256=wwFtORlvHjsYkqb68IT-772LHAhWxT4OtnkIcPICQB0,317
3658
- esphome-2025.7.2.dist-info/licenses/LICENSE,sha256=HzEjkBInJe44L4WvAOPfhPJJDNj6YbnqFyvGWRzArGM,36664
3659
- esphome-2025.7.2.dist-info/METADATA,sha256=DPtrT8cCskiqvQyqD7C-Mb3Ahj9UEjYZciLgbs5ciuo,3705
3660
- esphome-2025.7.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
3661
- esphome-2025.7.2.dist-info/entry_points.txt,sha256=mIxVNuWtbYzeEcaWCl-AQ-97aBOWbnYBAK8nbF6P4M0,50
3662
- esphome-2025.7.2.dist-info/top_level.txt,sha256=0GSXEW3cnITpgG3qnsSMz0qoqJHAFyfw7Y8MVtEf1Yk,8
3663
- esphome-2025.7.2.dist-info/RECORD,,
3658
+ esphome-2025.7.4.dist-info/licenses/LICENSE,sha256=HzEjkBInJe44L4WvAOPfhPJJDNj6YbnqFyvGWRzArGM,36664
3659
+ esphome-2025.7.4.dist-info/METADATA,sha256=Dlcq3C2cnO1s5Q9H8HB11OWErxRCjoN-MSri4PJV9TI,3705
3660
+ esphome-2025.7.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
3661
+ esphome-2025.7.4.dist-info/entry_points.txt,sha256=mIxVNuWtbYzeEcaWCl-AQ-97aBOWbnYBAK8nbF6P4M0,50
3662
+ esphome-2025.7.4.dist-info/top_level.txt,sha256=0GSXEW3cnITpgG3qnsSMz0qoqJHAFyfw7Y8MVtEf1Yk,8
3663
+ esphome-2025.7.4.dist-info/RECORD,,