esphome 2025.7.0b1__py3-none-any.whl → 2025.7.0b2__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.
- esphome/components/api/__init__.py +19 -5
- esphome/components/api/api_connection.cpp +53 -175
- esphome/components/api/api_connection.h +28 -25
- esphome/components/api/api_frame_helper.cpp +2 -3
- esphome/components/api/api_frame_helper.h +7 -9
- esphome/components/api/api_pb2.cpp +1862 -5133
- esphome/components/api/api_pb2.h +291 -533
- esphome/components/api/api_pb2_dump.cpp +99 -0
- esphome/components/api/api_pb2_service.cpp +4 -0
- esphome/components/api/api_pb2_service.h +6 -0
- esphome/components/api/api_server.cpp +2 -9
- esphome/components/api/api_server.h +7 -33
- esphome/components/api/custom_api_device.h +8 -0
- esphome/components/api/list_entities.cpp +2 -0
- esphome/components/api/list_entities.h +3 -1
- esphome/components/api/proto.h +506 -23
- esphome/components/api/user_services.h +2 -0
- esphome/components/debug/debug_esp32.cpp +2 -0
- esphome/components/esp32/__init__.py +1 -0
- esphome/components/esp32_camera/__init__.py +1 -1
- esphome/components/esp32_touch/esp32_touch_v1.cpp +12 -10
- esphome/components/esp8266/__init__.py +1 -0
- esphome/components/host/__init__.py +1 -0
- esphome/components/ld2410/ld2410.cpp +12 -28
- esphome/components/libretiny/__init__.py +1 -0
- esphome/components/mqtt/mqtt_backend_esp32.cpp +6 -2
- esphome/components/packet_transport/packet_transport.cpp +3 -0
- esphome/components/rp2040/__init__.py +1 -0
- esphome/components/sx126x/__init__.py +3 -3
- esphome/components/sx127x/__init__.py +2 -2
- esphome/components/usb_host/usb_host_client.cpp +10 -10
- esphome/components/usb_uart/cp210x.cpp +1 -1
- esphome/components/usb_uart/usb_uart.cpp +41 -44
- esphome/components/usb_uart/usb_uart.h +4 -3
- esphome/const.py +1 -1
- esphome/core/component_iterator.cpp +4 -2
- esphome/core/component_iterator.h +3 -3
- esphome/core/defines.h +1 -1
- esphome/core/entity_helpers.py +6 -0
- esphome/core/scheduler.cpp +9 -12
- esphome/core/scheduler.h +0 -3
- esphome/wizard.py +1 -1
- {esphome-2025.7.0b1.dist-info → esphome-2025.7.0b2.dist-info}/METADATA +2 -2
- {esphome-2025.7.0b1.dist-info → esphome-2025.7.0b2.dist-info}/RECORD +48 -49
- esphome/components/api/api_pb2_size.h +0 -359
- {esphome-2025.7.0b1.dist-info → esphome-2025.7.0b2.dist-info}/WHEEL +0 -0
- {esphome-2025.7.0b1.dist-info → esphome-2025.7.0b2.dist-info}/entry_points.txt +0 -0
- {esphome-2025.7.0b1.dist-info → esphome-2025.7.0b2.dist-info}/licenses/LICENSE +0 -0
- {esphome-2025.7.0b1.dist-info → esphome-2025.7.0b2.dist-info}/top_level.txt +0 -0
|
@@ -178,13 +178,8 @@ static constexpr uint8_t NO_MAC[] = {0x08, 0x05, 0x04, 0x03, 0x02, 0x01};
|
|
|
178
178
|
|
|
179
179
|
static inline int two_byte_to_int(char firstbyte, char secondbyte) { return (int16_t) (secondbyte << 8) + firstbyte; }
|
|
180
180
|
|
|
181
|
-
static bool validate_header_footer(const uint8_t *header_footer, const uint8_t *buffer) {
|
|
182
|
-
|
|
183
|
-
if (header_footer[i] != buffer[i]) {
|
|
184
|
-
return false; // Mismatch in header/footer
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
return true; // Valid header/footer
|
|
181
|
+
static inline bool validate_header_footer(const uint8_t *header_footer, const uint8_t *buffer) {
|
|
182
|
+
return std::memcmp(header_footer, buffer, HEADER_FOOTER_SIZE) == 0;
|
|
188
183
|
}
|
|
189
184
|
|
|
190
185
|
void LD2410Component::dump_config() {
|
|
@@ -300,14 +295,12 @@ void LD2410Component::send_command_(uint8_t command, const uint8_t *command_valu
|
|
|
300
295
|
if (command_value != nullptr) {
|
|
301
296
|
len += command_value_len;
|
|
302
297
|
}
|
|
303
|
-
|
|
298
|
+
// 2 length bytes (low, high) + 2 command bytes (low, high)
|
|
299
|
+
uint8_t len_cmd[] = {len, 0x00, command, 0x00};
|
|
304
300
|
this->write_array(len_cmd, sizeof(len_cmd));
|
|
305
|
-
|
|
306
301
|
// command value bytes
|
|
307
302
|
if (command_value != nullptr) {
|
|
308
|
-
|
|
309
|
-
this->write_byte(command_value[i]);
|
|
310
|
-
}
|
|
303
|
+
this->write_array(command_value, command_value_len);
|
|
311
304
|
}
|
|
312
305
|
// frame footer bytes
|
|
313
306
|
this->write_array(CMD_FRAME_FOOTER, sizeof(CMD_FRAME_FOOTER));
|
|
@@ -401,7 +394,7 @@ void LD2410Component::handle_periodic_data_() {
|
|
|
401
394
|
/*
|
|
402
395
|
Moving distance range: 18th byte
|
|
403
396
|
Still distance range: 19th byte
|
|
404
|
-
Moving
|
|
397
|
+
Moving energy: 20~28th bytes
|
|
405
398
|
*/
|
|
406
399
|
for (std::vector<sensor::Sensor *>::size_type i = 0; i != this->gate_move_sensors_.size(); i++) {
|
|
407
400
|
sensor::Sensor *s = this->gate_move_sensors_[i];
|
|
@@ -480,7 +473,7 @@ bool LD2410Component::handle_ack_data_() {
|
|
|
480
473
|
ESP_LOGE(TAG, "Invalid status");
|
|
481
474
|
return true;
|
|
482
475
|
}
|
|
483
|
-
if (
|
|
476
|
+
if (this->buffer_data_[8] || this->buffer_data_[9]) {
|
|
484
477
|
ESP_LOGW(TAG, "Invalid command: %02X, %02X", this->buffer_data_[8], this->buffer_data_[9]);
|
|
485
478
|
return true;
|
|
486
479
|
}
|
|
@@ -534,8 +527,8 @@ bool LD2410Component::handle_ack_data_() {
|
|
|
534
527
|
const auto *light_function_str = find_str(LIGHT_FUNCTIONS_BY_UINT, this->light_function_);
|
|
535
528
|
const auto *out_pin_level_str = find_str(OUT_PIN_LEVELS_BY_UINT, this->out_pin_level_);
|
|
536
529
|
ESP_LOGV(TAG,
|
|
537
|
-
"Light function
|
|
538
|
-
"Light threshold
|
|
530
|
+
"Light function: %s\n"
|
|
531
|
+
"Light threshold: %u\n"
|
|
539
532
|
"Out pin level: %s",
|
|
540
533
|
light_function_str, this->light_threshold_, out_pin_level_str);
|
|
541
534
|
#ifdef USE_SELECT
|
|
@@ -600,7 +593,7 @@ bool LD2410Component::handle_ack_data_() {
|
|
|
600
593
|
break;
|
|
601
594
|
|
|
602
595
|
case CMD_QUERY: { // Query parameters response
|
|
603
|
-
if (this->buffer_data_[10] !=
|
|
596
|
+
if (this->buffer_data_[10] != HEADER)
|
|
604
597
|
return true; // value head=0xAA
|
|
605
598
|
#ifdef USE_NUMBER
|
|
606
599
|
/*
|
|
@@ -656,17 +649,11 @@ void LD2410Component::readline_(int readch) {
|
|
|
656
649
|
if (this->buffer_pos_ < 4) {
|
|
657
650
|
return; // Not enough data to process yet
|
|
658
651
|
}
|
|
659
|
-
if (this->buffer_data_[this->buffer_pos_ - 4]
|
|
660
|
-
this->buffer_data_[this->buffer_pos_ - 3] == DATA_FRAME_FOOTER[1] &&
|
|
661
|
-
this->buffer_data_[this->buffer_pos_ - 2] == DATA_FRAME_FOOTER[2] &&
|
|
662
|
-
this->buffer_data_[this->buffer_pos_ - 1] == DATA_FRAME_FOOTER[3]) {
|
|
652
|
+
if (ld2410::validate_header_footer(DATA_FRAME_FOOTER, &this->buffer_data_[this->buffer_pos_ - 4])) {
|
|
663
653
|
ESP_LOGV(TAG, "Handling Periodic Data: %s", format_hex_pretty(this->buffer_data_, this->buffer_pos_).c_str());
|
|
664
654
|
this->handle_periodic_data_();
|
|
665
655
|
this->buffer_pos_ = 0; // Reset position index for next message
|
|
666
|
-
} else if (this->buffer_data_[this->buffer_pos_ - 4]
|
|
667
|
-
this->buffer_data_[this->buffer_pos_ - 3] == CMD_FRAME_FOOTER[1] &&
|
|
668
|
-
this->buffer_data_[this->buffer_pos_ - 2] == CMD_FRAME_FOOTER[2] &&
|
|
669
|
-
this->buffer_data_[this->buffer_pos_ - 1] == CMD_FRAME_FOOTER[3]) {
|
|
656
|
+
} else if (ld2410::validate_header_footer(CMD_FRAME_FOOTER, &this->buffer_data_[this->buffer_pos_ - 4])) {
|
|
670
657
|
ESP_LOGV(TAG, "Handling Ack Data: %s", format_hex_pretty(this->buffer_data_, this->buffer_pos_).c_str());
|
|
671
658
|
if (this->handle_ack_data_()) {
|
|
672
659
|
this->buffer_pos_ = 0; // Reset position index for next message
|
|
@@ -772,7 +759,6 @@ void LD2410Component::set_max_distances_timeout() {
|
|
|
772
759
|
0x00};
|
|
773
760
|
this->set_config_mode_(true);
|
|
774
761
|
this->send_command_(CMD_MAXDIST_DURATION, value, sizeof(value));
|
|
775
|
-
delay(50); // NOLINT
|
|
776
762
|
this->query_parameters_();
|
|
777
763
|
this->set_timeout(200, [this]() { this->restart_and_read_all_info(); });
|
|
778
764
|
this->set_config_mode_(false);
|
|
@@ -802,7 +788,6 @@ void LD2410Component::set_gate_threshold(uint8_t gate) {
|
|
|
802
788
|
0x01, 0x00, lowbyte(motion), highbyte(motion), 0x00, 0x00,
|
|
803
789
|
0x02, 0x00, lowbyte(still), highbyte(still), 0x00, 0x00};
|
|
804
790
|
this->send_command_(CMD_GATE_SENS, value, sizeof(value));
|
|
805
|
-
delay(50); // NOLINT
|
|
806
791
|
this->query_parameters_();
|
|
807
792
|
this->set_config_mode_(false);
|
|
808
793
|
}
|
|
@@ -833,7 +818,6 @@ void LD2410Component::set_light_out_control() {
|
|
|
833
818
|
this->set_config_mode_(true);
|
|
834
819
|
uint8_t value[4] = {this->light_function_, this->light_threshold_, this->out_pin_level_, 0x00};
|
|
835
820
|
this->send_command_(CMD_SET_LIGHT_CONTROL, value, sizeof(value));
|
|
836
|
-
delay(50); // NOLINT
|
|
837
821
|
this->query_light_control_();
|
|
838
822
|
this->set_timeout(200, [this]() { this->restart_and_read_all_info(); });
|
|
839
823
|
this->set_config_mode_(false);
|
|
@@ -268,6 +268,7 @@ async def component_to_code(config):
|
|
|
268
268
|
|
|
269
269
|
# disable library compatibility checks
|
|
270
270
|
cg.add_platformio_option("lib_ldf_mode", "off")
|
|
271
|
+
cg.add_platformio_option("lib_compat_mode", "soft")
|
|
271
272
|
# include <Arduino.h> in every file
|
|
272
273
|
cg.add_platformio_option("build_src_flags", "-include Arduino.h")
|
|
273
274
|
# dummy version code
|
|
@@ -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(
|
|
160
|
-
event.
|
|
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")
|
|
@@ -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]
|
|
171
|
-
raise cv.Invalid("Minimum
|
|
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.
|
|
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
|
|
168
|
-
raise cv.Invalid("Minimum
|
|
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
|
-
|
|
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 "(
|
|
131
|
+
return "(unspecified)";
|
|
132
132
|
char *p = buffer;
|
|
133
|
-
for (
|
|
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
|
-
|
|
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::
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
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
|
-
|
|
33
|
-
if (!
|
|
34
|
-
ESP_LOGE(TAG, "
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
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::
|
|
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->
|
|
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
|
-
|
|
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 =
|
|
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_.
|
|
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_.
|
|
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
|
|
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>
|
|
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>
|
|
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,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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
125
|
+
#ifdef USE_API_SERVICES
|
|
126
126
|
SERVICE,
|
|
127
127
|
#endif
|
|
128
128
|
#ifdef USE_CAMERA
|
esphome/core/defines.h
CHANGED
esphome/core/entity_helpers.py
CHANGED
|
@@ -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/scheduler.cpp
CHANGED
|
@@ -66,10 +66,8 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type
|
|
|
66
66
|
|
|
67
67
|
if (delay == SCHEDULER_DONT_RUN) {
|
|
68
68
|
// Still need to cancel existing timer if name is not empty
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
this->cancel_item_locked_(component, name_cstr, type);
|
|
72
|
-
}
|
|
69
|
+
LockGuard guard{this->lock_};
|
|
70
|
+
this->cancel_item_locked_(component, name_cstr, type);
|
|
73
71
|
return;
|
|
74
72
|
}
|
|
75
73
|
|
|
@@ -125,10 +123,8 @@ void HOT Scheduler::set_timer_common_(Component *component, SchedulerItem::Type
|
|
|
125
123
|
|
|
126
124
|
LockGuard guard{this->lock_};
|
|
127
125
|
// If name is provided, do atomic cancel-and-add
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
this->cancel_item_locked_(component, name_cstr, type);
|
|
131
|
-
}
|
|
126
|
+
// Cancel existing items
|
|
127
|
+
this->cancel_item_locked_(component, name_cstr, type);
|
|
132
128
|
// Add new item directly to to_add_
|
|
133
129
|
// since we have the lock held
|
|
134
130
|
this->to_add_.push_back(std::move(item));
|
|
@@ -442,10 +438,6 @@ bool HOT Scheduler::cancel_item_(Component *component, bool is_static_string, co
|
|
|
442
438
|
// Get the name as const char*
|
|
443
439
|
const char *name_cstr = this->get_name_cstr_(is_static_string, name_ptr);
|
|
444
440
|
|
|
445
|
-
// Handle null or empty names
|
|
446
|
-
if (!this->is_name_valid_(name_cstr))
|
|
447
|
-
return false;
|
|
448
|
-
|
|
449
441
|
// obtain lock because this function iterates and can be called from non-loop task context
|
|
450
442
|
LockGuard guard{this->lock_};
|
|
451
443
|
return this->cancel_item_locked_(component, name_cstr, type);
|
|
@@ -453,6 +445,11 @@ bool HOT Scheduler::cancel_item_(Component *component, bool is_static_string, co
|
|
|
453
445
|
|
|
454
446
|
// Helper to cancel items by name - must be called with lock held
|
|
455
447
|
bool HOT Scheduler::cancel_item_locked_(Component *component, const char *name_cstr, SchedulerItem::Type type) {
|
|
448
|
+
// Early return if name is invalid - no items to cancel
|
|
449
|
+
if (name_cstr == nullptr || name_cstr[0] == '\0') {
|
|
450
|
+
return false;
|
|
451
|
+
}
|
|
452
|
+
|
|
456
453
|
size_t total_cancelled = 0;
|
|
457
454
|
|
|
458
455
|
// Check all containers for matching items
|
esphome/core/scheduler.h
CHANGED
|
@@ -150,9 +150,6 @@ class Scheduler {
|
|
|
150
150
|
return is_static_string ? static_cast<const char *>(name_ptr) : static_cast<const std::string *>(name_ptr)->c_str();
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
// Helper to check if a name is valid (not null and not empty)
|
|
154
|
-
inline bool is_name_valid_(const char *name) { return name != nullptr && name[0] != '\0'; }
|
|
155
|
-
|
|
156
153
|
// Common implementation for cancel operations
|
|
157
154
|
bool cancel_item_(Component *component, bool is_static_string, const void *name_ptr, SchedulerItem::Type type);
|
|
158
155
|
|
esphome/wizard.py
CHANGED
|
@@ -411,7 +411,7 @@ def wizard(path):
|
|
|
411
411
|
safe_print("Options:")
|
|
412
412
|
for board_id, board_data in boards_list:
|
|
413
413
|
safe_print(f" - {board_id} - {board_data['name']}")
|
|
414
|
-
boards.append(board_id)
|
|
414
|
+
boards.append(board_id.lower())
|
|
415
415
|
|
|
416
416
|
while True:
|
|
417
417
|
board = safe_input(color(AnsiFore.BOLD_WHITE, "(board): "))
|