esphome 2025.7.0b3__py3-none-any.whl → 2025.7.0b5__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.
Files changed (49) hide show
  1. esphome/components/as3935_spi/as3935_spi.h +0 -2
  2. esphome/components/async_tcp/__init__.py +1 -1
  3. esphome/components/fan/fan.cpp +4 -0
  4. esphome/components/http_request/update/http_request_update.cpp +7 -7
  5. esphome/components/json/__init__.py +1 -1
  6. esphome/components/json/json_util.cpp +56 -63
  7. esphome/components/light/light_json_schema.cpp +17 -16
  8. esphome/components/mqtt/mqtt_alarm_control_panel.cpp +2 -1
  9. esphome/components/mqtt/mqtt_binary_sensor.cpp +1 -0
  10. esphome/components/mqtt/mqtt_button.cpp +4 -1
  11. esphome/components/mqtt/mqtt_client.cpp +2 -0
  12. esphome/components/mqtt/mqtt_climate.cpp +6 -4
  13. esphome/components/mqtt/mqtt_component.cpp +3 -1
  14. esphome/components/mqtt/mqtt_cover.cpp +1 -0
  15. esphome/components/mqtt/mqtt_date.cpp +4 -3
  16. esphome/components/mqtt/mqtt_datetime.cpp +7 -6
  17. esphome/components/mqtt/mqtt_event.cpp +6 -3
  18. esphome/components/mqtt/mqtt_fan.cpp +1 -0
  19. esphome/components/mqtt/mqtt_light.cpp +8 -4
  20. esphome/components/mqtt/mqtt_lock.cpp +3 -1
  21. esphome/components/mqtt/mqtt_number.cpp +1 -0
  22. esphome/components/mqtt/mqtt_select.cpp +2 -1
  23. esphome/components/mqtt/mqtt_sensor.cpp +3 -1
  24. esphome/components/mqtt/mqtt_switch.cpp +3 -1
  25. esphome/components/mqtt/mqtt_text.cpp +1 -0
  26. esphome/components/mqtt/mqtt_text_sensor.cpp +3 -1
  27. esphome/components/mqtt/mqtt_time.cpp +4 -3
  28. esphome/components/mqtt/mqtt_update.cpp +1 -0
  29. esphome/components/mqtt/mqtt_valve.cpp +3 -1
  30. esphome/components/ms8607/ms8607.cpp +1 -1
  31. esphome/components/online_image/__init__.py +4 -1
  32. esphome/components/online_image/online_image.cpp +11 -5
  33. esphome/components/online_image/online_image.h +6 -1
  34. esphome/components/opentherm/output/output.cpp +1 -1
  35. esphome/components/servo/servo.cpp +2 -2
  36. esphome/components/web_server/web_server.cpp +13 -9
  37. esphome/components/web_server_base/__init__.py +1 -1
  38. esphome/components/web_server_idf/web_server_idf.cpp +2 -0
  39. esphome/const.py +1 -1
  40. esphome/core/application.cpp +6 -0
  41. esphome/core/component.cpp +1 -0
  42. esphome/platformio_api.py +2 -0
  43. esphome/writer.py +23 -0
  44. {esphome-2025.7.0b3.dist-info → esphome-2025.7.0b5.dist-info}/METADATA +1 -1
  45. {esphome-2025.7.0b3.dist-info → esphome-2025.7.0b5.dist-info}/RECORD +49 -49
  46. {esphome-2025.7.0b3.dist-info → esphome-2025.7.0b5.dist-info}/WHEEL +0 -0
  47. {esphome-2025.7.0b3.dist-info → esphome-2025.7.0b5.dist-info}/entry_points.txt +0 -0
  48. {esphome-2025.7.0b3.dist-info → esphome-2025.7.0b5.dist-info}/licenses/LICENSE +0 -0
  49. {esphome-2025.7.0b3.dist-info → esphome-2025.7.0b5.dist-info}/top_level.txt +0 -0
@@ -44,8 +44,10 @@ void MQTTSensorComponent::set_expire_after(uint32_t expire_after) { this->expire
44
44
  void MQTTSensorComponent::disable_expire_after() { this->expire_after_ = 0; }
45
45
 
46
46
  void MQTTSensorComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
47
- if (!this->sensor_->get_device_class().empty())
47
+ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
48
+ if (!this->sensor_->get_device_class().empty()) {
48
49
  root[MQTT_DEVICE_CLASS] = this->sensor_->get_device_class();
50
+ }
49
51
 
50
52
  if (!this->sensor_->get_unit_of_measurement().empty())
51
53
  root[MQTT_UNIT_OF_MEASUREMENT] = this->sensor_->get_unit_of_measurement();
@@ -45,8 +45,10 @@ void MQTTSwitchComponent::dump_config() {
45
45
  std::string MQTTSwitchComponent::component_type() const { return "switch"; }
46
46
  const EntityBase *MQTTSwitchComponent::get_entity() const { return this->switch_; }
47
47
  void MQTTSwitchComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
48
- if (this->switch_->assumed_state())
48
+ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
49
+ if (this->switch_->assumed_state()) {
49
50
  root[MQTT_OPTIMISTIC] = true;
51
+ }
50
52
  }
51
53
  bool MQTTSwitchComponent::send_initial_state() { return this->publish_state(this->switch_->state); }
52
54
 
@@ -34,6 +34,7 @@ std::string MQTTTextComponent::component_type() const { return "text"; }
34
34
  const EntityBase *MQTTTextComponent::get_entity() const { return this->text_; }
35
35
 
36
36
  void MQTTTextComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
37
+ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
37
38
  switch (this->text_->traits.get_mode()) {
38
39
  case TEXT_MODE_TEXT:
39
40
  root[MQTT_MODE] = "text";
@@ -15,8 +15,10 @@ using namespace esphome::text_sensor;
15
15
 
16
16
  MQTTTextSensor::MQTTTextSensor(TextSensor *sensor) : sensor_(sensor) {}
17
17
  void MQTTTextSensor::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
18
- if (!this->sensor_->get_device_class().empty())
18
+ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
19
+ if (!this->sensor_->get_device_class().empty()) {
19
20
  root[MQTT_DEVICE_CLASS] = this->sensor_->get_device_class();
21
+ }
20
22
  config.command_topic = false;
21
23
  }
22
24
  void MQTTTextSensor::setup() {
@@ -20,13 +20,13 @@ MQTTTimeComponent::MQTTTimeComponent(TimeEntity *time) : time_(time) {}
20
20
  void MQTTTimeComponent::setup() {
21
21
  this->subscribe_json(this->get_command_topic_(), [this](const std::string &topic, JsonObject root) {
22
22
  auto call = this->time_->make_call();
23
- if (root.containsKey("hour")) {
23
+ if (root["hour"].is<uint8_t>()) {
24
24
  call.set_hour(root["hour"]);
25
25
  }
26
- if (root.containsKey("minute")) {
26
+ if (root["minute"].is<uint8_t>()) {
27
27
  call.set_minute(root["minute"]);
28
28
  }
29
- if (root.containsKey("second")) {
29
+ if (root["second"].is<uint8_t>()) {
30
30
  call.set_second(root["second"]);
31
31
  }
32
32
  call.perform();
@@ -55,6 +55,7 @@ bool MQTTTimeComponent::send_initial_state() {
55
55
  }
56
56
  bool MQTTTimeComponent::publish_state(uint8_t hour, uint8_t minute, uint8_t second) {
57
57
  return this->publish_json(this->get_state_topic_(), [hour, minute, second](JsonObject root) {
58
+ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
58
59
  root["hour"] = hour;
59
60
  root["minute"] = minute;
60
61
  root["second"] = second;
@@ -41,6 +41,7 @@ bool MQTTUpdateComponent::publish_state() {
41
41
  }
42
42
 
43
43
  void MQTTUpdateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
44
+ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
44
45
  root["schema"] = "json";
45
46
  root[MQTT_PAYLOAD_INSTALL] = "INSTALL";
46
47
  }
@@ -49,8 +49,10 @@ void MQTTValveComponent::dump_config() {
49
49
  }
50
50
  }
51
51
  void MQTTValveComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) {
52
- if (!this->valve_->get_device_class().empty())
52
+ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
53
+ if (!this->valve_->get_device_class().empty()) {
53
54
  root[MQTT_DEVICE_CLASS] = this->valve_->get_device_class();
55
+ }
54
56
 
55
57
  auto traits = this->valve_->get_traits();
56
58
  if (traits.get_is_assumed_state()) {
@@ -356,7 +356,7 @@ void MS8607Component::read_humidity_(float temperature_float) {
356
356
 
357
357
  // map 16 bit humidity value into range [-6%, 118%]
358
358
  float const humidity_partial = double(humidity) / (1 << 16);
359
- float const humidity_percentage = lerp(humidity_partial, -6.0, 118.0);
359
+ float const humidity_percentage = std::lerp(-6.0, 118.0, humidity_partial);
360
360
  float const compensated_humidity_percentage =
361
361
  humidity_percentage + (20 - temperature_float) * MS8607_H_TEMP_COEFFICIENT;
362
362
  ESP_LOGD(TAG, "Compensated for temperature, humidity=%.2f%%", compensated_humidity_percentage);
@@ -2,7 +2,7 @@ import logging
2
2
 
3
3
  from esphome import automation
4
4
  import esphome.codegen as cg
5
- from esphome.components.const import CONF_REQUEST_HEADERS
5
+ from esphome.components.const import CONF_BYTE_ORDER, CONF_REQUEST_HEADERS
6
6
  from esphome.components.http_request import CONF_HTTP_REQUEST_ID, HttpRequestComponent
7
7
  from esphome.components.image import (
8
8
  CONF_INVERT_ALPHA,
@@ -11,6 +11,7 @@ from esphome.components.image import (
11
11
  Image_,
12
12
  get_image_type_enum,
13
13
  get_transparency_enum,
14
+ validate_settings,
14
15
  )
15
16
  import esphome.config_validation as cv
16
17
  from esphome.const import (
@@ -161,6 +162,7 @@ CONFIG_SCHEMA = cv.Schema(
161
162
  rp2040_arduino=cv.Version(0, 0, 0),
162
163
  host=cv.Version(0, 0, 0),
163
164
  ),
165
+ validate_settings,
164
166
  )
165
167
  )
166
168
 
@@ -213,6 +215,7 @@ async def to_code(config):
213
215
  get_image_type_enum(config[CONF_TYPE]),
214
216
  transparent,
215
217
  config[CONF_BUFFER_SIZE],
218
+ config.get(CONF_BYTE_ORDER) != "LITTLE_ENDIAN",
216
219
  )
217
220
  await cg.register_component(var, config)
218
221
  await cg.register_parented(var, config[CONF_HTTP_REQUEST_ID])
@@ -35,14 +35,15 @@ inline bool is_color_on(const Color &color) {
35
35
  }
36
36
 
37
37
  OnlineImage::OnlineImage(const std::string &url, int width, int height, ImageFormat format, ImageType type,
38
- image::Transparency transparency, uint32_t download_buffer_size)
38
+ image::Transparency transparency, uint32_t download_buffer_size, bool is_big_endian)
39
39
  : Image(nullptr, 0, 0, type, transparency),
40
40
  buffer_(nullptr),
41
41
  download_buffer_(download_buffer_size),
42
42
  download_buffer_initial_size_(download_buffer_size),
43
43
  format_(format),
44
44
  fixed_width_(width),
45
- fixed_height_(height) {
45
+ fixed_height_(height),
46
+ is_big_endian_(is_big_endian) {
46
47
  this->set_url(url);
47
48
  }
48
49
 
@@ -296,7 +297,7 @@ void OnlineImage::draw_pixel_(int x, int y, Color color) {
296
297
  break;
297
298
  }
298
299
  case ImageType::IMAGE_TYPE_GRAYSCALE: {
299
- uint8_t gray = static_cast<uint8_t>(0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b);
300
+ auto gray = static_cast<uint8_t>(0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b);
300
301
  if (this->transparency_ == image::TRANSPARENCY_CHROMA_KEY) {
301
302
  if (gray == 1) {
302
303
  gray = 0;
@@ -314,8 +315,13 @@ void OnlineImage::draw_pixel_(int x, int y, Color color) {
314
315
  case ImageType::IMAGE_TYPE_RGB565: {
315
316
  this->map_chroma_key(color);
316
317
  uint16_t col565 = display::ColorUtil::color_to_565(color);
317
- this->buffer_[pos + 0] = static_cast<uint8_t>((col565 >> 8) & 0xFF);
318
- this->buffer_[pos + 1] = static_cast<uint8_t>(col565 & 0xFF);
318
+ if (this->is_big_endian_) {
319
+ this->buffer_[pos + 0] = static_cast<uint8_t>((col565 >> 8) & 0xFF);
320
+ this->buffer_[pos + 1] = static_cast<uint8_t>(col565 & 0xFF);
321
+ } else {
322
+ this->buffer_[pos + 0] = static_cast<uint8_t>(col565 & 0xFF);
323
+ this->buffer_[pos + 1] = static_cast<uint8_t>((col565 >> 8) & 0xFF);
324
+ }
319
325
  if (this->transparency_ == image::TRANSPARENCY_ALPHA_CHANNEL) {
320
326
  this->buffer_[pos + 2] = color.w;
321
327
  }
@@ -50,7 +50,7 @@ class OnlineImage : public PollingComponent,
50
50
  * @param buffer_size Size of the buffer used to download the image.
51
51
  */
52
52
  OnlineImage(const std::string &url, int width, int height, ImageFormat format, image::ImageType type,
53
- image::Transparency transparency, uint32_t buffer_size);
53
+ image::Transparency transparency, uint32_t buffer_size, bool is_big_endian);
54
54
 
55
55
  void draw(int x, int y, display::Display *display, Color color_on, Color color_off) override;
56
56
 
@@ -164,6 +164,11 @@ class OnlineImage : public PollingComponent,
164
164
  const int fixed_width_;
165
165
  /** height requested on configuration, or 0 if non specified. */
166
166
  const int fixed_height_;
167
+ /**
168
+ * Whether the image is stored in big-endian format.
169
+ * This is used to determine how to store 16 bit colors in the buffer.
170
+ */
171
+ bool is_big_endian_;
167
172
  /**
168
173
  * Actual width of the current image. If fixed_width_ is specified,
169
174
  * this will be equal to it; otherwise it will be set once the decoding
@@ -10,7 +10,7 @@ void opentherm::OpenthermOutput::write_state(float state) {
10
10
  ESP_LOGD(TAG, "Received state: %.2f. Min value: %.2f, max value: %.2f", state, min_value_, max_value_);
11
11
  this->state = state < 0.003 && this->zero_means_zero_
12
12
  ? 0.0
13
- : clamp(lerp(state, min_value_, max_value_), min_value_, max_value_);
13
+ : clamp(std::lerp(min_value_, max_value_, state), min_value_, max_value_);
14
14
  this->has_state_ = true;
15
15
  ESP_LOGD(TAG, "Output %s set to %.2f", this->id_, this->state);
16
16
  }
@@ -88,9 +88,9 @@ void Servo::internal_write(float value) {
88
88
  value = clamp(value, -1.0f, 1.0f);
89
89
  float level;
90
90
  if (value < 0.0) {
91
- level = lerp(-value, this->idle_level_, this->min_level_);
91
+ level = std::lerp(this->idle_level_, this->min_level_, -value);
92
92
  } else {
93
- level = lerp(value, this->idle_level_, this->max_level_);
93
+ level = std::lerp(this->idle_level_, this->max_level_, value);
94
94
  }
95
95
  this->output_->set_level(level);
96
96
  this->current_value_ = value;
@@ -792,7 +792,7 @@ std::string WebServer::light_json(light::LightState *obj, JsonDetail start_confi
792
792
 
793
793
  light::LightJSONSchema::dump_json(*obj, root);
794
794
  if (start_config == DETAIL_ALL) {
795
- JsonArray opt = root.createNestedArray("effects");
795
+ JsonArray opt = root["effects"].to<JsonArray>();
796
796
  opt.add("None");
797
797
  for (auto const &option : obj->get_effects()) {
798
798
  opt.add(option->get_name());
@@ -1238,7 +1238,7 @@ std::string WebServer::select_json(select::Select *obj, const std::string &value
1238
1238
  return json::build_json([this, obj, value, start_config](JsonObject root) {
1239
1239
  set_json_icon_state_value(root, obj, "select-" + obj->get_object_id(), value, value, start_config);
1240
1240
  if (start_config == DETAIL_ALL) {
1241
- JsonArray opt = root.createNestedArray("option");
1241
+ JsonArray opt = root["option"].to<JsonArray>();
1242
1242
  for (auto &option : obj->traits.get_options()) {
1243
1243
  opt.add(option);
1244
1244
  }
@@ -1322,6 +1322,7 @@ std::string WebServer::climate_all_json_generator(WebServer *web_server, void *s
1322
1322
  return web_server->climate_json((climate::Climate *) (source), DETAIL_ALL);
1323
1323
  }
1324
1324
  std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_config) {
1325
+ // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
1325
1326
  return json::build_json([this, obj, start_config](JsonObject root) {
1326
1327
  set_json_id(root, obj, "climate-" + obj->get_object_id(), start_config);
1327
1328
  const auto traits = obj->get_traits();
@@ -1330,32 +1331,32 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf
1330
1331
  char buf[16];
1331
1332
 
1332
1333
  if (start_config == DETAIL_ALL) {
1333
- JsonArray opt = root.createNestedArray("modes");
1334
+ JsonArray opt = root["modes"].to<JsonArray>();
1334
1335
  for (climate::ClimateMode m : traits.get_supported_modes())
1335
1336
  opt.add(PSTR_LOCAL(climate::climate_mode_to_string(m)));
1336
1337
  if (!traits.get_supported_custom_fan_modes().empty()) {
1337
- JsonArray opt = root.createNestedArray("fan_modes");
1338
+ JsonArray opt = root["fan_modes"].to<JsonArray>();
1338
1339
  for (climate::ClimateFanMode m : traits.get_supported_fan_modes())
1339
1340
  opt.add(PSTR_LOCAL(climate::climate_fan_mode_to_string(m)));
1340
1341
  }
1341
1342
 
1342
1343
  if (!traits.get_supported_custom_fan_modes().empty()) {
1343
- JsonArray opt = root.createNestedArray("custom_fan_modes");
1344
+ JsonArray opt = root["custom_fan_modes"].to<JsonArray>();
1344
1345
  for (auto const &custom_fan_mode : traits.get_supported_custom_fan_modes())
1345
1346
  opt.add(custom_fan_mode);
1346
1347
  }
1347
1348
  if (traits.get_supports_swing_modes()) {
1348
- JsonArray opt = root.createNestedArray("swing_modes");
1349
+ JsonArray opt = root["swing_modes"].to<JsonArray>();
1349
1350
  for (auto swing_mode : traits.get_supported_swing_modes())
1350
1351
  opt.add(PSTR_LOCAL(climate::climate_swing_mode_to_string(swing_mode)));
1351
1352
  }
1352
1353
  if (traits.get_supports_presets() && obj->preset.has_value()) {
1353
- JsonArray opt = root.createNestedArray("presets");
1354
+ JsonArray opt = root["presets"].to<JsonArray>();
1354
1355
  for (climate::ClimatePreset m : traits.get_supported_presets())
1355
1356
  opt.add(PSTR_LOCAL(climate::climate_preset_to_string(m)));
1356
1357
  }
1357
1358
  if (!traits.get_supported_custom_presets().empty() && obj->custom_preset.has_value()) {
1358
- JsonArray opt = root.createNestedArray("custom_presets");
1359
+ JsonArray opt = root["custom_presets"].to<JsonArray>();
1359
1360
  for (auto const &custom_preset : traits.get_supported_custom_presets())
1360
1361
  opt.add(custom_preset);
1361
1362
  }
@@ -1407,6 +1408,7 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf
1407
1408
  root["state"] = root["target_temperature"];
1408
1409
  }
1409
1410
  });
1411
+ // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
1410
1412
  }
1411
1413
  #endif
1412
1414
 
@@ -1635,7 +1637,7 @@ std::string WebServer::event_json(event::Event *obj, const std::string &event_ty
1635
1637
  root["event_type"] = event_type;
1636
1638
  }
1637
1639
  if (start_config == DETAIL_ALL) {
1638
- JsonArray event_types = root.createNestedArray("event_types");
1640
+ JsonArray event_types = root["event_types"].to<JsonArray>();
1639
1641
  for (auto const &event_type : obj->get_event_types()) {
1640
1642
  event_types.add(event_type);
1641
1643
  }
@@ -1682,6 +1684,7 @@ std::string WebServer::update_all_json_generator(WebServer *web_server, void *so
1682
1684
  return web_server->update_json((update::UpdateEntity *) (source), DETAIL_STATE);
1683
1685
  }
1684
1686
  std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_config) {
1687
+ // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
1685
1688
  return json::build_json([this, obj, start_config](JsonObject root) {
1686
1689
  set_json_id(root, obj, "update-" + obj->get_object_id(), start_config);
1687
1690
  root["value"] = obj->update_info.latest_version;
@@ -1707,6 +1710,7 @@ std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_c
1707
1710
  this->add_sorting_info_(root, obj);
1708
1711
  }
1709
1712
  });
1713
+ // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
1710
1714
  }
1711
1715
  #endif
1712
1716
 
@@ -40,4 +40,4 @@ async def to_code(config):
40
40
  if CORE.is_esp8266:
41
41
  cg.add_library("ESP8266WiFi", None)
42
42
  # https://github.com/ESP32Async/ESPAsyncWebServer/blob/main/library.json
43
- cg.add_library("ESP32Async/ESPAsyncWebServer", "3.7.8")
43
+ cg.add_library("ESP32Async/ESPAsyncWebServer", "3.7.10")
@@ -389,10 +389,12 @@ AsyncEventSourceResponse::AsyncEventSourceResponse(const AsyncWebServerRequest *
389
389
 
390
390
  #ifdef USE_WEBSERVER_SORTING
391
391
  for (auto &group : ws->sorting_groups_) {
392
+ // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
392
393
  message = json::build_json([group](JsonObject root) {
393
394
  root["name"] = group.second.name;
394
395
  root["sorting_weight"] = group.second.weight;
395
396
  });
397
+ // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
396
398
 
397
399
  // a (very) large number of these should be able to be queued initially without defer
398
400
  // since the only thing in the send buffer at this point is the initial ping/config
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.0b3"
7
+ __version__ = "2025.7.0b5"
8
8
 
9
9
  ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
10
10
  VALID_SUBSTITUTIONS_CHARACTERS = (
@@ -309,6 +309,12 @@ void Application::disable_component_loop_(Component *component) {
309
309
  if (this->in_loop_ && i == this->current_loop_index_) {
310
310
  // Decrement so we'll process the swapped component next
311
311
  this->current_loop_index_--;
312
+ // Update the loop start time to current time so the swapped component
313
+ // gets correct timing instead of inheriting stale timing.
314
+ // This prevents integer underflow in timing calculations by ensuring
315
+ // the swapped component starts with a fresh timing reference, avoiding
316
+ // errors caused by stale or wrapped timing values.
317
+ this->loop_component_start_time_ = millis();
312
318
  }
313
319
  }
314
320
  return;
@@ -264,6 +264,7 @@ void Component::set_retry(uint32_t initial_wait_time, uint8_t max_attempts, std:
264
264
  bool Component::is_failed() const { return (this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED; }
265
265
  bool Component::is_ready() const {
266
266
  return (this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_LOOP ||
267
+ (this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_LOOP_DONE ||
267
268
  (this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_SETUP;
268
269
  }
269
270
  bool Component::can_proceed() { return true; }
esphome/platformio_api.py CHANGED
@@ -78,6 +78,8 @@ def run_platformio_cli(*args, **kwargs) -> str | int:
78
78
  os.environ.setdefault(
79
79
  "PLATFORMIO_LIBDEPS_DIR", os.path.abspath(CORE.relative_piolibdeps_path())
80
80
  )
81
+ # Suppress Python syntax warnings from third-party scripts during compilation
82
+ os.environ.setdefault("PYTHONWARNINGS", "ignore::SyntaxWarning")
81
83
  cmd = ["platformio"] + list(args)
82
84
 
83
85
  if not CORE.verbose:
esphome/writer.py CHANGED
@@ -162,6 +162,9 @@ def get_ini_content():
162
162
  # Sort to avoid changing build unflags order
163
163
  CORE.add_platformio_option("build_unflags", sorted(CORE.build_unflags))
164
164
 
165
+ # Add extra script for C++ flags
166
+ CORE.add_platformio_option("extra_scripts", [f"pre:{CXX_FLAGS_FILE_NAME}"])
167
+
165
168
  content = "[platformio]\n"
166
169
  content += f"description = ESPHome {__version__}\n"
167
170
 
@@ -222,6 +225,9 @@ def write_platformio_project():
222
225
  write_gitignore()
223
226
  write_platformio_ini(content)
224
227
 
228
+ # Write extra script for C++ specific flags
229
+ write_cxx_flags_script()
230
+
225
231
 
226
232
  DEFINES_H_FORMAT = ESPHOME_H_FORMAT = """\
227
233
  #pragma once
@@ -394,3 +400,20 @@ def write_gitignore():
394
400
  if not os.path.isfile(path):
395
401
  with open(file=path, mode="w", encoding="utf-8") as f:
396
402
  f.write(GITIGNORE_CONTENT)
403
+
404
+
405
+ CXX_FLAGS_FILE_NAME = "cxx_flags.py"
406
+ CXX_FLAGS_FILE_CONTENTS = """# Auto-generated ESPHome script for C++ specific compiler flags
407
+ Import("env")
408
+
409
+ # Add C++ specific flags
410
+ """
411
+
412
+
413
+ def write_cxx_flags_script() -> None:
414
+ path = CORE.relative_build_path(CXX_FLAGS_FILE_NAME)
415
+ contents = CXX_FLAGS_FILE_CONTENTS
416
+ if not CORE.is_host:
417
+ contents += 'env.Append(CXXFLAGS=["-Wno-volatile"])'
418
+ contents += "\n"
419
+ write_file_if_changed(path, contents)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: esphome
3
- Version: 2025.7.0b3
3
+ Version: 2025.7.0b5
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