esphome 2025.7.0b1__py3-none-any.whl → 2025.7.0b3__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 (65) hide show
  1. esphome/components/api/__init__.py +19 -5
  2. esphome/components/api/api_connection.cpp +53 -175
  3. esphome/components/api/api_connection.h +28 -25
  4. esphome/components/api/api_frame_helper.cpp +2 -3
  5. esphome/components/api/api_frame_helper.h +7 -9
  6. esphome/components/api/api_pb2.cpp +1862 -5133
  7. esphome/components/api/api_pb2.h +291 -533
  8. esphome/components/api/api_pb2_dump.cpp +99 -0
  9. esphome/components/api/api_pb2_service.cpp +4 -0
  10. esphome/components/api/api_pb2_service.h +6 -0
  11. esphome/components/api/api_server.cpp +2 -9
  12. esphome/components/api/api_server.h +7 -33
  13. esphome/components/api/custom_api_device.h +8 -0
  14. esphome/components/api/list_entities.cpp +2 -0
  15. esphome/components/api/list_entities.h +3 -1
  16. esphome/components/api/proto.h +506 -23
  17. esphome/components/api/user_services.h +2 -0
  18. esphome/components/debug/debug_esp32.cpp +2 -0
  19. esphome/components/esp32/__init__.py +1 -0
  20. esphome/components/esp32_camera/__init__.py +1 -1
  21. esphome/components/esp32_touch/esp32_touch_v1.cpp +12 -10
  22. esphome/components/esp8266/__init__.py +1 -0
  23. esphome/components/esp_ldo/__init__.py +10 -8
  24. esphome/components/esp_ldo/esp_ldo.h +3 -0
  25. esphome/components/gpio/binary_sensor/__init__.py +24 -3
  26. esphome/components/host/__init__.py +1 -0
  27. esphome/components/i2s_audio/speaker/__init__.py +1 -1
  28. esphome/components/ld2410/ld2410.cpp +12 -28
  29. esphome/components/ld2420/binary_sensor/ld2420_binary_sensor.cpp +2 -2
  30. esphome/components/ld2420/button/reconfig_buttons.cpp +1 -1
  31. esphome/components/ld2420/ld2420.cpp +66 -57
  32. esphome/components/ld2420/ld2420.h +9 -11
  33. esphome/components/ld2420/number/gate_config_number.cpp +1 -1
  34. esphome/components/ld2420/select/operating_mode_select.cpp +1 -1
  35. esphome/components/ld2420/sensor/ld2420_sensor.cpp +2 -2
  36. esphome/components/ld2420/text_sensor/text_sensor.cpp +2 -2
  37. esphome/components/libretiny/__init__.py +1 -0
  38. esphome/components/lvgl/widgets/meter.py +20 -13
  39. esphome/components/mqtt/mqtt_backend_esp32.cpp +6 -2
  40. esphome/components/packet_transport/packet_transport.cpp +3 -0
  41. esphome/components/rp2040/__init__.py +1 -0
  42. esphome/components/substitutions/__init__.py +5 -2
  43. esphome/components/sx126x/__init__.py +3 -3
  44. esphome/components/sx127x/__init__.py +2 -2
  45. esphome/components/usb_host/usb_host_client.cpp +10 -10
  46. esphome/components/usb_uart/cp210x.cpp +1 -1
  47. esphome/components/usb_uart/usb_uart.cpp +41 -44
  48. esphome/components/usb_uart/usb_uart.h +4 -3
  49. esphome/const.py +1 -1
  50. esphome/core/component.cpp +8 -8
  51. esphome/core/component_iterator.cpp +4 -2
  52. esphome/core/component_iterator.h +3 -3
  53. esphome/core/defines.h +1 -1
  54. esphome/core/entity_helpers.py +6 -0
  55. esphome/core/helpers.h +1 -1
  56. esphome/core/scheduler.cpp +9 -12
  57. esphome/core/scheduler.h +0 -3
  58. esphome/wizard.py +1 -1
  59. {esphome-2025.7.0b1.dist-info → esphome-2025.7.0b3.dist-info}/METADATA +2 -2
  60. {esphome-2025.7.0b1.dist-info → esphome-2025.7.0b3.dist-info}/RECORD +64 -65
  61. esphome/components/api/api_pb2_size.h +0 -359
  62. {esphome-2025.7.0b1.dist-info → esphome-2025.7.0b3.dist-info}/WHEEL +0 -0
  63. {esphome-2025.7.0b1.dist-info → esphome-2025.7.0b3.dist-info}/entry_points.txt +0 -0
  64. {esphome-2025.7.0b1.dist-info → esphome-2025.7.0b3.dist-info}/licenses/LICENSE +0 -0
  65. {esphome-2025.7.0b1.dist-info → esphome-2025.7.0b3.dist-info}/top_level.txt +0 -0
@@ -29,9 +29,9 @@ from ..defines import (
29
29
  )
30
30
  from ..helpers import add_lv_use, lvgl_components_required
31
31
  from ..lv_validation import (
32
- angle,
33
32
  get_end_value,
34
33
  get_start_value,
34
+ lv_angle,
35
35
  lv_bool,
36
36
  lv_color,
37
37
  lv_float,
@@ -162,7 +162,7 @@ SCALE_SCHEMA = cv.Schema(
162
162
  cv.Optional(CONF_RANGE_FROM, default=0.0): cv.float_,
163
163
  cv.Optional(CONF_RANGE_TO, default=100.0): cv.float_,
164
164
  cv.Optional(CONF_ANGLE_RANGE, default=270): cv.int_range(0, 360),
165
- cv.Optional(CONF_ROTATION): angle,
165
+ cv.Optional(CONF_ROTATION): lv_angle,
166
166
  cv.Optional(CONF_INDICATORS): cv.ensure_list(INDICATOR_SCHEMA),
167
167
  }
168
168
  )
@@ -187,7 +187,7 @@ class MeterType(WidgetType):
187
187
  for scale_conf in config.get(CONF_SCALES, ()):
188
188
  rotation = 90 + (360 - scale_conf[CONF_ANGLE_RANGE]) / 2
189
189
  if CONF_ROTATION in scale_conf:
190
- rotation = scale_conf[CONF_ROTATION] // 10
190
+ rotation = await lv_angle.process(scale_conf[CONF_ROTATION])
191
191
  with LocalVariable(
192
192
  "meter_var", "lv_meter_scale_t", lv_expr.meter_add_scale(var)
193
193
  ) as meter_var:
@@ -205,21 +205,20 @@ class MeterType(WidgetType):
205
205
  var,
206
206
  meter_var,
207
207
  ticks[CONF_COUNT],
208
- ticks[CONF_WIDTH],
209
- ticks[CONF_LENGTH],
208
+ await size.process(ticks[CONF_WIDTH]),
209
+ await size.process(ticks[CONF_LENGTH]),
210
210
  color,
211
211
  )
212
212
  if CONF_MAJOR in ticks:
213
213
  major = ticks[CONF_MAJOR]
214
- color = await lv_color.process(major[CONF_COLOR])
215
214
  lv.meter_set_scale_major_ticks(
216
215
  var,
217
216
  meter_var,
218
217
  major[CONF_STRIDE],
219
- major[CONF_WIDTH],
220
- major[CONF_LENGTH],
221
- color,
222
- major[CONF_LABEL_GAP],
218
+ await size.process(major[CONF_WIDTH]),
219
+ await size.process(major[CONF_LENGTH]),
220
+ await lv_color.process(major[CONF_COLOR]),
221
+ await size.process(major[CONF_LABEL_GAP]),
223
222
  )
224
223
  for indicator in scale_conf.get(CONF_INDICATORS, ()):
225
224
  (t, v) = next(iter(indicator.items()))
@@ -233,7 +232,11 @@ class MeterType(WidgetType):
233
232
  lv_assign(
234
233
  ivar,
235
234
  lv_expr.meter_add_needle_line(
236
- var, meter_var, v[CONF_WIDTH], color, v[CONF_R_MOD]
235
+ var,
236
+ meter_var,
237
+ await size.process(v[CONF_WIDTH]),
238
+ color,
239
+ await size.process(v[CONF_R_MOD]),
237
240
  ),
238
241
  )
239
242
  if t == CONF_ARC:
@@ -241,7 +244,11 @@ class MeterType(WidgetType):
241
244
  lv_assign(
242
245
  ivar,
243
246
  lv_expr.meter_add_arc(
244
- var, meter_var, v[CONF_WIDTH], color, v[CONF_R_MOD]
247
+ var,
248
+ meter_var,
249
+ await size.process(v[CONF_WIDTH]),
250
+ color,
251
+ await size.process(v[CONF_R_MOD]),
245
252
  ),
246
253
  )
247
254
  if t == CONF_TICK_STYLE:
@@ -257,7 +264,7 @@ class MeterType(WidgetType):
257
264
  color_start,
258
265
  color_end,
259
266
  v[CONF_LOCAL],
260
- v[CONF_WIDTH],
267
+ size.process(v[CONF_WIDTH]),
261
268
  ),
262
269
  )
263
270
  if t == CONF_IMAGE:
@@ -153,11 +153,15 @@ void MQTTBackendESP32::mqtt_event_handler_(const Event &event) {
153
153
  case MQTT_EVENT_DATA: {
154
154
  static std::string topic;
155
155
  if (!event.topic.empty()) {
156
+ // When a single message arrives as multiple chunks, the topic will be empty
157
+ // on any but the first message, leading to event.topic being an empty string.
158
+ // To ensure handlers get the correct topic, cache the last seen topic to
159
+ // simulate always receiving the topic from underlying library
156
160
  topic = event.topic;
157
161
  }
158
162
  ESP_LOGV(TAG, "MQTT_EVENT_DATA %s", topic.c_str());
159
- this->on_message_.call(!event.topic.empty() ? topic.c_str() : nullptr, event.data.data(), event.data.size(),
160
- event.current_data_offset, event.total_data_len);
163
+ this->on_message_.call(topic.c_str(), event.data.data(), event.data.size(), event.current_data_offset,
164
+ event.total_data_len);
161
165
  } break;
162
166
  case MQTT_EVENT_ERROR:
163
167
  ESP_LOGE(TAG, "MQTT_EVENT_ERROR");
@@ -314,6 +314,9 @@ void PacketTransport::send_data_(bool all) {
314
314
  }
315
315
 
316
316
  void PacketTransport::update() {
317
+ if (!this->ping_pong_enable_) {
318
+ return;
319
+ }
317
320
  auto now = millis() / 1000;
318
321
  if (this->last_key_time_ + this->ping_pong_recyle_time_ < now) {
319
322
  this->resend_ping_key_ = this->ping_pong_enable_;
@@ -165,6 +165,7 @@ async def to_code(config):
165
165
  # Allow LDF to properly discover dependency including those in preprocessor
166
166
  # conditionals
167
167
  cg.add_platformio_option("lib_ldf_mode", "chain+")
168
+ cg.add_platformio_option("lib_compat_mode", "strict")
168
169
  cg.add_platformio_option("board", config[CONF_BOARD])
169
170
  cg.add_build_flag("-DUSE_RP2040")
170
171
  cg.set_cpp_standard("gnu++20")
@@ -151,8 +151,11 @@ def _substitute_item(substitutions, item, path, jinja, ignore_missing):
151
151
  if sub is not None:
152
152
  item[k] = sub
153
153
  for old, new in replace_keys:
154
- item[new] = merge_config(item.get(old), item.get(new))
155
- del item[old]
154
+ if str(new) == str(old):
155
+ item[new] = item[old]
156
+ else:
157
+ item[new] = merge_config(item.get(old), item.get(new))
158
+ del item[old]
156
159
  elif isinstance(item, str):
157
160
  sub = _expand_substitutions(substitutions, item, path, jinja, ignore_missing)
158
161
  if isinstance(sub, JinjaStr) or sub != item:
@@ -167,8 +167,8 @@ def validate_config(config):
167
167
  if config[CONF_MODULATION] == "LORA":
168
168
  if config[CONF_BANDWIDTH] not in lora_bws:
169
169
  raise cv.Invalid(f"{config[CONF_BANDWIDTH]} is not available with LORA")
170
- if config[CONF_PREAMBLE_SIZE] > 0 and config[CONF_PREAMBLE_SIZE] < 6:
171
- raise cv.Invalid("Minimum preamble size is 6 with LORA")
170
+ if config[CONF_PREAMBLE_SIZE] < 6:
171
+ raise cv.Invalid("Minimum 'preamble_size' is 6 with LORA")
172
172
  if config[CONF_SPREADING_FACTOR] == 6 and config[CONF_PAYLOAD_LENGTH] == 0:
173
173
  raise cv.Invalid("Payload length must be set when spreading factor is 6")
174
174
  else:
@@ -200,7 +200,7 @@ CONFIG_SCHEMA = (
200
200
  cv.Optional(CONF_PA_RAMP, default="40us"): cv.enum(RAMP),
201
201
  cv.Optional(CONF_PAYLOAD_LENGTH, default=0): cv.int_range(min=0, max=256),
202
202
  cv.Optional(CONF_PREAMBLE_DETECT, default=2): cv.int_range(min=0, max=4),
203
- cv.Required(CONF_PREAMBLE_SIZE): cv.int_range(min=1, max=65535),
203
+ cv.Optional(CONF_PREAMBLE_SIZE, default=8): cv.int_range(min=1, max=65535),
204
204
  cv.Required(CONF_RST_PIN): pins.internal_gpio_output_pin_schema,
205
205
  cv.Optional(CONF_RX_START, default=True): cv.boolean,
206
206
  cv.Required(CONF_RF_SWITCH): cv.boolean,
@@ -164,8 +164,8 @@ def validate_config(config):
164
164
  raise cv.Invalid(f"{config[CONF_BANDWIDTH]} is not available with LORA")
165
165
  if CONF_DIO0_PIN not in config:
166
166
  raise cv.Invalid("Cannot use LoRa without dio0_pin")
167
- if 0 < config[CONF_PREAMBLE_SIZE] < 6:
168
- raise cv.Invalid("Minimum preamble size is 6 with LORA")
167
+ if config[CONF_PREAMBLE_SIZE] < 6:
168
+ raise cv.Invalid("Minimum 'preamble_size' is 6 with LORA")
169
169
  if config[CONF_SPREADING_FACTOR] == 6 and config[CONF_PAYLOAD_LENGTH] == 0:
170
170
  raise cv.Invalid("Payload length must be set when spreading factor is 6")
171
171
  else:
@@ -70,7 +70,7 @@ static void usbh_print_cfg_desc(const usb_config_desc_t *cfg_desc) {
70
70
  ESP_LOGV(TAG, "bMaxPower %dmA", cfg_desc->bMaxPower * 2);
71
71
  }
72
72
 
73
- void usb_client_print_device_descriptor(const usb_device_desc_t *devc_desc) {
73
+ static void usb_client_print_device_descriptor(const usb_device_desc_t *devc_desc) {
74
74
  if (devc_desc == NULL) {
75
75
  return;
76
76
  }
@@ -92,8 +92,8 @@ void usb_client_print_device_descriptor(const usb_device_desc_t *devc_desc) {
92
92
  ESP_LOGV(TAG, "bNumConfigurations %d", devc_desc->bNumConfigurations);
93
93
  }
94
94
 
95
- void usb_client_print_config_descriptor(const usb_config_desc_t *cfg_desc,
96
- print_class_descriptor_cb class_specific_cb) {
95
+ static void usb_client_print_config_descriptor(const usb_config_desc_t *cfg_desc,
96
+ print_class_descriptor_cb class_specific_cb) {
97
97
  if (cfg_desc == nullptr) {
98
98
  return;
99
99
  }
@@ -128,9 +128,9 @@ void usb_client_print_config_descriptor(const usb_config_desc_t *cfg_desc,
128
128
  static std::string get_descriptor_string(const usb_str_desc_t *desc) {
129
129
  char buffer[256];
130
130
  if (desc == nullptr)
131
- return "(unknown)";
131
+ return "(unspecified)";
132
132
  char *p = buffer;
133
- for (size_t i = 0; i != desc->bLength / 2; i++) {
133
+ for (int i = 0; i != desc->bLength / 2; i++) {
134
134
  auto c = desc->wData[i];
135
135
  if (c < 0x100)
136
136
  *p++ = static_cast<char>(c);
@@ -169,7 +169,7 @@ void USBClient::setup() {
169
169
  this->mark_failed();
170
170
  return;
171
171
  }
172
- for (auto trq : this->trq_pool_) {
172
+ for (auto *trq : this->trq_pool_) {
173
173
  usb_host_transfer_alloc(64, 0, &trq->transfer);
174
174
  trq->client = this;
175
175
  }
@@ -197,7 +197,8 @@ void USBClient::loop() {
197
197
  ESP_LOGD(TAG, "Device descriptor: vid %X pid %X", desc->idVendor, desc->idProduct);
198
198
  if (desc->idVendor == this->vid_ && desc->idProduct == this->pid_ || this->vid_ == 0 && this->pid_ == 0) {
199
199
  usb_device_info_t dev_info;
200
- if ((err = usb_host_device_info(this->device_handle_, &dev_info)) != ESP_OK) {
200
+ err = usb_host_device_info(this->device_handle_, &dev_info);
201
+ if (err != ESP_OK) {
201
202
  ESP_LOGW(TAG, "Device info failed: %s", esp_err_to_name(err));
202
203
  this->disconnect();
203
204
  break;
@@ -336,7 +337,7 @@ static void transfer_callback(usb_transfer_t *xfer) {
336
337
  * @throws None.
337
338
  */
338
339
  void USBClient::transfer_in(uint8_t ep_address, const transfer_cb_t &callback, uint16_t length) {
339
- auto trq = this->get_trq_();
340
+ auto *trq = this->get_trq_();
340
341
  if (trq == nullptr) {
341
342
  ESP_LOGE(TAG, "Too many requests queued");
342
343
  return;
@@ -349,7 +350,6 @@ void USBClient::transfer_in(uint8_t ep_address, const transfer_cb_t &callback, u
349
350
  if (err != ESP_OK) {
350
351
  ESP_LOGE(TAG, "Failed to submit transfer, address=%x, length=%d, err=%x", ep_address, length, err);
351
352
  this->release_trq(trq);
352
- this->disconnect();
353
353
  }
354
354
  }
355
355
 
@@ -364,7 +364,7 @@ void USBClient::transfer_in(uint8_t ep_address, const transfer_cb_t &callback, u
364
364
  * @throws None.
365
365
  */
366
366
  void USBClient::transfer_out(uint8_t ep_address, const transfer_cb_t &callback, const uint8_t *data, uint16_t length) {
367
- auto trq = this->get_trq_();
367
+ auto *trq = this->get_trq_();
368
368
  if (trq == nullptr) {
369
369
  ESP_LOGE(TAG, "Too many requests queued");
370
370
  return;
@@ -43,7 +43,7 @@ static constexpr uint8_t SET_BAUDRATE = 0x1E; // Set the baud rate.
43
43
  static constexpr uint8_t SET_CHARS = 0x19; // Set special characters.
44
44
  static constexpr uint8_t VENDOR_SPECIFIC = 0xFF; // Vendor specific command.
45
45
 
46
- std::vector<CdcEps> USBUartTypeCP210X::parse_descriptors_(usb_device_handle_t dev_hdl) {
46
+ std::vector<CdcEps> USBUartTypeCP210X::parse_descriptors(usb_device_handle_t dev_hdl) {
47
47
  const usb_config_desc_t *config_desc;
48
48
  const usb_device_desc_t *device_desc;
49
49
  int conf_offset = 0, ep_offset;
@@ -18,52 +18,48 @@ namespace usb_uart {
18
18
  */
19
19
  static optional<CdcEps> get_cdc(const usb_config_desc_t *config_desc, uint8_t intf_idx) {
20
20
  int conf_offset, ep_offset;
21
- const usb_ep_desc_t *notify_ep{}, *in_ep{}, *out_ep{};
22
- uint8_t interface_number = 0;
23
- // look for an interface with one interrupt endpoint (notify), and an interface with two bulk endpoints (data in/out)
21
+ // look for an interface with an interrupt endpoint (notify), and one with two bulk endpoints (data in/out)
22
+ CdcEps eps{};
23
+ eps.bulk_interface_number = 0xFF;
24
24
  for (;;) {
25
- auto intf_desc = usb_parse_interface_descriptor(config_desc, intf_idx++, 0, &conf_offset);
25
+ const auto *intf_desc = usb_parse_interface_descriptor(config_desc, intf_idx++, 0, &conf_offset);
26
26
  if (!intf_desc) {
27
27
  ESP_LOGE(TAG, "usb_parse_interface_descriptor failed");
28
28
  return nullopt;
29
29
  }
30
- if (intf_desc->bNumEndpoints == 1) {
30
+ ESP_LOGD(TAG, "intf_desc: bInterfaceClass=%02X, bInterfaceSubClass=%02X, bInterfaceProtocol=%02X, bNumEndpoints=%d",
31
+ intf_desc->bInterfaceClass, intf_desc->bInterfaceSubClass, intf_desc->bInterfaceProtocol,
32
+ intf_desc->bNumEndpoints);
33
+ for (uint8_t i = 0; i != intf_desc->bNumEndpoints; i++) {
31
34
  ep_offset = conf_offset;
32
- notify_ep = usb_parse_endpoint_descriptor_by_index(intf_desc, 0, config_desc->wTotalLength, &ep_offset);
33
- if (!notify_ep) {
34
- ESP_LOGE(TAG, "notify_ep: usb_parse_endpoint_descriptor_by_index failed");
35
+ const auto *ep = usb_parse_endpoint_descriptor_by_index(intf_desc, i, config_desc->wTotalLength, &ep_offset);
36
+ if (!ep) {
37
+ ESP_LOGE(TAG, "Ran out of interfaces at %d before finding all endpoints", i);
35
38
  return nullopt;
36
39
  }
37
- if (notify_ep->bmAttributes != USB_BM_ATTRIBUTES_XFER_INT)
38
- notify_ep = nullptr;
39
- } else if (USB_CLASS_CDC_DATA && intf_desc->bNumEndpoints == 2) {
40
- interface_number = intf_desc->bInterfaceNumber;
41
- ep_offset = conf_offset;
42
- out_ep = usb_parse_endpoint_descriptor_by_index(intf_desc, 0, config_desc->wTotalLength, &ep_offset);
43
- if (!out_ep) {
44
- ESP_LOGE(TAG, "out_ep: usb_parse_endpoint_descriptor_by_index failed");
45
- return nullopt;
46
- }
47
- if (out_ep->bmAttributes != USB_BM_ATTRIBUTES_XFER_BULK)
48
- out_ep = nullptr;
49
- ep_offset = conf_offset;
50
- in_ep = usb_parse_endpoint_descriptor_by_index(intf_desc, 1, config_desc->wTotalLength, &ep_offset);
51
- if (!in_ep) {
52
- ESP_LOGE(TAG, "in_ep: usb_parse_endpoint_descriptor_by_index failed");
53
- return nullopt;
40
+ ESP_LOGD(TAG, "ep: bEndpointAddress=%02X, bmAttributes=%02X", ep->bEndpointAddress, ep->bmAttributes);
41
+ if (ep->bmAttributes == USB_BM_ATTRIBUTES_XFER_INT) {
42
+ eps.notify_ep = ep;
43
+ eps.interrupt_interface_number = intf_desc->bInterfaceNumber;
44
+ } else if (ep->bmAttributes == USB_BM_ATTRIBUTES_XFER_BULK && ep->bEndpointAddress & usb_host::USB_DIR_IN &&
45
+ (eps.bulk_interface_number == 0xFF || eps.bulk_interface_number == intf_desc->bInterfaceNumber)) {
46
+ eps.in_ep = ep;
47
+ eps.bulk_interface_number = intf_desc->bInterfaceNumber;
48
+ } else if (ep->bmAttributes == USB_BM_ATTRIBUTES_XFER_BULK && !(ep->bEndpointAddress & usb_host::USB_DIR_IN) &&
49
+ (eps.bulk_interface_number == 0xFF || eps.bulk_interface_number == intf_desc->bInterfaceNumber)) {
50
+ eps.out_ep = ep;
51
+ eps.bulk_interface_number = intf_desc->bInterfaceNumber;
52
+ } else {
53
+ ESP_LOGE(TAG, "Unexpected endpoint attributes: %02X", ep->bmAttributes);
54
+ continue;
54
55
  }
55
- if (in_ep->bmAttributes != USB_BM_ATTRIBUTES_XFER_BULK)
56
- in_ep = nullptr;
57
56
  }
58
- if (in_ep != nullptr && out_ep != nullptr && notify_ep != nullptr)
59
- break;
57
+ if (eps.in_ep != nullptr && eps.out_ep != nullptr && eps.notify_ep != nullptr)
58
+ return eps;
60
59
  }
61
- if (in_ep->bEndpointAddress & usb_host::USB_DIR_IN)
62
- return CdcEps{notify_ep, in_ep, out_ep, interface_number};
63
- return CdcEps{notify_ep, out_ep, in_ep, interface_number};
64
60
  }
65
61
 
66
- std::vector<CdcEps> USBUartTypeCdcAcm::parse_descriptors_(usb_device_handle_t dev_hdl) {
62
+ std::vector<CdcEps> USBUartTypeCdcAcm::parse_descriptors(usb_device_handle_t dev_hdl) {
67
63
  const usb_config_desc_t *config_desc;
68
64
  const usb_device_desc_t *device_desc;
69
65
  int desc_offset = 0;
@@ -78,7 +74,7 @@ std::vector<CdcEps> USBUartTypeCdcAcm::parse_descriptors_(usb_device_handle_t de
78
74
  ESP_LOGE(TAG, "get_active_config_descriptor failed");
79
75
  return {};
80
76
  }
81
- if (device_desc->bDeviceClass == USB_CLASS_COMM) {
77
+ if (device_desc->bDeviceClass == USB_CLASS_COMM || device_desc->bDeviceClass == USB_CLASS_VENDOR_SPEC) {
82
78
  // single CDC-ACM device
83
79
  if (auto eps = get_cdc(config_desc, 0)) {
84
80
  ESP_LOGV(TAG, "Found CDC-ACM device");
@@ -194,7 +190,7 @@ void USBUartComponent::start_input(USBUartChannel *channel) {
194
190
  if (!channel->initialised_ || channel->input_started_ ||
195
191
  channel->input_buffer_.get_free_space() < channel->cdc_dev_.in_ep->wMaxPacketSize)
196
192
  return;
197
- auto ep = channel->cdc_dev_.in_ep;
193
+ const auto *ep = channel->cdc_dev_.in_ep;
198
194
  auto callback = [this, channel](const usb_host::TransferStatus &status) {
199
195
  ESP_LOGV(TAG, "Transfer result: length: %u; status %X", status.data_len, status.error_code);
200
196
  if (!status.success) {
@@ -227,7 +223,7 @@ void USBUartComponent::start_output(USBUartChannel *channel) {
227
223
  if (channel->output_buffer_.is_empty()) {
228
224
  return;
229
225
  }
230
- auto ep = channel->cdc_dev_.out_ep;
226
+ const auto *ep = channel->cdc_dev_.out_ep;
231
227
  auto callback = [this, channel](const usb_host::TransferStatus &status) {
232
228
  ESP_LOGV(TAG, "Output Transfer result: length: %u; status %X", status.data_len, status.error_code);
233
229
  channel->output_started_ = false;
@@ -259,15 +255,15 @@ static void fix_mps(const usb_ep_desc_t *ep) {
259
255
  }
260
256
  }
261
257
  void USBUartTypeCdcAcm::on_connected() {
262
- auto cdc_devs = this->parse_descriptors_(this->device_handle_);
258
+ auto cdc_devs = this->parse_descriptors(this->device_handle_);
263
259
  if (cdc_devs.empty()) {
264
260
  this->status_set_error("No CDC-ACM device found");
265
261
  this->disconnect();
266
262
  return;
267
263
  }
268
264
  ESP_LOGD(TAG, "Found %zu CDC-ACM devices", cdc_devs.size());
269
- auto i = 0;
270
- for (auto channel : this->channels_) {
265
+ size_t i = 0;
266
+ for (auto *channel : this->channels_) {
271
267
  if (i == cdc_devs.size()) {
272
268
  ESP_LOGE(TAG, "No configuration found for channel %d", channel->index_);
273
269
  this->status_set_warning("No configuration found for channel");
@@ -277,10 +273,11 @@ void USBUartTypeCdcAcm::on_connected() {
277
273
  fix_mps(channel->cdc_dev_.in_ep);
278
274
  fix_mps(channel->cdc_dev_.out_ep);
279
275
  channel->initialised_ = true;
280
- auto err = usb_host_interface_claim(this->handle_, this->device_handle_, channel->cdc_dev_.interface_number, 0);
276
+ auto err =
277
+ usb_host_interface_claim(this->handle_, this->device_handle_, channel->cdc_dev_.bulk_interface_number, 0);
281
278
  if (err != ESP_OK) {
282
279
  ESP_LOGE(TAG, "usb_host_interface_claim failed: %s, channel=%d, intf=%d", esp_err_to_name(err), channel->index_,
283
- channel->cdc_dev_.interface_number);
280
+ channel->cdc_dev_.bulk_interface_number);
284
281
  this->status_set_error("usb_host_interface_claim failed");
285
282
  this->disconnect();
286
283
  return;
@@ -290,7 +287,7 @@ void USBUartTypeCdcAcm::on_connected() {
290
287
  }
291
288
 
292
289
  void USBUartTypeCdcAcm::on_disconnected() {
293
- for (auto channel : this->channels_) {
290
+ for (auto *channel : this->channels_) {
294
291
  if (channel->cdc_dev_.in_ep != nullptr) {
295
292
  usb_host_endpoint_halt(this->device_handle_, channel->cdc_dev_.in_ep->bEndpointAddress);
296
293
  usb_host_endpoint_flush(this->device_handle_, channel->cdc_dev_.in_ep->bEndpointAddress);
@@ -303,7 +300,7 @@ void USBUartTypeCdcAcm::on_disconnected() {
303
300
  usb_host_endpoint_halt(this->device_handle_, channel->cdc_dev_.notify_ep->bEndpointAddress);
304
301
  usb_host_endpoint_flush(this->device_handle_, channel->cdc_dev_.notify_ep->bEndpointAddress);
305
302
  }
306
- usb_host_interface_release(this->handle_, this->device_handle_, channel->cdc_dev_.interface_number);
303
+ usb_host_interface_release(this->handle_, this->device_handle_, channel->cdc_dev_.bulk_interface_number);
307
304
  channel->initialised_ = false;
308
305
  channel->input_started_ = false;
309
306
  channel->output_started_ = false;
@@ -314,7 +311,7 @@ void USBUartTypeCdcAcm::on_disconnected() {
314
311
  }
315
312
 
316
313
  void USBUartTypeCdcAcm::enable_channels() {
317
- for (auto channel : this->channels_) {
314
+ for (auto *channel : this->channels_) {
318
315
  if (!channel->initialised_)
319
316
  continue;
320
317
  channel->input_started_ = false;
@@ -25,7 +25,8 @@ struct CdcEps {
25
25
  const usb_ep_desc_t *notify_ep;
26
26
  const usb_ep_desc_t *in_ep;
27
27
  const usb_ep_desc_t *out_ep;
28
- uint8_t interface_number;
28
+ uint8_t bulk_interface_number;
29
+ uint8_t interrupt_interface_number;
29
30
  };
30
31
 
31
32
  enum UARTParityOptions {
@@ -123,7 +124,7 @@ class USBUartTypeCdcAcm : public USBUartComponent {
123
124
  USBUartTypeCdcAcm(uint16_t vid, uint16_t pid) : USBUartComponent(vid, pid) {}
124
125
 
125
126
  protected:
126
- virtual std::vector<CdcEps> parse_descriptors_(usb_device_handle_t dev_hdl);
127
+ virtual std::vector<CdcEps> parse_descriptors(usb_device_handle_t dev_hdl);
127
128
  void on_connected() override;
128
129
  virtual void enable_channels();
129
130
  void on_disconnected() override;
@@ -134,7 +135,7 @@ class USBUartTypeCP210X : public USBUartTypeCdcAcm {
134
135
  USBUartTypeCP210X(uint16_t vid, uint16_t pid) : USBUartTypeCdcAcm(vid, pid) {}
135
136
 
136
137
  protected:
137
- std::vector<CdcEps> parse_descriptors_(usb_device_handle_t dev_hdl) override;
138
+ std::vector<CdcEps> parse_descriptors(usb_device_handle_t dev_hdl) override;
138
139
  void enable_channels() override;
139
140
  };
140
141
  class USBUartTypeCH34X : public USBUartTypeCdcAcm {
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.0b1"
7
+ __version__ = "2025.7.0b3"
8
8
 
9
9
  ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
10
10
  VALID_SUBSTITUTIONS_CHARACTERS = (
@@ -138,7 +138,7 @@ void Component::call_dump_config() {
138
138
  }
139
139
  }
140
140
  }
141
- ESP_LOGE(TAG, " Component %s is marked FAILED: %s", this->get_component_source(), error_msg);
141
+ ESP_LOGE(TAG, " %s is marked FAILED: %s", this->get_component_source(), error_msg);
142
142
  }
143
143
  }
144
144
 
@@ -191,7 +191,7 @@ bool Component::should_warn_of_blocking(uint32_t blocking_time) {
191
191
  return false;
192
192
  }
193
193
  void Component::mark_failed() {
194
- ESP_LOGE(TAG, "Component %s was marked as failed", this->get_component_source());
194
+ ESP_LOGE(TAG, "%s was marked as failed", this->get_component_source());
195
195
  this->component_state_ &= ~COMPONENT_STATE_MASK;
196
196
  this->component_state_ |= COMPONENT_STATE_FAILED;
197
197
  this->status_set_error();
@@ -229,7 +229,7 @@ void IRAM_ATTR HOT Component::enable_loop_soon_any_context() {
229
229
  }
230
230
  void Component::reset_to_construction_state() {
231
231
  if ((this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED) {
232
- ESP_LOGI(TAG, "Component %s is being reset to construction state", this->get_component_source());
232
+ ESP_LOGI(TAG, "%s is being reset to construction state", this->get_component_source());
233
233
  this->component_state_ &= ~COMPONENT_STATE_MASK;
234
234
  this->component_state_ |= COMPONENT_STATE_CONSTRUCTION;
235
235
  // Clear error status when resetting
@@ -275,14 +275,14 @@ void Component::status_set_warning(const char *message) {
275
275
  return;
276
276
  this->component_state_ |= STATUS_LED_WARNING;
277
277
  App.app_state_ |= STATUS_LED_WARNING;
278
- ESP_LOGW(TAG, "Component %s set Warning flag: %s", this->get_component_source(), message);
278
+ ESP_LOGW(TAG, "%s set Warning flag: %s", this->get_component_source(), message);
279
279
  }
280
280
  void Component::status_set_error(const char *message) {
281
281
  if ((this->component_state_ & STATUS_LED_ERROR) != 0)
282
282
  return;
283
283
  this->component_state_ |= STATUS_LED_ERROR;
284
284
  App.app_state_ |= STATUS_LED_ERROR;
285
- ESP_LOGE(TAG, "Component %s set Error flag: %s", this->get_component_source(), message);
285
+ ESP_LOGE(TAG, "%s set Error flag: %s", this->get_component_source(), message);
286
286
  if (strcmp(message, "unspecified") != 0) {
287
287
  // Lazy allocate the error messages vector if needed
288
288
  if (!component_error_messages) {
@@ -303,13 +303,13 @@ void Component::status_clear_warning() {
303
303
  if ((this->component_state_ & STATUS_LED_WARNING) == 0)
304
304
  return;
305
305
  this->component_state_ &= ~STATUS_LED_WARNING;
306
- ESP_LOGW(TAG, "Component %s cleared Warning flag", this->get_component_source());
306
+ ESP_LOGW(TAG, "%s cleared Warning flag", this->get_component_source());
307
307
  }
308
308
  void Component::status_clear_error() {
309
309
  if ((this->component_state_ & STATUS_LED_ERROR) == 0)
310
310
  return;
311
311
  this->component_state_ &= ~STATUS_LED_ERROR;
312
- ESP_LOGE(TAG, "Component %s cleared Error flag", this->get_component_source());
312
+ ESP_LOGE(TAG, "%s cleared Error flag", this->get_component_source());
313
313
  }
314
314
  void Component::status_momentary_warning(const std::string &name, uint32_t length) {
315
315
  this->status_set_warning();
@@ -403,7 +403,7 @@ uint32_t WarnIfComponentBlockingGuard::finish() {
403
403
  }
404
404
  if (should_warn) {
405
405
  const char *src = component_ == nullptr ? "<null>" : component_->get_component_source();
406
- ESP_LOGW(TAG, "Component %s took a long time for an operation (%" PRIu32 " ms)", src, blocking_time);
406
+ ESP_LOGW(TAG, "%s took a long time for an operation (%" PRIu32 " ms)", src, blocking_time);
407
407
  ESP_LOGW(TAG, "Components should block for at most 30 ms");
408
408
  }
409
409
 
@@ -4,6 +4,8 @@
4
4
 
5
5
  #ifdef USE_API
6
6
  #include "esphome/components/api/api_server.h"
7
+ #endif
8
+ #ifdef USE_API_SERVICES
7
9
  #include "esphome/components/api/user_services.h"
8
10
  #endif
9
11
 
@@ -148,7 +150,7 @@ void ComponentIterator::advance() {
148
150
  }
149
151
  break;
150
152
  #endif
151
- #ifdef USE_API
153
+ #ifdef USE_API_SERVICES
152
154
  case IteratorState ::SERVICE:
153
155
  if (this->at_ >= api::global_api_server->get_user_services().size()) {
154
156
  advance_platform = true;
@@ -383,7 +385,7 @@ void ComponentIterator::advance() {
383
385
  }
384
386
  bool ComponentIterator::on_end() { return true; }
385
387
  bool ComponentIterator::on_begin() { return true; }
386
- #ifdef USE_API
388
+ #ifdef USE_API_SERVICES
387
389
  bool ComponentIterator::on_service(api::UserServiceDescriptor *service) { return true; }
388
390
  #endif
389
391
  #ifdef USE_CAMERA
@@ -10,7 +10,7 @@
10
10
 
11
11
  namespace esphome {
12
12
 
13
- #ifdef USE_API
13
+ #ifdef USE_API_SERVICES
14
14
  namespace api {
15
15
  class UserServiceDescriptor;
16
16
  } // namespace api
@@ -45,7 +45,7 @@ class ComponentIterator {
45
45
  #ifdef USE_TEXT_SENSOR
46
46
  virtual bool on_text_sensor(text_sensor::TextSensor *text_sensor) = 0;
47
47
  #endif
48
- #ifdef USE_API
48
+ #ifdef USE_API_SERVICES
49
49
  virtual bool on_service(api::UserServiceDescriptor *service);
50
50
  #endif
51
51
  #ifdef USE_CAMERA
@@ -122,7 +122,7 @@ class ComponentIterator {
122
122
  #ifdef USE_TEXT_SENSOR
123
123
  TEXT_SENSOR,
124
124
  #endif
125
- #ifdef USE_API
125
+ #ifdef USE_API_SERVICES
126
126
  SERVICE,
127
127
  #endif
128
128
  #ifdef USE_CAMERA
esphome/core/defines.h CHANGED
@@ -108,7 +108,7 @@
108
108
  #define USE_API_CLIENT_DISCONNECTED_TRIGGER
109
109
  #define USE_API_NOISE
110
110
  #define USE_API_PLAINTEXT
111
- #define USE_API_YAML_SERVICES
111
+ #define USE_API_SERVICES
112
112
  #define USE_MD5
113
113
  #define USE_MQTT
114
114
  #define USE_NETWORK
@@ -187,6 +187,12 @@ def entity_duplicate_validator(platform: str) -> Callable[[ConfigType], ConfigTy
187
187
  # No name to validate
188
188
  return config
189
189
 
190
+ # Skip validation for internal entities
191
+ # Internal entities are not exposed to Home Assistant and don't use the hash-based
192
+ # entity state tracking system, so name collisions don't matter for them
193
+ if config.get(CONF_INTERNAL, False):
194
+ return config
195
+
190
196
  # Get the entity name
191
197
  entity_name = config[CONF_NAME]
192
198
 
esphome/core/helpers.h CHANGED
@@ -783,7 +783,7 @@ template<class T> class RAMAllocator {
783
783
  T *reallocate(T *p, size_t n) { return this->reallocate(p, n, sizeof(T)); }
784
784
 
785
785
  T *reallocate(T *p, size_t n, size_t manual_size) {
786
- size_t size = n * sizeof(T);
786
+ size_t size = n * manual_size;
787
787
  T *ptr = nullptr;
788
788
  #ifdef USE_ESP32
789
789
  if (this->flags_ & Flags::ALLOC_EXTERNAL) {