1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-05 19:33:47 +01:00

Merge branch 'message_creator_ram' into integration

This commit is contained in:
J. Nick Koston
2025-06-25 17:22:41 +02:00
188 changed files with 4368 additions and 1096 deletions

View File

@@ -12,7 +12,7 @@ repos:
# Run the formatter. # Run the formatter.
- id: ruff-format - id: ruff-format
- repo: https://github.com/PyCQA/flake8 - repo: https://github.com/PyCQA/flake8
rev: 7.2.0 rev: 7.3.0
hooks: hooks:
- id: flake8 - id: flake8
additional_dependencies: additional_dependencies:

View File

@@ -323,6 +323,7 @@ esphome/components/one_wire/* @ssieb
esphome/components/online_image/* @clydebarrow @guillempages esphome/components/online_image/* @clydebarrow @guillempages
esphome/components/opentherm/* @olegtarasov esphome/components/opentherm/* @olegtarasov
esphome/components/openthread/* @mrene esphome/components/openthread/* @mrene
esphome/components/opt3001/* @ccutrer
esphome/components/ota/* @esphome/core esphome/components/ota/* @esphome/core
esphome/components/output/* @esphome/core esphome/components/output/* @esphome/core
esphome/components/packet_transport/* @clydebarrow esphome/components/packet_transport/* @clydebarrow

View File

@@ -14,8 +14,8 @@ from esphome.const import (
CONF_WEB_SERVER, CONF_WEB_SERVER,
) )
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
from esphome.cpp_generator import MockObjClass from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@grahambrown11", "@hwstar"] CODEOWNERS = ["@grahambrown11", "@hwstar"]
IS_PLATFORM_COMPONENT = True IS_PLATFORM_COMPONENT = True
@@ -149,6 +149,9 @@ _ALARM_CONTROL_PANEL_SCHEMA = (
) )
_ALARM_CONTROL_PANEL_SCHEMA.add_extra(entity_duplicate_validator("alarm_control_panel"))
def alarm_control_panel_schema( def alarm_control_panel_schema(
class_: MockObjClass, class_: MockObjClass,
*, *,
@@ -190,7 +193,7 @@ ALARM_CONTROL_PANEL_CONDITION_SCHEMA = maybe_simple_id(
async def setup_alarm_control_panel_core_(var, config): async def setup_alarm_control_panel_core_(var, config):
await setup_entity(var, config) await setup_entity(var, config, "alarm_control_panel")
for conf in config.get(CONF_ON_STATE, []): for conf in config.get(CONF_ON_STATE, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf) await automation.build_automation(trigger, [], conf)

View File

@@ -188,6 +188,17 @@ message DeviceInfoRequest {
// Empty // Empty
} }
message AreaInfo {
uint32 area_id = 1;
string name = 2;
}
message DeviceInfo {
uint32 device_id = 1;
string name = 2;
uint32 area_id = 3;
}
message DeviceInfoResponse { message DeviceInfoResponse {
option (id) = 10; option (id) = 10;
option (source) = SOURCE_SERVER; option (source) = SOURCE_SERVER;
@@ -236,6 +247,12 @@ message DeviceInfoResponse {
// Supports receiving and saving api encryption key // Supports receiving and saving api encryption key
bool api_encryption_supported = 19; bool api_encryption_supported = 19;
repeated DeviceInfo devices = 20;
repeated AreaInfo areas = 21;
// Top-level area info to phase out suggested_area
AreaInfo area = 22;
} }
message ListEntitiesRequest { message ListEntitiesRequest {
@@ -280,6 +297,7 @@ message ListEntitiesBinarySensorResponse {
bool disabled_by_default = 7; bool disabled_by_default = 7;
string icon = 8; string icon = 8;
EntityCategory entity_category = 9; EntityCategory entity_category = 9;
uint32 device_id = 10;
} }
message BinarySensorStateResponse { message BinarySensorStateResponse {
option (id) = 21; option (id) = 21;
@@ -315,6 +333,7 @@ message ListEntitiesCoverResponse {
string icon = 10; string icon = 10;
EntityCategory entity_category = 11; EntityCategory entity_category = 11;
bool supports_stop = 12; bool supports_stop = 12;
uint32 device_id = 13;
} }
enum LegacyCoverState { enum LegacyCoverState {
@@ -388,6 +407,7 @@ message ListEntitiesFanResponse {
string icon = 10; string icon = 10;
EntityCategory entity_category = 11; EntityCategory entity_category = 11;
repeated string supported_preset_modes = 12; repeated string supported_preset_modes = 12;
uint32 device_id = 13;
} }
enum FanSpeed { enum FanSpeed {
FAN_SPEED_LOW = 0; FAN_SPEED_LOW = 0;
@@ -471,6 +491,7 @@ message ListEntitiesLightResponse {
bool disabled_by_default = 13; bool disabled_by_default = 13;
string icon = 14; string icon = 14;
EntityCategory entity_category = 15; EntityCategory entity_category = 15;
uint32 device_id = 16;
} }
message LightStateResponse { message LightStateResponse {
option (id) = 24; option (id) = 24;
@@ -563,6 +584,7 @@ message ListEntitiesSensorResponse {
SensorLastResetType legacy_last_reset_type = 11; SensorLastResetType legacy_last_reset_type = 11;
bool disabled_by_default = 12; bool disabled_by_default = 12;
EntityCategory entity_category = 13; EntityCategory entity_category = 13;
uint32 device_id = 14;
} }
message SensorStateResponse { message SensorStateResponse {
option (id) = 25; option (id) = 25;
@@ -595,6 +617,7 @@ message ListEntitiesSwitchResponse {
bool disabled_by_default = 7; bool disabled_by_default = 7;
EntityCategory entity_category = 8; EntityCategory entity_category = 8;
string device_class = 9; string device_class = 9;
uint32 device_id = 10;
} }
message SwitchStateResponse { message SwitchStateResponse {
option (id) = 26; option (id) = 26;
@@ -632,6 +655,7 @@ message ListEntitiesTextSensorResponse {
bool disabled_by_default = 6; bool disabled_by_default = 6;
EntityCategory entity_category = 7; EntityCategory entity_category = 7;
string device_class = 8; string device_class = 8;
uint32 device_id = 9;
} }
message TextSensorStateResponse { message TextSensorStateResponse {
option (id) = 27; option (id) = 27;
@@ -814,6 +838,7 @@ message ListEntitiesCameraResponse {
bool disabled_by_default = 5; bool disabled_by_default = 5;
string icon = 6; string icon = 6;
EntityCategory entity_category = 7; EntityCategory entity_category = 7;
uint32 device_id = 8;
} }
message CameraImageResponse { message CameraImageResponse {
@@ -916,6 +941,7 @@ message ListEntitiesClimateResponse {
bool supports_target_humidity = 23; bool supports_target_humidity = 23;
float visual_min_humidity = 24; float visual_min_humidity = 24;
float visual_max_humidity = 25; float visual_max_humidity = 25;
uint32 device_id = 26;
} }
message ClimateStateResponse { message ClimateStateResponse {
option (id) = 47; option (id) = 47;
@@ -999,6 +1025,7 @@ message ListEntitiesNumberResponse {
string unit_of_measurement = 11; string unit_of_measurement = 11;
NumberMode mode = 12; NumberMode mode = 12;
string device_class = 13; string device_class = 13;
uint32 device_id = 14;
} }
message NumberStateResponse { message NumberStateResponse {
option (id) = 50; option (id) = 50;
@@ -1039,6 +1066,7 @@ message ListEntitiesSelectResponse {
repeated string options = 6; repeated string options = 6;
bool disabled_by_default = 7; bool disabled_by_default = 7;
EntityCategory entity_category = 8; EntityCategory entity_category = 8;
uint32 device_id = 9;
} }
message SelectStateResponse { message SelectStateResponse {
option (id) = 53; option (id) = 53;
@@ -1081,6 +1109,7 @@ message ListEntitiesSirenResponse {
bool supports_duration = 8; bool supports_duration = 8;
bool supports_volume = 9; bool supports_volume = 9;
EntityCategory entity_category = 10; EntityCategory entity_category = 10;
uint32 device_id = 11;
} }
message SirenStateResponse { message SirenStateResponse {
option (id) = 56; option (id) = 56;
@@ -1144,6 +1173,7 @@ message ListEntitiesLockResponse {
// Not yet implemented: // Not yet implemented:
string code_format = 11; string code_format = 11;
uint32 device_id = 12;
} }
message LockStateResponse { message LockStateResponse {
option (id) = 59; option (id) = 59;
@@ -1183,6 +1213,7 @@ message ListEntitiesButtonResponse {
bool disabled_by_default = 6; bool disabled_by_default = 6;
EntityCategory entity_category = 7; EntityCategory entity_category = 7;
string device_class = 8; string device_class = 8;
uint32 device_id = 9;
} }
message ButtonCommandRequest { message ButtonCommandRequest {
option (id) = 62; option (id) = 62;
@@ -1238,6 +1269,8 @@ message ListEntitiesMediaPlayerResponse {
bool supports_pause = 8; bool supports_pause = 8;
repeated MediaPlayerSupportedFormat supported_formats = 9; repeated MediaPlayerSupportedFormat supported_formats = 9;
uint32 device_id = 10;
} }
message MediaPlayerStateResponse { message MediaPlayerStateResponse {
option (id) = 64; option (id) = 64;
@@ -1778,6 +1811,7 @@ message ListEntitiesAlarmControlPanelResponse {
uint32 supported_features = 8; uint32 supported_features = 8;
bool requires_code = 9; bool requires_code = 9;
bool requires_code_to_arm = 10; bool requires_code_to_arm = 10;
uint32 device_id = 11;
} }
message AlarmControlPanelStateResponse { message AlarmControlPanelStateResponse {
@@ -1823,6 +1857,7 @@ message ListEntitiesTextResponse {
uint32 max_length = 9; uint32 max_length = 9;
string pattern = 10; string pattern = 10;
TextMode mode = 11; TextMode mode = 11;
uint32 device_id = 12;
} }
message TextStateResponse { message TextStateResponse {
option (id) = 98; option (id) = 98;
@@ -1863,6 +1898,7 @@ message ListEntitiesDateResponse {
string icon = 5; string icon = 5;
bool disabled_by_default = 6; bool disabled_by_default = 6;
EntityCategory entity_category = 7; EntityCategory entity_category = 7;
uint32 device_id = 8;
} }
message DateStateResponse { message DateStateResponse {
option (id) = 101; option (id) = 101;
@@ -1906,6 +1942,7 @@ message ListEntitiesTimeResponse {
string icon = 5; string icon = 5;
bool disabled_by_default = 6; bool disabled_by_default = 6;
EntityCategory entity_category = 7; EntityCategory entity_category = 7;
uint32 device_id = 8;
} }
message TimeStateResponse { message TimeStateResponse {
option (id) = 104; option (id) = 104;
@@ -1952,6 +1989,7 @@ message ListEntitiesEventResponse {
string device_class = 8; string device_class = 8;
repeated string event_types = 9; repeated string event_types = 9;
uint32 device_id = 10;
} }
message EventResponse { message EventResponse {
option (id) = 108; option (id) = 108;
@@ -1983,6 +2021,7 @@ message ListEntitiesValveResponse {
bool assumed_state = 9; bool assumed_state = 9;
bool supports_position = 10; bool supports_position = 10;
bool supports_stop = 11; bool supports_stop = 11;
uint32 device_id = 12;
} }
enum ValveOperation { enum ValveOperation {
@@ -2029,6 +2068,7 @@ message ListEntitiesDateTimeResponse {
string icon = 5; string icon = 5;
bool disabled_by_default = 6; bool disabled_by_default = 6;
EntityCategory entity_category = 7; EntityCategory entity_category = 7;
uint32 device_id = 8;
} }
message DateTimeStateResponse { message DateTimeStateResponse {
option (id) = 113; option (id) = 113;
@@ -2069,6 +2109,7 @@ message ListEntitiesUpdateResponse {
bool disabled_by_default = 6; bool disabled_by_default = 6;
EntityCategory entity_category = 7; EntityCategory entity_category = 7;
string device_class = 8; string device_class = 8;
uint32 device_id = 9;
} }
message UpdateStateResponse { message UpdateStateResponse {
option (id) = 117; option (id) = 117;

View File

@@ -1440,7 +1440,7 @@ void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRe
#ifdef USE_EVENT #ifdef USE_EVENT
void APIConnection::send_event(event::Event *event, const std::string &event_type) { void APIConnection::send_event(event::Event *event, const std::string &event_type) {
this->schedule_message_(event, MessageCreator(event_type, EventResponse::MESSAGE_TYPE), EventResponse::MESSAGE_TYPE); this->schedule_message_(event, MessageCreator(event_type), EventResponse::MESSAGE_TYPE);
} }
void APIConnection::send_event_info(event::Event *event) { void APIConnection::send_event_info(event::Event *event) {
this->schedule_message_(event, &APIConnection::try_send_event_info, ListEntitiesEventResponse::MESSAGE_TYPE); this->schedule_message_(event, &APIConnection::try_send_event_info, ListEntitiesEventResponse::MESSAGE_TYPE);
@@ -1629,6 +1629,23 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
#endif #endif
#ifdef USE_API_NOISE #ifdef USE_API_NOISE
resp.api_encryption_supported = true; resp.api_encryption_supported = true;
#endif
#ifdef USE_DEVICES
for (auto const &device : App.get_devices()) {
DeviceInfo device_info;
device_info.device_id = device->get_device_id();
device_info.name = device->get_name();
device_info.area_id = device->get_area_id();
resp.devices.push_back(device_info);
}
#endif
#ifdef USE_AREAS
for (auto const &area : App.get_areas()) {
AreaInfo area_info;
area_info.area_id = area->get_area_id();
area_info.name = area->get_name();
resp.areas.push_back(area_info);
}
#endif #endif
return resp; return resp;
} }
@@ -1778,7 +1795,8 @@ void APIConnection::process_batch_() {
const auto &item = this->deferred_batch_.items[0]; const auto &item = this->deferred_batch_.items[0];
// Let the creator calculate size and encode if it fits // Let the creator calculate size and encode if it fits
uint16_t payload_size = item.creator(item.entity, this, std::numeric_limits<uint16_t>::max(), true); uint16_t payload_size =
item.creator(item.entity, this, std::numeric_limits<uint16_t>::max(), true, item.message_type);
if (payload_size > 0 && if (payload_size > 0 &&
this->send_buffer(ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, item.message_type)) { this->send_buffer(ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, item.message_type)) {
@@ -1828,7 +1846,7 @@ void APIConnection::process_batch_() {
for (const auto &item : this->deferred_batch_.items) { for (const auto &item : this->deferred_batch_.items) {
// Try to encode message // Try to encode message
// The creator will calculate overhead to determine if the message fits // The creator will calculate overhead to determine if the message fits
uint16_t payload_size = item.creator(item.entity, this, remaining_size, false); uint16_t payload_size = item.creator(item.entity, this, remaining_size, false, item.message_type);
if (payload_size == 0) { if (payload_size == 0) {
// Message won't fit, stop processing // Message won't fit, stop processing
@@ -1891,21 +1909,23 @@ void APIConnection::process_batch_() {
} }
uint16_t APIConnection::MessageCreator::operator()(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, uint16_t APIConnection::MessageCreator::operator()(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
bool is_single) const { bool is_single, uint16_t message_type) const {
switch (message_type_) { if (is_string()) {
case 0: // Function pointer // Handle string-based messages
return data_.ptr(entity, conn, remaining_size, is_single); switch (message_type) {
#ifdef USE_EVENT #ifdef USE_EVENT
case EventResponse::MESSAGE_TYPE: { case EventResponse::MESSAGE_TYPE: {
auto *e = static_cast<event::Event *>(entity); auto *e = static_cast<event::Event *>(entity);
return APIConnection::try_send_event_response(e, *data_.string_ptr, conn, remaining_size, is_single); return APIConnection::try_send_event_response(e, *get_string_ptr(), conn, remaining_size, is_single);
} }
#endif #endif
default:
default: // Should not happen, return 0 to indicate no message
// Should not happen, return 0 to indicate no message return 0;
return 0; }
} else {
// Function pointer case
return data_.ptr(entity, conn, remaining_size, is_single);
} }
} }

View File

@@ -301,6 +301,9 @@ class APIConnection : public APIServerConnection {
response.icon = entity->get_icon(); response.icon = entity->get_icon();
response.disabled_by_default = entity->is_disabled_by_default(); response.disabled_by_default = entity->is_disabled_by_default();
response.entity_category = static_cast<enums::EntityCategory>(entity->get_entity_category()); response.entity_category = static_cast<enums::EntityCategory>(entity->get_entity_category());
#ifdef USE_DEVICES
response.device_id = entity->get_device_id();
#endif
} }
// Helper function to fill common entity state fields // Helper function to fill common entity state fields
@@ -480,55 +483,57 @@ class APIConnection : public APIServerConnection {
// Function pointer type for message encoding // Function pointer type for message encoding
using MessageCreatorPtr = uint16_t (*)(EntityBase *, APIConnection *, uint32_t remaining_size, bool is_single); using MessageCreatorPtr = uint16_t (*)(EntityBase *, APIConnection *, uint32_t remaining_size, bool is_single);
// Optimized MessageCreator class using union dispatch // Optimized MessageCreator class using tagged pointer
class MessageCreator { class MessageCreator {
// Ensure pointer alignment allows LSB tagging
static_assert(alignof(std::string *) > 1, "String pointer alignment must be > 1 for LSB tagging");
public: public:
// Constructor for function pointer (message_type = 0) // Constructor for function pointer
MessageCreator(MessageCreatorPtr ptr) : message_type_(0) { data_.ptr = ptr; } MessageCreator(MessageCreatorPtr ptr) {
// Function pointers are always aligned, so LSB is 0
data_.ptr = ptr;
}
// Constructor for string state capture // Constructor for string state capture
MessageCreator(const std::string &value, uint16_t msg_type) : message_type_(msg_type) { explicit MessageCreator(const std::string &str_value) {
data_.string_ptr = new std::string(value); // Allocate string and tag the pointer
auto *str = new std::string(str_value);
// Set LSB to 1 to indicate string pointer
data_.tagged = reinterpret_cast<uintptr_t>(str) | 1;
} }
// Destructor // Destructor
~MessageCreator() { ~MessageCreator() {
// Clean up string data for string-based message types if (has_tagged_string_ptr()) {
if (uses_string_data_()) { delete get_string_ptr();
delete data_.string_ptr;
} }
} }
// Copy constructor // Copy constructor
MessageCreator(const MessageCreator &other) : message_type_(other.message_type_) { MessageCreator(const MessageCreator &other) {
if (message_type_ == 0) { if (other.has_tagged_string_ptr()) {
data_.ptr = other.data_.ptr; auto *str = new std::string(*other.get_string_ptr());
} else if (uses_string_data_()) { data_.tagged = reinterpret_cast<uintptr_t>(str) | 1;
data_.string_ptr = new std::string(*other.data_.string_ptr);
} else { } else {
data_ = other.data_; // For POD types data_ = other.data_;
} }
} }
// Move constructor // Move constructor
MessageCreator(MessageCreator &&other) noexcept : data_(other.data_), message_type_(other.message_type_) { MessageCreator(MessageCreator &&other) noexcept : data_(other.data_) { other.data_.ptr = nullptr; }
other.message_type_ = 0; // Reset other to function pointer type
other.data_.ptr = nullptr;
}
// Assignment operators (needed for batch deduplication) // Assignment operators (needed for batch deduplication)
MessageCreator &operator=(const MessageCreator &other) { MessageCreator &operator=(const MessageCreator &other) {
if (this != &other) { if (this != &other) {
// Clean up current string data if needed // Clean up current string data if needed
if (uses_string_data_()) { if (has_tagged_string_ptr()) {
delete data_.string_ptr; delete get_string_ptr();
} }
// Copy new data // Copy new data
message_type_ = other.message_type_; if (other.has_tagged_string_ptr()) {
if (other.message_type_ == 0) { auto *str = new std::string(*other.get_string_ptr());
data_.ptr = other.data_.ptr; data_.tagged = reinterpret_cast<uintptr_t>(str) | 1;
} else if (other.uses_string_data_()) {
data_.string_ptr = new std::string(*other.data_.string_ptr);
} else { } else {
data_ = other.data_; data_ = other.data_;
} }
@@ -539,30 +544,32 @@ class APIConnection : public APIServerConnection {
MessageCreator &operator=(MessageCreator &&other) noexcept { MessageCreator &operator=(MessageCreator &&other) noexcept {
if (this != &other) { if (this != &other) {
// Clean up current string data if needed // Clean up current string data if needed
if (uses_string_data_()) { if (has_tagged_string_ptr()) {
delete data_.string_ptr; delete get_string_ptr();
} }
// Move data // Move data
message_type_ = other.message_type_;
data_ = other.data_; data_ = other.data_;
// Reset other to safe state // Reset other to safe state
other.message_type_ = 0;
other.data_.ptr = nullptr; other.data_.ptr = nullptr;
} }
return *this; return *this;
} }
// Call operator // Call operator - now accepts message_type as parameter
uint16_t operator()(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single) const; uint16_t operator()(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single,
uint16_t message_type) const;
private: private:
// Helper to check if this message type uses heap-allocated strings // Check if this contains a string pointer
bool uses_string_data_() const { return message_type_ == EventResponse::MESSAGE_TYPE; } bool has_tagged_string_ptr() const { return (data_.tagged & 1) != 0; }
union CreatorData {
MessageCreatorPtr ptr; // 8 bytes // Get the actual string pointer (clears the tag bit)
std::string *string_ptr; // 8 bytes std::string *get_string_ptr() const { return reinterpret_cast<std::string *>(data_.tagged & ~uintptr_t(1)); }
} data_; // 8 bytes
uint16_t message_type_; // 2 bytes (0 = function ptr, >0 = state capture) union {
MessageCreatorPtr ptr;
uintptr_t tagged;
} data_; // 4 bytes on 32-bit
}; };
// Generic batching mechanism for both state updates and entity info // Generic batching mechanism for both state updates and entity info

View File

@@ -812,6 +812,103 @@ void PingResponse::dump_to(std::string &out) const { out.append("PingResponse {}
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void DeviceInfoRequest::dump_to(std::string &out) const { out.append("DeviceInfoRequest {}"); } void DeviceInfoRequest::dump_to(std::string &out) const { out.append("DeviceInfoRequest {}"); }
#endif #endif
bool AreaInfo::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1: {
this->area_id = value.as_uint32();
return true;
}
default:
return false;
}
}
bool AreaInfo::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 2: {
this->name = value.as_string();
return true;
}
default:
return false;
}
}
void AreaInfo::encode(ProtoWriteBuffer buffer) const {
buffer.encode_uint32(1, this->area_id);
buffer.encode_string(2, this->name);
}
void AreaInfo::calculate_size(uint32_t &total_size) const {
ProtoSize::add_uint32_field(total_size, 1, this->area_id, false);
ProtoSize::add_string_field(total_size, 1, this->name, false);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void AreaInfo::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("AreaInfo {\n");
out.append(" area_id: ");
sprintf(buffer, "%" PRIu32, this->area_id);
out.append(buffer);
out.append("\n");
out.append(" name: ");
out.append("'").append(this->name).append("'");
out.append("\n");
out.append("}");
}
#endif
bool DeviceInfo::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1: {
this->device_id = value.as_uint32();
return true;
}
case 3: {
this->area_id = value.as_uint32();
return true;
}
default:
return false;
}
}
bool DeviceInfo::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 2: {
this->name = value.as_string();
return true;
}
default:
return false;
}
}
void DeviceInfo::encode(ProtoWriteBuffer buffer) const {
buffer.encode_uint32(1, this->device_id);
buffer.encode_string(2, this->name);
buffer.encode_uint32(3, this->area_id);
}
void DeviceInfo::calculate_size(uint32_t &total_size) const {
ProtoSize::add_uint32_field(total_size, 1, this->device_id, false);
ProtoSize::add_string_field(total_size, 1, this->name, false);
ProtoSize::add_uint32_field(total_size, 1, this->area_id, false);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void DeviceInfo::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("DeviceInfo {\n");
out.append(" device_id: ");
sprintf(buffer, "%" PRIu32, this->device_id);
out.append(buffer);
out.append("\n");
out.append(" name: ");
out.append("'").append(this->name).append("'");
out.append("\n");
out.append(" area_id: ");
sprintf(buffer, "%" PRIu32, this->area_id);
out.append(buffer);
out.append("\n");
out.append("}");
}
#endif
bool DeviceInfoResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { bool DeviceInfoResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) { switch (field_id) {
case 1: { case 1: {
@@ -896,6 +993,18 @@ bool DeviceInfoResponse::decode_length(uint32_t field_id, ProtoLengthDelimited v
this->bluetooth_mac_address = value.as_string(); this->bluetooth_mac_address = value.as_string();
return true; return true;
} }
case 20: {
this->devices.push_back(value.as_message<DeviceInfo>());
return true;
}
case 21: {
this->areas.push_back(value.as_message<AreaInfo>());
return true;
}
case 22: {
this->area = value.as_message<AreaInfo>();
return true;
}
default: default:
return false; return false;
} }
@@ -920,6 +1029,13 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(16, this->suggested_area); buffer.encode_string(16, this->suggested_area);
buffer.encode_string(18, this->bluetooth_mac_address); buffer.encode_string(18, this->bluetooth_mac_address);
buffer.encode_bool(19, this->api_encryption_supported); buffer.encode_bool(19, this->api_encryption_supported);
for (auto &it : this->devices) {
buffer.encode_message<DeviceInfo>(20, it, true);
}
for (auto &it : this->areas) {
buffer.encode_message<AreaInfo>(21, it, true);
}
buffer.encode_message<AreaInfo>(22, this->area);
} }
void DeviceInfoResponse::calculate_size(uint32_t &total_size) const { void DeviceInfoResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_bool_field(total_size, 1, this->uses_password, false); ProtoSize::add_bool_field(total_size, 1, this->uses_password, false);
@@ -941,6 +1057,9 @@ void DeviceInfoResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 2, this->suggested_area, false); ProtoSize::add_string_field(total_size, 2, this->suggested_area, false);
ProtoSize::add_string_field(total_size, 2, this->bluetooth_mac_address, false); ProtoSize::add_string_field(total_size, 2, this->bluetooth_mac_address, false);
ProtoSize::add_bool_field(total_size, 2, this->api_encryption_supported, false); ProtoSize::add_bool_field(total_size, 2, this->api_encryption_supported, false);
ProtoSize::add_repeated_message(total_size, 2, this->devices);
ProtoSize::add_repeated_message(total_size, 2, this->areas);
ProtoSize::add_message_object(total_size, 2, this->area, false);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void DeviceInfoResponse::dump_to(std::string &out) const { void DeviceInfoResponse::dump_to(std::string &out) const {
@@ -1026,6 +1145,22 @@ void DeviceInfoResponse::dump_to(std::string &out) const {
out.append(" api_encryption_supported: "); out.append(" api_encryption_supported: ");
out.append(YESNO(this->api_encryption_supported)); out.append(YESNO(this->api_encryption_supported));
out.append("\n"); out.append("\n");
for (const auto &it : this->devices) {
out.append(" devices: ");
it.dump_to(out);
out.append("\n");
}
for (const auto &it : this->areas) {
out.append(" areas: ");
it.dump_to(out);
out.append("\n");
}
out.append(" area: ");
this->area.dump_to(out);
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@@ -1052,6 +1187,10 @@ bool ListEntitiesBinarySensorResponse::decode_varint(uint32_t field_id, ProtoVar
this->entity_category = value.as_enum<enums::EntityCategory>(); this->entity_category = value.as_enum<enums::EntityCategory>();
return true; return true;
} }
case 10: {
this->device_id = value.as_uint32();
return true;
}
default: default:
return false; return false;
} }
@@ -1102,6 +1241,7 @@ void ListEntitiesBinarySensorResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(7, this->disabled_by_default); buffer.encode_bool(7, this->disabled_by_default);
buffer.encode_string(8, this->icon); buffer.encode_string(8, this->icon);
buffer.encode_enum<enums::EntityCategory>(9, this->entity_category); buffer.encode_enum<enums::EntityCategory>(9, this->entity_category);
buffer.encode_uint32(10, this->device_id);
} }
void ListEntitiesBinarySensorResponse::calculate_size(uint32_t &total_size) const { void ListEntitiesBinarySensorResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, this->object_id, false); ProtoSize::add_string_field(total_size, 1, this->object_id, false);
@@ -1113,6 +1253,7 @@ void ListEntitiesBinarySensorResponse::calculate_size(uint32_t &total_size) cons
ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false);
ProtoSize::add_string_field(total_size, 1, this->icon, false); ProtoSize::add_string_field(total_size, 1, this->icon, false);
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category), false); ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category), false);
ProtoSize::add_uint32_field(total_size, 1, this->device_id, false);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesBinarySensorResponse::dump_to(std::string &out) const { void ListEntitiesBinarySensorResponse::dump_to(std::string &out) const {
@@ -1154,6 +1295,11 @@ void ListEntitiesBinarySensorResponse::dump_to(std::string &out) const {
out.append(" entity_category: "); out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category)); out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n"); out.append("\n");
out.append(" device_id: ");
sprintf(buffer, "%" PRIu32, this->device_id);
out.append(buffer);
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@@ -1236,6 +1382,10 @@ bool ListEntitiesCoverResponse::decode_varint(uint32_t field_id, ProtoVarInt val
this->supports_stop = value.as_bool(); this->supports_stop = value.as_bool();
return true; return true;
} }
case 13: {
this->device_id = value.as_uint32();
return true;
}
default: default:
return false; return false;
} }
@@ -1289,6 +1439,7 @@ void ListEntitiesCoverResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(10, this->icon); buffer.encode_string(10, this->icon);
buffer.encode_enum<enums::EntityCategory>(11, this->entity_category); buffer.encode_enum<enums::EntityCategory>(11, this->entity_category);
buffer.encode_bool(12, this->supports_stop); buffer.encode_bool(12, this->supports_stop);
buffer.encode_uint32(13, this->device_id);
} }
void ListEntitiesCoverResponse::calculate_size(uint32_t &total_size) const { void ListEntitiesCoverResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, this->object_id, false); ProtoSize::add_string_field(total_size, 1, this->object_id, false);
@@ -1303,6 +1454,7 @@ void ListEntitiesCoverResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, this->icon, false); ProtoSize::add_string_field(total_size, 1, this->icon, false);
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category), false); ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category), false);
ProtoSize::add_bool_field(total_size, 1, this->supports_stop, false); ProtoSize::add_bool_field(total_size, 1, this->supports_stop, false);
ProtoSize::add_uint32_field(total_size, 1, this->device_id, false);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesCoverResponse::dump_to(std::string &out) const { void ListEntitiesCoverResponse::dump_to(std::string &out) const {
@@ -1356,6 +1508,11 @@ void ListEntitiesCoverResponse::dump_to(std::string &out) const {
out.append(" supports_stop: "); out.append(" supports_stop: ");
out.append(YESNO(this->supports_stop)); out.append(YESNO(this->supports_stop));
out.append("\n"); out.append("\n");
out.append(" device_id: ");
sprintf(buffer, "%" PRIu32, this->device_id);
out.append(buffer);
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@@ -1565,6 +1722,10 @@ bool ListEntitiesFanResponse::decode_varint(uint32_t field_id, ProtoVarInt value
this->entity_category = value.as_enum<enums::EntityCategory>(); this->entity_category = value.as_enum<enums::EntityCategory>();
return true; return true;
} }
case 13: {
this->device_id = value.as_uint32();
return true;
}
default: default:
return false; return false;
} }
@@ -1620,6 +1781,7 @@ void ListEntitiesFanResponse::encode(ProtoWriteBuffer buffer) const {
for (auto &it : this->supported_preset_modes) { for (auto &it : this->supported_preset_modes) {
buffer.encode_string(12, it, true); buffer.encode_string(12, it, true);
} }
buffer.encode_uint32(13, this->device_id);
} }
void ListEntitiesFanResponse::calculate_size(uint32_t &total_size) const { void ListEntitiesFanResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, this->object_id, false); ProtoSize::add_string_field(total_size, 1, this->object_id, false);
@@ -1638,6 +1800,7 @@ void ListEntitiesFanResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, it, true); ProtoSize::add_string_field(total_size, 1, it, true);
} }
} }
ProtoSize::add_uint32_field(total_size, 1, this->device_id, false);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesFanResponse::dump_to(std::string &out) const { void ListEntitiesFanResponse::dump_to(std::string &out) const {
@@ -1694,6 +1857,11 @@ void ListEntitiesFanResponse::dump_to(std::string &out) const {
out.append("'").append(it).append("'"); out.append("'").append(it).append("'");
out.append("\n"); out.append("\n");
} }
out.append(" device_id: ");
sprintf(buffer, "%" PRIu32, this->device_id);
out.append(buffer);
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@@ -1987,6 +2155,10 @@ bool ListEntitiesLightResponse::decode_varint(uint32_t field_id, ProtoVarInt val
this->entity_category = value.as_enum<enums::EntityCategory>(); this->entity_category = value.as_enum<enums::EntityCategory>();
return true; return true;
} }
case 16: {
this->device_id = value.as_uint32();
return true;
}
default: default:
return false; return false;
} }
@@ -2055,6 +2227,7 @@ void ListEntitiesLightResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(13, this->disabled_by_default); buffer.encode_bool(13, this->disabled_by_default);
buffer.encode_string(14, this->icon); buffer.encode_string(14, this->icon);
buffer.encode_enum<enums::EntityCategory>(15, this->entity_category); buffer.encode_enum<enums::EntityCategory>(15, this->entity_category);
buffer.encode_uint32(16, this->device_id);
} }
void ListEntitiesLightResponse::calculate_size(uint32_t &total_size) const { void ListEntitiesLightResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, this->object_id, false); ProtoSize::add_string_field(total_size, 1, this->object_id, false);
@@ -2080,6 +2253,7 @@ void ListEntitiesLightResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false);
ProtoSize::add_string_field(total_size, 1, this->icon, false); ProtoSize::add_string_field(total_size, 1, this->icon, false);
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category), false); ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category), false);
ProtoSize::add_uint32_field(total_size, 2, this->device_id, false);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesLightResponse::dump_to(std::string &out) const { void ListEntitiesLightResponse::dump_to(std::string &out) const {
@@ -2151,6 +2325,11 @@ void ListEntitiesLightResponse::dump_to(std::string &out) const {
out.append(" entity_category: "); out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category)); out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n"); out.append("\n");
out.append(" device_id: ");
sprintf(buffer, "%" PRIu32, this->device_id);
out.append(buffer);
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@@ -2658,6 +2837,10 @@ bool ListEntitiesSensorResponse::decode_varint(uint32_t field_id, ProtoVarInt va
this->entity_category = value.as_enum<enums::EntityCategory>(); this->entity_category = value.as_enum<enums::EntityCategory>();
return true; return true;
} }
case 14: {
this->device_id = value.as_uint32();
return true;
}
default: default:
return false; return false;
} }
@@ -2716,6 +2899,7 @@ void ListEntitiesSensorResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_enum<enums::SensorLastResetType>(11, this->legacy_last_reset_type); buffer.encode_enum<enums::SensorLastResetType>(11, this->legacy_last_reset_type);
buffer.encode_bool(12, this->disabled_by_default); buffer.encode_bool(12, this->disabled_by_default);
buffer.encode_enum<enums::EntityCategory>(13, this->entity_category); buffer.encode_enum<enums::EntityCategory>(13, this->entity_category);
buffer.encode_uint32(14, this->device_id);
} }
void ListEntitiesSensorResponse::calculate_size(uint32_t &total_size) const { void ListEntitiesSensorResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, this->object_id, false); ProtoSize::add_string_field(total_size, 1, this->object_id, false);
@@ -2731,6 +2915,7 @@ void ListEntitiesSensorResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->legacy_last_reset_type), false); ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->legacy_last_reset_type), false);
ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false);
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category), false); ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category), false);
ProtoSize::add_uint32_field(total_size, 1, this->device_id, false);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesSensorResponse::dump_to(std::string &out) const { void ListEntitiesSensorResponse::dump_to(std::string &out) const {
@@ -2789,6 +2974,11 @@ void ListEntitiesSensorResponse::dump_to(std::string &out) const {
out.append(" entity_category: "); out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category)); out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n"); out.append("\n");
out.append(" device_id: ");
sprintf(buffer, "%" PRIu32, this->device_id);
out.append(buffer);
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@@ -2860,6 +3050,10 @@ bool ListEntitiesSwitchResponse::decode_varint(uint32_t field_id, ProtoVarInt va
this->entity_category = value.as_enum<enums::EntityCategory>(); this->entity_category = value.as_enum<enums::EntityCategory>();
return true; return true;
} }
case 10: {
this->device_id = value.as_uint32();
return true;
}
default: default:
return false; return false;
} }
@@ -2910,6 +3104,7 @@ void ListEntitiesSwitchResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(7, this->disabled_by_default); buffer.encode_bool(7, this->disabled_by_default);
buffer.encode_enum<enums::EntityCategory>(8, this->entity_category); buffer.encode_enum<enums::EntityCategory>(8, this->entity_category);
buffer.encode_string(9, this->device_class); buffer.encode_string(9, this->device_class);
buffer.encode_uint32(10, this->device_id);
} }
void ListEntitiesSwitchResponse::calculate_size(uint32_t &total_size) const { void ListEntitiesSwitchResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, this->object_id, false); ProtoSize::add_string_field(total_size, 1, this->object_id, false);
@@ -2921,6 +3116,7 @@ void ListEntitiesSwitchResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false);
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category), false); ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category), false);
ProtoSize::add_string_field(total_size, 1, this->device_class, false); ProtoSize::add_string_field(total_size, 1, this->device_class, false);
ProtoSize::add_uint32_field(total_size, 1, this->device_id, false);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesSwitchResponse::dump_to(std::string &out) const { void ListEntitiesSwitchResponse::dump_to(std::string &out) const {
@@ -2962,6 +3158,11 @@ void ListEntitiesSwitchResponse::dump_to(std::string &out) const {
out.append(" device_class: "); out.append(" device_class: ");
out.append("'").append(this->device_class).append("'"); out.append("'").append(this->device_class).append("'");
out.append("\n"); out.append("\n");
out.append(" device_id: ");
sprintf(buffer, "%" PRIu32, this->device_id);
out.append(buffer);
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@@ -3061,6 +3262,10 @@ bool ListEntitiesTextSensorResponse::decode_varint(uint32_t field_id, ProtoVarIn
this->entity_category = value.as_enum<enums::EntityCategory>(); this->entity_category = value.as_enum<enums::EntityCategory>();
return true; return true;
} }
case 9: {
this->device_id = value.as_uint32();
return true;
}
default: default:
return false; return false;
} }
@@ -3110,6 +3315,7 @@ void ListEntitiesTextSensorResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(6, this->disabled_by_default); buffer.encode_bool(6, this->disabled_by_default);
buffer.encode_enum<enums::EntityCategory>(7, this->entity_category); buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
buffer.encode_string(8, this->device_class); buffer.encode_string(8, this->device_class);
buffer.encode_uint32(9, this->device_id);
} }
void ListEntitiesTextSensorResponse::calculate_size(uint32_t &total_size) const { void ListEntitiesTextSensorResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, this->object_id, false); ProtoSize::add_string_field(total_size, 1, this->object_id, false);
@@ -3120,6 +3326,7 @@ void ListEntitiesTextSensorResponse::calculate_size(uint32_t &total_size) const
ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false);
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category), false); ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category), false);
ProtoSize::add_string_field(total_size, 1, this->device_class, false); ProtoSize::add_string_field(total_size, 1, this->device_class, false);
ProtoSize::add_uint32_field(total_size, 1, this->device_id, false);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesTextSensorResponse::dump_to(std::string &out) const { void ListEntitiesTextSensorResponse::dump_to(std::string &out) const {
@@ -3157,6 +3364,11 @@ void ListEntitiesTextSensorResponse::dump_to(std::string &out) const {
out.append(" device_class: "); out.append(" device_class: ");
out.append("'").append(this->device_class).append("'"); out.append("'").append(this->device_class).append("'");
out.append("\n"); out.append("\n");
out.append(" device_id: ");
sprintf(buffer, "%" PRIu32, this->device_id);
out.append(buffer);
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@@ -3922,6 +4134,10 @@ bool ListEntitiesCameraResponse::decode_varint(uint32_t field_id, ProtoVarInt va
this->entity_category = value.as_enum<enums::EntityCategory>(); this->entity_category = value.as_enum<enums::EntityCategory>();
return true; return true;
} }
case 8: {
this->device_id = value.as_uint32();
return true;
}
default: default:
return false; return false;
} }
@@ -3966,6 +4182,7 @@ void ListEntitiesCameraResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(5, this->disabled_by_default); buffer.encode_bool(5, this->disabled_by_default);
buffer.encode_string(6, this->icon); buffer.encode_string(6, this->icon);
buffer.encode_enum<enums::EntityCategory>(7, this->entity_category); buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
buffer.encode_uint32(8, this->device_id);
} }
void ListEntitiesCameraResponse::calculate_size(uint32_t &total_size) const { void ListEntitiesCameraResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, this->object_id, false); ProtoSize::add_string_field(total_size, 1, this->object_id, false);
@@ -3975,6 +4192,7 @@ void ListEntitiesCameraResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false);
ProtoSize::add_string_field(total_size, 1, this->icon, false); ProtoSize::add_string_field(total_size, 1, this->icon, false);
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category), false); ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category), false);
ProtoSize::add_uint32_field(total_size, 1, this->device_id, false);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesCameraResponse::dump_to(std::string &out) const { void ListEntitiesCameraResponse::dump_to(std::string &out) const {
@@ -4008,6 +4226,11 @@ void ListEntitiesCameraResponse::dump_to(std::string &out) const {
out.append(" entity_category: "); out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category)); out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n"); out.append("\n");
out.append(" device_id: ");
sprintf(buffer, "%" PRIu32, this->device_id);
out.append(buffer);
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@@ -4156,6 +4379,10 @@ bool ListEntitiesClimateResponse::decode_varint(uint32_t field_id, ProtoVarInt v
this->supports_target_humidity = value.as_bool(); this->supports_target_humidity = value.as_bool();
return true; return true;
} }
case 26: {
this->device_id = value.as_uint32();
return true;
}
default: default:
return false; return false;
} }
@@ -4262,6 +4489,7 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(23, this->supports_target_humidity); buffer.encode_bool(23, this->supports_target_humidity);
buffer.encode_float(24, this->visual_min_humidity); buffer.encode_float(24, this->visual_min_humidity);
buffer.encode_float(25, this->visual_max_humidity); buffer.encode_float(25, this->visual_max_humidity);
buffer.encode_uint32(26, this->device_id);
} }
void ListEntitiesClimateResponse::calculate_size(uint32_t &total_size) const { void ListEntitiesClimateResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, this->object_id, false); ProtoSize::add_string_field(total_size, 1, this->object_id, false);
@@ -4313,6 +4541,7 @@ void ListEntitiesClimateResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_bool_field(total_size, 2, this->supports_target_humidity, false); ProtoSize::add_bool_field(total_size, 2, this->supports_target_humidity, false);
ProtoSize::add_fixed_field<4>(total_size, 2, this->visual_min_humidity != 0.0f, false); ProtoSize::add_fixed_field<4>(total_size, 2, this->visual_min_humidity != 0.0f, false);
ProtoSize::add_fixed_field<4>(total_size, 2, this->visual_max_humidity != 0.0f, false); ProtoSize::add_fixed_field<4>(total_size, 2, this->visual_max_humidity != 0.0f, false);
ProtoSize::add_uint32_field(total_size, 2, this->device_id, false);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesClimateResponse::dump_to(std::string &out) const { void ListEntitiesClimateResponse::dump_to(std::string &out) const {
@@ -4436,6 +4665,11 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const {
sprintf(buffer, "%g", this->visual_max_humidity); sprintf(buffer, "%g", this->visual_max_humidity);
out.append(buffer); out.append(buffer);
out.append("\n"); out.append("\n");
out.append(" device_id: ");
sprintf(buffer, "%" PRIu32, this->device_id);
out.append(buffer);
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@@ -4901,6 +5135,10 @@ bool ListEntitiesNumberResponse::decode_varint(uint32_t field_id, ProtoVarInt va
this->mode = value.as_enum<enums::NumberMode>(); this->mode = value.as_enum<enums::NumberMode>();
return true; return true;
} }
case 14: {
this->device_id = value.as_uint32();
return true;
}
default: default:
return false; return false;
} }
@@ -4971,6 +5209,7 @@ void ListEntitiesNumberResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(11, this->unit_of_measurement); buffer.encode_string(11, this->unit_of_measurement);
buffer.encode_enum<enums::NumberMode>(12, this->mode); buffer.encode_enum<enums::NumberMode>(12, this->mode);
buffer.encode_string(13, this->device_class); buffer.encode_string(13, this->device_class);
buffer.encode_uint32(14, this->device_id);
} }
void ListEntitiesNumberResponse::calculate_size(uint32_t &total_size) const { void ListEntitiesNumberResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, this->object_id, false); ProtoSize::add_string_field(total_size, 1, this->object_id, false);
@@ -4986,6 +5225,7 @@ void ListEntitiesNumberResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, this->unit_of_measurement, false); ProtoSize::add_string_field(total_size, 1, this->unit_of_measurement, false);
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->mode), false); ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->mode), false);
ProtoSize::add_string_field(total_size, 1, this->device_class, false); ProtoSize::add_string_field(total_size, 1, this->device_class, false);
ProtoSize::add_uint32_field(total_size, 1, this->device_id, false);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesNumberResponse::dump_to(std::string &out) const { void ListEntitiesNumberResponse::dump_to(std::string &out) const {
@@ -5046,6 +5286,11 @@ void ListEntitiesNumberResponse::dump_to(std::string &out) const {
out.append(" device_class: "); out.append(" device_class: ");
out.append("'").append(this->device_class).append("'"); out.append("'").append(this->device_class).append("'");
out.append("\n"); out.append("\n");
out.append(" device_id: ");
sprintf(buffer, "%" PRIu32, this->device_id);
out.append(buffer);
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@@ -5151,6 +5396,10 @@ bool ListEntitiesSelectResponse::decode_varint(uint32_t field_id, ProtoVarInt va
this->entity_category = value.as_enum<enums::EntityCategory>(); this->entity_category = value.as_enum<enums::EntityCategory>();
return true; return true;
} }
case 9: {
this->device_id = value.as_uint32();
return true;
}
default: default:
return false; return false;
} }
@@ -5202,6 +5451,7 @@ void ListEntitiesSelectResponse::encode(ProtoWriteBuffer buffer) const {
} }
buffer.encode_bool(7, this->disabled_by_default); buffer.encode_bool(7, this->disabled_by_default);
buffer.encode_enum<enums::EntityCategory>(8, this->entity_category); buffer.encode_enum<enums::EntityCategory>(8, this->entity_category);
buffer.encode_uint32(9, this->device_id);
} }
void ListEntitiesSelectResponse::calculate_size(uint32_t &total_size) const { void ListEntitiesSelectResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, this->object_id, false); ProtoSize::add_string_field(total_size, 1, this->object_id, false);
@@ -5216,6 +5466,7 @@ void ListEntitiesSelectResponse::calculate_size(uint32_t &total_size) const {
} }
ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false);
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category), false); ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category), false);
ProtoSize::add_uint32_field(total_size, 1, this->device_id, false);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesSelectResponse::dump_to(std::string &out) const { void ListEntitiesSelectResponse::dump_to(std::string &out) const {
@@ -5255,6 +5506,11 @@ void ListEntitiesSelectResponse::dump_to(std::string &out) const {
out.append(" entity_category: "); out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category)); out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n"); out.append("\n");
out.append(" device_id: ");
sprintf(buffer, "%" PRIu32, this->device_id);
out.append(buffer);
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@@ -5378,6 +5634,10 @@ bool ListEntitiesSirenResponse::decode_varint(uint32_t field_id, ProtoVarInt val
this->entity_category = value.as_enum<enums::EntityCategory>(); this->entity_category = value.as_enum<enums::EntityCategory>();
return true; return true;
} }
case 11: {
this->device_id = value.as_uint32();
return true;
}
default: default:
return false; return false;
} }
@@ -5431,6 +5691,7 @@ void ListEntitiesSirenResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(8, this->supports_duration); buffer.encode_bool(8, this->supports_duration);
buffer.encode_bool(9, this->supports_volume); buffer.encode_bool(9, this->supports_volume);
buffer.encode_enum<enums::EntityCategory>(10, this->entity_category); buffer.encode_enum<enums::EntityCategory>(10, this->entity_category);
buffer.encode_uint32(11, this->device_id);
} }
void ListEntitiesSirenResponse::calculate_size(uint32_t &total_size) const { void ListEntitiesSirenResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, this->object_id, false); ProtoSize::add_string_field(total_size, 1, this->object_id, false);
@@ -5447,6 +5708,7 @@ void ListEntitiesSirenResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_bool_field(total_size, 1, this->supports_duration, false); ProtoSize::add_bool_field(total_size, 1, this->supports_duration, false);
ProtoSize::add_bool_field(total_size, 1, this->supports_volume, false); ProtoSize::add_bool_field(total_size, 1, this->supports_volume, false);
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category), false); ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category), false);
ProtoSize::add_uint32_field(total_size, 1, this->device_id, false);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesSirenResponse::dump_to(std::string &out) const { void ListEntitiesSirenResponse::dump_to(std::string &out) const {
@@ -5494,6 +5756,11 @@ void ListEntitiesSirenResponse::dump_to(std::string &out) const {
out.append(" entity_category: "); out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category)); out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n"); out.append("\n");
out.append(" device_id: ");
sprintf(buffer, "%" PRIu32, this->device_id);
out.append(buffer);
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@@ -5683,6 +5950,10 @@ bool ListEntitiesLockResponse::decode_varint(uint32_t field_id, ProtoVarInt valu
this->requires_code = value.as_bool(); this->requires_code = value.as_bool();
return true; return true;
} }
case 12: {
this->device_id = value.as_uint32();
return true;
}
default: default:
return false; return false;
} }
@@ -5735,6 +6006,7 @@ void ListEntitiesLockResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(9, this->supports_open); buffer.encode_bool(9, this->supports_open);
buffer.encode_bool(10, this->requires_code); buffer.encode_bool(10, this->requires_code);
buffer.encode_string(11, this->code_format); buffer.encode_string(11, this->code_format);
buffer.encode_uint32(12, this->device_id);
} }
void ListEntitiesLockResponse::calculate_size(uint32_t &total_size) const { void ListEntitiesLockResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, this->object_id, false); ProtoSize::add_string_field(total_size, 1, this->object_id, false);
@@ -5748,6 +6020,7 @@ void ListEntitiesLockResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_bool_field(total_size, 1, this->supports_open, false); ProtoSize::add_bool_field(total_size, 1, this->supports_open, false);
ProtoSize::add_bool_field(total_size, 1, this->requires_code, false); ProtoSize::add_bool_field(total_size, 1, this->requires_code, false);
ProtoSize::add_string_field(total_size, 1, this->code_format, false); ProtoSize::add_string_field(total_size, 1, this->code_format, false);
ProtoSize::add_uint32_field(total_size, 1, this->device_id, false);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesLockResponse::dump_to(std::string &out) const { void ListEntitiesLockResponse::dump_to(std::string &out) const {
@@ -5797,6 +6070,11 @@ void ListEntitiesLockResponse::dump_to(std::string &out) const {
out.append(" code_format: "); out.append(" code_format: ");
out.append("'").append(this->code_format).append("'"); out.append("'").append(this->code_format).append("'");
out.append("\n"); out.append("\n");
out.append(" device_id: ");
sprintf(buffer, "%" PRIu32, this->device_id);
out.append(buffer);
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@@ -5922,6 +6200,10 @@ bool ListEntitiesButtonResponse::decode_varint(uint32_t field_id, ProtoVarInt va
this->entity_category = value.as_enum<enums::EntityCategory>(); this->entity_category = value.as_enum<enums::EntityCategory>();
return true; return true;
} }
case 9: {
this->device_id = value.as_uint32();
return true;
}
default: default:
return false; return false;
} }
@@ -5971,6 +6253,7 @@ void ListEntitiesButtonResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(6, this->disabled_by_default); buffer.encode_bool(6, this->disabled_by_default);
buffer.encode_enum<enums::EntityCategory>(7, this->entity_category); buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
buffer.encode_string(8, this->device_class); buffer.encode_string(8, this->device_class);
buffer.encode_uint32(9, this->device_id);
} }
void ListEntitiesButtonResponse::calculate_size(uint32_t &total_size) const { void ListEntitiesButtonResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, this->object_id, false); ProtoSize::add_string_field(total_size, 1, this->object_id, false);
@@ -5981,6 +6264,7 @@ void ListEntitiesButtonResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false);
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category), false); ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category), false);
ProtoSize::add_string_field(total_size, 1, this->device_class, false); ProtoSize::add_string_field(total_size, 1, this->device_class, false);
ProtoSize::add_uint32_field(total_size, 1, this->device_id, false);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesButtonResponse::dump_to(std::string &out) const { void ListEntitiesButtonResponse::dump_to(std::string &out) const {
@@ -6018,6 +6302,11 @@ void ListEntitiesButtonResponse::dump_to(std::string &out) const {
out.append(" device_class: "); out.append(" device_class: ");
out.append("'").append(this->device_class).append("'"); out.append("'").append(this->device_class).append("'");
out.append("\n"); out.append("\n");
out.append(" device_id: ");
sprintf(buffer, "%" PRIu32, this->device_id);
out.append(buffer);
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@@ -6135,6 +6424,10 @@ bool ListEntitiesMediaPlayerResponse::decode_varint(uint32_t field_id, ProtoVarI
this->supports_pause = value.as_bool(); this->supports_pause = value.as_bool();
return true; return true;
} }
case 10: {
this->device_id = value.as_uint32();
return true;
}
default: default:
return false; return false;
} }
@@ -6187,6 +6480,7 @@ void ListEntitiesMediaPlayerResponse::encode(ProtoWriteBuffer buffer) const {
for (auto &it : this->supported_formats) { for (auto &it : this->supported_formats) {
buffer.encode_message<MediaPlayerSupportedFormat>(9, it, true); buffer.encode_message<MediaPlayerSupportedFormat>(9, it, true);
} }
buffer.encode_uint32(10, this->device_id);
} }
void ListEntitiesMediaPlayerResponse::calculate_size(uint32_t &total_size) const { void ListEntitiesMediaPlayerResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, this->object_id, false); ProtoSize::add_string_field(total_size, 1, this->object_id, false);
@@ -6198,6 +6492,7 @@ void ListEntitiesMediaPlayerResponse::calculate_size(uint32_t &total_size) const
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category), false); ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category), false);
ProtoSize::add_bool_field(total_size, 1, this->supports_pause, false); ProtoSize::add_bool_field(total_size, 1, this->supports_pause, false);
ProtoSize::add_repeated_message(total_size, 1, this->supported_formats); ProtoSize::add_repeated_message(total_size, 1, this->supported_formats);
ProtoSize::add_uint32_field(total_size, 1, this->device_id, false);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesMediaPlayerResponse::dump_to(std::string &out) const { void ListEntitiesMediaPlayerResponse::dump_to(std::string &out) const {
@@ -6241,6 +6536,11 @@ void ListEntitiesMediaPlayerResponse::dump_to(std::string &out) const {
it.dump_to(out); it.dump_to(out);
out.append("\n"); out.append("\n");
} }
out.append(" device_id: ");
sprintf(buffer, "%" PRIu32, this->device_id);
out.append(buffer);
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@@ -8551,6 +8851,10 @@ bool ListEntitiesAlarmControlPanelResponse::decode_varint(uint32_t field_id, Pro
this->requires_code_to_arm = value.as_bool(); this->requires_code_to_arm = value.as_bool();
return true; return true;
} }
case 11: {
this->device_id = value.as_uint32();
return true;
}
default: default:
return false; return false;
} }
@@ -8598,6 +8902,7 @@ void ListEntitiesAlarmControlPanelResponse::encode(ProtoWriteBuffer buffer) cons
buffer.encode_uint32(8, this->supported_features); buffer.encode_uint32(8, this->supported_features);
buffer.encode_bool(9, this->requires_code); buffer.encode_bool(9, this->requires_code);
buffer.encode_bool(10, this->requires_code_to_arm); buffer.encode_bool(10, this->requires_code_to_arm);
buffer.encode_uint32(11, this->device_id);
} }
void ListEntitiesAlarmControlPanelResponse::calculate_size(uint32_t &total_size) const { void ListEntitiesAlarmControlPanelResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, this->object_id, false); ProtoSize::add_string_field(total_size, 1, this->object_id, false);
@@ -8610,6 +8915,7 @@ void ListEntitiesAlarmControlPanelResponse::calculate_size(uint32_t &total_size)
ProtoSize::add_uint32_field(total_size, 1, this->supported_features, false); ProtoSize::add_uint32_field(total_size, 1, this->supported_features, false);
ProtoSize::add_bool_field(total_size, 1, this->requires_code, false); ProtoSize::add_bool_field(total_size, 1, this->requires_code, false);
ProtoSize::add_bool_field(total_size, 1, this->requires_code_to_arm, false); ProtoSize::add_bool_field(total_size, 1, this->requires_code_to_arm, false);
ProtoSize::add_uint32_field(total_size, 1, this->device_id, false);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesAlarmControlPanelResponse::dump_to(std::string &out) const { void ListEntitiesAlarmControlPanelResponse::dump_to(std::string &out) const {
@@ -8656,6 +8962,11 @@ void ListEntitiesAlarmControlPanelResponse::dump_to(std::string &out) const {
out.append(" requires_code_to_arm: "); out.append(" requires_code_to_arm: ");
out.append(YESNO(this->requires_code_to_arm)); out.append(YESNO(this->requires_code_to_arm));
out.append("\n"); out.append("\n");
out.append(" device_id: ");
sprintf(buffer, "%" PRIu32, this->device_id);
out.append(buffer);
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@@ -8783,6 +9094,10 @@ bool ListEntitiesTextResponse::decode_varint(uint32_t field_id, ProtoVarInt valu
this->mode = value.as_enum<enums::TextMode>(); this->mode = value.as_enum<enums::TextMode>();
return true; return true;
} }
case 12: {
this->device_id = value.as_uint32();
return true;
}
default: default:
return false; return false;
} }
@@ -8835,6 +9150,7 @@ void ListEntitiesTextResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_uint32(9, this->max_length); buffer.encode_uint32(9, this->max_length);
buffer.encode_string(10, this->pattern); buffer.encode_string(10, this->pattern);
buffer.encode_enum<enums::TextMode>(11, this->mode); buffer.encode_enum<enums::TextMode>(11, this->mode);
buffer.encode_uint32(12, this->device_id);
} }
void ListEntitiesTextResponse::calculate_size(uint32_t &total_size) const { void ListEntitiesTextResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, this->object_id, false); ProtoSize::add_string_field(total_size, 1, this->object_id, false);
@@ -8848,6 +9164,7 @@ void ListEntitiesTextResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_uint32_field(total_size, 1, this->max_length, false); ProtoSize::add_uint32_field(total_size, 1, this->max_length, false);
ProtoSize::add_string_field(total_size, 1, this->pattern, false); ProtoSize::add_string_field(total_size, 1, this->pattern, false);
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->mode), false); ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->mode), false);
ProtoSize::add_uint32_field(total_size, 1, this->device_id, false);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesTextResponse::dump_to(std::string &out) const { void ListEntitiesTextResponse::dump_to(std::string &out) const {
@@ -8899,6 +9216,11 @@ void ListEntitiesTextResponse::dump_to(std::string &out) const {
out.append(" mode: "); out.append(" mode: ");
out.append(proto_enum_to_string<enums::TextMode>(this->mode)); out.append(proto_enum_to_string<enums::TextMode>(this->mode));
out.append("\n"); out.append("\n");
out.append(" device_id: ");
sprintf(buffer, "%" PRIu32, this->device_id);
out.append(buffer);
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@@ -9014,6 +9336,10 @@ bool ListEntitiesDateResponse::decode_varint(uint32_t field_id, ProtoVarInt valu
this->entity_category = value.as_enum<enums::EntityCategory>(); this->entity_category = value.as_enum<enums::EntityCategory>();
return true; return true;
} }
case 8: {
this->device_id = value.as_uint32();
return true;
}
default: default:
return false; return false;
} }
@@ -9058,6 +9384,7 @@ void ListEntitiesDateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(5, this->icon); buffer.encode_string(5, this->icon);
buffer.encode_bool(6, this->disabled_by_default); buffer.encode_bool(6, this->disabled_by_default);
buffer.encode_enum<enums::EntityCategory>(7, this->entity_category); buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
buffer.encode_uint32(8, this->device_id);
} }
void ListEntitiesDateResponse::calculate_size(uint32_t &total_size) const { void ListEntitiesDateResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, this->object_id, false); ProtoSize::add_string_field(total_size, 1, this->object_id, false);
@@ -9067,6 +9394,7 @@ void ListEntitiesDateResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, this->icon, false); ProtoSize::add_string_field(total_size, 1, this->icon, false);
ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false);
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category), false); ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category), false);
ProtoSize::add_uint32_field(total_size, 1, this->device_id, false);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesDateResponse::dump_to(std::string &out) const { void ListEntitiesDateResponse::dump_to(std::string &out) const {
@@ -9100,6 +9428,11 @@ void ListEntitiesDateResponse::dump_to(std::string &out) const {
out.append(" entity_category: "); out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category)); out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n"); out.append("\n");
out.append(" device_id: ");
sprintf(buffer, "%" PRIu32, this->device_id);
out.append(buffer);
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@@ -9255,6 +9588,10 @@ bool ListEntitiesTimeResponse::decode_varint(uint32_t field_id, ProtoVarInt valu
this->entity_category = value.as_enum<enums::EntityCategory>(); this->entity_category = value.as_enum<enums::EntityCategory>();
return true; return true;
} }
case 8: {
this->device_id = value.as_uint32();
return true;
}
default: default:
return false; return false;
} }
@@ -9299,6 +9636,7 @@ void ListEntitiesTimeResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(5, this->icon); buffer.encode_string(5, this->icon);
buffer.encode_bool(6, this->disabled_by_default); buffer.encode_bool(6, this->disabled_by_default);
buffer.encode_enum<enums::EntityCategory>(7, this->entity_category); buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
buffer.encode_uint32(8, this->device_id);
} }
void ListEntitiesTimeResponse::calculate_size(uint32_t &total_size) const { void ListEntitiesTimeResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, this->object_id, false); ProtoSize::add_string_field(total_size, 1, this->object_id, false);
@@ -9308,6 +9646,7 @@ void ListEntitiesTimeResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, this->icon, false); ProtoSize::add_string_field(total_size, 1, this->icon, false);
ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false);
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category), false); ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category), false);
ProtoSize::add_uint32_field(total_size, 1, this->device_id, false);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesTimeResponse::dump_to(std::string &out) const { void ListEntitiesTimeResponse::dump_to(std::string &out) const {
@@ -9341,6 +9680,11 @@ void ListEntitiesTimeResponse::dump_to(std::string &out) const {
out.append(" entity_category: "); out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category)); out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n"); out.append("\n");
out.append(" device_id: ");
sprintf(buffer, "%" PRIu32, this->device_id);
out.append(buffer);
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@@ -9496,6 +9840,10 @@ bool ListEntitiesEventResponse::decode_varint(uint32_t field_id, ProtoVarInt val
this->entity_category = value.as_enum<enums::EntityCategory>(); this->entity_category = value.as_enum<enums::EntityCategory>();
return true; return true;
} }
case 10: {
this->device_id = value.as_uint32();
return true;
}
default: default:
return false; return false;
} }
@@ -9552,6 +9900,7 @@ void ListEntitiesEventResponse::encode(ProtoWriteBuffer buffer) const {
for (auto &it : this->event_types) { for (auto &it : this->event_types) {
buffer.encode_string(9, it, true); buffer.encode_string(9, it, true);
} }
buffer.encode_uint32(10, this->device_id);
} }
void ListEntitiesEventResponse::calculate_size(uint32_t &total_size) const { void ListEntitiesEventResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, this->object_id, false); ProtoSize::add_string_field(total_size, 1, this->object_id, false);
@@ -9567,6 +9916,7 @@ void ListEntitiesEventResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, it, true); ProtoSize::add_string_field(total_size, 1, it, true);
} }
} }
ProtoSize::add_uint32_field(total_size, 1, this->device_id, false);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesEventResponse::dump_to(std::string &out) const { void ListEntitiesEventResponse::dump_to(std::string &out) const {
@@ -9610,6 +9960,11 @@ void ListEntitiesEventResponse::dump_to(std::string &out) const {
out.append("'").append(it).append("'"); out.append("'").append(it).append("'");
out.append("\n"); out.append("\n");
} }
out.append(" device_id: ");
sprintf(buffer, "%" PRIu32, this->device_id);
out.append(buffer);
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@@ -9678,6 +10033,10 @@ bool ListEntitiesValveResponse::decode_varint(uint32_t field_id, ProtoVarInt val
this->supports_stop = value.as_bool(); this->supports_stop = value.as_bool();
return true; return true;
} }
case 12: {
this->device_id = value.as_uint32();
return true;
}
default: default:
return false; return false;
} }
@@ -9730,6 +10089,7 @@ void ListEntitiesValveResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(9, this->assumed_state); buffer.encode_bool(9, this->assumed_state);
buffer.encode_bool(10, this->supports_position); buffer.encode_bool(10, this->supports_position);
buffer.encode_bool(11, this->supports_stop); buffer.encode_bool(11, this->supports_stop);
buffer.encode_uint32(12, this->device_id);
} }
void ListEntitiesValveResponse::calculate_size(uint32_t &total_size) const { void ListEntitiesValveResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, this->object_id, false); ProtoSize::add_string_field(total_size, 1, this->object_id, false);
@@ -9743,6 +10103,7 @@ void ListEntitiesValveResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_bool_field(total_size, 1, this->assumed_state, false); ProtoSize::add_bool_field(total_size, 1, this->assumed_state, false);
ProtoSize::add_bool_field(total_size, 1, this->supports_position, false); ProtoSize::add_bool_field(total_size, 1, this->supports_position, false);
ProtoSize::add_bool_field(total_size, 1, this->supports_stop, false); ProtoSize::add_bool_field(total_size, 1, this->supports_stop, false);
ProtoSize::add_uint32_field(total_size, 1, this->device_id, false);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesValveResponse::dump_to(std::string &out) const { void ListEntitiesValveResponse::dump_to(std::string &out) const {
@@ -9792,6 +10153,11 @@ void ListEntitiesValveResponse::dump_to(std::string &out) const {
out.append(" supports_stop: "); out.append(" supports_stop: ");
out.append(YESNO(this->supports_stop)); out.append(YESNO(this->supports_stop));
out.append("\n"); out.append("\n");
out.append(" device_id: ");
sprintf(buffer, "%" PRIu32, this->device_id);
out.append(buffer);
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@@ -9923,6 +10289,10 @@ bool ListEntitiesDateTimeResponse::decode_varint(uint32_t field_id, ProtoVarInt
this->entity_category = value.as_enum<enums::EntityCategory>(); this->entity_category = value.as_enum<enums::EntityCategory>();
return true; return true;
} }
case 8: {
this->device_id = value.as_uint32();
return true;
}
default: default:
return false; return false;
} }
@@ -9967,6 +10337,7 @@ void ListEntitiesDateTimeResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(5, this->icon); buffer.encode_string(5, this->icon);
buffer.encode_bool(6, this->disabled_by_default); buffer.encode_bool(6, this->disabled_by_default);
buffer.encode_enum<enums::EntityCategory>(7, this->entity_category); buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
buffer.encode_uint32(8, this->device_id);
} }
void ListEntitiesDateTimeResponse::calculate_size(uint32_t &total_size) const { void ListEntitiesDateTimeResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, this->object_id, false); ProtoSize::add_string_field(total_size, 1, this->object_id, false);
@@ -9976,6 +10347,7 @@ void ListEntitiesDateTimeResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, this->icon, false); ProtoSize::add_string_field(total_size, 1, this->icon, false);
ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false);
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category), false); ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category), false);
ProtoSize::add_uint32_field(total_size, 1, this->device_id, false);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesDateTimeResponse::dump_to(std::string &out) const { void ListEntitiesDateTimeResponse::dump_to(std::string &out) const {
@@ -10009,6 +10381,11 @@ void ListEntitiesDateTimeResponse::dump_to(std::string &out) const {
out.append(" entity_category: "); out.append(" entity_category: ");
out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category)); out.append(proto_enum_to_string<enums::EntityCategory>(this->entity_category));
out.append("\n"); out.append("\n");
out.append(" device_id: ");
sprintf(buffer, "%" PRIu32, this->device_id);
out.append(buffer);
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif
@@ -10114,6 +10491,10 @@ bool ListEntitiesUpdateResponse::decode_varint(uint32_t field_id, ProtoVarInt va
this->entity_category = value.as_enum<enums::EntityCategory>(); this->entity_category = value.as_enum<enums::EntityCategory>();
return true; return true;
} }
case 9: {
this->device_id = value.as_uint32();
return true;
}
default: default:
return false; return false;
} }
@@ -10163,6 +10544,7 @@ void ListEntitiesUpdateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(6, this->disabled_by_default); buffer.encode_bool(6, this->disabled_by_default);
buffer.encode_enum<enums::EntityCategory>(7, this->entity_category); buffer.encode_enum<enums::EntityCategory>(7, this->entity_category);
buffer.encode_string(8, this->device_class); buffer.encode_string(8, this->device_class);
buffer.encode_uint32(9, this->device_id);
} }
void ListEntitiesUpdateResponse::calculate_size(uint32_t &total_size) const { void ListEntitiesUpdateResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, this->object_id, false); ProtoSize::add_string_field(total_size, 1, this->object_id, false);
@@ -10173,6 +10555,7 @@ void ListEntitiesUpdateResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false); ProtoSize::add_bool_field(total_size, 1, this->disabled_by_default, false);
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category), false); ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->entity_category), false);
ProtoSize::add_string_field(total_size, 1, this->device_class, false); ProtoSize::add_string_field(total_size, 1, this->device_class, false);
ProtoSize::add_uint32_field(total_size, 1, this->device_id, false);
} }
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesUpdateResponse::dump_to(std::string &out) const { void ListEntitiesUpdateResponse::dump_to(std::string &out) const {
@@ -10210,6 +10593,11 @@ void ListEntitiesUpdateResponse::dump_to(std::string &out) const {
out.append(" device_class: "); out.append(" device_class: ");
out.append("'").append(this->device_class).append("'"); out.append("'").append(this->device_class).append("'");
out.append("\n"); out.append("\n");
out.append(" device_id: ");
sprintf(buffer, "%" PRIu32, this->device_id);
out.append(buffer);
out.append("\n");
out.append("}"); out.append("}");
} }
#endif #endif

View File

@@ -264,6 +264,7 @@ class InfoResponseProtoMessage : public ProtoMessage {
bool disabled_by_default{false}; bool disabled_by_default{false};
std::string icon{}; std::string icon{};
enums::EntityCategory entity_category{}; enums::EntityCategory entity_category{};
uint32_t device_id{0};
protected: protected:
}; };
@@ -415,10 +416,39 @@ class DeviceInfoRequest : public ProtoMessage {
protected: protected:
}; };
class AreaInfo : public ProtoMessage {
public:
uint32_t area_id{0};
std::string name{};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class DeviceInfo : public ProtoMessage {
public:
uint32_t device_id{0};
std::string name{};
uint32_t area_id{0};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class DeviceInfoResponse : public ProtoMessage { class DeviceInfoResponse : public ProtoMessage {
public: public:
static constexpr uint16_t MESSAGE_TYPE = 10; static constexpr uint16_t MESSAGE_TYPE = 10;
static constexpr uint16_t ESTIMATED_SIZE = 129; static constexpr uint16_t ESTIMATED_SIZE = 219;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "device_info_response"; } static constexpr const char *message_name() { return "device_info_response"; }
#endif #endif
@@ -441,6 +471,9 @@ class DeviceInfoResponse : public ProtoMessage {
std::string suggested_area{}; std::string suggested_area{};
std::string bluetooth_mac_address{}; std::string bluetooth_mac_address{};
bool api_encryption_supported{false}; bool api_encryption_supported{false};
std::vector<DeviceInfo> devices{};
std::vector<AreaInfo> areas{};
AreaInfo area{};
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override; void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
@@ -493,7 +526,7 @@ class SubscribeStatesRequest : public ProtoMessage {
class ListEntitiesBinarySensorResponse : public InfoResponseProtoMessage { class ListEntitiesBinarySensorResponse : public InfoResponseProtoMessage {
public: public:
static constexpr uint16_t MESSAGE_TYPE = 12; static constexpr uint16_t MESSAGE_TYPE = 12;
static constexpr uint16_t ESTIMATED_SIZE = 56; static constexpr uint16_t ESTIMATED_SIZE = 60;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_binary_sensor_response"; } static constexpr const char *message_name() { return "list_entities_binary_sensor_response"; }
#endif #endif
@@ -532,7 +565,7 @@ class BinarySensorStateResponse : public StateResponseProtoMessage {
class ListEntitiesCoverResponse : public InfoResponseProtoMessage { class ListEntitiesCoverResponse : public InfoResponseProtoMessage {
public: public:
static constexpr uint16_t MESSAGE_TYPE = 13; static constexpr uint16_t MESSAGE_TYPE = 13;
static constexpr uint16_t ESTIMATED_SIZE = 62; static constexpr uint16_t ESTIMATED_SIZE = 66;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_cover_response"; } static constexpr const char *message_name() { return "list_entities_cover_response"; }
#endif #endif
@@ -601,7 +634,7 @@ class CoverCommandRequest : public ProtoMessage {
class ListEntitiesFanResponse : public InfoResponseProtoMessage { class ListEntitiesFanResponse : public InfoResponseProtoMessage {
public: public:
static constexpr uint16_t MESSAGE_TYPE = 14; static constexpr uint16_t MESSAGE_TYPE = 14;
static constexpr uint16_t ESTIMATED_SIZE = 73; static constexpr uint16_t ESTIMATED_SIZE = 77;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_fan_response"; } static constexpr const char *message_name() { return "list_entities_fan_response"; }
#endif #endif
@@ -679,7 +712,7 @@ class FanCommandRequest : public ProtoMessage {
class ListEntitiesLightResponse : public InfoResponseProtoMessage { class ListEntitiesLightResponse : public InfoResponseProtoMessage {
public: public:
static constexpr uint16_t MESSAGE_TYPE = 15; static constexpr uint16_t MESSAGE_TYPE = 15;
static constexpr uint16_t ESTIMATED_SIZE = 85; static constexpr uint16_t ESTIMATED_SIZE = 90;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_light_response"; } static constexpr const char *message_name() { return "list_entities_light_response"; }
#endif #endif
@@ -780,7 +813,7 @@ class LightCommandRequest : public ProtoMessage {
class ListEntitiesSensorResponse : public InfoResponseProtoMessage { class ListEntitiesSensorResponse : public InfoResponseProtoMessage {
public: public:
static constexpr uint16_t MESSAGE_TYPE = 16; static constexpr uint16_t MESSAGE_TYPE = 16;
static constexpr uint16_t ESTIMATED_SIZE = 73; static constexpr uint16_t ESTIMATED_SIZE = 77;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_sensor_response"; } static constexpr const char *message_name() { return "list_entities_sensor_response"; }
#endif #endif
@@ -823,7 +856,7 @@ class SensorStateResponse : public StateResponseProtoMessage {
class ListEntitiesSwitchResponse : public InfoResponseProtoMessage { class ListEntitiesSwitchResponse : public InfoResponseProtoMessage {
public: public:
static constexpr uint16_t MESSAGE_TYPE = 17; static constexpr uint16_t MESSAGE_TYPE = 17;
static constexpr uint16_t ESTIMATED_SIZE = 56; static constexpr uint16_t ESTIMATED_SIZE = 60;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_switch_response"; } static constexpr const char *message_name() { return "list_entities_switch_response"; }
#endif #endif
@@ -880,7 +913,7 @@ class SwitchCommandRequest : public ProtoMessage {
class ListEntitiesTextSensorResponse : public InfoResponseProtoMessage { class ListEntitiesTextSensorResponse : public InfoResponseProtoMessage {
public: public:
static constexpr uint16_t MESSAGE_TYPE = 18; static constexpr uint16_t MESSAGE_TYPE = 18;
static constexpr uint16_t ESTIMATED_SIZE = 54; static constexpr uint16_t ESTIMATED_SIZE = 58;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_text_sensor_response"; } static constexpr const char *message_name() { return "list_entities_text_sensor_response"; }
#endif #endif
@@ -1196,7 +1229,7 @@ class ExecuteServiceRequest : public ProtoMessage {
class ListEntitiesCameraResponse : public InfoResponseProtoMessage { class ListEntitiesCameraResponse : public InfoResponseProtoMessage {
public: public:
static constexpr uint16_t MESSAGE_TYPE = 43; static constexpr uint16_t MESSAGE_TYPE = 43;
static constexpr uint16_t ESTIMATED_SIZE = 45; static constexpr uint16_t ESTIMATED_SIZE = 49;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_camera_response"; } static constexpr const char *message_name() { return "list_entities_camera_response"; }
#endif #endif
@@ -1253,7 +1286,7 @@ class CameraImageRequest : public ProtoMessage {
class ListEntitiesClimateResponse : public InfoResponseProtoMessage { class ListEntitiesClimateResponse : public InfoResponseProtoMessage {
public: public:
static constexpr uint16_t MESSAGE_TYPE = 46; static constexpr uint16_t MESSAGE_TYPE = 46;
static constexpr uint16_t ESTIMATED_SIZE = 151; static constexpr uint16_t ESTIMATED_SIZE = 156;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_climate_response"; } static constexpr const char *message_name() { return "list_entities_climate_response"; }
#endif #endif
@@ -1362,7 +1395,7 @@ class ClimateCommandRequest : public ProtoMessage {
class ListEntitiesNumberResponse : public InfoResponseProtoMessage { class ListEntitiesNumberResponse : public InfoResponseProtoMessage {
public: public:
static constexpr uint16_t MESSAGE_TYPE = 49; static constexpr uint16_t MESSAGE_TYPE = 49;
static constexpr uint16_t ESTIMATED_SIZE = 80; static constexpr uint16_t ESTIMATED_SIZE = 84;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_number_response"; } static constexpr const char *message_name() { return "list_entities_number_response"; }
#endif #endif
@@ -1423,7 +1456,7 @@ class NumberCommandRequest : public ProtoMessage {
class ListEntitiesSelectResponse : public InfoResponseProtoMessage { class ListEntitiesSelectResponse : public InfoResponseProtoMessage {
public: public:
static constexpr uint16_t MESSAGE_TYPE = 52; static constexpr uint16_t MESSAGE_TYPE = 52;
static constexpr uint16_t ESTIMATED_SIZE = 63; static constexpr uint16_t ESTIMATED_SIZE = 67;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_select_response"; } static constexpr const char *message_name() { return "list_entities_select_response"; }
#endif #endif
@@ -1481,7 +1514,7 @@ class SelectCommandRequest : public ProtoMessage {
class ListEntitiesSirenResponse : public InfoResponseProtoMessage { class ListEntitiesSirenResponse : public InfoResponseProtoMessage {
public: public:
static constexpr uint16_t MESSAGE_TYPE = 55; static constexpr uint16_t MESSAGE_TYPE = 55;
static constexpr uint16_t ESTIMATED_SIZE = 67; static constexpr uint16_t ESTIMATED_SIZE = 71;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_siren_response"; } static constexpr const char *message_name() { return "list_entities_siren_response"; }
#endif #endif
@@ -1547,7 +1580,7 @@ class SirenCommandRequest : public ProtoMessage {
class ListEntitiesLockResponse : public InfoResponseProtoMessage { class ListEntitiesLockResponse : public InfoResponseProtoMessage {
public: public:
static constexpr uint16_t MESSAGE_TYPE = 58; static constexpr uint16_t MESSAGE_TYPE = 58;
static constexpr uint16_t ESTIMATED_SIZE = 60; static constexpr uint16_t ESTIMATED_SIZE = 64;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_lock_response"; } static constexpr const char *message_name() { return "list_entities_lock_response"; }
#endif #endif
@@ -1609,7 +1642,7 @@ class LockCommandRequest : public ProtoMessage {
class ListEntitiesButtonResponse : public InfoResponseProtoMessage { class ListEntitiesButtonResponse : public InfoResponseProtoMessage {
public: public:
static constexpr uint16_t MESSAGE_TYPE = 61; static constexpr uint16_t MESSAGE_TYPE = 61;
static constexpr uint16_t ESTIMATED_SIZE = 54; static constexpr uint16_t ESTIMATED_SIZE = 58;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_button_response"; } static constexpr const char *message_name() { return "list_entities_button_response"; }
#endif #endif
@@ -1662,7 +1695,7 @@ class MediaPlayerSupportedFormat : public ProtoMessage {
class ListEntitiesMediaPlayerResponse : public InfoResponseProtoMessage { class ListEntitiesMediaPlayerResponse : public InfoResponseProtoMessage {
public: public:
static constexpr uint16_t MESSAGE_TYPE = 63; static constexpr uint16_t MESSAGE_TYPE = 63;
static constexpr uint16_t ESTIMATED_SIZE = 81; static constexpr uint16_t ESTIMATED_SIZE = 85;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_media_player_response"; } static constexpr const char *message_name() { return "list_entities_media_player_response"; }
#endif #endif
@@ -2532,7 +2565,7 @@ class VoiceAssistantSetConfiguration : public ProtoMessage {
class ListEntitiesAlarmControlPanelResponse : public InfoResponseProtoMessage { class ListEntitiesAlarmControlPanelResponse : public InfoResponseProtoMessage {
public: public:
static constexpr uint16_t MESSAGE_TYPE = 94; static constexpr uint16_t MESSAGE_TYPE = 94;
static constexpr uint16_t ESTIMATED_SIZE = 53; static constexpr uint16_t ESTIMATED_SIZE = 57;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_alarm_control_panel_response"; } static constexpr const char *message_name() { return "list_entities_alarm_control_panel_response"; }
#endif #endif
@@ -2592,7 +2625,7 @@ class AlarmControlPanelCommandRequest : public ProtoMessage {
class ListEntitiesTextResponse : public InfoResponseProtoMessage { class ListEntitiesTextResponse : public InfoResponseProtoMessage {
public: public:
static constexpr uint16_t MESSAGE_TYPE = 97; static constexpr uint16_t MESSAGE_TYPE = 97;
static constexpr uint16_t ESTIMATED_SIZE = 64; static constexpr uint16_t ESTIMATED_SIZE = 68;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_text_response"; } static constexpr const char *message_name() { return "list_entities_text_response"; }
#endif #endif
@@ -2653,7 +2686,7 @@ class TextCommandRequest : public ProtoMessage {
class ListEntitiesDateResponse : public InfoResponseProtoMessage { class ListEntitiesDateResponse : public InfoResponseProtoMessage {
public: public:
static constexpr uint16_t MESSAGE_TYPE = 100; static constexpr uint16_t MESSAGE_TYPE = 100;
static constexpr uint16_t ESTIMATED_SIZE = 45; static constexpr uint16_t ESTIMATED_SIZE = 49;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_date_response"; } static constexpr const char *message_name() { return "list_entities_date_response"; }
#endif #endif
@@ -2713,7 +2746,7 @@ class DateCommandRequest : public ProtoMessage {
class ListEntitiesTimeResponse : public InfoResponseProtoMessage { class ListEntitiesTimeResponse : public InfoResponseProtoMessage {
public: public:
static constexpr uint16_t MESSAGE_TYPE = 103; static constexpr uint16_t MESSAGE_TYPE = 103;
static constexpr uint16_t ESTIMATED_SIZE = 45; static constexpr uint16_t ESTIMATED_SIZE = 49;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_time_response"; } static constexpr const char *message_name() { return "list_entities_time_response"; }
#endif #endif
@@ -2773,7 +2806,7 @@ class TimeCommandRequest : public ProtoMessage {
class ListEntitiesEventResponse : public InfoResponseProtoMessage { class ListEntitiesEventResponse : public InfoResponseProtoMessage {
public: public:
static constexpr uint16_t MESSAGE_TYPE = 107; static constexpr uint16_t MESSAGE_TYPE = 107;
static constexpr uint16_t ESTIMATED_SIZE = 72; static constexpr uint16_t ESTIMATED_SIZE = 76;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_event_response"; } static constexpr const char *message_name() { return "list_entities_event_response"; }
#endif #endif
@@ -2811,7 +2844,7 @@ class EventResponse : public StateResponseProtoMessage {
class ListEntitiesValveResponse : public InfoResponseProtoMessage { class ListEntitiesValveResponse : public InfoResponseProtoMessage {
public: public:
static constexpr uint16_t MESSAGE_TYPE = 109; static constexpr uint16_t MESSAGE_TYPE = 109;
static constexpr uint16_t ESTIMATED_SIZE = 60; static constexpr uint16_t ESTIMATED_SIZE = 64;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_valve_response"; } static constexpr const char *message_name() { return "list_entities_valve_response"; }
#endif #endif
@@ -2873,7 +2906,7 @@ class ValveCommandRequest : public ProtoMessage {
class ListEntitiesDateTimeResponse : public InfoResponseProtoMessage { class ListEntitiesDateTimeResponse : public InfoResponseProtoMessage {
public: public:
static constexpr uint16_t MESSAGE_TYPE = 112; static constexpr uint16_t MESSAGE_TYPE = 112;
static constexpr uint16_t ESTIMATED_SIZE = 45; static constexpr uint16_t ESTIMATED_SIZE = 49;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_date_time_response"; } static constexpr const char *message_name() { return "list_entities_date_time_response"; }
#endif #endif
@@ -2928,7 +2961,7 @@ class DateTimeCommandRequest : public ProtoMessage {
class ListEntitiesUpdateResponse : public InfoResponseProtoMessage { class ListEntitiesUpdateResponse : public InfoResponseProtoMessage {
public: public:
static constexpr uint16_t MESSAGE_TYPE = 116; static constexpr uint16_t MESSAGE_TYPE = 116;
static constexpr uint16_t ESTIMATED_SIZE = 54; static constexpr uint16_t ESTIMATED_SIZE = 58;
#ifdef HAS_PROTO_MESSAGE_DUMP #ifdef HAS_PROTO_MESSAGE_DUMP
static constexpr const char *message_name() { return "list_entities_update_response"; } static constexpr const char *message_name() { return "list_entities_update_response"; }
#endif #endif

View File

@@ -86,7 +86,7 @@ bool AudioTransferBuffer::reallocate(size_t new_buffer_size) {
bool AudioTransferBuffer::allocate_buffer_(size_t buffer_size) { bool AudioTransferBuffer::allocate_buffer_(size_t buffer_size) {
this->buffer_size_ = buffer_size; this->buffer_size_ = buffer_size;
RAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE); RAMAllocator<uint8_t> allocator;
this->buffer_ = allocator.allocate(this->buffer_size_); this->buffer_ = allocator.allocate(this->buffer_size_);
if (this->buffer_ == nullptr) { if (this->buffer_ == nullptr) {
@@ -101,7 +101,7 @@ bool AudioTransferBuffer::allocate_buffer_(size_t buffer_size) {
void AudioTransferBuffer::deallocate_buffer_() { void AudioTransferBuffer::deallocate_buffer_() {
if (this->buffer_ != nullptr) { if (this->buffer_ != nullptr) {
RAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE); RAMAllocator<uint8_t> allocator;
allocator.deallocate(this->buffer_, this->buffer_size_); allocator.deallocate(this->buffer_, this->buffer_size_);
this->buffer_ = nullptr; this->buffer_ = nullptr;
this->data_start_ = nullptr; this->data_start_ = nullptr;

View File

@@ -7,11 +7,13 @@
extern "C" { extern "C" {
#include "rtos_pub.h" #include "rtos_pub.h"
#include "spi.h" // rtos_pub.h must be included before the rest of the includes
#include "arm_arch.h" #include "arm_arch.h"
#include "general_dma_pub.h" #include "general_dma_pub.h"
#include "gpio_pub.h" #include "gpio_pub.h"
#include "icu_pub.h" #include "icu_pub.h"
#include "spi.h"
#undef SPI_DAT #undef SPI_DAT
#undef SPI_BASE #undef SPI_BASE
}; };
@@ -124,7 +126,7 @@ void BekenSPILEDStripLightOutput::setup() {
size_t buffer_size = this->get_buffer_size_(); size_t buffer_size = this->get_buffer_size_();
size_t dma_buffer_size = (buffer_size * 8) + (2 * 64); size_t dma_buffer_size = (buffer_size * 8) + (2 * 64);
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE); RAMAllocator<uint8_t> allocator;
this->buf_ = allocator.allocate(buffer_size); this->buf_ = allocator.allocate(buffer_size);
if (this->buf_ == nullptr) { if (this->buf_ == nullptr) {
ESP_LOGE(TAG, "Cannot allocate LED buffer!"); ESP_LOGE(TAG, "Cannot allocate LED buffer!");

View File

@@ -50,7 +50,7 @@ void BH1750Sensor::read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function<
// turn on (after one-shot sensor automatically powers down) // turn on (after one-shot sensor automatically powers down)
uint8_t turn_on = BH1750_COMMAND_POWER_ON; uint8_t turn_on = BH1750_COMMAND_POWER_ON;
if (this->write(&turn_on, 1) != i2c::ERROR_OK) { if (this->write(&turn_on, 1) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Turning on BH1750 failed"); ESP_LOGW(TAG, "Power on failed");
f(NAN); f(NAN);
return; return;
} }
@@ -60,7 +60,7 @@ void BH1750Sensor::read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function<
uint8_t mtreg_hi = BH1750_COMMAND_MT_REG_HI | ((mtreg >> 5) & 0b111); uint8_t mtreg_hi = BH1750_COMMAND_MT_REG_HI | ((mtreg >> 5) & 0b111);
uint8_t mtreg_lo = BH1750_COMMAND_MT_REG_LO | ((mtreg >> 0) & 0b11111); uint8_t mtreg_lo = BH1750_COMMAND_MT_REG_LO | ((mtreg >> 0) & 0b11111);
if (this->write(&mtreg_hi, 1) != i2c::ERROR_OK || this->write(&mtreg_lo, 1) != i2c::ERROR_OK) { if (this->write(&mtreg_hi, 1) != i2c::ERROR_OK || this->write(&mtreg_lo, 1) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Setting measurement time for BH1750 failed"); ESP_LOGW(TAG, "Set measurement time failed");
active_mtreg_ = 0; active_mtreg_ = 0;
f(NAN); f(NAN);
return; return;
@@ -88,7 +88,7 @@ void BH1750Sensor::read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function<
return; return;
} }
if (this->write(&cmd, 1) != i2c::ERROR_OK) { if (this->write(&cmd, 1) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Starting measurement for BH1750 failed"); ESP_LOGW(TAG, "Start measurement failed");
f(NAN); f(NAN);
return; return;
} }
@@ -99,7 +99,7 @@ void BH1750Sensor::read_lx_(BH1750Mode mode, uint8_t mtreg, const std::function<
this->set_timeout("read", meas_time, [this, mode, mtreg, f]() { this->set_timeout("read", meas_time, [this, mode, mtreg, f]() {
uint16_t raw_value; uint16_t raw_value;
if (this->read(reinterpret_cast<uint8_t *>(&raw_value), 2) != i2c::ERROR_OK) { if (this->read(reinterpret_cast<uint8_t *>(&raw_value), 2) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Reading BH1750 data failed"); ESP_LOGW(TAG, "Read data failed");
f(NAN); f(NAN);
return; return;
} }
@@ -156,7 +156,7 @@ void BH1750Sensor::update() {
this->publish_state(NAN); this->publish_state(NAN);
return; return;
} }
ESP_LOGD(TAG, "'%s': Got illuminance=%.1flx", this->get_name().c_str(), val); ESP_LOGD(TAG, "'%s': Illuminance=%.1flx", this->get_name().c_str(), val);
this->status_clear_warning(); this->status_clear_warning();
this->publish_state(val); this->publish_state(val);
}); });

View File

@@ -60,8 +60,8 @@ from esphome.const import (
DEVICE_CLASS_WINDOW, DEVICE_CLASS_WINDOW,
) )
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
from esphome.cpp_generator import MockObjClass from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
from esphome.util import Registry from esphome.util import Registry
CODEOWNERS = ["@esphome/core"] CODEOWNERS = ["@esphome/core"]
@@ -148,6 +148,7 @@ BinarySensorCondition = binary_sensor_ns.class_("BinarySensorCondition", Conditi
# Filters # Filters
Filter = binary_sensor_ns.class_("Filter") Filter = binary_sensor_ns.class_("Filter")
TimeoutFilter = binary_sensor_ns.class_("TimeoutFilter", Filter, cg.Component)
DelayedOnOffFilter = binary_sensor_ns.class_("DelayedOnOffFilter", Filter, cg.Component) DelayedOnOffFilter = binary_sensor_ns.class_("DelayedOnOffFilter", Filter, cg.Component)
DelayedOnFilter = binary_sensor_ns.class_("DelayedOnFilter", Filter, cg.Component) DelayedOnFilter = binary_sensor_ns.class_("DelayedOnFilter", Filter, cg.Component)
DelayedOffFilter = binary_sensor_ns.class_("DelayedOffFilter", Filter, cg.Component) DelayedOffFilter = binary_sensor_ns.class_("DelayedOffFilter", Filter, cg.Component)
@@ -171,6 +172,19 @@ async def invert_filter_to_code(config, filter_id):
return cg.new_Pvariable(filter_id) return cg.new_Pvariable(filter_id)
@register_filter(
"timeout",
TimeoutFilter,
cv.templatable(cv.positive_time_period_milliseconds),
)
async def timeout_filter_to_code(config, filter_id):
var = cg.new_Pvariable(filter_id)
await cg.register_component(var, {})
template_ = await cg.templatable(config, [], cg.uint32)
cg.add(var.set_timeout_value(template_))
return var
@register_filter( @register_filter(
"delayed_on_off", "delayed_on_off",
DelayedOnOffFilter, DelayedOnOffFilter,
@@ -491,6 +505,9 @@ _BINARY_SENSOR_SCHEMA = (
) )
_BINARY_SENSOR_SCHEMA.add_extra(entity_duplicate_validator("binary_sensor"))
def binary_sensor_schema( def binary_sensor_schema(
class_: MockObjClass = cv.UNDEFINED, class_: MockObjClass = cv.UNDEFINED,
*, *,
@@ -521,7 +538,7 @@ BINARY_SENSOR_SCHEMA.add_extra(cv.deprecated_schema_constant("binary_sensor"))
async def setup_binary_sensor_core_(var, config): async def setup_binary_sensor_core_(var, config):
await setup_entity(var, config) await setup_entity(var, config, "binary_sensor")
if (device_class := config.get(CONF_DEVICE_CLASS)) is not None: if (device_class := config.get(CONF_DEVICE_CLASS)) is not None:
cg.add(var.set_device_class(device_class)) cg.add(var.set_device_class(device_class))

View File

@@ -25,6 +25,12 @@ void Filter::input(bool value) {
} }
} }
void TimeoutFilter::input(bool value) {
this->set_timeout("timeout", this->timeout_delay_.value(), [this]() { this->parent_->invalidate_state(); });
// we do not de-dup here otherwise changes from invalid to valid state will not be output
this->output(value);
}
optional<bool> DelayedOnOffFilter::new_value(bool value) { optional<bool> DelayedOnOffFilter::new_value(bool value) {
if (value) { if (value) {
this->set_timeout("ON_OFF", this->on_delay_.value(), [this]() { this->output(true); }); this->set_timeout("ON_OFF", this->on_delay_.value(), [this]() { this->output(true); });

View File

@@ -16,7 +16,7 @@ class Filter {
public: public:
virtual optional<bool> new_value(bool value) = 0; virtual optional<bool> new_value(bool value) = 0;
void input(bool value); virtual void input(bool value);
void output(bool value); void output(bool value);
@@ -28,6 +28,16 @@ class Filter {
Deduplicator<bool> dedup_; Deduplicator<bool> dedup_;
}; };
class TimeoutFilter : public Filter, public Component {
public:
optional<bool> new_value(bool value) override { return value; }
void input(bool value) override;
template<typename T> void set_timeout_value(T timeout) { this->timeout_delay_ = timeout; }
protected:
TemplatableValue<uint32_t> timeout_delay_{};
};
class DelayedOnOffFilter : public Filter, public Component { class DelayedOnOffFilter : public Filter, public Component {
public: public:
optional<bool> new_value(bool value) override; optional<bool> new_value(bool value) override;

View File

@@ -18,8 +18,8 @@ from esphome.const import (
DEVICE_CLASS_UPDATE, DEVICE_CLASS_UPDATE,
) )
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
from esphome.cpp_generator import MockObjClass from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@esphome/core"] CODEOWNERS = ["@esphome/core"]
IS_PLATFORM_COMPONENT = True IS_PLATFORM_COMPONENT = True
@@ -61,6 +61,9 @@ _BUTTON_SCHEMA = (
) )
_BUTTON_SCHEMA.add_extra(entity_duplicate_validator("button"))
def button_schema( def button_schema(
class_: MockObjClass, class_: MockObjClass,
*, *,
@@ -87,7 +90,7 @@ BUTTON_SCHEMA.add_extra(cv.deprecated_schema_constant("button"))
async def setup_button_core_(var, config): async def setup_button_core_(var, config):
await setup_entity(var, config) await setup_entity(var, config, "button")
for conf in config.get(CONF_ON_PRESS, []): for conf in config.get(CONF_ON_PRESS, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)

View File

@@ -48,8 +48,8 @@ from esphome.const import (
CONF_WEB_SERVER, CONF_WEB_SERVER,
) )
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
from esphome.cpp_generator import MockObjClass from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
IS_PLATFORM_COMPONENT = True IS_PLATFORM_COMPONENT = True
@@ -247,6 +247,9 @@ _CLIMATE_SCHEMA = (
) )
_CLIMATE_SCHEMA.add_extra(entity_duplicate_validator("climate"))
def climate_schema( def climate_schema(
class_: MockObjClass, class_: MockObjClass,
*, *,
@@ -273,7 +276,7 @@ CLIMATE_SCHEMA.add_extra(cv.deprecated_schema_constant("climate"))
async def setup_climate_core_(var, config): async def setup_climate_core_(var, config):
await setup_entity(var, config) await setup_entity(var, config, "climate")
visual = config[CONF_VISUAL] visual = config[CONF_VISUAL]
if (min_temp := visual.get(CONF_MIN_TEMPERATURE)) is not None: if (min_temp := visual.get(CONF_MIN_TEMPERATURE)) is not None:

View File

@@ -33,8 +33,8 @@ from esphome.const import (
DEVICE_CLASS_WINDOW, DEVICE_CLASS_WINDOW,
) )
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
from esphome.cpp_generator import MockObjClass from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
IS_PLATFORM_COMPONENT = True IS_PLATFORM_COMPONENT = True
@@ -126,6 +126,9 @@ _COVER_SCHEMA = (
) )
_COVER_SCHEMA.add_extra(entity_duplicate_validator("cover"))
def cover_schema( def cover_schema(
class_: MockObjClass, class_: MockObjClass,
*, *,
@@ -154,7 +157,7 @@ COVER_SCHEMA.add_extra(cv.deprecated_schema_constant("cover"))
async def setup_cover_core_(var, config): async def setup_cover_core_(var, config):
await setup_entity(var, config) await setup_entity(var, config, "cover")
if (device_class := config.get(CONF_DEVICE_CLASS)) is not None: if (device_class := config.get(CONF_DEVICE_CLASS)) is not None:
cg.add(var.set_device_class(device_class)) cg.add(var.set_device_class(device_class))

View File

@@ -22,8 +22,8 @@ from esphome.const import (
CONF_YEAR, CONF_YEAR,
) )
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
from esphome.cpp_generator import MockObjClass from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@rfdarter", "@jesserockz"] CODEOWNERS = ["@rfdarter", "@jesserockz"]
@@ -84,6 +84,8 @@ _DATETIME_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA) .extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
).add_extra(_validate_time_present) ).add_extra(_validate_time_present)
_DATETIME_SCHEMA.add_extra(entity_duplicate_validator("datetime"))
def date_schema(class_: MockObjClass) -> cv.Schema: def date_schema(class_: MockObjClass) -> cv.Schema:
schema = cv.Schema( schema = cv.Schema(
@@ -133,7 +135,7 @@ def datetime_schema(class_: MockObjClass) -> cv.Schema:
async def setup_datetime_core_(var, config): async def setup_datetime_core_(var, config):
await setup_entity(var, config) await setup_entity(var, config, "datetime")
if (mqtt_id := config.get(CONF_MQTT_ID)) is not None: if (mqtt_id := config.get(CONF_MQTT_ID)) is not None:
mqtt_ = cg.new_Pvariable(mqtt_id, var) mqtt_ = cg.new_Pvariable(mqtt_id, var)

View File

@@ -455,7 +455,7 @@ CONFIG_SCHEMA = cv.Schema(
CONF_NAME: "Demo Plain Sensor", CONF_NAME: "Demo Plain Sensor",
}, },
{ {
CONF_NAME: "Demo Temperature Sensor", CONF_NAME: "Demo Temperature Sensor 1",
CONF_UNIT_OF_MEASUREMENT: UNIT_CELSIUS, CONF_UNIT_OF_MEASUREMENT: UNIT_CELSIUS,
CONF_ICON: ICON_THERMOMETER, CONF_ICON: ICON_THERMOMETER,
CONF_ACCURACY_DECIMALS: 1, CONF_ACCURACY_DECIMALS: 1,
@@ -463,7 +463,7 @@ CONFIG_SCHEMA = cv.Schema(
CONF_STATE_CLASS: STATE_CLASS_MEASUREMENT, CONF_STATE_CLASS: STATE_CLASS_MEASUREMENT,
}, },
{ {
CONF_NAME: "Demo Temperature Sensor", CONF_NAME: "Demo Temperature Sensor 2",
CONF_UNIT_OF_MEASUREMENT: UNIT_CELSIUS, CONF_UNIT_OF_MEASUREMENT: UNIT_CELSIUS,
CONF_ICON: ICON_THERMOMETER, CONF_ICON: ICON_THERMOMETER,
CONF_ACCURACY_DECIMALS: 1, CONF_ACCURACY_DECIMALS: 1,

View File

@@ -11,7 +11,7 @@ namespace display {
static const char *const TAG = "display"; static const char *const TAG = "display";
void DisplayBuffer::init_internal_(uint32_t buffer_length) { void DisplayBuffer::init_internal_(uint32_t buffer_length) {
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE); RAMAllocator<uint8_t> allocator;
this->buffer_ = allocator.allocate(buffer_length); this->buffer_ = allocator.allocate(buffer_length);
if (this->buffer_ == nullptr) { if (this->buffer_ == nullptr) {
ESP_LOGE(TAG, "Could not allocate buffer for display!"); ESP_LOGE(TAG, "Could not allocate buffer for display!");

View File

@@ -4,6 +4,7 @@
#include "ble_event_pool.h" #include "ble_event_pool.h"
#include "esphome/core/application.h" #include "esphome/core/application.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include <esp_bt.h> #include <esp_bt.h>
@@ -516,13 +517,12 @@ void ESP32BLE::dump_config() {
break; break;
} }
ESP_LOGCONFIG(TAG, ESP_LOGCONFIG(TAG,
"ESP32 BLE:\n" "BLE:\n"
" MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n" " MAC address: %s\n"
" IO Capability: %s", " IO Capability: %s",
mac_address[0], mac_address[1], mac_address[2], mac_address[3], mac_address[4], mac_address[5], format_mac_address_pretty(mac_address).c_str(), io_capability_s);
io_capability_s);
} else { } else {
ESP_LOGCONFIG(TAG, "ESP32 BLE: bluetooth stack is not enabled"); ESP_LOGCONFIG(TAG, "Bluetooth stack is not enabled");
} }
} }

View File

@@ -522,6 +522,7 @@ optional<ESPBLEiBeacon> ESPBLEiBeacon::from_manufacturer_data(const ServiceData
} }
void ESPBTDevice::parse_scan_rst(const BLEScanResult &scan_result) { void ESPBTDevice::parse_scan_rst(const BLEScanResult &scan_result) {
this->scan_result_ = &scan_result;
for (uint8_t i = 0; i < ESP_BD_ADDR_LEN; i++) for (uint8_t i = 0; i < ESP_BD_ADDR_LEN; i++)
this->address_[i] = scan_result.bda[i]; this->address_[i] = scan_result.bda[i];
this->address_type_ = static_cast<esp_ble_addr_type_t>(scan_result.ble_addr_type); this->address_type_ = static_cast<esp_ble_addr_type_t>(scan_result.ble_addr_type);

View File

@@ -85,6 +85,9 @@ class ESPBTDevice {
const std::vector<ServiceData> &get_service_datas() const { return service_datas_; } const std::vector<ServiceData> &get_service_datas() const { return service_datas_; }
// Exposed through a function for use in lambdas
const BLEScanResult &get_scan_result() const { return *scan_result_; }
bool resolve_irk(const uint8_t *irk) const; bool resolve_irk(const uint8_t *irk) const;
optional<ESPBLEiBeacon> get_ibeacon() const { optional<ESPBLEiBeacon> get_ibeacon() const {
@@ -111,6 +114,7 @@ class ESPBTDevice {
std::vector<ESPBTUUID> service_uuids_{}; std::vector<ESPBTUUID> service_uuids_{};
std::vector<ServiceData> manufacturer_datas_{}; std::vector<ServiceData> manufacturer_datas_{};
std::vector<ServiceData> service_datas_{}; std::vector<ServiceData> service_datas_{};
const BLEScanResult *scan_result_{nullptr};
}; };
class ESP32BLETracker; class ESP32BLETracker;

View File

@@ -1,5 +1,6 @@
from esphome import automation, pins from esphome import automation, pins
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import i2c
from esphome.components.esp32 import add_idf_component from esphome.components.esp32 import add_idf_component
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
@@ -7,6 +8,7 @@ from esphome.const import (
CONF_CONTRAST, CONF_CONTRAST,
CONF_DATA_PINS, CONF_DATA_PINS,
CONF_FREQUENCY, CONF_FREQUENCY,
CONF_I2C_ID,
CONF_ID, CONF_ID,
CONF_PIN, CONF_PIN,
CONF_RESET_PIN, CONF_RESET_PIN,
@@ -17,7 +19,7 @@ from esphome.const import (
CONF_VSYNC_PIN, CONF_VSYNC_PIN,
) )
from esphome.core import CORE from esphome.core import CORE
from esphome.cpp_helpers import setup_entity from esphome.core.entity_helpers import setup_entity
DEPENDENCIES = ["esp32"] DEPENDENCIES = ["esp32"]
@@ -149,93 +151,104 @@ CONF_ON_IMAGE = "on_image"
camera_range_param = cv.int_range(min=-2, max=2) camera_range_param = cv.int_range(min=-2, max=2)
CONFIG_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend( CONFIG_SCHEMA = cv.All(
{ cv.ENTITY_BASE_SCHEMA.extend(
cv.GenerateID(): cv.declare_id(ESP32Camera), {
# pin assignment cv.GenerateID(): cv.declare_id(ESP32Camera),
cv.Required(CONF_DATA_PINS): cv.All( # pin assignment
[pins.internal_gpio_input_pin_number], cv.Length(min=8, max=8) cv.Required(CONF_DATA_PINS): cv.All(
), [pins.internal_gpio_input_pin_number], cv.Length(min=8, max=8)
cv.Required(CONF_VSYNC_PIN): pins.internal_gpio_input_pin_number, ),
cv.Required(CONF_HREF_PIN): pins.internal_gpio_input_pin_number, cv.Required(CONF_VSYNC_PIN): pins.internal_gpio_input_pin_number,
cv.Required(CONF_PIXEL_CLOCK_PIN): pins.internal_gpio_input_pin_number, cv.Required(CONF_HREF_PIN): pins.internal_gpio_input_pin_number,
cv.Required(CONF_EXTERNAL_CLOCK): cv.Schema( cv.Required(CONF_PIXEL_CLOCK_PIN): pins.internal_gpio_input_pin_number,
{ cv.Required(CONF_EXTERNAL_CLOCK): cv.Schema(
cv.Required(CONF_PIN): pins.internal_gpio_input_pin_number, {
cv.Optional(CONF_FREQUENCY, default="20MHz"): cv.All( cv.Required(CONF_PIN): pins.internal_gpio_input_pin_number,
cv.frequency, cv.Range(min=8e6, max=20e6) cv.Optional(CONF_FREQUENCY, default="20MHz"): cv.All(
), cv.frequency, cv.Range(min=8e6, max=20e6)
} ),
), }
cv.Required(CONF_I2C_PINS): cv.Schema( ),
{ cv.Optional(CONF_I2C_PINS): cv.Schema(
cv.Required(CONF_SDA): pins.internal_gpio_output_pin_number, {
cv.Required(CONF_SCL): pins.internal_gpio_output_pin_number, cv.Required(CONF_SDA): pins.internal_gpio_output_pin_number,
} cv.Required(CONF_SCL): pins.internal_gpio_output_pin_number,
), }
cv.Optional(CONF_RESET_PIN): pins.internal_gpio_output_pin_number, ),
cv.Optional(CONF_POWER_DOWN_PIN): pins.internal_gpio_output_pin_number, cv.Optional(CONF_I2C_ID): cv.Any(
# image cv.use_id(i2c.InternalI2CBus),
cv.Optional(CONF_RESOLUTION, default="640X480"): cv.enum( msg="I2C bus must be an internal ESP32 I2C bus",
FRAME_SIZES, upper=True ),
), cv.Optional(CONF_RESET_PIN): pins.internal_gpio_output_pin_number,
cv.Optional(CONF_JPEG_QUALITY, default=10): cv.int_range(min=6, max=63), cv.Optional(CONF_POWER_DOWN_PIN): pins.internal_gpio_output_pin_number,
cv.Optional(CONF_CONTRAST, default=0): camera_range_param, # image
cv.Optional(CONF_BRIGHTNESS, default=0): camera_range_param, cv.Optional(CONF_RESOLUTION, default="640X480"): cv.enum(
cv.Optional(CONF_SATURATION, default=0): camera_range_param, FRAME_SIZES, upper=True
cv.Optional(CONF_VERTICAL_FLIP, default=True): cv.boolean, ),
cv.Optional(CONF_HORIZONTAL_MIRROR, default=True): cv.boolean, cv.Optional(CONF_JPEG_QUALITY, default=10): cv.int_range(min=6, max=63),
cv.Optional(CONF_SPECIAL_EFFECT, default="NONE"): cv.enum( cv.Optional(CONF_CONTRAST, default=0): camera_range_param,
ENUM_SPECIAL_EFFECT, upper=True cv.Optional(CONF_BRIGHTNESS, default=0): camera_range_param,
), cv.Optional(CONF_SATURATION, default=0): camera_range_param,
# exposure cv.Optional(CONF_VERTICAL_FLIP, default=True): cv.boolean,
cv.Optional(CONF_AGC_MODE, default="AUTO"): cv.enum( cv.Optional(CONF_HORIZONTAL_MIRROR, default=True): cv.boolean,
ENUM_GAIN_CONTROL_MODE, upper=True cv.Optional(CONF_SPECIAL_EFFECT, default="NONE"): cv.enum(
), ENUM_SPECIAL_EFFECT, upper=True
cv.Optional(CONF_AEC2, default=False): cv.boolean, ),
cv.Optional(CONF_AE_LEVEL, default=0): camera_range_param, # exposure
cv.Optional(CONF_AEC_VALUE, default=300): cv.int_range(min=0, max=1200), cv.Optional(CONF_AGC_MODE, default="AUTO"): cv.enum(
# gains ENUM_GAIN_CONTROL_MODE, upper=True
cv.Optional(CONF_AEC_MODE, default="AUTO"): cv.enum( ),
ENUM_GAIN_CONTROL_MODE, upper=True cv.Optional(CONF_AEC2, default=False): cv.boolean,
), cv.Optional(CONF_AE_LEVEL, default=0): camera_range_param,
cv.Optional(CONF_AGC_VALUE, default=0): cv.int_range(min=0, max=30), cv.Optional(CONF_AEC_VALUE, default=300): cv.int_range(min=0, max=1200),
cv.Optional(CONF_AGC_GAIN_CEILING, default="2X"): cv.enum( # gains
ENUM_GAIN_CEILING, upper=True cv.Optional(CONF_AEC_MODE, default="AUTO"): cv.enum(
), ENUM_GAIN_CONTROL_MODE, upper=True
# white balance ),
cv.Optional(CONF_WB_MODE, default="AUTO"): cv.enum(ENUM_WB_MODE, upper=True), cv.Optional(CONF_AGC_VALUE, default=0): cv.int_range(min=0, max=30),
# test pattern cv.Optional(CONF_AGC_GAIN_CEILING, default="2X"): cv.enum(
cv.Optional(CONF_TEST_PATTERN, default=False): cv.boolean, ENUM_GAIN_CEILING, upper=True
# framerates ),
cv.Optional(CONF_MAX_FRAMERATE, default="10 fps"): cv.All( # white balance
cv.framerate, cv.Range(min=0, min_included=False, max=60) cv.Optional(CONF_WB_MODE, default="AUTO"): cv.enum(
), ENUM_WB_MODE, upper=True
cv.Optional(CONF_IDLE_FRAMERATE, default="0.1 fps"): cv.All( ),
cv.framerate, cv.Range(min=0, max=1) # test pattern
), cv.Optional(CONF_TEST_PATTERN, default=False): cv.boolean,
cv.Optional(CONF_FRAME_BUFFER_COUNT, default=1): cv.int_range(min=1, max=2), # framerates
cv.Optional(CONF_ON_STREAM_START): automation.validate_automation( cv.Optional(CONF_MAX_FRAMERATE, default="10 fps"): cv.All(
{ cv.framerate, cv.Range(min=0, min_included=False, max=60)
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( ),
ESP32CameraStreamStartTrigger cv.Optional(CONF_IDLE_FRAMERATE, default="0.1 fps"): cv.All(
), cv.framerate, cv.Range(min=0, max=1)
} ),
), cv.Optional(CONF_FRAME_BUFFER_COUNT, default=1): cv.int_range(min=1, max=2),
cv.Optional(CONF_ON_STREAM_STOP): automation.validate_automation( cv.Optional(CONF_ON_STREAM_START): automation.validate_automation(
{ {
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
ESP32CameraStreamStopTrigger ESP32CameraStreamStartTrigger
), ),
} }
), ),
cv.Optional(CONF_ON_IMAGE): automation.validate_automation( cv.Optional(CONF_ON_STREAM_STOP): automation.validate_automation(
{ {
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESP32CameraImageTrigger), cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
} ESP32CameraStreamStopTrigger
), ),
} }
).extend(cv.COMPONENT_SCHEMA) ),
cv.Optional(CONF_ON_IMAGE): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
ESP32CameraImageTrigger
),
}
),
}
).extend(cv.COMPONENT_SCHEMA),
cv.has_exactly_one_key(CONF_I2C_PINS, CONF_I2C_ID),
)
SETTERS = { SETTERS = {
# pin assignment # pin assignment
@@ -271,7 +284,7 @@ SETTERS = {
async def to_code(config): async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
await setup_entity(var, config) await setup_entity(var, config, "camera")
await cg.register_component(var, config) await cg.register_component(var, config)
for key, setter in SETTERS.items(): for key, setter in SETTERS.items():
@@ -280,8 +293,12 @@ async def to_code(config):
extclk = config[CONF_EXTERNAL_CLOCK] extclk = config[CONF_EXTERNAL_CLOCK]
cg.add(var.set_external_clock(extclk[CONF_PIN], extclk[CONF_FREQUENCY])) cg.add(var.set_external_clock(extclk[CONF_PIN], extclk[CONF_FREQUENCY]))
i2c_pins = config[CONF_I2C_PINS] if i2c_id := config.get(CONF_I2C_ID):
cg.add(var.set_i2c_pins(i2c_pins[CONF_SDA], i2c_pins[CONF_SCL])) i2c_hub = await cg.get_variable(i2c_id)
cg.add(var.set_i2c_id(i2c_hub))
else:
i2c_pins = config[CONF_I2C_PINS]
cg.add(var.set_i2c_pins(i2c_pins[CONF_SDA], i2c_pins[CONF_SCL]))
cg.add(var.set_max_update_interval(1000 / config[CONF_MAX_FRAMERATE])) cg.add(var.set_max_update_interval(1000 / config[CONF_MAX_FRAMERATE]))
if config[CONF_IDLE_FRAMERATE] == 0: if config[CONF_IDLE_FRAMERATE] == 0:
cg.add(var.set_idle_update_interval(0)) cg.add(var.set_idle_update_interval(0))

View File

@@ -1,9 +1,9 @@
#ifdef USE_ESP32 #ifdef USE_ESP32
#include "esp32_camera.h" #include "esp32_camera.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
#include "esphome/core/application.h" #include "esphome/core/application.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
#include <freertos/task.h> #include <freertos/task.h>
@@ -16,6 +16,12 @@ static const char *const TAG = "esp32_camera";
void ESP32Camera::setup() { void ESP32Camera::setup() {
global_esp32_camera = this; global_esp32_camera = this;
#ifdef USE_I2C
if (this->i2c_bus_ != nullptr) {
this->config_.sccb_i2c_port = this->i2c_bus_->get_port();
}
#endif
/* initialize time to now */ /* initialize time to now */
this->last_update_ = millis(); this->last_update_ = millis();
@@ -246,6 +252,13 @@ void ESP32Camera::set_i2c_pins(uint8_t sda, uint8_t scl) {
this->config_.pin_sccb_sda = sda; this->config_.pin_sccb_sda = sda;
this->config_.pin_sccb_scl = scl; this->config_.pin_sccb_scl = scl;
} }
#ifdef USE_I2C
void ESP32Camera::set_i2c_id(i2c::InternalI2CBus *i2c_bus) {
this->i2c_bus_ = i2c_bus;
this->config_.pin_sccb_sda = -1;
this->config_.pin_sccb_scl = -1;
}
#endif // USE_I2C
void ESP32Camera::set_reset_pin(uint8_t pin) { this->config_.pin_reset = pin; } void ESP32Camera::set_reset_pin(uint8_t pin) { this->config_.pin_reset = pin; }
void ESP32Camera::set_power_down_pin(uint8_t pin) { this->config_.pin_pwdn = pin; } void ESP32Camera::set_power_down_pin(uint8_t pin) { this->config_.pin_pwdn = pin; }

View File

@@ -2,13 +2,17 @@
#ifdef USE_ESP32 #ifdef USE_ESP32
#include <esp_camera.h>
#include <freertos/FreeRTOS.h>
#include <freertos/queue.h>
#include "esphome/core/automation.h" #include "esphome/core/automation.h"
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/entity_base.h" #include "esphome/core/entity_base.h"
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
#include <esp_camera.h>
#include <freertos/FreeRTOS.h> #ifdef USE_I2C
#include <freertos/queue.h> #include "esphome/components/i2c/i2c_bus.h"
#endif // USE_I2C
namespace esphome { namespace esphome {
namespace esp32_camera { namespace esp32_camera {
@@ -118,6 +122,9 @@ class ESP32Camera : public EntityBase, public Component {
void set_pixel_clock_pin(uint8_t pin); void set_pixel_clock_pin(uint8_t pin);
void set_external_clock(uint8_t pin, uint32_t frequency); void set_external_clock(uint8_t pin, uint32_t frequency);
void set_i2c_pins(uint8_t sda, uint8_t scl); void set_i2c_pins(uint8_t sda, uint8_t scl);
#ifdef USE_I2C
void set_i2c_id(i2c::InternalI2CBus *i2c_bus);
#endif // USE_I2C
void set_reset_pin(uint8_t pin); void set_reset_pin(uint8_t pin);
void set_power_down_pin(uint8_t pin); void set_power_down_pin(uint8_t pin);
/* -- image */ /* -- image */
@@ -210,6 +217,9 @@ class ESP32Camera : public EntityBase, public Component {
uint32_t last_idle_request_{0}; uint32_t last_idle_request_{0};
uint32_t last_update_{0}; uint32_t last_update_{0};
#ifdef USE_I2C
i2c::InternalI2CBus *i2c_bus_{nullptr};
#endif // USE_I2C
}; };
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)

View File

@@ -0,0 +1,5 @@
import esphome.config_validation as cv
CONFIG_SCHEMA = cv.invalid(
"The esp32_hall component has been removed as of ESPHome 2025.7.0. See https://github.com/esphome/esphome/pull/9117 for details."
)

View File

@@ -18,8 +18,8 @@ from esphome.const import (
DEVICE_CLASS_MOTION, DEVICE_CLASS_MOTION,
) )
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
from esphome.cpp_generator import MockObjClass from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@nohat"] CODEOWNERS = ["@nohat"]
IS_PLATFORM_COMPONENT = True IS_PLATFORM_COMPONENT = True
@@ -59,6 +59,9 @@ _EVENT_SCHEMA = (
) )
_EVENT_SCHEMA.add_extra(entity_duplicate_validator("event"))
def event_schema( def event_schema(
class_: MockObjClass = cv.UNDEFINED, class_: MockObjClass = cv.UNDEFINED,
*, *,
@@ -88,7 +91,7 @@ EVENT_SCHEMA.add_extra(cv.deprecated_schema_constant("event"))
async def setup_event_core_(var, config, *, event_types: list[str]): async def setup_event_core_(var, config, *, event_types: list[str]):
await setup_entity(var, config) await setup_entity(var, config, "event")
for conf in config.get(CONF_ON_EVENT, []): for conf in config.get(CONF_ON_EVENT, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)

View File

@@ -32,7 +32,7 @@ from esphome.const import (
CONF_WEB_SERVER, CONF_WEB_SERVER,
) )
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_helpers import setup_entity from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
IS_PLATFORM_COMPONENT = True IS_PLATFORM_COMPONENT = True
@@ -161,6 +161,9 @@ _FAN_SCHEMA = (
) )
_FAN_SCHEMA.add_extra(entity_duplicate_validator("fan"))
def fan_schema( def fan_schema(
class_: cg.Pvariable, class_: cg.Pvariable,
*, *,
@@ -225,7 +228,7 @@ def validate_preset_modes(value):
async def setup_fan_core_(var, config): async def setup_fan_core_(var, config):
await setup_entity(var, config) await setup_entity(var, config, "fan")
cg.add(var.set_restore_mode(config[CONF_RESTORE_MODE])) cg.add(var.set_restore_mode(config[CONF_RESTORE_MODE]))

View File

@@ -1,6 +1,7 @@
from collections.abc import MutableMapping from collections.abc import MutableMapping
import functools import functools
import hashlib import hashlib
from itertools import accumulate
import logging import logging
import os import os
from pathlib import Path from pathlib import Path
@@ -468,8 +469,9 @@ class EFont:
class GlyphInfo: class GlyphInfo:
def __init__(self, data_len, advance, offset_x, offset_y, width, height): def __init__(self, glyph, data, advance, offset_x, offset_y, width, height):
self.data_len = data_len self.glyph = glyph
self.bitmap_data = data
self.advance = advance self.advance = advance
self.offset_x = offset_x self.offset_x = offset_x
self.offset_y = offset_y self.offset_y = offset_y
@@ -477,6 +479,62 @@ class GlyphInfo:
self.height = height self.height = height
def glyph_to_glyphinfo(glyph, font, size, bpp):
scale = 256 // (1 << bpp)
if not font.is_scalable:
sizes = [pt_to_px(x.size) for x in font.available_sizes]
if size in sizes:
font.select_size(sizes.index(size))
else:
font.set_pixel_sizes(size, 0)
flags = FT_LOAD_RENDER
if bpp != 1:
flags |= FT_LOAD_NO_BITMAP
else:
flags |= FT_LOAD_TARGET_MONO
font.load_char(glyph, flags)
width = font.glyph.bitmap.width
height = font.glyph.bitmap.rows
buffer = font.glyph.bitmap.buffer
pitch = font.glyph.bitmap.pitch
glyph_data = [0] * ((height * width * bpp + 7) // 8)
src_mode = font.glyph.bitmap.pixel_mode
pos = 0
for y in range(height):
for x in range(width):
if src_mode == ft_pixel_mode_mono:
pixel = (
(1 << bpp) - 1
if buffer[y * pitch + x // 8] & (1 << (7 - x % 8))
else 0
)
else:
pixel = buffer[y * pitch + x] // scale
for bit_num in range(bpp):
if pixel & (1 << (bpp - bit_num - 1)):
glyph_data[pos // 8] |= 0x80 >> (pos % 8)
pos += 1
ascender = pt_to_px(font.size.ascender)
if ascender == 0:
if not font.is_scalable:
ascender = size
else:
_LOGGER.error(
"Unable to determine ascender of font %s %s",
font.family_name,
font.style_name,
)
return GlyphInfo(
glyph,
glyph_data,
pt_to_px(font.glyph.metrics.horiAdvance),
font.glyph.bitmap_left,
ascender - font.glyph.bitmap_top,
width,
height,
)
async def to_code(config): async def to_code(config):
""" """
Collect all glyph codepoints, construct a map from a codepoint to a font file. Collect all glyph codepoints, construct a map from a codepoint to a font file.
@@ -506,98 +564,47 @@ async def to_code(config):
codepoints = list(point_set) codepoints = list(point_set)
codepoints.sort(key=functools.cmp_to_key(glyph_comparator)) codepoints.sort(key=functools.cmp_to_key(glyph_comparator))
glyph_args = {}
data = []
bpp = config[CONF_BPP] bpp = config[CONF_BPP]
scale = 256 // (1 << bpp)
size = config[CONF_SIZE] size = config[CONF_SIZE]
# create the data array for all glyphs # create the data array for all glyphs
for codepoint in codepoints: glyph_args = [
font = point_font_map[codepoint] glyph_to_glyphinfo(x, point_font_map[x], size, bpp) for x in codepoints
if not font.is_scalable: ]
sizes = [pt_to_px(x.size) for x in font.available_sizes] rhs = [HexInt(x) for x in flatten([x.bitmap_data for x in glyph_args])]
if size in sizes:
font.select_size(sizes.index(size))
else:
font.set_pixel_sizes(size, 0)
flags = FT_LOAD_RENDER
if bpp != 1:
flags |= FT_LOAD_NO_BITMAP
else:
flags |= FT_LOAD_TARGET_MONO
font.load_char(codepoint, flags)
width = font.glyph.bitmap.width
height = font.glyph.bitmap.rows
buffer = font.glyph.bitmap.buffer
pitch = font.glyph.bitmap.pitch
glyph_data = [0] * ((height * width * bpp + 7) // 8)
src_mode = font.glyph.bitmap.pixel_mode
pos = 0
for y in range(height):
for x in range(width):
if src_mode == ft_pixel_mode_mono:
pixel = (
(1 << bpp) - 1
if buffer[y * pitch + x // 8] & (1 << (7 - x % 8))
else 0
)
else:
pixel = buffer[y * pitch + x] // scale
for bit_num in range(bpp):
if pixel & (1 << (bpp - bit_num - 1)):
glyph_data[pos // 8] |= 0x80 >> (pos % 8)
pos += 1
ascender = pt_to_px(font.size.ascender)
if ascender == 0:
if not font.is_scalable:
ascender = size
else:
_LOGGER.error(
"Unable to determine ascender of font %s", config[CONF_FILE]
)
glyph_args[codepoint] = GlyphInfo(
len(data),
pt_to_px(font.glyph.metrics.horiAdvance),
font.glyph.bitmap_left,
ascender - font.glyph.bitmap_top,
width,
height,
)
data += glyph_data
rhs = [HexInt(x) for x in data]
prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
# Create the glyph table that points to data in the above array. # Create the glyph table that points to data in the above array.
glyph_initializer = [] glyph_initializer = [
for codepoint in codepoints: cg.StructInitializer(
glyph_initializer.append( GlyphData,
cg.StructInitializer( (
GlyphData, "a_char",
( cg.RawExpression(f"(const uint8_t *){cpp_string_escape(x.glyph)}"),
"a_char", ),
cg.RawExpression( (
f"(const uint8_t *){cpp_string_escape(codepoint)}" "data",
), cg.RawExpression(f"{str(prog_arr)} + {str(y - len(x.bitmap_data))}"),
), ),
( ("advance", x.advance),
"data", ("offset_x", x.offset_x),
cg.RawExpression( ("offset_y", x.offset_y),
f"{str(prog_arr)} + {str(glyph_args[codepoint].data_len)}" ("width", x.width),
), ("height", x.height),
),
("advance", glyph_args[codepoint].advance),
("offset_x", glyph_args[codepoint].offset_x),
("offset_y", glyph_args[codepoint].offset_y),
("width", glyph_args[codepoint].width),
("height", glyph_args[codepoint].height),
)
) )
for (x, y) in zip(
glyph_args, list(accumulate([len(x.bitmap_data) for x in glyph_args]))
)
]
glyphs = cg.static_const_array(config[CONF_RAW_GLYPH_ID], glyph_initializer) glyphs = cg.static_const_array(config[CONF_RAW_GLYPH_ID], glyph_initializer)
font_height = pt_to_px(base_font.size.height) font_height = pt_to_px(base_font.size.height)
ascender = pt_to_px(base_font.size.ascender) ascender = pt_to_px(base_font.size.ascender)
descender = abs(pt_to_px(base_font.size.descender))
g = glyph_to_glyphinfo("x", base_font, size, bpp)
xheight = g.height if len(g.bitmap_data) > 1 else 0
g = glyph_to_glyphinfo("X", base_font, size, bpp)
capheight = g.height if len(g.bitmap_data) > 1 else 0
if font_height == 0: if font_height == 0:
if not base_font.is_scalable: if not base_font.is_scalable:
font_height = size font_height = size
@@ -610,5 +617,8 @@ async def to_code(config):
len(glyph_initializer), len(glyph_initializer),
ascender, ascender,
font_height, font_height,
descender,
xheight,
capheight,
bpp, bpp,
) )

View File

@@ -45,8 +45,15 @@ void Glyph::scan_area(int *x1, int *y1, int *width, int *height) const {
*height = this->glyph_data_->height; *height = this->glyph_data_->height;
} }
Font::Font(const GlyphData *data, int data_nr, int baseline, int height, uint8_t bpp) Font::Font(const GlyphData *data, int data_nr, int baseline, int height, int descender, int xheight, int capheight,
: baseline_(baseline), height_(height), bpp_(bpp) { uint8_t bpp)
: baseline_(baseline),
height_(height),
descender_(descender),
linegap_(height - baseline - descender),
xheight_(xheight),
capheight_(capheight),
bpp_(bpp) {
glyphs_.reserve(data_nr); glyphs_.reserve(data_nr);
for (int i = 0; i < data_nr; ++i) for (int i = 0; i < data_nr; ++i)
glyphs_.emplace_back(&data[i]); glyphs_.emplace_back(&data[i]);

View File

@@ -50,11 +50,17 @@ class Font
public: public:
/** Construct the font with the given glyphs. /** Construct the font with the given glyphs.
* *
* @param glyphs A vector of glyphs, must be sorted lexicographically. * @param data A vector of glyphs, must be sorted lexicographically.
* @param data_nr The number of glyphs in data.
* @param baseline The y-offset from the top of the text to the baseline. * @param baseline The y-offset from the top of the text to the baseline.
* @param bottom The y-offset from the top of the text to the bottom (i.e. height). * @param height The y-offset from the top of the text to the bottom.
* @param descender The y-offset from the baseline to the lowest stroke in the font (e.g. from letters like g or p).
* @param xheight The height of lowercase letters, usually measured at the "x" glyph.
* @param capheight The height of capital letters, usually measured at the "X" glyph.
* @param bpp The bits per pixel used for this font. Used to read data out of the glyph bitmaps.
*/ */
Font(const GlyphData *data, int data_nr, int baseline, int height, uint8_t bpp = 1); Font(const GlyphData *data, int data_nr, int baseline, int height, int descender, int xheight, int capheight,
uint8_t bpp = 1);
int match_next_glyph(const uint8_t *str, int *match_length); int match_next_glyph(const uint8_t *str, int *match_length);
@@ -65,14 +71,23 @@ class Font
#endif #endif
inline int get_baseline() { return this->baseline_; } inline int get_baseline() { return this->baseline_; }
inline int get_height() { return this->height_; } inline int get_height() { return this->height_; }
inline int get_ascender() { return this->baseline_; }
inline int get_descender() { return this->descender_; }
inline int get_linegap() { return this->linegap_; }
inline int get_xheight() { return this->xheight_; }
inline int get_capheight() { return this->capheight_; }
inline int get_bpp() { return this->bpp_; } inline int get_bpp() { return this->bpp_; }
const std::vector<Glyph, ExternalRAMAllocator<Glyph>> &get_glyphs() const { return glyphs_; } const std::vector<Glyph, RAMAllocator<Glyph>> &get_glyphs() const { return glyphs_; }
protected: protected:
std::vector<Glyph, ExternalRAMAllocator<Glyph>> glyphs_; std::vector<Glyph, RAMAllocator<Glyph>> glyphs_;
int baseline_; int baseline_;
int height_; int height_;
int descender_;
int linegap_;
int xheight_;
int capheight_;
uint8_t bpp_; // bits per pixel uint8_t bpp_; // bits per pixel
}; };

View File

@@ -41,6 +41,6 @@ CONFIG_SCHEMA = cv.All(
async def to_code(config): async def to_code(config):
cg.add_build_flag("-DUSE_HOST") cg.add_build_flag("-DUSE_HOST")
cg.add_define("USE_ESPHOME_HOST_MAC_ADDRESS", config[CONF_MAC_ADDRESS].parts) cg.add_define("USE_ESPHOME_HOST_MAC_ADDRESS", config[CONF_MAC_ADDRESS].parts)
cg.add_build_flag("-std=c++17") cg.add_build_flag("-std=gnu++17")
cg.add_define("ESPHOME_BOARD", "host") cg.add_define("ESPHOME_BOARD", "host")
cg.add_platformio_option("platform", "platformio/native") cg.add_platformio_option("platform", "platformio/native")

View File

@@ -239,7 +239,7 @@ template<typename... Ts> class HttpRequestSendAction : public Action<Ts...> {
std::string response_body; std::string response_body;
if (this->capture_response_.value(x...)) { if (this->capture_response_.value(x...)) {
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE); RAMAllocator<uint8_t> allocator;
uint8_t *buf = allocator.allocate(max_length); uint8_t *buf = allocator.allocate(max_length);
if (buf != nullptr) { if (buf != nullptr) {
size_t read_index = 0; size_t read_index = 0;

View File

@@ -54,7 +54,7 @@ void HttpRequestUpdate::update_task(void *params) {
UPDATE_RETURN; UPDATE_RETURN;
} }
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE); RAMAllocator<uint8_t> allocator;
uint8_t *data = allocator.allocate(container->content_length); uint8_t *data = allocator.allocate(container->content_length);
if (data == nullptr) { if (data == nullptr) {
std::string msg = str_sprintf("Failed to allocate %d bytes for manifest", container->content_length); std::string msg = str_sprintf("Failed to allocate %d bytes for manifest", container->content_length);

View File

@@ -22,8 +22,9 @@ import esphome.final_validate as fv
CODEOWNERS = ["@esphome/core"] CODEOWNERS = ["@esphome/core"]
i2c_ns = cg.esphome_ns.namespace("i2c") i2c_ns = cg.esphome_ns.namespace("i2c")
I2CBus = i2c_ns.class_("I2CBus") I2CBus = i2c_ns.class_("I2CBus")
ArduinoI2CBus = i2c_ns.class_("ArduinoI2CBus", I2CBus, cg.Component) InternalI2CBus = i2c_ns.class_("InternalI2CBus", I2CBus)
IDFI2CBus = i2c_ns.class_("IDFI2CBus", I2CBus, cg.Component) ArduinoI2CBus = i2c_ns.class_("ArduinoI2CBus", InternalI2CBus, cg.Component)
IDFI2CBus = i2c_ns.class_("IDFI2CBus", InternalI2CBus, cg.Component)
I2CDevice = i2c_ns.class_("I2CDevice") I2CDevice = i2c_ns.class_("I2CDevice")
@@ -71,6 +72,7 @@ CONFIG_SCHEMA = cv.All(
@coroutine_with_priority(1.0) @coroutine_with_priority(1.0)
async def to_code(config): async def to_code(config):
cg.add_global(i2c_ns.using) cg.add_global(i2c_ns.using)
cg.add_define("USE_I2C")
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config) await cg.register_component(var, config)

View File

@@ -1,6 +1,6 @@
#pragma once #pragma once
#include <cstdint>
#include <cstddef> #include <cstddef>
#include <cstdint>
#include <utility> #include <utility>
#include <vector> #include <vector>
@@ -108,5 +108,12 @@ class I2CBus {
bool scan_{false}; ///< Should we scan ? Can be set in the yaml bool scan_{false}; ///< Should we scan ? Can be set in the yaml
}; };
class InternalI2CBus : public I2CBus {
public:
/// @brief Returns the I2C port number.
/// @return the port number of the internal I2C bus
virtual int get_port() const = 0;
};
} // namespace i2c } // namespace i2c
} // namespace esphome } // namespace esphome

View File

@@ -1,11 +1,11 @@
#ifdef USE_ARDUINO #ifdef USE_ARDUINO
#include "i2c_bus_arduino.h" #include "i2c_bus_arduino.h"
#include <Arduino.h>
#include <cstring>
#include "esphome/core/application.h" #include "esphome/core/application.h"
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include <Arduino.h>
#include <cstring>
namespace esphome { namespace esphome {
namespace i2c { namespace i2c {
@@ -23,6 +23,7 @@ void ArduinoI2CBus::setup() {
} else { } else {
wire_ = new TwoWire(next_bus_num); // NOLINT(cppcoreguidelines-owning-memory) wire_ = new TwoWire(next_bus_num); // NOLINT(cppcoreguidelines-owning-memory)
} }
this->port_ = next_bus_num;
next_bus_num++; next_bus_num++;
#elif defined(USE_ESP8266) #elif defined(USE_ESP8266)
wire_ = new TwoWire(); // NOLINT(cppcoreguidelines-owning-memory) wire_ = new TwoWire(); // NOLINT(cppcoreguidelines-owning-memory)

View File

@@ -2,9 +2,9 @@
#ifdef USE_ARDUINO #ifdef USE_ARDUINO
#include "i2c_bus.h"
#include "esphome/core/component.h"
#include <Wire.h> #include <Wire.h>
#include "esphome/core/component.h"
#include "i2c_bus.h"
namespace esphome { namespace esphome {
namespace i2c { namespace i2c {
@@ -15,7 +15,7 @@ enum RecoveryCode {
RECOVERY_COMPLETED, RECOVERY_COMPLETED,
}; };
class ArduinoI2CBus : public I2CBus, public Component { class ArduinoI2CBus : public InternalI2CBus, public Component {
public: public:
void setup() override; void setup() override;
void dump_config() override; void dump_config() override;
@@ -29,12 +29,15 @@ class ArduinoI2CBus : public I2CBus, public Component {
void set_frequency(uint32_t frequency) { frequency_ = frequency; } void set_frequency(uint32_t frequency) { frequency_ = frequency; }
void set_timeout(uint32_t timeout) { timeout_ = timeout; } void set_timeout(uint32_t timeout) { timeout_ = timeout; }
int get_port() const override { return this->port_; }
private: private:
void recover_(); void recover_();
void set_pins_and_clock_(); void set_pins_and_clock_();
RecoveryCode recovery_result_; RecoveryCode recovery_result_;
protected: protected:
int8_t port_{-1};
TwoWire *wire_; TwoWire *wire_;
uint8_t sda_pin_; uint8_t sda_pin_;
uint8_t scl_pin_; uint8_t scl_pin_;

View File

@@ -2,9 +2,9 @@
#ifdef USE_ESP_IDF #ifdef USE_ESP_IDF
#include "i2c_bus.h"
#include "esphome/core/component.h"
#include <driver/i2c.h> #include <driver/i2c.h>
#include "esphome/core/component.h"
#include "i2c_bus.h"
namespace esphome { namespace esphome {
namespace i2c { namespace i2c {
@@ -15,7 +15,7 @@ enum RecoveryCode {
RECOVERY_COMPLETED, RECOVERY_COMPLETED,
}; };
class IDFI2CBus : public I2CBus, public Component { class IDFI2CBus : public InternalI2CBus, public Component {
public: public:
void setup() override; void setup() override;
void dump_config() override; void dump_config() override;
@@ -31,6 +31,8 @@ class IDFI2CBus : public I2CBus, public Component {
void set_frequency(uint32_t frequency) { frequency_ = frequency; } void set_frequency(uint32_t frequency) { frequency_ = frequency; }
void set_timeout(uint32_t timeout) { timeout_ = timeout; } void set_timeout(uint32_t timeout) { timeout_ = timeout; }
int get_port() const override { return static_cast<int>(this->port_); }
private: private:
void recover_(); void recover_();
RecoveryCode recovery_result_; RecoveryCode recovery_result_;

View File

@@ -484,7 +484,7 @@ bool I2SAudioSpeaker::send_esp_err_to_event_group_(esp_err_t err) {
esp_err_t I2SAudioSpeaker::allocate_buffers_(size_t data_buffer_size, size_t ring_buffer_size) { esp_err_t I2SAudioSpeaker::allocate_buffers_(size_t data_buffer_size, size_t ring_buffer_size) {
if (this->data_buffer_ == nullptr) { if (this->data_buffer_ == nullptr) {
// Allocate data buffer for temporarily storing audio from the ring buffer before writing to the I2S bus // Allocate data buffer for temporarily storing audio from the ring buffer before writing to the I2S bus
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE); RAMAllocator<uint8_t> allocator;
this->data_buffer_ = allocator.allocate(data_buffer_size); this->data_buffer_ = allocator.allocate(data_buffer_size);
} }
@@ -698,7 +698,7 @@ void I2SAudioSpeaker::delete_task_(size_t buffer_size) {
this->audio_ring_buffer_.reset(); // Releases ownership of the shared_ptr this->audio_ring_buffer_.reset(); // Releases ownership of the shared_ptr
if (this->data_buffer_ != nullptr) { if (this->data_buffer_ != nullptr) {
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE); RAMAllocator<uint8_t> allocator;
allocator.deallocate(this->data_buffer_, buffer_size); allocator.deallocate(this->data_buffer_, buffer_size);
this->data_buffer_ = nullptr; this->data_buffer_ = nullptr;
} }

View File

@@ -57,8 +57,8 @@ void Inkplate6::setup() {
* Allocate buffers. May be called after setup to re-initialise if e.g. greyscale is changed. * Allocate buffers. May be called after setup to re-initialise if e.g. greyscale is changed.
*/ */
void Inkplate6::initialize_() { void Inkplate6::initialize_() {
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE); RAMAllocator<uint8_t> allocator;
ExternalRAMAllocator<uint32_t> allocator32(ExternalRAMAllocator<uint32_t>::ALLOW_FAILURE); RAMAllocator<uint32_t> allocator32;
uint32_t buffer_size = this->get_buffer_length_(); uint32_t buffer_size = this->get_buffer_length_();
if (buffer_size == 0) if (buffer_size == 0)
return; return;

View File

@@ -8,6 +8,8 @@
#include "esphome/components/sensor/sensor.h" #include "esphome/components/sensor/sensor.h"
#endif #endif
#include "esphome/core/application.h"
#define highbyte(val) (uint8_t)((val) >> 8) #define highbyte(val) (uint8_t)((val) >> 8)
#define lowbyte(val) (uint8_t)((val) &0xff) #define lowbyte(val) (uint8_t)((val) &0xff)
@@ -73,9 +75,9 @@ void LD2410Component::dump_config() {
#endif #endif
this->read_all_info(); this->read_all_info();
ESP_LOGCONFIG(TAG, ESP_LOGCONFIG(TAG,
" Throttle_ : %ums\n" " Throttle: %ums\n"
" MAC Address : %s\n" " MAC address: %s\n"
" Firmware Version : %s", " Firmware version: %s",
this->throttle_, const_cast<char *>(this->mac_.c_str()), const_cast<char *>(this->version_.c_str())); this->throttle_, const_cast<char *>(this->mac_.c_str()), const_cast<char *>(this->version_.c_str()));
} }
@@ -153,7 +155,7 @@ void LD2410Component::handle_periodic_data_(uint8_t *buffer, int len) {
/* /*
Reduce data update rate to prevent home assistant database size grow fast Reduce data update rate to prevent home assistant database size grow fast
*/ */
int32_t current_millis = millis(); int32_t current_millis = App.get_loop_component_start_time();
if (current_millis - last_periodic_millis_ < this->throttle_) if (current_millis - last_periodic_millis_ < this->throttle_)
return; return;
last_periodic_millis_ = current_millis; last_periodic_millis_ = current_millis;
@@ -299,21 +301,6 @@ const char MAC_FMT[] = "%02X:%02X:%02X:%02X:%02X:%02X";
const std::string UNKNOWN_MAC("unknown"); const std::string UNKNOWN_MAC("unknown");
const std::string NO_MAC("08:05:04:03:02:01"); const std::string NO_MAC("08:05:04:03:02:01");
std::string format_mac(uint8_t *buffer) {
std::string::size_type mac_size = 256;
std::string mac;
do {
mac.resize(mac_size + 1);
mac_size = std::snprintf(&mac[0], mac.size(), MAC_FMT, buffer[10], buffer[11], buffer[12], buffer[13], buffer[14],
buffer[15]);
} while (mac_size + 1 > mac.size());
mac.resize(mac_size);
if (mac == NO_MAC) {
return UNKNOWN_MAC;
}
return mac;
}
#ifdef USE_NUMBER #ifdef USE_NUMBER
std::function<void(void)> set_number_value(number::Number *n, float value) { std::function<void(void)> set_number_value(number::Number *n, float value) {
float normalized_value = value * 1.0; float normalized_value = value * 1.0;
@@ -328,40 +315,40 @@ std::function<void(void)> set_number_value(number::Number *n, float value) {
bool LD2410Component::handle_ack_data_(uint8_t *buffer, int len) { bool LD2410Component::handle_ack_data_(uint8_t *buffer, int len) {
ESP_LOGV(TAG, "Handling ACK DATA for COMMAND %02X", buffer[COMMAND]); ESP_LOGV(TAG, "Handling ACK DATA for COMMAND %02X", buffer[COMMAND]);
if (len < 10) { if (len < 10) {
ESP_LOGE(TAG, "Error with last command : incorrect length"); ESP_LOGE(TAG, "Invalid length");
return true; return true;
} }
if (buffer[0] != 0xFD || buffer[1] != 0xFC || buffer[2] != 0xFB || buffer[3] != 0xFA) { // check 4 frame start bytes if (buffer[0] != 0xFD || buffer[1] != 0xFC || buffer[2] != 0xFB || buffer[3] != 0xFA) { // check 4 frame start bytes
ESP_LOGE(TAG, "Error with last command : incorrect Header"); ESP_LOGE(TAG, "Invalid header");
return true; return true;
} }
if (buffer[COMMAND_STATUS] != 0x01) { if (buffer[COMMAND_STATUS] != 0x01) {
ESP_LOGE(TAG, "Error with last command : status != 0x01"); ESP_LOGE(TAG, "Invalid status");
return true; return true;
} }
if (this->two_byte_to_int_(buffer[8], buffer[9]) != 0x00) { if (this->two_byte_to_int_(buffer[8], buffer[9]) != 0x00) {
ESP_LOGE(TAG, "Error with last command , last buffer was: %u , %u", buffer[8], buffer[9]); ESP_LOGE(TAG, "Invalid command: %u, %u", buffer[8], buffer[9]);
return true; return true;
} }
switch (buffer[COMMAND]) { switch (buffer[COMMAND]) {
case lowbyte(CMD_ENABLE_CONF): case lowbyte(CMD_ENABLE_CONF):
ESP_LOGV(TAG, "Handled Enable conf command"); ESP_LOGV(TAG, "Enable conf");
break; break;
case lowbyte(CMD_DISABLE_CONF): case lowbyte(CMD_DISABLE_CONF):
ESP_LOGV(TAG, "Handled Disabled conf command"); ESP_LOGV(TAG, "Disabled conf");
break; break;
case lowbyte(CMD_SET_BAUD_RATE): case lowbyte(CMD_SET_BAUD_RATE):
ESP_LOGV(TAG, "Handled baud rate change command"); ESP_LOGV(TAG, "Baud rate change");
#ifdef USE_SELECT #ifdef USE_SELECT
if (this->baud_rate_select_ != nullptr) { if (this->baud_rate_select_ != nullptr) {
ESP_LOGE(TAG, "Change baud rate component config to %s and reinstall", this->baud_rate_select_->state.c_str()); ESP_LOGE(TAG, "Configure baud rate to %s and reinstall", this->baud_rate_select_->state.c_str());
} }
#endif #endif
break; break;
case lowbyte(CMD_VERSION): case lowbyte(CMD_VERSION):
this->version_ = format_version(buffer); this->version_ = format_version(buffer);
ESP_LOGV(TAG, "FW Version is: %s", const_cast<char *>(this->version_.c_str())); ESP_LOGV(TAG, "Firmware version: %s", const_cast<char *>(this->version_.c_str()));
#ifdef USE_TEXT_SENSOR #ifdef USE_TEXT_SENSOR
if (this->version_text_sensor_ != nullptr) { if (this->version_text_sensor_ != nullptr) {
this->version_text_sensor_->publish_state(this->version_); this->version_text_sensor_->publish_state(this->version_);
@@ -371,7 +358,7 @@ bool LD2410Component::handle_ack_data_(uint8_t *buffer, int len) {
case lowbyte(CMD_QUERY_DISTANCE_RESOLUTION): { case lowbyte(CMD_QUERY_DISTANCE_RESOLUTION): {
std::string distance_resolution = std::string distance_resolution =
DISTANCE_RESOLUTION_INT_TO_ENUM.at(this->two_byte_to_int_(buffer[10], buffer[11])); DISTANCE_RESOLUTION_INT_TO_ENUM.at(this->two_byte_to_int_(buffer[10], buffer[11]));
ESP_LOGV(TAG, "Distance resolution is: %s", const_cast<char *>(distance_resolution.c_str())); ESP_LOGV(TAG, "Distance resolution: %s", const_cast<char *>(distance_resolution.c_str()));
#ifdef USE_SELECT #ifdef USE_SELECT
if (this->distance_resolution_select_ != nullptr && if (this->distance_resolution_select_ != nullptr &&
this->distance_resolution_select_->state != distance_resolution) { this->distance_resolution_select_->state != distance_resolution) {
@@ -383,9 +370,9 @@ bool LD2410Component::handle_ack_data_(uint8_t *buffer, int len) {
this->light_function_ = LIGHT_FUNCTION_INT_TO_ENUM.at(buffer[10]); this->light_function_ = LIGHT_FUNCTION_INT_TO_ENUM.at(buffer[10]);
this->light_threshold_ = buffer[11] * 1.0; this->light_threshold_ = buffer[11] * 1.0;
this->out_pin_level_ = OUT_PIN_LEVEL_INT_TO_ENUM.at(buffer[12]); this->out_pin_level_ = OUT_PIN_LEVEL_INT_TO_ENUM.at(buffer[12]);
ESP_LOGV(TAG, "Light function is: %s", const_cast<char *>(this->light_function_.c_str())); ESP_LOGV(TAG, "Light function: %s", const_cast<char *>(this->light_function_.c_str()));
ESP_LOGV(TAG, "Light threshold is: %f", this->light_threshold_); ESP_LOGV(TAG, "Light threshold: %f", this->light_threshold_);
ESP_LOGV(TAG, "Out pin level is: %s", const_cast<char *>(this->out_pin_level_.c_str())); ESP_LOGV(TAG, "Out pin level: %s", const_cast<char *>(this->out_pin_level_.c_str()));
#ifdef USE_SELECT #ifdef USE_SELECT
if (this->light_function_select_ != nullptr && this->light_function_select_->state != this->light_function_) { if (this->light_function_select_ != nullptr && this->light_function_select_->state != this->light_function_) {
this->light_function_select_->publish_state(this->light_function_); this->light_function_select_->publish_state(this->light_function_);
@@ -406,11 +393,11 @@ bool LD2410Component::handle_ack_data_(uint8_t *buffer, int len) {
if (len < 20) { if (len < 20) {
return false; return false;
} }
this->mac_ = format_mac(buffer); this->mac_ = format_mac_address_pretty(&buffer[10]);
ESP_LOGV(TAG, "MAC Address is: %s", const_cast<char *>(this->mac_.c_str())); ESP_LOGV(TAG, "MAC address: %s", this->mac_.c_str());
#ifdef USE_TEXT_SENSOR #ifdef USE_TEXT_SENSOR
if (this->mac_text_sensor_ != nullptr) { if (this->mac_text_sensor_ != nullptr) {
this->mac_text_sensor_->publish_state(this->mac_); this->mac_text_sensor_->publish_state(this->mac_ == NO_MAC ? UNKNOWN_MAC : this->mac_);
} }
#endif #endif
#ifdef USE_SWITCH #ifdef USE_SWITCH
@@ -420,19 +407,19 @@ bool LD2410Component::handle_ack_data_(uint8_t *buffer, int len) {
#endif #endif
break; break;
case lowbyte(CMD_GATE_SENS): case lowbyte(CMD_GATE_SENS):
ESP_LOGV(TAG, "Handled sensitivity command"); ESP_LOGV(TAG, "Sensitivity");
break; break;
case lowbyte(CMD_BLUETOOTH): case lowbyte(CMD_BLUETOOTH):
ESP_LOGV(TAG, "Handled bluetooth command"); ESP_LOGV(TAG, "Bluetooth");
break; break;
case lowbyte(CMD_SET_DISTANCE_RESOLUTION): case lowbyte(CMD_SET_DISTANCE_RESOLUTION):
ESP_LOGV(TAG, "Handled set distance resolution command"); ESP_LOGV(TAG, "Set distance resolution");
break; break;
case lowbyte(CMD_SET_LIGHT_CONTROL): case lowbyte(CMD_SET_LIGHT_CONTROL):
ESP_LOGV(TAG, "Handled set light control command"); ESP_LOGV(TAG, "Set light control");
break; break;
case lowbyte(CMD_BT_PASSWORD): case lowbyte(CMD_BT_PASSWORD):
ESP_LOGV(TAG, "Handled set bluetooth password command"); ESP_LOGV(TAG, "Set bluetooth password");
break; break;
case lowbyte(CMD_QUERY): // Query parameters response case lowbyte(CMD_QUERY): // Query parameters response
{ {
@@ -532,7 +519,7 @@ void LD2410Component::set_baud_rate(const std::string &state) {
void LD2410Component::set_bluetooth_password(const std::string &password) { void LD2410Component::set_bluetooth_password(const std::string &password) {
if (password.length() != 6) { if (password.length() != 6) {
ESP_LOGE(TAG, "set_bluetooth_password(): invalid password length, must be exactly 6 chars '%s'", password.c_str()); ESP_LOGE(TAG, "Password must be exactly 6 chars");
return; return;
} }
this->set_config_mode_(true); this->set_config_mode_(true);
@@ -544,7 +531,7 @@ void LD2410Component::set_bluetooth_password(const std::string &password) {
void LD2410Component::set_engineering_mode(bool enable) { void LD2410Component::set_engineering_mode(bool enable) {
this->set_config_mode_(true); this->set_config_mode_(true);
last_engineering_mode_change_millis_ = millis(); last_engineering_mode_change_millis_ = App.get_loop_component_start_time();
uint8_t cmd = enable ? CMD_ENABLE_ENG : CMD_DISABLE_ENG; uint8_t cmd = enable ? CMD_ENABLE_ENG : CMD_DISABLE_ENG;
this->send_command_(cmd, nullptr, 0); this->send_command_(cmd, nullptr, 0);
this->set_config_mode_(false); this->set_config_mode_(false);

View File

@@ -1,4 +1,5 @@
#include "ld2420.h" #include "ld2420.h"
#include "esphome/core/application.h"
#include "esphome/core/helpers.h" #include "esphome/core/helpers.h"
/* /*
@@ -40,7 +41,7 @@ There are three documented parameters for modes:
00 04 = Energy output mode 00 04 = Energy output mode
This mode outputs detailed signal energy values for each gate and the target distance. This mode outputs detailed signal energy values for each gate and the target distance.
The data format consist of the following. The data format consist of the following.
Header HH, Length LL, Persence PP, Distance DD, 16 Gate Energies EE, Footer FF Header HH, Length LL, Presence PP, Distance DD, 16 Gate Energies EE, Footer FF
HH HH HH HH LL LL PP DD DD EE EE .. 16x .. FF FF FF FF HH HH HH HH LL LL PP DD DD EE EE .. 16x .. FF FF FF FF
F4 F3 F2 F1 23 00 00 00 00 00 00 .. .. .. .. F8 F7 F6 F5 F4 F3 F2 F1 23 00 00 00 00 00 00 .. .. .. .. F8 F7 F6 F5
00 00 = debug output mode 00 00 = debug output mode
@@ -67,10 +68,10 @@ float LD2420Component::get_setup_priority() const { return setup_priority::BUS;
void LD2420Component::dump_config() { void LD2420Component::dump_config() {
ESP_LOGCONFIG(TAG, ESP_LOGCONFIG(TAG,
"LD2420:\n" "LD2420:\n"
" Firmware Version : %7s\n" " Firmware version: %7s",
"LD2420 Number:",
this->ld2420_firmware_ver_); this->ld2420_firmware_ver_);
#ifdef USE_NUMBER #ifdef USE_NUMBER
ESP_LOGCONFIG(TAG, "Number:");
LOG_NUMBER(TAG, " Gate Timeout:", this->gate_timeout_number_); LOG_NUMBER(TAG, " Gate Timeout:", this->gate_timeout_number_);
LOG_NUMBER(TAG, " Gate Max Distance:", this->max_gate_distance_number_); LOG_NUMBER(TAG, " Gate Max Distance:", this->max_gate_distance_number_);
LOG_NUMBER(TAG, " Gate Min Distance:", this->min_gate_distance_number_); LOG_NUMBER(TAG, " Gate Min Distance:", this->min_gate_distance_number_);
@@ -86,10 +87,10 @@ void LD2420Component::dump_config() {
LOG_BUTTON(TAG, " Factory Reset:", this->factory_reset_button_); LOG_BUTTON(TAG, " Factory Reset:", this->factory_reset_button_);
LOG_BUTTON(TAG, " Restart Module:", this->restart_module_button_); LOG_BUTTON(TAG, " Restart Module:", this->restart_module_button_);
#endif #endif
ESP_LOGCONFIG(TAG, "LD2420 Select:"); ESP_LOGCONFIG(TAG, "Select:");
LOG_SELECT(TAG, " Operating Mode", this->operating_selector_); LOG_SELECT(TAG, " Operating Mode", this->operating_selector_);
if (this->get_firmware_int_(ld2420_firmware_ver_) < CALIBRATE_VERSION_MIN) { if (LD2420Component::get_firmware_int(this->ld2420_firmware_ver_) < CALIBRATE_VERSION_MIN) {
ESP_LOGW(TAG, "LD2420 Firmware Version %s and older are only supported in Simple Mode", ld2420_firmware_ver_); ESP_LOGW(TAG, "Firmware version %s and older supports Simple Mode only", this->ld2420_firmware_ver_);
} }
} }
@@ -102,7 +103,7 @@ uint8_t LD2420Component::calc_checksum(void *data, size_t size) {
return checksum; return checksum;
} }
int LD2420Component::get_firmware_int_(const char *version_string) { int LD2420Component::get_firmware_int(const char *version_string) {
std::string version_str = version_string; std::string version_str = version_string;
if (version_str[0] == 'v') { if (version_str[0] == 'v') {
version_str = version_str.substr(1); version_str = version_str.substr(1);
@@ -115,7 +116,7 @@ int LD2420Component::get_firmware_int_(const char *version_string) {
void LD2420Component::setup() { void LD2420Component::setup() {
ESP_LOGCONFIG(TAG, "Running setup"); ESP_LOGCONFIG(TAG, "Running setup");
if (this->set_config_mode(true) == LD2420_ERROR_TIMEOUT) { if (this->set_config_mode(true) == LD2420_ERROR_TIMEOUT) {
ESP_LOGE(TAG, "LD2420 module has failed to respond, check baud rate and serial connections."); ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
this->mark_failed(); this->mark_failed();
return; return;
} }
@@ -127,7 +128,7 @@ void LD2420Component::setup() {
const char *pfw = this->ld2420_firmware_ver_; const char *pfw = this->ld2420_firmware_ver_;
std::string fw_str(pfw); std::string fw_str(pfw);
for (auto &listener : listeners_) { for (auto &listener : this->listeners_) {
listener->on_fw_version(fw_str); listener->on_fw_version(fw_str);
} }
@@ -137,11 +138,11 @@ void LD2420Component::setup() {
} }
memcpy(&this->new_config, &this->current_config, sizeof(this->current_config)); memcpy(&this->new_config, &this->current_config, sizeof(this->current_config));
if (get_firmware_int_(ld2420_firmware_ver_) < CALIBRATE_VERSION_MIN) { if (LD2420Component::get_firmware_int(this->ld2420_firmware_ver_) < CALIBRATE_VERSION_MIN) {
this->set_operating_mode(OP_SIMPLE_MODE_STRING); this->set_operating_mode(OP_SIMPLE_MODE_STRING);
this->operating_selector_->publish_state(OP_SIMPLE_MODE_STRING); this->operating_selector_->publish_state(OP_SIMPLE_MODE_STRING);
this->set_mode_(CMD_SYSTEM_MODE_SIMPLE); this->set_mode_(CMD_SYSTEM_MODE_SIMPLE);
ESP_LOGW(TAG, "LD2420 Frimware Version %s and older are only supported in Simple Mode", ld2420_firmware_ver_); ESP_LOGW(TAG, "Firmware version %s and older supports Simple Mode only", this->ld2420_firmware_ver_);
} else { } else {
this->set_mode_(CMD_SYSTEM_MODE_ENERGY); this->set_mode_(CMD_SYSTEM_MODE_ENERGY);
this->operating_selector_->publish_state(OP_NORMAL_MODE_STRING); this->operating_selector_->publish_state(OP_NORMAL_MODE_STRING);
@@ -151,18 +152,17 @@ void LD2420Component::setup() {
#endif #endif
this->set_system_mode(this->system_mode_); this->set_system_mode(this->system_mode_);
this->set_config_mode(false); this->set_config_mode(false);
ESP_LOGCONFIG(TAG, "LD2420 setup complete.");
} }
void LD2420Component::apply_config_action() { void LD2420Component::apply_config_action() {
const uint8_t checksum = calc_checksum(&this->new_config, sizeof(this->new_config)); const uint8_t checksum = calc_checksum(&this->new_config, sizeof(this->new_config));
if (checksum == calc_checksum(&this->current_config, sizeof(this->current_config))) { if (checksum == calc_checksum(&this->current_config, sizeof(this->current_config))) {
ESP_LOGCONFIG(TAG, "No configuration change detected"); ESP_LOGD(TAG, "No configuration change detected");
return; return;
} }
ESP_LOGCONFIG(TAG, "Reconfiguring LD2420"); ESP_LOGD(TAG, "Reconfiguring");
if (this->set_config_mode(true) == LD2420_ERROR_TIMEOUT) { if (this->set_config_mode(true) == LD2420_ERROR_TIMEOUT) {
ESP_LOGE(TAG, "LD2420 module has failed to respond, check baud rate and serial connections."); ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
this->mark_failed(); this->mark_failed();
return; return;
} }
@@ -178,13 +178,12 @@ void LD2420Component::apply_config_action() {
this->set_system_mode(this->system_mode_); this->set_system_mode(this->system_mode_);
this->set_config_mode(false); // Disable config mode to save new values in LD2420 nvm this->set_config_mode(false); // Disable config mode to save new values in LD2420 nvm
this->set_operating_mode(OP_NORMAL_MODE_STRING); this->set_operating_mode(OP_NORMAL_MODE_STRING);
ESP_LOGCONFIG(TAG, "LD2420 reconfig complete.");
} }
void LD2420Component::factory_reset_action() { void LD2420Component::factory_reset_action() {
ESP_LOGCONFIG(TAG, "Setting factory defaults"); ESP_LOGD(TAG, "Setting factory defaults");
if (this->set_config_mode(true) == LD2420_ERROR_TIMEOUT) { if (this->set_config_mode(true) == LD2420_ERROR_TIMEOUT) {
ESP_LOGE(TAG, "LD2420 module has failed to respond, check baud rate and serial connections."); ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
this->mark_failed(); this->mark_failed();
return; return;
} }
@@ -207,18 +206,16 @@ void LD2420Component::factory_reset_action() {
this->init_gate_config_numbers(); this->init_gate_config_numbers();
this->refresh_gate_config_numbers(); this->refresh_gate_config_numbers();
#endif #endif
ESP_LOGCONFIG(TAG, "LD2420 factory reset complete.");
} }
void LD2420Component::restart_module_action() { void LD2420Component::restart_module_action() {
ESP_LOGCONFIG(TAG, "Restarting LD2420 module"); ESP_LOGD(TAG, "Restarting");
this->send_module_restart(); this->send_module_restart();
this->set_timeout(250, [this]() { this->set_timeout(250, [this]() {
this->set_config_mode(true); this->set_config_mode(true);
this->set_system_mode(system_mode_); this->set_system_mode(this->system_mode_);
this->set_config_mode(false); this->set_config_mode(false);
}); });
ESP_LOGCONFIG(TAG, "LD2420 Restarted.");
} }
void LD2420Component::revert_config_action() { void LD2420Component::revert_config_action() {
@@ -226,18 +223,18 @@ void LD2420Component::revert_config_action() {
#ifdef USE_NUMBER #ifdef USE_NUMBER
this->init_gate_config_numbers(); this->init_gate_config_numbers();
#endif #endif
ESP_LOGCONFIG(TAG, "Reverted config number edits."); ESP_LOGD(TAG, "Reverted config number edits");
} }
void LD2420Component::loop() { void LD2420Component::loop() {
// If there is a active send command do not process it here, the send command call will handle it. // If there is a active send command do not process it here, the send command call will handle it.
if (!get_cmd_active_()) { if (!this->get_cmd_active_()) {
if (!available()) if (!this->available())
return; return;
static uint8_t buffer[2048]; static uint8_t buffer[2048];
static uint8_t rx_data; static uint8_t rx_data;
while (available()) { while (this->available()) {
rx_data = read(); rx_data = this->read();
this->readline_(rx_data, buffer, sizeof(buffer)); this->readline_(rx_data, buffer, sizeof(buffer));
} }
} }
@@ -292,7 +289,7 @@ void LD2420Component::report_gate_data() {
void LD2420Component::set_operating_mode(const std::string &state) { void LD2420Component::set_operating_mode(const std::string &state) {
// If unsupported firmware ignore mode select // If unsupported firmware ignore mode select
if (get_firmware_int_(ld2420_firmware_ver_) >= CALIBRATE_VERSION_MIN) { if (LD2420Component::get_firmware_int(ld2420_firmware_ver_) >= CALIBRATE_VERSION_MIN) {
this->current_operating_mode = OP_MODE_TO_UINT.at(state); this->current_operating_mode = OP_MODE_TO_UINT.at(state);
// Entering Auto Calibrate we need to clear the privoiuos data collection // Entering Auto Calibrate we need to clear the privoiuos data collection
this->operating_selector_->publish_state(state); this->operating_selector_->publish_state(state);
@@ -365,13 +362,13 @@ void LD2420Component::handle_energy_mode_(uint8_t *buffer, int len) {
} }
// Resonable refresh rate for home assistant database size health // Resonable refresh rate for home assistant database size health
const int32_t current_millis = millis(); const int32_t current_millis = App.get_loop_component_start_time();
if (current_millis - this->last_periodic_millis < REFRESH_RATE_MS) if (current_millis - this->last_periodic_millis < REFRESH_RATE_MS)
return; return;
this->last_periodic_millis = current_millis; this->last_periodic_millis = current_millis;
for (auto &listener : this->listeners_) { for (auto &listener : this->listeners_) {
listener->on_distance(get_distance_()); listener->on_distance(this->get_distance_());
listener->on_presence(get_presence_()); listener->on_presence(this->get_presence_());
listener->on_energy(this->gate_energy_, sizeof(this->gate_energy_) / sizeof(this->gate_energy_[0])); listener->on_energy(this->gate_energy_, sizeof(this->gate_energy_) / sizeof(this->gate_energy_[0]));
} }
@@ -392,9 +389,9 @@ void LD2420Component::handle_simple_mode_(const uint8_t *inbuf, int len) {
char outbuf[bufsize]{0}; char outbuf[bufsize]{0};
while (true) { while (true) {
if (inbuf[pos - 2] == 'O' && inbuf[pos - 1] == 'F' && inbuf[pos] == 'F') { if (inbuf[pos - 2] == 'O' && inbuf[pos - 1] == 'F' && inbuf[pos] == 'F') {
set_presence_(false); this->set_presence_(false);
} else if (inbuf[pos - 1] == 'O' && inbuf[pos] == 'N') { } else if (inbuf[pos - 1] == 'O' && inbuf[pos] == 'N') {
set_presence_(true); this->set_presence_(true);
} }
if (inbuf[pos] >= '0' && inbuf[pos] <= '9') { if (inbuf[pos] >= '0' && inbuf[pos] <= '9') {
if (index < bufsize - 1) { if (index < bufsize - 1) {
@@ -411,18 +408,18 @@ void LD2420Component::handle_simple_mode_(const uint8_t *inbuf, int len) {
} }
outbuf[index] = '\0'; outbuf[index] = '\0';
if (index > 1) if (index > 1)
set_distance_(strtol(outbuf, &endptr, 10)); this->set_distance_(strtol(outbuf, &endptr, 10));
if (get_mode_() == CMD_SYSTEM_MODE_SIMPLE) { if (this->get_mode_() == CMD_SYSTEM_MODE_SIMPLE) {
// Resonable refresh rate for home assistant database size health // Resonable refresh rate for home assistant database size health
const int32_t current_millis = millis(); const int32_t current_millis = App.get_loop_component_start_time();
if (current_millis - this->last_normal_periodic_millis < REFRESH_RATE_MS) if (current_millis - this->last_normal_periodic_millis < REFRESH_RATE_MS)
return; return;
this->last_normal_periodic_millis = current_millis; this->last_normal_periodic_millis = current_millis;
for (auto &listener : this->listeners_) for (auto &listener : this->listeners_)
listener->on_distance(get_distance_()); listener->on_distance(this->get_distance_());
for (auto &listener : this->listeners_) for (auto &listener : this->listeners_)
listener->on_presence(get_presence_()); listener->on_presence(this->get_presence_());
} }
} }
@@ -433,10 +430,10 @@ void LD2420Component::handle_ack_data_(uint8_t *buffer, int len) {
uint8_t data_element = 0; uint8_t data_element = 0;
uint16_t data_pos = 0; uint16_t data_pos = 0;
if (this->cmd_reply_.length > CMD_MAX_BYTES) { if (this->cmd_reply_.length > CMD_MAX_BYTES) {
ESP_LOGW(TAG, "LD2420 reply - received command reply frame is corrupt, length exceeds %d bytes.", CMD_MAX_BYTES); ESP_LOGW(TAG, "Reply frame too long");
return; return;
} else if (this->cmd_reply_.length < 2) { } else if (this->cmd_reply_.length < 2) {
ESP_LOGW(TAG, "LD2420 reply - received command frame is corrupt, length is less than 2 bytes."); ESP_LOGW(TAG, "Command frame too short");
return; return;
} }
memcpy(&this->cmd_reply_.error, &buffer[CMD_ERROR_WORD], sizeof(this->cmd_reply_.error)); memcpy(&this->cmd_reply_.error, &buffer[CMD_ERROR_WORD], sizeof(this->cmd_reply_.error));
@@ -447,13 +444,13 @@ void LD2420Component::handle_ack_data_(uint8_t *buffer, int len) {
this->cmd_reply_.ack = true; this->cmd_reply_.ack = true;
switch ((uint16_t) this->cmd_reply_.command) { switch ((uint16_t) this->cmd_reply_.command) {
case (CMD_ENABLE_CONF): case (CMD_ENABLE_CONF):
ESP_LOGD(TAG, "LD2420 reply - set config enable: CMD = %2X %s", CMD_ENABLE_CONF, result); ESP_LOGV(TAG, "Set config enable: CMD = %2X %s", CMD_ENABLE_CONF, result);
break; break;
case (CMD_DISABLE_CONF): case (CMD_DISABLE_CONF):
ESP_LOGD(TAG, "LD2420 reply - set config disable: CMD = %2X %s", CMD_DISABLE_CONF, result); ESP_LOGV(TAG, "Set config disable: CMD = %2X %s", CMD_DISABLE_CONF, result);
break; break;
case (CMD_READ_REGISTER): case (CMD_READ_REGISTER):
ESP_LOGD(TAG, "LD2420 reply - read register: CMD = %2X %s", CMD_READ_REGISTER, result); ESP_LOGV(TAG, "Read register: CMD = %2X %s", CMD_READ_REGISTER, result);
// TODO Read/Write register is not implemented yet, this will get flushed out to a proper header file // TODO Read/Write register is not implemented yet, this will get flushed out to a proper header file
data_pos = 0x0A; data_pos = 0x0A;
for (uint16_t index = 0; index < (CMD_REG_DATA_REPLY_SIZE * // NOLINT for (uint16_t index = 0; index < (CMD_REG_DATA_REPLY_SIZE * // NOLINT
@@ -465,13 +462,13 @@ void LD2420Component::handle_ack_data_(uint8_t *buffer, int len) {
} }
break; break;
case (CMD_WRITE_REGISTER): case (CMD_WRITE_REGISTER):
ESP_LOGD(TAG, "LD2420 reply - write register: CMD = %2X %s", CMD_WRITE_REGISTER, result); ESP_LOGV(TAG, "Write register: CMD = %2X %s", CMD_WRITE_REGISTER, result);
break; break;
case (CMD_WRITE_ABD_PARAM): case (CMD_WRITE_ABD_PARAM):
ESP_LOGD(TAG, "LD2420 reply - write gate parameter(s): %2X %s", CMD_WRITE_ABD_PARAM, result); ESP_LOGV(TAG, "Write gate parameter(s): %2X %s", CMD_WRITE_ABD_PARAM, result);
break; break;
case (CMD_READ_ABD_PARAM): case (CMD_READ_ABD_PARAM):
ESP_LOGD(TAG, "LD2420 reply - read gate parameter(s): %2X %s", CMD_READ_ABD_PARAM, result); ESP_LOGV(TAG, "Read gate parameter(s): %2X %s", CMD_READ_ABD_PARAM, result);
data_pos = CMD_ABD_DATA_REPLY_START; data_pos = CMD_ABD_DATA_REPLY_START;
for (uint16_t index = 0; index < (CMD_ABD_DATA_REPLY_SIZE * // NOLINT for (uint16_t index = 0; index < (CMD_ABD_DATA_REPLY_SIZE * // NOLINT
((buffer[CMD_FRAME_DATA_LENGTH] - 4) / CMD_ABD_DATA_REPLY_SIZE)); ((buffer[CMD_FRAME_DATA_LENGTH] - 4) / CMD_ABD_DATA_REPLY_SIZE));
@@ -483,11 +480,11 @@ void LD2420Component::handle_ack_data_(uint8_t *buffer, int len) {
} }
break; break;
case (CMD_WRITE_SYS_PARAM): case (CMD_WRITE_SYS_PARAM):
ESP_LOGD(TAG, "LD2420 reply - set system parameter(s): %2X %s", CMD_WRITE_SYS_PARAM, result); ESP_LOGV(TAG, "Set system parameter(s): %2X %s", CMD_WRITE_SYS_PARAM, result);
break; break;
case (CMD_READ_VERSION): case (CMD_READ_VERSION):
memcpy(this->ld2420_firmware_ver_, &buffer[12], buffer[10]); memcpy(this->ld2420_firmware_ver_, &buffer[12], buffer[10]);
ESP_LOGD(TAG, "LD2420 reply - module firmware version: %7s %s", this->ld2420_firmware_ver_, result); ESP_LOGV(TAG, "Firmware version: %7s %s", this->ld2420_firmware_ver_, result);
break; break;
default: default:
break; break;
@@ -533,7 +530,7 @@ int LD2420Component::send_cmd_from_array(CmdFrameT frame) {
} }
while (!this->cmd_reply_.ack) { while (!this->cmd_reply_.ack) {
while (available()) { while (this->available()) {
this->readline_(read(), ack_buffer, sizeof(ack_buffer)); this->readline_(read(), ack_buffer, sizeof(ack_buffer));
} }
delay_microseconds_safe(1450); delay_microseconds_safe(1450);
@@ -548,7 +545,7 @@ int LD2420Component::send_cmd_from_array(CmdFrameT frame) {
if (this->cmd_reply_.ack) if (this->cmd_reply_.ack)
retry = 0; retry = 0;
if (this->cmd_reply_.error > 0) if (this->cmd_reply_.error > 0)
handle_cmd_error(error); this->handle_cmd_error(error);
} }
return error; return error;
} }
@@ -563,7 +560,7 @@ uint8_t LD2420Component::set_config_mode(bool enable) {
cmd_frame.data_length += sizeof(CMD_PROTOCOL_VER); cmd_frame.data_length += sizeof(CMD_PROTOCOL_VER);
} }
cmd_frame.footer = CMD_FRAME_FOOTER; cmd_frame.footer = CMD_FRAME_FOOTER;
ESP_LOGD(TAG, "Sending set config %s command: %2X", enable ? "enable" : "disable", cmd_frame.command); ESP_LOGV(TAG, "Sending set config %s command: %2X", enable ? "enable" : "disable", cmd_frame.command);
return this->send_cmd_from_array(cmd_frame); return this->send_cmd_from_array(cmd_frame);
} }
@@ -576,7 +573,7 @@ void LD2420Component::ld2420_restart() {
cmd_frame.header = CMD_FRAME_HEADER; cmd_frame.header = CMD_FRAME_HEADER;
cmd_frame.command = CMD_RESTART; cmd_frame.command = CMD_RESTART;
cmd_frame.footer = CMD_FRAME_FOOTER; cmd_frame.footer = CMD_FRAME_FOOTER;
ESP_LOGD(TAG, "Sending restart command: %2X", cmd_frame.command); ESP_LOGV(TAG, "Sending restart command: %2X", cmd_frame.command);
this->send_cmd_from_array(cmd_frame); this->send_cmd_from_array(cmd_frame);
} }
@@ -588,7 +585,7 @@ void LD2420Component::get_reg_value_(uint16_t reg) {
cmd_frame.data[1] = reg; cmd_frame.data[1] = reg;
cmd_frame.data_length += 2; cmd_frame.data_length += 2;
cmd_frame.footer = CMD_FRAME_FOOTER; cmd_frame.footer = CMD_FRAME_FOOTER;
ESP_LOGD(TAG, "Sending read register %4X command: %2X", reg, cmd_frame.command); ESP_LOGV(TAG, "Sending read register %4X command: %2X", reg, cmd_frame.command);
this->send_cmd_from_array(cmd_frame); this->send_cmd_from_array(cmd_frame);
} }
@@ -602,11 +599,11 @@ void LD2420Component::set_reg_value(uint16_t reg, uint16_t value) {
memcpy(&cmd_frame.data[cmd_frame.data_length], &value, sizeof(CMD_REG_DATA_REPLY_SIZE)); memcpy(&cmd_frame.data[cmd_frame.data_length], &value, sizeof(CMD_REG_DATA_REPLY_SIZE));
cmd_frame.data_length += 2; cmd_frame.data_length += 2;
cmd_frame.footer = CMD_FRAME_FOOTER; cmd_frame.footer = CMD_FRAME_FOOTER;
ESP_LOGD(TAG, "Sending write register %4X command: %2X data = %4X", reg, cmd_frame.command, value); ESP_LOGV(TAG, "Sending write register %4X command: %2X data = %4X", reg, cmd_frame.command, value);
this->send_cmd_from_array(cmd_frame); this->send_cmd_from_array(cmd_frame);
} }
void LD2420Component::handle_cmd_error(uint8_t error) { ESP_LOGI(TAG, "Command failed: %s", ERR_MESSAGE[error]); } void LD2420Component::handle_cmd_error(uint8_t error) { ESP_LOGE(TAG, "Command failed: %s", ERR_MESSAGE[error]); }
int LD2420Component::get_gate_threshold_(uint8_t gate) { int LD2420Component::get_gate_threshold_(uint8_t gate) {
uint8_t error; uint8_t error;
@@ -619,7 +616,7 @@ int LD2420Component::get_gate_threshold_(uint8_t gate) {
memcpy(&cmd_frame.data[cmd_frame.data_length], &CMD_GATE_STILL_THRESH[gate], sizeof(CMD_GATE_STILL_THRESH[gate])); memcpy(&cmd_frame.data[cmd_frame.data_length], &CMD_GATE_STILL_THRESH[gate], sizeof(CMD_GATE_STILL_THRESH[gate]));
cmd_frame.data_length += 2; cmd_frame.data_length += 2;
cmd_frame.footer = CMD_FRAME_FOOTER; cmd_frame.footer = CMD_FRAME_FOOTER;
ESP_LOGD(TAG, "Sending read gate %d high/low theshold command: %2X", gate, cmd_frame.command); ESP_LOGV(TAG, "Sending read gate %d high/low threshold command: %2X", gate, cmd_frame.command);
error = this->send_cmd_from_array(cmd_frame); error = this->send_cmd_from_array(cmd_frame);
if (error == 0) { if (error == 0) {
this->current_config.move_thresh[gate] = cmd_reply_.data[0]; this->current_config.move_thresh[gate] = cmd_reply_.data[0];
@@ -644,7 +641,7 @@ int LD2420Component::get_min_max_distances_timeout_() {
sizeof(CMD_TIMEOUT_REG)); // Register: global delay time sizeof(CMD_TIMEOUT_REG)); // Register: global delay time
cmd_frame.data_length += sizeof(CMD_TIMEOUT_REG); cmd_frame.data_length += sizeof(CMD_TIMEOUT_REG);
cmd_frame.footer = CMD_FRAME_FOOTER; cmd_frame.footer = CMD_FRAME_FOOTER;
ESP_LOGD(TAG, "Sending read gate min max and timeout command: %2X", cmd_frame.command); ESP_LOGV(TAG, "Sending read gate min max and timeout command: %2X", cmd_frame.command);
error = this->send_cmd_from_array(cmd_frame); error = this->send_cmd_from_array(cmd_frame);
if (error == 0) { if (error == 0) {
this->current_config.min_gate = (uint16_t) cmd_reply_.data[0]; this->current_config.min_gate = (uint16_t) cmd_reply_.data[0];
@@ -667,9 +664,9 @@ void LD2420Component::set_system_mode(uint16_t mode) {
memcpy(&cmd_frame.data[cmd_frame.data_length], &unknown_parm, sizeof(unknown_parm)); memcpy(&cmd_frame.data[cmd_frame.data_length], &unknown_parm, sizeof(unknown_parm));
cmd_frame.data_length += sizeof(unknown_parm); cmd_frame.data_length += sizeof(unknown_parm);
cmd_frame.footer = CMD_FRAME_FOOTER; cmd_frame.footer = CMD_FRAME_FOOTER;
ESP_LOGD(TAG, "Sending write system mode command: %2X", cmd_frame.command); ESP_LOGV(TAG, "Sending write system mode command: %2X", cmd_frame.command);
if (this->send_cmd_from_array(cmd_frame) == 0) if (this->send_cmd_from_array(cmd_frame) == 0)
set_mode_(mode); this->set_mode_(mode);
} }
void LD2420Component::get_firmware_version_() { void LD2420Component::get_firmware_version_() {
@@ -679,7 +676,7 @@ void LD2420Component::get_firmware_version_() {
cmd_frame.command = CMD_READ_VERSION; cmd_frame.command = CMD_READ_VERSION;
cmd_frame.footer = CMD_FRAME_FOOTER; cmd_frame.footer = CMD_FRAME_FOOTER;
ESP_LOGD(TAG, "Sending read firmware version command: %2X", cmd_frame.command); ESP_LOGV(TAG, "Sending read firmware version command: %2X", cmd_frame.command);
this->send_cmd_from_array(cmd_frame); this->send_cmd_from_array(cmd_frame);
} }
@@ -712,7 +709,7 @@ void LD2420Component::set_min_max_distances_timeout(uint32_t max_gate_distance,
cmd_frame.data_length += sizeof(timeout); cmd_frame.data_length += sizeof(timeout);
cmd_frame.footer = CMD_FRAME_FOOTER; cmd_frame.footer = CMD_FRAME_FOOTER;
ESP_LOGD(TAG, "Sending write gate min max and timeout command: %2X", cmd_frame.command); ESP_LOGV(TAG, "Sending write gate min max and timeout command: %2X", cmd_frame.command);
this->send_cmd_from_array(cmd_frame); this->send_cmd_from_array(cmd_frame);
} }
@@ -738,7 +735,7 @@ void LD2420Component::set_gate_threshold(uint8_t gate) {
sizeof(this->new_config.still_thresh[gate])); sizeof(this->new_config.still_thresh[gate]));
cmd_frame.data_length += sizeof(this->new_config.still_thresh[gate]); cmd_frame.data_length += sizeof(this->new_config.still_thresh[gate]);
cmd_frame.footer = CMD_FRAME_FOOTER; cmd_frame.footer = CMD_FRAME_FOOTER;
ESP_LOGD(TAG, "Sending set gate %4X sensitivity command: %2X", gate, cmd_frame.command); ESP_LOGV(TAG, "Sending set gate %4X sensitivity command: %2X", gate, cmd_frame.command);
this->send_cmd_from_array(cmd_frame); this->send_cmd_from_array(cmd_frame);
} }

View File

@@ -179,7 +179,7 @@ class LD2420Component : public Component, public uart::UARTDevice {
void set_operating_mode(const std::string &state); void set_operating_mode(const std::string &state);
void auto_calibrate_sensitivity(); void auto_calibrate_sensitivity();
void update_radar_data(uint16_t const *gate_energy, uint8_t sample_number); void update_radar_data(uint16_t const *gate_energy, uint8_t sample_number);
uint8_t calc_checksum(void *data, size_t size); static uint8_t calc_checksum(void *data, size_t size);
RegConfigT current_config; RegConfigT current_config;
RegConfigT new_config; RegConfigT new_config;
@@ -222,7 +222,7 @@ class LD2420Component : public Component, public uart::UARTDevice {
volatile bool ack; volatile bool ack;
}; };
int get_firmware_int_(const char *version_string); static int get_firmware_int(const char *version_string);
void get_firmware_version_(); void get_firmware_version_();
int get_gate_threshold_(uint8_t gate); int get_gate_threshold_(uint8_t gate);
void get_reg_value_(uint16_t reg); void get_reg_value_(uint16_t reg);

View File

@@ -6,7 +6,9 @@
#ifdef USE_SENSOR #ifdef USE_SENSOR
#include "esphome/components/sensor/sensor.h" #include "esphome/components/sensor/sensor.h"
#endif #endif
#include "esphome/core/application.h"
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#define highbyte(val) (uint8_t)((val) >> 8) #define highbyte(val) (uint8_t)((val) >> 8)
#define lowbyte(val) (uint8_t)((val) &0xff) #define lowbyte(val) (uint8_t)((val) &0xff)
@@ -96,11 +98,6 @@ static inline std::string get_direction(int16_t speed) {
return STATIONARY; return STATIONARY;
} }
static inline std::string format_mac(uint8_t *buffer) {
return str_snprintf("%02X:%02X:%02X:%02X:%02X:%02X", 17, buffer[10], buffer[11], buffer[12], buffer[13], buffer[14],
buffer[15]);
}
static inline std::string format_version(uint8_t *buffer) { static inline std::string format_version(uint8_t *buffer) {
return str_sprintf("%u.%02X.%02X%02X%02X%02X", buffer[13], buffer[12], buffer[17], buffer[16], buffer[15], return str_sprintf("%u.%02X.%02X%02X%02X%02X", buffer[13], buffer[12], buffer[17], buffer[16], buffer[15],
buffer[14]); buffer[14]);
@@ -120,7 +117,7 @@ void LD2450Component::setup() {
} }
void LD2450Component::dump_config() { void LD2450Component::dump_config() {
ESP_LOGCONFIG(TAG, "HLK-LD2450 Human motion tracking radar module:"); ESP_LOGCONFIG(TAG, "LD2450:");
#ifdef USE_BINARY_SENSOR #ifdef USE_BINARY_SENSOR
LOG_BINARY_SENSOR(" ", "TargetBinarySensor", this->target_binary_sensor_); LOG_BINARY_SENSOR(" ", "TargetBinarySensor", this->target_binary_sensor_);
LOG_BINARY_SENSOR(" ", "MovingTargetBinarySensor", this->moving_target_binary_sensor_); LOG_BINARY_SENSOR(" ", "MovingTargetBinarySensor", this->moving_target_binary_sensor_);
@@ -189,9 +186,9 @@ void LD2450Component::dump_config() {
LOG_NUMBER(" ", "PresenceTimeoutNumber", this->presence_timeout_number_); LOG_NUMBER(" ", "PresenceTimeoutNumber", this->presence_timeout_number_);
#endif #endif
ESP_LOGCONFIG(TAG, ESP_LOGCONFIG(TAG,
" Throttle : %ums\n" " Throttle: %ums\n"
" MAC Address : %s\n" " MAC Address: %s\n"
" Firmware version : %s", " Firmware version: %s",
this->throttle_, const_cast<char *>(this->mac_.c_str()), const_cast<char *>(this->version_.c_str())); this->throttle_, const_cast<char *>(this->mac_.c_str()), const_cast<char *>(this->version_.c_str()));
} }
@@ -266,8 +263,7 @@ bool LD2450Component::get_timeout_status_(uint32_t check_millis) {
if (this->timeout_ == 0) { if (this->timeout_ == 0) {
this->timeout_ = ld2450::convert_seconds_to_ms(DEFAULT_PRESENCE_TIMEOUT); this->timeout_ = ld2450::convert_seconds_to_ms(DEFAULT_PRESENCE_TIMEOUT);
} }
auto current_millis = millis(); return App.get_loop_component_start_time() - check_millis >= this->timeout_;
return current_millis - check_millis >= this->timeout_;
} }
// Extract, store and publish zone details LD2450 buffer // Extract, store and publish zone details LD2450 buffer
@@ -354,25 +350,24 @@ void LD2450Component::send_command_(uint8_t command, const uint8_t *command_valu
// Header Target 1 Target 2 Target 3 End // Header Target 1 Target 2 Target 3 End
void LD2450Component::handle_periodic_data_(uint8_t *buffer, uint8_t len) { void LD2450Component::handle_periodic_data_(uint8_t *buffer, uint8_t len) {
if (len < 29) { // header (4 bytes) + 8 x 3 target data + footer (2 bytes) if (len < 29) { // header (4 bytes) + 8 x 3 target data + footer (2 bytes)
ESP_LOGE(TAG, "Periodic data: invalid message length"); ESP_LOGE(TAG, "Invalid message length");
return; return;
} }
if (buffer[0] != 0xAA || buffer[1] != 0xFF || buffer[2] != 0x03 || buffer[3] != 0x00) { // header if (buffer[0] != 0xAA || buffer[1] != 0xFF || buffer[2] != 0x03 || buffer[3] != 0x00) { // header
ESP_LOGE(TAG, "Periodic data: invalid message header"); ESP_LOGE(TAG, "Invalid message header");
return; return;
} }
if (buffer[len - 2] != 0x55 || buffer[len - 1] != 0xCC) { // footer if (buffer[len - 2] != 0x55 || buffer[len - 1] != 0xCC) { // footer
ESP_LOGE(TAG, "Periodic data: invalid message footer"); ESP_LOGE(TAG, "Invalid message footer");
return; return;
} }
auto current_millis = millis(); if (App.get_loop_component_start_time() - this->last_periodic_millis_ < this->throttle_) {
if (current_millis - this->last_periodic_millis_ < this->throttle_) {
ESP_LOGV(TAG, "Throttling: %d", this->throttle_); ESP_LOGV(TAG, "Throttling: %d", this->throttle_);
return; return;
} }
this->last_periodic_millis_ = current_millis; this->last_periodic_millis_ = App.get_loop_component_start_time();
int16_t target_count = 0; int16_t target_count = 0;
int16_t still_target_count = 0; int16_t still_target_count = 0;
@@ -555,13 +550,13 @@ void LD2450Component::handle_periodic_data_(uint8_t *buffer, uint8_t len) {
#ifdef USE_SENSOR #ifdef USE_SENSOR
// For presence timeout check // For presence timeout check
if (target_count > 0) { if (target_count > 0) {
this->presence_millis_ = millis(); this->presence_millis_ = App.get_loop_component_start_time();
} }
if (moving_target_count > 0) { if (moving_target_count > 0) {
this->moving_presence_millis_ = millis(); this->moving_presence_millis_ = App.get_loop_component_start_time();
} }
if (still_target_count > 0) { if (still_target_count > 0) {
this->still_presence_millis_ = millis(); this->still_presence_millis_ = App.get_loop_component_start_time();
} }
#endif #endif
} }
@@ -569,31 +564,31 @@ void LD2450Component::handle_periodic_data_(uint8_t *buffer, uint8_t len) {
bool LD2450Component::handle_ack_data_(uint8_t *buffer, uint8_t len) { bool LD2450Component::handle_ack_data_(uint8_t *buffer, uint8_t len) {
ESP_LOGV(TAG, "Handling ack data for command %02X", buffer[COMMAND]); ESP_LOGV(TAG, "Handling ack data for command %02X", buffer[COMMAND]);
if (len < 10) { if (len < 10) {
ESP_LOGE(TAG, "Ack data: invalid length"); ESP_LOGE(TAG, "Invalid ack length");
return true; return true;
} }
if (buffer[0] != 0xFD || buffer[1] != 0xFC || buffer[2] != 0xFB || buffer[3] != 0xFA) { // frame header if (buffer[0] != 0xFD || buffer[1] != 0xFC || buffer[2] != 0xFB || buffer[3] != 0xFA) { // frame header
ESP_LOGE(TAG, "Ack data: invalid header (command %02X)", buffer[COMMAND]); ESP_LOGE(TAG, "Invalid ack header (command %02X)", buffer[COMMAND]);
return true; return true;
} }
if (buffer[COMMAND_STATUS] != 0x01) { if (buffer[COMMAND_STATUS] != 0x01) {
ESP_LOGE(TAG, "Ack data: invalid status"); ESP_LOGE(TAG, "Invalid ack status");
return true; return true;
} }
if (buffer[8] || buffer[9]) { if (buffer[8] || buffer[9]) {
ESP_LOGE(TAG, "Ack data: last buffer was %u, %u", buffer[8], buffer[9]); ESP_LOGE(TAG, "Last buffer was %u, %u", buffer[8], buffer[9]);
return true; return true;
} }
switch (buffer[COMMAND]) { switch (buffer[COMMAND]) {
case lowbyte(CMD_ENABLE_CONF): case lowbyte(CMD_ENABLE_CONF):
ESP_LOGV(TAG, "Got enable conf command"); ESP_LOGV(TAG, "Enable conf command");
break; break;
case lowbyte(CMD_DISABLE_CONF): case lowbyte(CMD_DISABLE_CONF):
ESP_LOGV(TAG, "Got disable conf command"); ESP_LOGV(TAG, "Disable conf command");
break; break;
case lowbyte(CMD_SET_BAUD_RATE): case lowbyte(CMD_SET_BAUD_RATE):
ESP_LOGV(TAG, "Got baud rate change command"); ESP_LOGV(TAG, "Baud rate change command");
#ifdef USE_SELECT #ifdef USE_SELECT
if (this->baud_rate_select_ != nullptr) { if (this->baud_rate_select_ != nullptr) {
ESP_LOGV(TAG, "Change baud rate to %s", this->baud_rate_select_->state.c_str()); ESP_LOGV(TAG, "Change baud rate to %s", this->baud_rate_select_->state.c_str());
@@ -613,7 +608,7 @@ bool LD2450Component::handle_ack_data_(uint8_t *buffer, uint8_t len) {
if (len < 20) { if (len < 20) {
return false; return false;
} }
this->mac_ = ld2450::format_mac(buffer); this->mac_ = format_mac_address_pretty(&buffer[10]);
ESP_LOGV(TAG, "MAC address: %s", this->mac_.c_str()); ESP_LOGV(TAG, "MAC address: %s", this->mac_.c_str());
#ifdef USE_TEXT_SENSOR #ifdef USE_TEXT_SENSOR
if (this->mac_text_sensor_ != nullptr) { if (this->mac_text_sensor_ != nullptr) {
@@ -622,15 +617,15 @@ bool LD2450Component::handle_ack_data_(uint8_t *buffer, uint8_t len) {
#endif #endif
#ifdef USE_SWITCH #ifdef USE_SWITCH
if (this->bluetooth_switch_ != nullptr) { if (this->bluetooth_switch_ != nullptr) {
this->bluetooth_switch_->publish_state(this->mac_ != NO_MAC); this->bluetooth_switch_->publish_state(this->mac_ != UNKNOWN_MAC);
} }
#endif #endif
break; break;
case lowbyte(CMD_BLUETOOTH): case lowbyte(CMD_BLUETOOTH):
ESP_LOGV(TAG, "Got Bluetooth command"); ESP_LOGV(TAG, "Bluetooth command");
break; break;
case lowbyte(CMD_SINGLE_TARGET_MODE): case lowbyte(CMD_SINGLE_TARGET_MODE):
ESP_LOGV(TAG, "Got single target conf command"); ESP_LOGV(TAG, "Single target conf command");
#ifdef USE_SWITCH #ifdef USE_SWITCH
if (this->multi_target_switch_ != nullptr) { if (this->multi_target_switch_ != nullptr) {
this->multi_target_switch_->publish_state(false); this->multi_target_switch_->publish_state(false);
@@ -638,7 +633,7 @@ bool LD2450Component::handle_ack_data_(uint8_t *buffer, uint8_t len) {
#endif #endif
break; break;
case lowbyte(CMD_MULTI_TARGET_MODE): case lowbyte(CMD_MULTI_TARGET_MODE):
ESP_LOGV(TAG, "Got multi target conf command"); ESP_LOGV(TAG, "Multi target conf command");
#ifdef USE_SWITCH #ifdef USE_SWITCH
if (this->multi_target_switch_ != nullptr) { if (this->multi_target_switch_ != nullptr) {
this->multi_target_switch_->publish_state(true); this->multi_target_switch_->publish_state(true);
@@ -646,7 +641,7 @@ bool LD2450Component::handle_ack_data_(uint8_t *buffer, uint8_t len) {
#endif #endif
break; break;
case lowbyte(CMD_QUERY_TARGET_MODE): case lowbyte(CMD_QUERY_TARGET_MODE):
ESP_LOGV(TAG, "Got query target tracking mode command"); ESP_LOGV(TAG, "Query target tracking mode command");
#ifdef USE_SWITCH #ifdef USE_SWITCH
if (this->multi_target_switch_ != nullptr) { if (this->multi_target_switch_ != nullptr) {
this->multi_target_switch_->publish_state(buffer[10] == 0x02); this->multi_target_switch_->publish_state(buffer[10] == 0x02);
@@ -654,7 +649,7 @@ bool LD2450Component::handle_ack_data_(uint8_t *buffer, uint8_t len) {
#endif #endif
break; break;
case lowbyte(CMD_QUERY_ZONE): case lowbyte(CMD_QUERY_ZONE):
ESP_LOGV(TAG, "Got query zone conf command"); ESP_LOGV(TAG, "Query zone conf command");
this->zone_type_ = std::stoi(std::to_string(buffer[10]), nullptr, 16); this->zone_type_ = std::stoi(std::to_string(buffer[10]), nullptr, 16);
this->publish_zone_type(); this->publish_zone_type();
#ifdef USE_SELECT #ifdef USE_SELECT
@@ -674,7 +669,7 @@ bool LD2450Component::handle_ack_data_(uint8_t *buffer, uint8_t len) {
this->process_zone_(buffer); this->process_zone_(buffer);
break; break;
case lowbyte(CMD_SET_ZONE): case lowbyte(CMD_SET_ZONE):
ESP_LOGV(TAG, "Got set zone conf command"); ESP_LOGV(TAG, "Set zone conf command");
this->query_zone_info(); this->query_zone_info();
break; break;
default: default:

View File

@@ -38,8 +38,8 @@ from esphome.const import (
CONF_WHITE, CONF_WHITE,
) )
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
from esphome.cpp_generator import MockObjClass from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
from .automation import LIGHT_STATE_SCHEMA from .automation import LIGHT_STATE_SCHEMA
from .effects import ( from .effects import (
@@ -110,6 +110,8 @@ LIGHT_SCHEMA = (
) )
) )
LIGHT_SCHEMA.add_extra(entity_duplicate_validator("light"))
BINARY_LIGHT_SCHEMA = LIGHT_SCHEMA.extend( BINARY_LIGHT_SCHEMA = LIGHT_SCHEMA.extend(
{ {
cv.Optional(CONF_EFFECTS): validate_effects(BINARY_EFFECTS), cv.Optional(CONF_EFFECTS): validate_effects(BINARY_EFFECTS),
@@ -207,7 +209,7 @@ def validate_color_temperature_channels(value):
async def setup_light_core_(light_var, output_var, config): async def setup_light_core_(light_var, output_var, config):
await setup_entity(light_var, config) await setup_entity(light_var, config, "light")
cg.add(light_var.set_restore_mode(config[CONF_RESTORE_MODE])) cg.add(light_var.set_restore_mode(config[CONF_RESTORE_MODE]))

View File

@@ -14,8 +14,8 @@ from esphome.const import (
CONF_WEB_SERVER, CONF_WEB_SERVER,
) )
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
from esphome.cpp_generator import MockObjClass from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@esphome/core"] CODEOWNERS = ["@esphome/core"]
IS_PLATFORM_COMPONENT = True IS_PLATFORM_COMPONENT = True
@@ -67,6 +67,9 @@ _LOCK_SCHEMA = (
) )
_LOCK_SCHEMA.add_extra(entity_duplicate_validator("lock"))
def lock_schema( def lock_schema(
class_: MockObjClass = cv.UNDEFINED, class_: MockObjClass = cv.UNDEFINED,
*, *,
@@ -94,7 +97,7 @@ LOCK_SCHEMA.add_extra(cv.deprecated_schema_constant("lock"))
async def _setup_lock_core(var, config): async def _setup_lock_core(var, config):
await setup_entity(var, config) await setup_entity(var, config, "lock")
for conf in config.get(CONF_ON_LOCK, []): for conf in config.get(CONF_ON_LOCK, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)

View File

@@ -184,7 +184,9 @@ CONFIG_SCHEMA = cv.All(
{ {
cv.GenerateID(): cv.declare_id(Logger), cv.GenerateID(): cv.declare_id(Logger),
cv.Optional(CONF_BAUD_RATE, default=115200): cv.positive_int, cv.Optional(CONF_BAUD_RATE, default=115200): cv.positive_int,
cv.Optional(CONF_TX_BUFFER_SIZE, default=512): cv.validate_bytes, cv.Optional(CONF_TX_BUFFER_SIZE, default=512): cv.All(
cv.validate_bytes, cv.int_range(min=160, max=65535)
),
cv.Optional(CONF_DEASSERT_RTS_DTR, default=False): cv.boolean, cv.Optional(CONF_DEASSERT_RTS_DTR, default=False): cv.boolean,
cv.SplitDefault( cv.SplitDefault(
CONF_TASK_LOG_BUFFER_SIZE, CONF_TASK_LOG_BUFFER_SIZE,

View File

@@ -24,7 +24,7 @@ static const char *const TAG = "logger";
// - Messages are serialized through main loop for proper console output // - Messages are serialized through main loop for proper console output
// - Fallback to emergency console logging only if ring buffer is full // - Fallback to emergency console logging only if ring buffer is full
// - WITHOUT task log buffer: Only emergency console output, no callbacks // - WITHOUT task log buffer: Only emergency console output, no callbacks
void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *format, va_list args) { // NOLINT void HOT Logger::log_vprintf_(uint8_t level, const char *tag, int line, const char *format, va_list args) { // NOLINT
if (level > this->level_for(tag)) if (level > this->level_for(tag))
return; return;
@@ -46,8 +46,8 @@ void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *
bool message_sent = false; bool message_sent = false;
#ifdef USE_ESPHOME_TASK_LOG_BUFFER #ifdef USE_ESPHOME_TASK_LOG_BUFFER
// For non-main tasks, queue the message for callbacks - but only if we have any callbacks registered // For non-main tasks, queue the message for callbacks - but only if we have any callbacks registered
message_sent = this->log_buffer_->send_message_thread_safe(static_cast<uint8_t>(level), tag, message_sent =
static_cast<uint16_t>(line), current_task, format, args); this->log_buffer_->send_message_thread_safe(level, tag, static_cast<uint16_t>(line), current_task, format, args);
#endif // USE_ESPHOME_TASK_LOG_BUFFER #endif // USE_ESPHOME_TASK_LOG_BUFFER
// Emergency console logging for non-main tasks when ring buffer is full or disabled // Emergency console logging for non-main tasks when ring buffer is full or disabled
@@ -58,7 +58,7 @@ void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *
// Maximum size for console log messages (includes null terminator) // Maximum size for console log messages (includes null terminator)
static const size_t MAX_CONSOLE_LOG_MSG_SIZE = 144; static const size_t MAX_CONSOLE_LOG_MSG_SIZE = 144;
char console_buffer[MAX_CONSOLE_LOG_MSG_SIZE]; // MUST be stack allocated for thread safety char console_buffer[MAX_CONSOLE_LOG_MSG_SIZE]; // MUST be stack allocated for thread safety
int buffer_at = 0; // Initialize buffer position uint16_t buffer_at = 0; // Initialize buffer position
this->format_log_to_buffer_with_terminator_(level, tag, line, format, args, console_buffer, &buffer_at, this->format_log_to_buffer_with_terminator_(level, tag, line, format, args, console_buffer, &buffer_at,
MAX_CONSOLE_LOG_MSG_SIZE); MAX_CONSOLE_LOG_MSG_SIZE);
this->write_msg_(console_buffer); this->write_msg_(console_buffer);
@@ -69,7 +69,7 @@ void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *
} }
#else #else
// Implementation for all other platforms // Implementation for all other platforms
void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *format, va_list args) { // NOLINT void HOT Logger::log_vprintf_(uint8_t level, const char *tag, int line, const char *format, va_list args) { // NOLINT
if (level > this->level_for(tag) || global_recursion_guard_) if (level > this->level_for(tag) || global_recursion_guard_)
return; return;
@@ -85,7 +85,7 @@ void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *
#ifdef USE_STORE_LOG_STR_IN_FLASH #ifdef USE_STORE_LOG_STR_IN_FLASH
// Implementation for ESP8266 with flash string support. // Implementation for ESP8266 with flash string support.
// Note: USE_STORE_LOG_STR_IN_FLASH is only defined for ESP8266. // Note: USE_STORE_LOG_STR_IN_FLASH is only defined for ESP8266.
void Logger::log_vprintf_(int level, const char *tag, int line, const __FlashStringHelper *format, void Logger::log_vprintf_(uint8_t level, const char *tag, int line, const __FlashStringHelper *format,
va_list args) { // NOLINT va_list args) { // NOLINT
if (level > this->level_for(tag) || global_recursion_guard_) if (level > this->level_for(tag) || global_recursion_guard_)
return; return;
@@ -122,7 +122,7 @@ void Logger::log_vprintf_(int level, const char *tag, int line, const __FlashStr
} }
#endif // USE_STORE_LOG_STR_IN_FLASH #endif // USE_STORE_LOG_STR_IN_FLASH
inline int Logger::level_for(const char *tag) { inline uint8_t Logger::level_for(const char *tag) {
auto it = this->log_levels_.find(tag); auto it = this->log_levels_.find(tag);
if (it != this->log_levels_.end()) if (it != this->log_levels_.end())
return it->second; return it->second;
@@ -195,13 +195,13 @@ void Logger::loop() {
#endif #endif
void Logger::set_baud_rate(uint32_t baud_rate) { this->baud_rate_ = baud_rate; } void Logger::set_baud_rate(uint32_t baud_rate) { this->baud_rate_ = baud_rate; }
void Logger::set_log_level(const std::string &tag, int log_level) { this->log_levels_[tag] = log_level; } void Logger::set_log_level(const std::string &tag, uint8_t log_level) { this->log_levels_[tag] = log_level; }
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY) #if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY)
UARTSelection Logger::get_uart() const { return this->uart_; } UARTSelection Logger::get_uart() const { return this->uart_; }
#endif #endif
void Logger::add_on_log_callback(std::function<void(int, const char *, const char *)> &&callback) { void Logger::add_on_log_callback(std::function<void(uint8_t, const char *, const char *)> &&callback) {
this->log_callback_.add(std::move(callback)); this->log_callback_.add(std::move(callback));
} }
float Logger::get_setup_priority() const { return setup_priority::BUS + 500.0f; } float Logger::get_setup_priority() const { return setup_priority::BUS + 500.0f; }
@@ -230,7 +230,7 @@ void Logger::dump_config() {
} }
} }
void Logger::set_log_level(int level) { void Logger::set_log_level(uint8_t level) {
if (level > ESPHOME_LOG_LEVEL) { if (level > ESPHOME_LOG_LEVEL) {
level = ESPHOME_LOG_LEVEL; level = ESPHOME_LOG_LEVEL;
ESP_LOGW(TAG, "Cannot set log level higher than pre-compiled %s", LOG_LEVELS[ESPHOME_LOG_LEVEL]); ESP_LOGW(TAG, "Cannot set log level higher than pre-compiled %s", LOG_LEVELS[ESPHOME_LOG_LEVEL]);

View File

@@ -61,7 +61,7 @@ static const char *const LOG_LEVEL_LETTERS[] = {
* *
* Advanced configuration (pin selection, etc) is not supported. * Advanced configuration (pin selection, etc) is not supported.
*/ */
enum UARTSelection { enum UARTSelection : uint8_t {
#ifdef USE_LIBRETINY #ifdef USE_LIBRETINY
UART_SELECTION_DEFAULT = 0, UART_SELECTION_DEFAULT = 0,
UART_SELECTION_UART0, UART_SELECTION_UART0,
@@ -129,10 +129,10 @@ class Logger : public Component {
#endif #endif
/// Set the default log level for this logger. /// Set the default log level for this logger.
void set_log_level(int level); void set_log_level(uint8_t level);
/// Set the log level of the specified tag. /// Set the log level of the specified tag.
void set_log_level(const std::string &tag, int log_level); void set_log_level(const std::string &tag, uint8_t log_level);
int get_log_level() { return this->current_level_; } uint8_t get_log_level() { return this->current_level_; }
// ========== INTERNAL METHODS ========== // ========== INTERNAL METHODS ==========
// (In most use cases you won't need these) // (In most use cases you won't need these)
@@ -140,19 +140,20 @@ class Logger : public Component {
void pre_setup(); void pre_setup();
void dump_config() override; void dump_config() override;
inline int level_for(const char *tag); inline uint8_t level_for(const char *tag);
/// Register a callback that will be called for every log message sent /// Register a callback that will be called for every log message sent
void add_on_log_callback(std::function<void(int, const char *, const char *)> &&callback); void add_on_log_callback(std::function<void(uint8_t, const char *, const char *)> &&callback);
// add a listener for log level changes // add a listener for log level changes
void add_listener(std::function<void(int)> &&callback) { this->level_callback_.add(std::move(callback)); } void add_listener(std::function<void(uint8_t)> &&callback) { this->level_callback_.add(std::move(callback)); }
float get_setup_priority() const override; float get_setup_priority() const override;
void log_vprintf_(int level, const char *tag, int line, const char *format, va_list args); // NOLINT void log_vprintf_(uint8_t level, const char *tag, int line, const char *format, va_list args); // NOLINT
#ifdef USE_STORE_LOG_STR_IN_FLASH #ifdef USE_STORE_LOG_STR_IN_FLASH
void log_vprintf_(int level, const char *tag, int line, const __FlashStringHelper *format, va_list args); // NOLINT void log_vprintf_(uint8_t level, const char *tag, int line, const __FlashStringHelper *format,
va_list args); // NOLINT
#endif #endif
protected: protected:
@@ -160,8 +161,9 @@ class Logger : public Component {
// Format a log message with printf-style arguments and write it to a buffer with header, footer, and null terminator // Format a log message with printf-style arguments and write it to a buffer with header, footer, and null terminator
// It's the caller's responsibility to initialize buffer_at (typically to 0) // It's the caller's responsibility to initialize buffer_at (typically to 0)
inline void HOT format_log_to_buffer_with_terminator_(int level, const char *tag, int line, const char *format, inline void HOT format_log_to_buffer_with_terminator_(uint8_t level, const char *tag, int line, const char *format,
va_list args, char *buffer, int *buffer_at, int buffer_size) { va_list args, char *buffer, uint16_t *buffer_at,
uint16_t buffer_size) {
#if defined(USE_ESP32) || defined(USE_LIBRETINY) #if defined(USE_ESP32) || defined(USE_LIBRETINY)
this->write_header_to_buffer_(level, tag, line, this->get_thread_name_(), buffer, buffer_at, buffer_size); this->write_header_to_buffer_(level, tag, line, this->get_thread_name_(), buffer, buffer_at, buffer_size);
#else #else
@@ -180,7 +182,7 @@ class Logger : public Component {
} }
// Helper to format and send a log message to both console and callbacks // Helper to format and send a log message to both console and callbacks
inline void HOT log_message_to_buffer_and_send_(int level, const char *tag, int line, const char *format, inline void HOT log_message_to_buffer_and_send_(uint8_t level, const char *tag, int line, const char *format,
va_list args) { va_list args) {
// Format to tx_buffer and prepare for output // Format to tx_buffer and prepare for output
this->tx_buffer_at_ = 0; // Initialize buffer position this->tx_buffer_at_ = 0; // Initialize buffer position
@@ -194,11 +196,12 @@ class Logger : public Component {
} }
// Write the body of the log message to the buffer // Write the body of the log message to the buffer
inline void write_body_to_buffer_(const char *value, size_t length, char *buffer, int *buffer_at, int buffer_size) { inline void write_body_to_buffer_(const char *value, size_t length, char *buffer, uint16_t *buffer_at,
uint16_t buffer_size) {
// Calculate available space // Calculate available space
const int available = buffer_size - *buffer_at; if (*buffer_at >= buffer_size)
if (available <= 0)
return; return;
const uint16_t available = buffer_size - *buffer_at;
// Determine copy length (minimum of remaining capacity and string length) // Determine copy length (minimum of remaining capacity and string length)
const size_t copy_len = (length < static_cast<size_t>(available)) ? length : available; const size_t copy_len = (length < static_cast<size_t>(available)) ? length : available;
@@ -211,7 +214,7 @@ class Logger : public Component {
} }
// Format string to explicit buffer with varargs // Format string to explicit buffer with varargs
inline void printf_to_buffer_(char *buffer, int *buffer_at, int buffer_size, const char *format, ...) { inline void printf_to_buffer_(char *buffer, uint16_t *buffer_at, uint16_t buffer_size, const char *format, ...) {
va_list arg; va_list arg;
va_start(arg, format); va_start(arg, format);
this->format_body_to_buffer_(buffer, buffer_at, buffer_size, format, arg); this->format_body_to_buffer_(buffer, buffer_at, buffer_size, format, arg);
@@ -222,41 +225,50 @@ class Logger : public Component {
const char *get_uart_selection_(); const char *get_uart_selection_();
#endif #endif
// Group 4-byte aligned members first
uint32_t baud_rate_; uint32_t baud_rate_;
char *tx_buffer_{nullptr}; char *tx_buffer_{nullptr};
int tx_buffer_at_{0}; #ifdef USE_ARDUINO
int tx_buffer_size_{0}; Stream *hw_serial_{nullptr};
#endif
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
void *main_task_ = nullptr; // Only used for thread name identification
#endif
#ifdef USE_ESP32
// Task-specific recursion guards:
// - Main task uses a dedicated member variable for efficiency
// - Other tasks use pthread TLS with a dynamically created key via pthread_key_create
pthread_key_t log_recursion_key_; // 4 bytes
#endif
#ifdef USE_ESP_IDF
uart_port_t uart_num_; // 4 bytes (enum defaults to int size)
#endif
// Large objects (internally aligned)
std::map<std::string, uint8_t> log_levels_{};
CallbackManager<void(uint8_t, const char *, const char *)> log_callback_{};
CallbackManager<void(uint8_t)> level_callback_{};
#ifdef USE_ESPHOME_TASK_LOG_BUFFER
std::unique_ptr<logger::TaskLogBuffer> log_buffer_; // Will be initialized with init_log_buffer
#endif
// Group smaller types together at the end
uint16_t tx_buffer_at_{0};
uint16_t tx_buffer_size_{0};
uint8_t current_level_{ESPHOME_LOG_LEVEL_VERY_VERBOSE};
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) #if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040)
UARTSelection uart_{UART_SELECTION_UART0}; UARTSelection uart_{UART_SELECTION_UART0};
#endif #endif
#ifdef USE_LIBRETINY #ifdef USE_LIBRETINY
UARTSelection uart_{UART_SELECTION_DEFAULT}; UARTSelection uart_{UART_SELECTION_DEFAULT};
#endif #endif
#ifdef USE_ARDUINO
Stream *hw_serial_{nullptr};
#endif
#ifdef USE_ESP_IDF
uart_port_t uart_num_;
#endif
std::map<std::string, int> log_levels_{};
CallbackManager<void(int, const char *, const char *)> log_callback_{};
int current_level_{ESPHOME_LOG_LEVEL_VERY_VERBOSE};
#ifdef USE_ESPHOME_TASK_LOG_BUFFER
std::unique_ptr<logger::TaskLogBuffer> log_buffer_; // Will be initialized with init_log_buffer
#endif
#ifdef USE_ESP32 #ifdef USE_ESP32
// Task-specific recursion guards:
// - Main task uses a dedicated member variable for efficiency
// - Other tasks use pthread TLS with a dynamically created key via pthread_key_create
bool main_task_recursion_guard_{false}; bool main_task_recursion_guard_{false};
pthread_key_t log_recursion_key_;
#else #else
bool global_recursion_guard_{false}; // Simple global recursion guard for single-task platforms bool global_recursion_guard_{false}; // Simple global recursion guard for single-task platforms
#endif #endif
CallbackManager<void(int)> level_callback_{};
#if defined(USE_ESP32) || defined(USE_LIBRETINY) #if defined(USE_ESP32) || defined(USE_LIBRETINY)
void *main_task_ = nullptr; // Only used for thread name identification
const char *HOT get_thread_name_() { const char *HOT get_thread_name_() {
TaskHandle_t current_task = xTaskGetCurrentTaskHandle(); TaskHandle_t current_task = xTaskGetCurrentTaskHandle();
if (current_task == main_task_) { if (current_task == main_task_) {
@@ -297,11 +309,10 @@ class Logger : public Component {
} }
#endif #endif
inline void HOT write_header_to_buffer_(int level, const char *tag, int line, const char *thread_name, char *buffer, inline void HOT write_header_to_buffer_(uint8_t level, const char *tag, int line, const char *thread_name,
int *buffer_at, int buffer_size) { char *buffer, uint16_t *buffer_at, uint16_t buffer_size) {
// Format header // Format header
if (level < 0) // uint8_t level is already bounded 0-255, just ensure it's <= 7
level = 0;
if (level > 7) if (level > 7)
level = 7; level = 7;
@@ -320,12 +331,12 @@ class Logger : public Component {
this->printf_to_buffer_(buffer, buffer_at, buffer_size, "%s[%s][%s:%03u]: ", color, letter, tag, line); this->printf_to_buffer_(buffer, buffer_at, buffer_size, "%s[%s][%s:%03u]: ", color, letter, tag, line);
} }
inline void HOT format_body_to_buffer_(char *buffer, int *buffer_at, int buffer_size, const char *format, inline void HOT format_body_to_buffer_(char *buffer, uint16_t *buffer_at, uint16_t buffer_size, const char *format,
va_list args) { va_list args) {
// Get remaining capacity in the buffer // Get remaining capacity in the buffer
const int remaining = buffer_size - *buffer_at; if (*buffer_at >= buffer_size)
if (remaining <= 0)
return; return;
const uint16_t remaining = buffer_size - *buffer_at;
const int ret = vsnprintf(buffer + *buffer_at, remaining, format, args); const int ret = vsnprintf(buffer + *buffer_at, remaining, format, args);
@@ -334,7 +345,7 @@ class Logger : public Component {
} }
// Update buffer_at with the formatted length (handle truncation) // Update buffer_at with the formatted length (handle truncation)
int formatted_len = (ret >= remaining) ? remaining : ret; uint16_t formatted_len = (ret >= remaining) ? remaining : ret;
*buffer_at += formatted_len; *buffer_at += formatted_len;
// Remove all trailing newlines right after formatting // Remove all trailing newlines right after formatting
@@ -343,18 +354,18 @@ class Logger : public Component {
} }
} }
inline void HOT write_footer_to_buffer_(char *buffer, int *buffer_at, int buffer_size) { inline void HOT write_footer_to_buffer_(char *buffer, uint16_t *buffer_at, uint16_t buffer_size) {
static const int RESET_COLOR_LEN = strlen(ESPHOME_LOG_RESET_COLOR); static const uint16_t RESET_COLOR_LEN = strlen(ESPHOME_LOG_RESET_COLOR);
this->write_body_to_buffer_(ESPHOME_LOG_RESET_COLOR, RESET_COLOR_LEN, buffer, buffer_at, buffer_size); this->write_body_to_buffer_(ESPHOME_LOG_RESET_COLOR, RESET_COLOR_LEN, buffer, buffer_at, buffer_size);
} }
}; };
extern Logger *global_logger; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) extern Logger *global_logger; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
class LoggerMessageTrigger : public Trigger<int, const char *, const char *> { class LoggerMessageTrigger : public Trigger<uint8_t, const char *, const char *> {
public: public:
explicit LoggerMessageTrigger(Logger *parent, int level) { explicit LoggerMessageTrigger(Logger *parent, uint8_t level) {
this->level_ = level; this->level_ = level;
parent->add_on_log_callback([this](int level, const char *tag, const char *message) { parent->add_on_log_callback([this](uint8_t level, const char *tag, const char *message) {
if (level <= this->level_) { if (level <= this->level_) {
this->trigger(level, tag, message); this->trigger(level, tag, message);
} }
@@ -362,7 +373,7 @@ class LoggerMessageTrigger : public Trigger<int, const char *, const char *> {
} }
protected: protected:
int level_; uint8_t level_;
}; };
} // namespace logger } // namespace logger

View File

@@ -454,9 +454,13 @@ def container_validator(schema, widget_type: WidgetType):
""" """
def validator(value): def validator(value):
result = schema
if w_sch := widget_type.schema: if w_sch := widget_type.schema:
result = result.extend(w_sch) if isinstance(w_sch, dict):
w_sch = cv.Schema(w_sch)
# order is important here to preserve extras
result = w_sch.extend(schema)
else:
result = schema
ltype = df.TYPE_NONE ltype = df.TYPE_NONE
if value and (layout := value.get(df.CONF_LAYOUT)): if value and (layout := value.get(df.CONF_LAYOUT)):
if not isinstance(layout, dict): if not isinstance(layout, dict):

View File

@@ -3,7 +3,6 @@ import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import CONF_ID from esphome.const import CONF_ID
from esphome.core import ID from esphome.core import ID
from esphome.cpp_generator import MockObj
from .defines import ( from .defines import (
CONF_STYLE_DEFINITIONS, CONF_STYLE_DEFINITIONS,
@@ -13,12 +12,13 @@ from .defines import (
literal, literal,
) )
from .helpers import add_lv_use from .helpers import add_lv_use
from .lvcode import LambdaContext, LocalVariable, lv, lv_assign, lv_variable from .lvcode import LambdaContext, LocalVariable, lv
from .schemas import ALL_STYLES, FULL_STYLE_SCHEMA, STYLE_REMAP from .schemas import ALL_STYLES, FULL_STYLE_SCHEMA, STYLE_REMAP
from .types import ObjUpdateAction, lv_lambda_t, lv_obj_t, lv_obj_t_ptr, lv_style_t from .types import ObjUpdateAction, lv_obj_t, lv_style_t
from .widgets import ( from .widgets import (
Widget, Widget,
add_widgets, add_widgets,
collect_parts,
set_obj_properties, set_obj_properties,
theme_widget_map, theme_widget_map,
wait_for_widgets, wait_for_widgets,
@@ -37,12 +37,18 @@ async def style_set(svar, style):
lv.call(f"style_set_{remapped_prop}", svar, literal(value)) lv.call(f"style_set_{remapped_prop}", svar, literal(value))
async def create_style(style, id_name):
style_id = ID(id_name, True, lv_style_t)
svar = cg.new_Pvariable(style_id)
lv.style_init(svar)
await style_set(svar, style)
return svar
async def styles_to_code(config): async def styles_to_code(config):
"""Convert styles to C__ code.""" """Convert styles to C__ code."""
for style in config.get(CONF_STYLE_DEFINITIONS, ()): for style in config.get(CONF_STYLE_DEFINITIONS, ()):
svar = cg.new_Pvariable(style[CONF_ID]) await create_style(style, style[CONF_ID].id)
lv.style_init(svar)
await style_set(svar, style)
@automation.register_action( @automation.register_action(
@@ -68,16 +74,18 @@ async def theme_to_code(config):
if theme := config.get(CONF_THEME): if theme := config.get(CONF_THEME):
add_lv_use(CONF_THEME) add_lv_use(CONF_THEME)
for w_name, style in theme.items(): for w_name, style in theme.items():
if not isinstance(style, dict): # Work around Python 3.10 bug with nested async comprehensions
continue # With Python 3.11 this could be simplified
styles = {}
lname = "lv_theme_apply_" + w_name for part, states in collect_parts(style).items():
apply = lv_variable(lv_lambda_t, lname) styles[part] = {
theme_widget_map[w_name] = apply state: await create_style(
ow = Widget.create("obj", MockObj(ID("obj")), obj_spec) props,
async with LambdaContext([(lv_obj_t_ptr, "obj")], where=w_name) as context: "_lv_theme_style_" + w_name + "_" + part + "_" + state,
await set_obj_properties(ow, style) )
lv_assign(apply, await context.get_lambda()) for state, props in states.items()
}
theme_widget_map[w_name] = styles
async def add_top_layer(lv_component, config): async def add_top_layer(lv_component, config):

View File

@@ -6,7 +6,7 @@ from esphome.config_validation import Invalid
from esphome.const import CONF_DEFAULT, CONF_GROUP, CONF_ID, CONF_STATE, CONF_TYPE from esphome.const import CONF_DEFAULT, CONF_GROUP, CONF_ID, CONF_STATE, CONF_TYPE
from esphome.core import ID, TimePeriod from esphome.core import ID, TimePeriod
from esphome.coroutine import FakeAwaitable from esphome.coroutine import FakeAwaitable
from esphome.cpp_generator import CallExpression, MockObj from esphome.cpp_generator import MockObj
from ..defines import ( from ..defines import (
CONF_FLEX_ALIGN_CROSS, CONF_FLEX_ALIGN_CROSS,
@@ -453,7 +453,17 @@ async def widget_to_code(w_cnfig, w_type: WidgetType, parent):
w = Widget.create(wid, var, spec, w_cnfig) w = Widget.create(wid, var, spec, w_cnfig)
if theme := theme_widget_map.get(w_type): if theme := theme_widget_map.get(w_type):
lv_add(CallExpression(theme, w.obj)) for part, states in theme.items():
part = "LV_PART_" + part.upper()
for state, style in states.items():
state = "LV_STATE_" + state.upper()
if state == "LV_STATE_DEFAULT":
lv_state = literal(part)
elif part == "LV_PART_MAIN":
lv_state = literal(state)
else:
lv_state = join_enums((state, part))
lv.obj_add_style(w.obj, style, lv_state)
await set_obj_properties(w, w_cnfig) await set_obj_properties(w, w_cnfig)
await add_widgets(w, w_cnfig) await add_widgets(w, w_cnfig)
await spec.to_code(w, w_cnfig) await spec.to_code(w, w_cnfig)

View File

@@ -1,8 +1,15 @@
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import CONF_MAX_VALUE, CONF_MIN_VALUE, CONF_MODE, CONF_VALUE from esphome.const import CONF_MAX_VALUE, CONF_MIN_VALUE, CONF_MODE, CONF_VALUE
from ..defines import BAR_MODES, CONF_ANIMATED, CONF_INDICATOR, CONF_MAIN, literal from ..defines import (
from ..lv_validation import animated, get_start_value, lv_float BAR_MODES,
CONF_ANIMATED,
CONF_INDICATOR,
CONF_MAIN,
CONF_START_VALUE,
literal,
)
from ..lv_validation import animated, lv_int
from ..lvcode import lv from ..lvcode import lv
from ..types import LvNumber, NumberType from ..types import LvNumber, NumberType
from . import Widget from . import Widget
@@ -10,22 +17,30 @@ from . import Widget
# Note this file cannot be called "bar.py" because that name is disallowed. # Note this file cannot be called "bar.py" because that name is disallowed.
CONF_BAR = "bar" CONF_BAR = "bar"
BAR_MODIFY_SCHEMA = cv.Schema(
{
cv.Optional(CONF_VALUE): lv_float, def validate_bar(config):
cv.Optional(CONF_ANIMATED, default=True): animated, if config.get(CONF_MODE) != "LV_BAR_MODE_RANGE" and CONF_START_VALUE in config:
} raise cv.Invalid(
) f"{CONF_START_VALUE} is only allowed when {CONF_MODE} is set to 'RANGE'"
)
if (CONF_MIN_VALUE in config) != (CONF_MAX_VALUE in config):
raise cv.Invalid(
f"If either {CONF_MIN_VALUE} or {CONF_MAX_VALUE} is set, both must be set"
)
return config
BAR_SCHEMA = cv.Schema( BAR_SCHEMA = cv.Schema(
{ {
cv.Optional(CONF_VALUE): lv_float, cv.Optional(CONF_VALUE): lv_int,
cv.Optional(CONF_MIN_VALUE, default=0): cv.int_, cv.Optional(CONF_START_VALUE): lv_int,
cv.Optional(CONF_MAX_VALUE, default=100): cv.int_, cv.Optional(CONF_MIN_VALUE): lv_int,
cv.Optional(CONF_MODE, default="NORMAL"): BAR_MODES.one_of, cv.Optional(CONF_MAX_VALUE): lv_int,
cv.Optional(CONF_MODE): BAR_MODES.one_of,
cv.Optional(CONF_ANIMATED, default=True): animated, cv.Optional(CONF_ANIMATED, default=True): animated,
} }
) ).add_extra(validate_bar)
class BarType(NumberType): class BarType(NumberType):
@@ -35,17 +50,23 @@ class BarType(NumberType):
LvNumber("lv_bar_t"), LvNumber("lv_bar_t"),
parts=(CONF_MAIN, CONF_INDICATOR), parts=(CONF_MAIN, CONF_INDICATOR),
schema=BAR_SCHEMA, schema=BAR_SCHEMA,
modify_schema=BAR_MODIFY_SCHEMA,
) )
async def to_code(self, w: Widget, config): async def to_code(self, w: Widget, config):
var = w.obj var = w.obj
if mode := config.get(CONF_MODE):
lv.bar_set_mode(var, literal(mode))
is_animated = literal(config[CONF_ANIMATED])
if CONF_MIN_VALUE in config: if CONF_MIN_VALUE in config:
lv.bar_set_range(var, config[CONF_MIN_VALUE], config[CONF_MAX_VALUE]) lv.bar_set_range(
lv.bar_set_mode(var, literal(config[CONF_MODE])) var,
value = await get_start_value(config) await lv_int.process(config[CONF_MIN_VALUE]),
if value is not None: await lv_int.process(config[CONF_MAX_VALUE]),
lv.bar_set_value(var, value, literal(config[CONF_ANIMATED])) )
if value := await lv_int.process(config.get(CONF_VALUE)):
lv.bar_set_value(var, value, is_animated)
if start_value := await lv_int.process(config.get(CONF_START_VALUE)):
lv.bar_set_start_value(var, start_value, is_animated)
@property @property
def animated(self): def animated(self):

View File

@@ -3,7 +3,7 @@ import esphome.config_validation as cv
from esphome.const import CONF_SIZE, CONF_TEXT from esphome.const import CONF_SIZE, CONF_TEXT
from esphome.cpp_generator import MockObjClass from esphome.cpp_generator import MockObjClass
from ..defines import CONF_MAIN, literal from ..defines import CONF_MAIN
from ..lv_validation import color, color_retmapper, lv_text from ..lv_validation import color, color_retmapper, lv_text
from ..lvcode import LocalVariable, lv, lv_expr from ..lvcode import LocalVariable, lv, lv_expr
from ..schemas import TEXT_SCHEMA from ..schemas import TEXT_SCHEMA
@@ -34,7 +34,7 @@ class QrCodeType(WidgetType):
) )
def get_uses(self): def get_uses(self):
return ("canvas", "img") return ("canvas", "img", "label")
def obj_creator(self, parent: MockObjClass, config: dict): def obj_creator(self, parent: MockObjClass, config: dict):
dark_color = color_retmapper(config[CONF_DARK_COLOR]) dark_color = color_retmapper(config[CONF_DARK_COLOR])
@@ -45,10 +45,8 @@ class QrCodeType(WidgetType):
async def to_code(self, w: Widget, config): async def to_code(self, w: Widget, config):
if (value := config.get(CONF_TEXT)) is not None: if (value := config.get(CONF_TEXT)) is not None:
value = await lv_text.process(value) value = await lv_text.process(value)
with LocalVariable( with LocalVariable("qr_text", cg.std_string, value, modifier="") as str_obj:
"qr_text", cg.const_char_ptr, value, modifier="" lv.qrcode_update(w.obj, str_obj.c_str(), str_obj.size())
) as str_obj:
lv.qrcode_update(w.obj, str_obj, literal(f"strlen({str_obj})"))
qr_code_spec = QrCodeType() qr_code_spec = QrCodeType()

View File

@@ -8,7 +8,7 @@ namespace m5stack_8angle {
static const char *const TAG = "m5stack_8angle.light"; static const char *const TAG = "m5stack_8angle.light";
void M5Stack8AngleLightOutput::setup() { void M5Stack8AngleLightOutput::setup() {
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE); RAMAllocator<uint8_t> allocator;
this->buf_ = allocator.allocate(M5STACK_8ANGLE_NUM_LEDS * M5STACK_8ANGLE_BYTES_PER_LED); this->buf_ = allocator.allocate(M5STACK_8ANGLE_NUM_LEDS * M5STACK_8ANGLE_BYTES_PER_LED);
if (this->buf_ == nullptr) { if (this->buf_ == nullptr) {
ESP_LOGE(TAG, "Failed to allocate buffer of size %u", M5STACK_8ANGLE_NUM_LEDS * M5STACK_8ANGLE_BYTES_PER_LED); ESP_LOGE(TAG, "Failed to allocate buffer of size %u", M5STACK_8ANGLE_NUM_LEDS * M5STACK_8ANGLE_BYTES_PER_LED);

View File

@@ -11,9 +11,9 @@ from esphome.const import (
CONF_VOLUME, CONF_VOLUME,
) )
from esphome.core import CORE from esphome.core import CORE
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
from esphome.coroutine import coroutine_with_priority from esphome.coroutine import coroutine_with_priority
from esphome.cpp_generator import MockObjClass from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@jesserockz"] CODEOWNERS = ["@jesserockz"]
@@ -81,7 +81,7 @@ IsAnnouncingCondition = media_player_ns.class_(
async def setup_media_player_core_(var, config): async def setup_media_player_core_(var, config):
await setup_entity(var, config) await setup_entity(var, config, "media_player")
for conf in config.get(CONF_ON_STATE, []): for conf in config.get(CONF_ON_STATE, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
await automation.build_automation(trigger, [], conf) await automation.build_automation(trigger, [], conf)
@@ -143,6 +143,8 @@ _MEDIA_PLAYER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
} }
) )
_MEDIA_PLAYER_SCHEMA.add_extra(entity_duplicate_validator("media_player"))
def media_player_schema( def media_player_schema(
class_: MockObjClass, class_: MockObjClass,
@@ -166,7 +168,6 @@ def media_player_schema(
MEDIA_PLAYER_SCHEMA = media_player_schema(MediaPlayer) MEDIA_PLAYER_SCHEMA = media_player_schema(MediaPlayer)
MEDIA_PLAYER_SCHEMA.add_extra(cv.deprecated_schema_constant("media_player")) MEDIA_PLAYER_SCHEMA.add_extra(cv.deprecated_schema_constant("media_player"))
MEDIA_PLAYER_ACTION_SCHEMA = automation.maybe_simple_id( MEDIA_PLAYER_ACTION_SCHEMA = automation.maybe_simple_id(
cv.Schema( cv.Schema(
{ {

View File

@@ -27,7 +27,7 @@ void VADModel::log_model_config() {
} }
bool StreamingModel::load_model_() { bool StreamingModel::load_model_() {
RAMAllocator<uint8_t> arena_allocator(RAMAllocator<uint8_t>::ALLOW_FAILURE); RAMAllocator<uint8_t> arena_allocator;
if (this->tensor_arena_ == nullptr) { if (this->tensor_arena_ == nullptr) {
this->tensor_arena_ = arena_allocator.allocate(this->tensor_arena_size_); this->tensor_arena_ = arena_allocator.allocate(this->tensor_arena_size_);
@@ -96,7 +96,7 @@ bool StreamingModel::load_model_() {
void StreamingModel::unload_model() { void StreamingModel::unload_model() {
this->interpreter_.reset(); this->interpreter_.reset();
RAMAllocator<uint8_t> arena_allocator(RAMAllocator<uint8_t>::ALLOW_FAILURE); RAMAllocator<uint8_t> arena_allocator;
if (this->tensor_arena_ != nullptr) { if (this->tensor_arena_ != nullptr) {
arena_allocator.deallocate(this->tensor_arena_, this->tensor_arena_size_); arena_allocator.deallocate(this->tensor_arena_, this->tensor_arena_size_);

View File

@@ -472,3 +472,4 @@ async def to_code(config):
cg.add(var.set_writer(lambda_)) cg.add(var.set_writer(lambda_))
await display.register_display(var, config) await display.register_display(var, config)
await spi.register_spi_device(var, config) await spi.register_spi_device(var, config)
cg.add(var.set_write_only(True))

View File

@@ -64,6 +64,14 @@ class ModbusDevice {
this->parent_->send(this->address_, function, start_address, number_of_entities, payload_len, payload); this->parent_->send(this->address_, function, start_address, number_of_entities, payload_len, payload);
} }
void send_raw(const std::vector<uint8_t> &payload) { this->parent_->send_raw(payload); } void send_raw(const std::vector<uint8_t> &payload) { this->parent_->send_raw(payload); }
void send_error(uint8_t function_code, uint8_t exception_code) {
std::vector<uint8_t> error_response;
error_response.reserve(3);
error_response.push_back(this->address_);
error_response.push_back(function_code | 0x80);
error_response.push_back(exception_code);
this->send_raw(error_response);
}
// If more than one device is connected block sending a new command before a response is received // If more than one device is connected block sending a new command before a response is received
bool waiting_for_response() { return parent_->waiting_for_response != 0; } bool waiting_for_response() { return parent_->waiting_for_response != 0; }

View File

@@ -112,6 +112,22 @@ TYPE_REGISTER_MAP = {
"FP32_R": 2, "FP32_R": 2,
} }
CPP_TYPE_REGISTER_MAP = {
"RAW": cg.uint16,
"U_WORD": cg.uint16,
"S_WORD": cg.int16,
"U_DWORD": cg.uint32,
"U_DWORD_R": cg.uint32,
"S_DWORD": cg.int32,
"S_DWORD_R": cg.int32,
"U_QWORD": cg.uint64,
"U_QWORD_R": cg.uint64,
"S_QWORD": cg.int64,
"S_QWORD_R": cg.int64,
"FP32": cg.float_,
"FP32_R": cg.float_,
}
ModbusCommandSentTrigger = modbus_controller_ns.class_( ModbusCommandSentTrigger = modbus_controller_ns.class_(
"ModbusCommandSentTrigger", automation.Trigger.template(cg.int_, cg.int_) "ModbusCommandSentTrigger", automation.Trigger.template(cg.int_, cg.int_)
) )
@@ -285,21 +301,24 @@ async def to_code(config):
cg.add(var.set_offline_skip_updates(config[CONF_OFFLINE_SKIP_UPDATES])) cg.add(var.set_offline_skip_updates(config[CONF_OFFLINE_SKIP_UPDATES]))
if CONF_SERVER_REGISTERS in config: if CONF_SERVER_REGISTERS in config:
for server_register in config[CONF_SERVER_REGISTERS]: for server_register in config[CONF_SERVER_REGISTERS]:
server_register_var = cg.new_Pvariable(
server_register[CONF_ID],
server_register[CONF_ADDRESS],
server_register[CONF_VALUE_TYPE],
TYPE_REGISTER_MAP[server_register[CONF_VALUE_TYPE]],
)
cpp_type = CPP_TYPE_REGISTER_MAP[server_register[CONF_VALUE_TYPE]]
cg.add( cg.add(
var.add_server_register( server_register_var.set_read_lambda(
cg.new_Pvariable( cg.TemplateArguments(cpp_type),
server_register[CONF_ID], await cg.process_lambda(
server_register[CONF_ADDRESS], server_register[CONF_READ_LAMBDA],
server_register[CONF_VALUE_TYPE], [(cg.uint16, "address")],
TYPE_REGISTER_MAP[server_register[CONF_VALUE_TYPE]], return_type=cpp_type,
await cg.process_lambda( ),
server_register[CONF_READ_LAMBDA],
[],
return_type=cg.float_,
),
)
) )
) )
cg.add(var.add_server_register(server_register_var))
await register_modbus_device(var, config) await register_modbus_device(var, config)
for conf in config.get(CONF_ON_COMMAND_SENT, []): for conf in config.get(CONF_ON_COMMAND_SENT, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)

View File

@@ -117,12 +117,17 @@ void ModbusController::on_modbus_read_registers(uint8_t function_code, uint16_t
bool found = false; bool found = false;
for (auto *server_register : this->server_registers_) { for (auto *server_register : this->server_registers_) {
if (server_register->address == current_address) { if (server_register->address == current_address) {
float value = server_register->read_lambda(); if (!server_register->read_lambda) {
break;
}
int64_t value = server_register->read_lambda();
ESP_LOGD(TAG, "Matched register. Address: 0x%02X. Value type: %zu. Register count: %u. Value: %s.",
server_register->address, static_cast<size_t>(server_register->value_type),
server_register->register_count, server_register->format_value(value).c_str());
ESP_LOGD(TAG, "Matched register. Address: 0x%02X. Value type: %zu. Register count: %u. Value: %0.1f.", std::vector<uint16_t> payload;
server_register->address, static_cast<uint8_t>(server_register->value_type), payload.reserve(server_register->register_count * 2);
server_register->register_count, value); number_to_payload(payload, value, server_register->value_type);
std::vector<uint16_t> payload = float_to_payload(value, server_register->value_type);
sixteen_bit_response.insert(sixteen_bit_response.end(), payload.cbegin(), payload.cend()); sixteen_bit_response.insert(sixteen_bit_response.end(), payload.cbegin(), payload.cend());
current_address += server_register->register_count; current_address += server_register->register_count;
found = true; found = true;
@@ -132,11 +137,7 @@ void ModbusController::on_modbus_read_registers(uint8_t function_code, uint16_t
if (!found) { if (!found) {
ESP_LOGW(TAG, "Could not match any register to address %02X. Sending exception response.", current_address); ESP_LOGW(TAG, "Could not match any register to address %02X. Sending exception response.", current_address);
std::vector<uint8_t> error_response; send_error(function_code, 0x02);
error_response.push_back(this->address_);
error_response.push_back(0x81);
error_response.push_back(0x02);
this->send_raw(error_response);
return; return;
} }
} }

View File

@@ -63,6 +63,10 @@ enum class SensorValueType : uint8_t {
FP32_R = 0xD FP32_R = 0xD
}; };
inline bool value_type_is_float(SensorValueType v) {
return v == SensorValueType::FP32 || v == SensorValueType::FP32_R;
}
inline ModbusFunctionCode modbus_register_read_function(ModbusRegisterType reg_type) { inline ModbusFunctionCode modbus_register_read_function(ModbusRegisterType reg_type) {
switch (reg_type) { switch (reg_type) {
case ModbusRegisterType::COIL: case ModbusRegisterType::COIL:
@@ -253,18 +257,53 @@ class SensorItem {
}; };
class ServerRegister { class ServerRegister {
using ReadLambda = std::function<int64_t()>;
public: public:
ServerRegister(uint16_t address, SensorValueType value_type, uint8_t register_count, ServerRegister(uint16_t address, SensorValueType value_type, uint8_t register_count) {
std::function<float()> read_lambda) {
this->address = address; this->address = address;
this->value_type = value_type; this->value_type = value_type;
this->register_count = register_count; this->register_count = register_count;
this->read_lambda = std::move(read_lambda);
} }
template<typename T> void set_read_lambda(const std::function<T(uint16_t address)> &&user_read_lambda) {
this->read_lambda = [this, user_read_lambda]() -> int64_t {
T user_value = user_read_lambda(this->address);
if constexpr (std::is_same_v<T, float>) {
return bit_cast<uint32_t>(user_value);
} else {
return static_cast<int64_t>(user_value);
}
};
}
// Formats a raw value into a string representation based on the value type for debugging
std::string format_value(int64_t value) const {
switch (this->value_type) {
case SensorValueType::U_WORD:
case SensorValueType::U_DWORD:
case SensorValueType::U_DWORD_R:
case SensorValueType::U_QWORD:
case SensorValueType::U_QWORD_R:
return std::to_string(static_cast<uint64_t>(value));
case SensorValueType::S_WORD:
case SensorValueType::S_DWORD:
case SensorValueType::S_DWORD_R:
case SensorValueType::S_QWORD:
case SensorValueType::S_QWORD_R:
return std::to_string(value);
case SensorValueType::FP32_R:
case SensorValueType::FP32:
return str_sprintf("%.1f", bit_cast<float>(static_cast<uint32_t>(value)));
default:
return std::to_string(value);
}
}
uint16_t address{0}; uint16_t address{0};
SensorValueType value_type{SensorValueType::RAW}; SensorValueType value_type{SensorValueType::RAW};
uint8_t register_count{0}; uint8_t register_count{0};
std::function<float()> read_lambda; ReadLambda read_lambda;
}; };
// ModbusController::create_register_ranges_ tries to optimize register range // ModbusController::create_register_ranges_ tries to optimize register range
@@ -444,7 +483,7 @@ class ModbusController : public PollingComponent, public modbus::ModbusDevice {
void on_modbus_data(const std::vector<uint8_t> &data) override; void on_modbus_data(const std::vector<uint8_t> &data) override;
/// called when a modbus error response was received /// called when a modbus error response was received
void on_modbus_error(uint8_t function_code, uint8_t exception_code) override; void on_modbus_error(uint8_t function_code, uint8_t exception_code) override;
/// called when a modbus request (function code 3 or 4) was parsed without errors /// called when a modbus request (function code 0x03 or 0x04) was parsed without errors
void on_modbus_read_registers(uint8_t function_code, uint16_t start_address, uint16_t number_of_registers) final; void on_modbus_read_registers(uint8_t function_code, uint16_t start_address, uint16_t number_of_registers) final;
/// default delegate called by process_modbus_data when a response has retrieved from the incoming queue /// default delegate called by process_modbus_data when a response has retrieved from the incoming queue
void on_register_data(ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data); void on_register_data(ModbusRegisterType register_type, uint16_t start_address, const std::vector<uint8_t> &data);
@@ -529,7 +568,7 @@ inline float payload_to_float(const std::vector<uint8_t> &data, const SensorItem
int64_t number = payload_to_number(data, item.sensor_value_type, item.offset, item.bitmask); int64_t number = payload_to_number(data, item.sensor_value_type, item.offset, item.bitmask);
float float_value; float float_value;
if (item.sensor_value_type == SensorValueType::FP32 || item.sensor_value_type == SensorValueType::FP32_R) { if (value_type_is_float(item.sensor_value_type)) {
float_value = bit_cast<float>(static_cast<uint32_t>(number)); float_value = bit_cast<float>(static_cast<uint32_t>(number));
} else { } else {
float_value = static_cast<float>(number); float_value = static_cast<float>(number);
@@ -541,7 +580,7 @@ inline float payload_to_float(const std::vector<uint8_t> &data, const SensorItem
inline std::vector<uint16_t> float_to_payload(float value, SensorValueType value_type) { inline std::vector<uint16_t> float_to_payload(float value, SensorValueType value_type) {
int64_t val; int64_t val;
if (value_type == SensorValueType::FP32 || value_type == SensorValueType::FP32_R) { if (value_type_is_float(value_type)) {
val = bit_cast<uint32_t>(value); val = bit_cast<uint32_t>(value);
} else { } else {
val = llroundf(value); val = llroundf(value);

View File

@@ -68,6 +68,7 @@ def AUTO_LOAD():
CONF_DISCOVER_IP = "discover_ip" CONF_DISCOVER_IP = "discover_ip"
CONF_IDF_SEND_ASYNC = "idf_send_async" CONF_IDF_SEND_ASYNC = "idf_send_async"
CONF_WAIT_FOR_CONNECTION = "wait_for_connection"
def validate_message_just_topic(value): def validate_message_just_topic(value):
@@ -298,6 +299,7 @@ CONFIG_SCHEMA = cv.All(
} }
), ),
cv.Optional(CONF_PUBLISH_NAN_AS_NONE, default=False): cv.boolean, cv.Optional(CONF_PUBLISH_NAN_AS_NONE, default=False): cv.boolean,
cv.Optional(CONF_WAIT_FOR_CONNECTION, default=False): cv.boolean,
} }
), ),
validate_config, validate_config,
@@ -453,6 +455,8 @@ async def to_code(config):
cg.add(var.set_publish_nan_as_none(config[CONF_PUBLISH_NAN_AS_NONE])) cg.add(var.set_publish_nan_as_none(config[CONF_PUBLISH_NAN_AS_NONE]))
cg.add(var.set_wait_for_connection(config[CONF_WAIT_FOR_CONNECTION]))
MQTT_PUBLISH_ACTION_SCHEMA = cv.Schema( MQTT_PUBLISH_ACTION_SCHEMA = cv.Schema(
{ {

View File

@@ -17,7 +17,8 @@ enum class MQTTClientDisconnectReason : int8_t {
MQTT_MALFORMED_CREDENTIALS = 4, MQTT_MALFORMED_CREDENTIALS = 4,
MQTT_NOT_AUTHORIZED = 5, MQTT_NOT_AUTHORIZED = 5,
ESP8266_NOT_ENOUGH_SPACE = 6, ESP8266_NOT_ENOUGH_SPACE = 6,
TLS_BAD_FINGERPRINT = 7 TLS_BAD_FINGERPRINT = 7,
DNS_RESOLVE_ERROR = 8
}; };
/// internal struct for MQTT messages. /// internal struct for MQTT messages.

View File

@@ -176,7 +176,8 @@ void MQTTClientComponent::dump_config() {
} }
} }
bool MQTTClientComponent::can_proceed() { bool MQTTClientComponent::can_proceed() {
return network::is_disabled() || this->state_ == MQTT_CLIENT_DISABLED || this->is_connected(); return network::is_disabled() || this->state_ == MQTT_CLIENT_DISABLED || this->is_connected() ||
!this->wait_for_connection_;
} }
void MQTTClientComponent::start_dnslookup_() { void MQTTClientComponent::start_dnslookup_() {
@@ -228,6 +229,8 @@ void MQTTClientComponent::check_dnslookup_() {
if (this->dns_resolve_error_) { if (this->dns_resolve_error_) {
ESP_LOGW(TAG, "Couldn't resolve IP address for '%s'", this->credentials_.address.c_str()); ESP_LOGW(TAG, "Couldn't resolve IP address for '%s'", this->credentials_.address.c_str());
this->state_ = MQTT_CLIENT_DISCONNECTED; this->state_ = MQTT_CLIENT_DISCONNECTED;
this->disconnect_reason_ = MQTTClientDisconnectReason::DNS_RESOLVE_ERROR;
this->on_disconnect_.call(MQTTClientDisconnectReason::DNS_RESOLVE_ERROR);
return; return;
} }
@@ -697,7 +700,9 @@ void MQTTClientComponent::set_on_connect(mqtt_on_connect_callback_t &&callback)
} }
void MQTTClientComponent::set_on_disconnect(mqtt_on_disconnect_callback_t &&callback) { void MQTTClientComponent::set_on_disconnect(mqtt_on_disconnect_callback_t &&callback) {
auto callback_copy = callback;
this->mqtt_backend_.set_on_disconnect(std::forward<mqtt_on_disconnect_callback_t>(callback)); this->mqtt_backend_.set_on_disconnect(std::forward<mqtt_on_disconnect_callback_t>(callback));
this->on_disconnect_.add(std::move(callback_copy));
} }
#if ASYNC_TCP_SSL_ENABLED #if ASYNC_TCP_SSL_ENABLED

View File

@@ -4,11 +4,12 @@
#ifdef USE_MQTT #ifdef USE_MQTT
#include "esphome/core/component.h"
#include "esphome/core/automation.h"
#include "esphome/core/log.h"
#include "esphome/components/json/json_util.h" #include "esphome/components/json/json_util.h"
#include "esphome/components/network/ip_address.h" #include "esphome/components/network/ip_address.h"
#include "esphome/core/automation.h"
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#if defined(USE_ESP32) #if defined(USE_ESP32)
#include "mqtt_backend_esp32.h" #include "mqtt_backend_esp32.h"
#elif defined(USE_ESP8266) #elif defined(USE_ESP8266)
@@ -267,6 +268,8 @@ class MQTTClientComponent : public Component {
void set_publish_nan_as_none(bool publish_nan_as_none); void set_publish_nan_as_none(bool publish_nan_as_none);
bool is_publish_nan_as_none() const; bool is_publish_nan_as_none() const;
void set_wait_for_connection(bool wait_for_connection) { this->wait_for_connection_ = wait_for_connection; }
protected: protected:
void send_device_info_(); void send_device_info_();
@@ -332,8 +335,10 @@ class MQTTClientComponent : public Component {
uint32_t connect_begin_; uint32_t connect_begin_;
uint32_t last_connected_{0}; uint32_t last_connected_{0};
optional<MQTTClientDisconnectReason> disconnect_reason_{}; optional<MQTTClientDisconnectReason> disconnect_reason_{};
CallbackManager<MQTTBackend::on_disconnect_callback_t> on_disconnect_;
bool publish_nan_as_none_{false}; bool publish_nan_as_none_{false};
bool wait_for_connection_{false};
}; };
extern MQTTClientComponent *global_mqtt_client; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) extern MQTTClientComponent *global_mqtt_client; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)

View File

@@ -1,8 +1,8 @@
#include "nextion.h" #include "nextion.h"
#include "esphome/core/util.h"
#include "esphome/core/log.h"
#include "esphome/core/application.h"
#include <cinttypes> #include <cinttypes>
#include "esphome/core/application.h"
#include "esphome/core/log.h"
#include "esphome/core/util.h"
namespace esphome { namespace esphome {
namespace nextion { namespace nextion {
@@ -33,6 +33,7 @@ bool Nextion::send_command_(const std::string &command) {
#ifdef USE_NEXTION_COMMAND_SPACING #ifdef USE_NEXTION_COMMAND_SPACING
if (!this->ignore_is_setup_ && !this->command_pacer_.can_send()) { if (!this->ignore_is_setup_ && !this->command_pacer_.can_send()) {
ESP_LOGN(TAG, "Command spacing: delaying command '%s'", command.c_str());
return false; return false;
} }
#endif // USE_NEXTION_COMMAND_SPACING #endif // USE_NEXTION_COMMAND_SPACING
@@ -43,10 +44,6 @@ bool Nextion::send_command_(const std::string &command) {
const uint8_t to_send[3] = {0xFF, 0xFF, 0xFF}; const uint8_t to_send[3] = {0xFF, 0xFF, 0xFF};
this->write_array(to_send, sizeof(to_send)); this->write_array(to_send, sizeof(to_send));
#ifdef USE_NEXTION_COMMAND_SPACING
this->command_pacer_.mark_sent();
#endif // USE_NEXTION_COMMAND_SPACING
return true; return true;
} }
@@ -74,13 +71,13 @@ bool Nextion::check_connect_() {
} }
this->send_command_("connect"); this->send_command_("connect");
this->comok_sent_ = millis(); this->comok_sent_ = App.get_loop_component_start_time();
this->ignore_is_setup_ = false; this->ignore_is_setup_ = false;
return false; return false;
} }
if (millis() - this->comok_sent_ <= 500) // Wait 500 ms if (App.get_loop_component_start_time() - this->comok_sent_ <= 500) // Wait 500 ms
return false; return false;
std::string response; std::string response;
@@ -321,15 +318,38 @@ void Nextion::loop() {
if (!this->nextion_reports_is_setup_) { if (!this->nextion_reports_is_setup_) {
if (this->started_ms_ == 0) if (this->started_ms_ == 0)
this->started_ms_ = millis(); this->started_ms_ = App.get_loop_component_start_time();
if (this->started_ms_ + this->startup_override_ms_ < millis()) { if (this->started_ms_ + this->startup_override_ms_ < App.get_loop_component_start_time()) {
ESP_LOGD(TAG, "Manual ready set"); ESP_LOGD(TAG, "Manual ready set");
this->nextion_reports_is_setup_ = true; this->nextion_reports_is_setup_ = true;
} }
} }
#ifdef USE_NEXTION_COMMAND_SPACING
// Try to send any pending commands if spacing allows
this->process_pending_in_queue_();
#endif // USE_NEXTION_COMMAND_SPACING
} }
#ifdef USE_NEXTION_COMMAND_SPACING
void Nextion::process_pending_in_queue_() {
if (this->nextion_queue_.empty() || !this->command_pacer_.can_send()) {
return;
}
// Check if first item in queue has a pending command
auto *front_item = this->nextion_queue_.front();
if (front_item && !front_item->pending_command.empty()) {
if (this->send_command_(front_item->pending_command)) {
// Command sent successfully, clear the pending command
front_item->pending_command.clear();
ESP_LOGVV(TAG, "Pending command sent: %s", front_item->component->get_variable_name().c_str());
}
}
}
#endif // USE_NEXTION_COMMAND_SPACING
bool Nextion::remove_from_q_(bool report_empty) { bool Nextion::remove_from_q_(bool report_empty) {
if (this->nextion_queue_.empty()) { if (this->nextion_queue_.empty()) {
if (report_empty) { if (report_empty) {
@@ -377,12 +397,6 @@ void Nextion::process_nextion_commands_() {
size_t commands_processed = 0; size_t commands_processed = 0;
#endif // USE_NEXTION_MAX_COMMANDS_PER_LOOP #endif // USE_NEXTION_MAX_COMMANDS_PER_LOOP
#ifdef USE_NEXTION_COMMAND_SPACING
if (!this->command_pacer_.can_send()) {
return; // Will try again in next loop iteration
}
#endif
size_t to_process_length = 0; size_t to_process_length = 0;
std::string to_process; std::string to_process;
@@ -418,7 +432,7 @@ void Nextion::process_nextion_commands_() {
case 0x01: // instruction sent by user was successful case 0x01: // instruction sent by user was successful
ESP_LOGVV(TAG, "Cmd OK"); ESP_LOGVV(TAG, "Cmd OK");
ESP_LOGN(TAG, "this->nextion_queue_.empty() %s", this->nextion_queue_.empty() ? "True" : "False"); ESP_LOGN(TAG, "this->nextion_queue_.empty() %s", YESNO(this->nextion_queue_.empty()));
this->remove_from_q_(); this->remove_from_q_();
if (!this->is_setup_) { if (!this->is_setup_) {
@@ -430,6 +444,7 @@ void Nextion::process_nextion_commands_() {
} }
#ifdef USE_NEXTION_COMMAND_SPACING #ifdef USE_NEXTION_COMMAND_SPACING
this->command_pacer_.mark_sent(); // Here is where we should mark the command as sent this->command_pacer_.mark_sent(); // Here is where we should mark the command as sent
ESP_LOGN(TAG, "Command spacing: marked command sent");
#endif #endif
break; break;
case 0x02: // invalid Component ID or name was used case 0x02: // invalid Component ID or name was used
@@ -813,7 +828,7 @@ void Nextion::process_nextion_commands_() {
this->command_data_.erase(0, to_process_length + COMMAND_DELIMITER.length() + 1); this->command_data_.erase(0, to_process_length + COMMAND_DELIMITER.length() + 1);
} }
uint32_t ms = millis(); uint32_t ms = App.get_loop_component_start_time();
if (!this->nextion_queue_.empty() && this->nextion_queue_.front()->queue_time + this->max_q_age_ms_ < ms) { if (!this->nextion_queue_.empty() && this->nextion_queue_.front()->queue_time + this->max_q_age_ms_ < ms) {
for (size_t i = 0; i < this->nextion_queue_.size(); i++) { for (size_t i = 0; i < this->nextion_queue_.size(); i++) {
@@ -948,11 +963,10 @@ uint16_t Nextion::recv_ret_string_(std::string &response, uint32_t timeout, bool
uint16_t ret = 0; uint16_t ret = 0;
uint8_t c = 0; uint8_t c = 0;
uint8_t nr_of_ff_bytes = 0; uint8_t nr_of_ff_bytes = 0;
uint64_t start;
bool exit_flag = false; bool exit_flag = false;
bool ff_flag = false; bool ff_flag = false;
start = millis(); const uint32_t start = millis();
while ((timeout == 0 && this->available()) || millis() - start <= timeout) { while ((timeout == 0 && this->available()) || millis() - start <= timeout) {
if (!this->available()) { if (!this->available()) {
@@ -1011,7 +1025,7 @@ void Nextion::add_no_result_to_queue_(const std::string &variable_name) {
} }
#endif #endif
ExternalRAMAllocator<nextion::NextionQueue> allocator(ExternalRAMAllocator<nextion::NextionQueue>::ALLOW_FAILURE); RAMAllocator<nextion::NextionQueue> allocator;
nextion::NextionQueue *nextion_queue = allocator.allocate(1); nextion::NextionQueue *nextion_queue = allocator.allocate(1);
if (nextion_queue == nullptr) { if (nextion_queue == nullptr) {
ESP_LOGW(TAG, "Queue alloc failed"); ESP_LOGW(TAG, "Queue alloc failed");
@@ -1042,9 +1056,42 @@ void Nextion::add_no_result_to_queue_with_command_(const std::string &variable_n
if (this->send_command_(command)) { if (this->send_command_(command)) {
this->add_no_result_to_queue_(variable_name); this->add_no_result_to_queue_(variable_name);
#ifdef USE_NEXTION_COMMAND_SPACING
} else {
// Command blocked by spacing, add to queue WITH the command for retry
this->add_no_result_to_queue_with_pending_command_(variable_name, command);
#endif // USE_NEXTION_COMMAND_SPACING
} }
} }
#ifdef USE_NEXTION_COMMAND_SPACING
void Nextion::add_no_result_to_queue_with_pending_command_(const std::string &variable_name,
const std::string &command) {
#ifdef USE_NEXTION_MAX_QUEUE_SIZE
if (this->max_queue_size_ > 0 && this->nextion_queue_.size() >= this->max_queue_size_) {
ESP_LOGW(TAG, "Queue full (%zu), drop: %s", this->nextion_queue_.size(), variable_name.c_str());
return;
}
#endif
RAMAllocator<nextion::NextionQueue> allocator;
nextion::NextionQueue *nextion_queue = allocator.allocate(1);
if (nextion_queue == nullptr) {
ESP_LOGW(TAG, "Queue alloc failed");
return;
}
new (nextion_queue) nextion::NextionQueue();
nextion_queue->component = new nextion::NextionComponentBase;
nextion_queue->component->set_variable_name(variable_name);
nextion_queue->queue_time = App.get_loop_component_start_time();
nextion_queue->pending_command = command; // Store command for retry
this->nextion_queue_.push_back(nextion_queue);
ESP_LOGVV(TAG, "Queue with pending command: %s", variable_name.c_str());
}
#endif // USE_NEXTION_COMMAND_SPACING
bool Nextion::add_no_result_to_queue_with_ignore_sleep_printf_(const std::string &variable_name, const char *format, bool Nextion::add_no_result_to_queue_with_ignore_sleep_printf_(const std::string &variable_name, const char *format,
...) { ...) {
if ((!this->is_setup() && !this->ignore_is_setup_)) if ((!this->is_setup() && !this->ignore_is_setup_))
@@ -1167,7 +1214,7 @@ void Nextion::add_to_get_queue(NextionComponentBase *component) {
} }
#endif #endif
ExternalRAMAllocator<nextion::NextionQueue> allocator(ExternalRAMAllocator<nextion::NextionQueue>::ALLOW_FAILURE); RAMAllocator<nextion::NextionQueue> allocator;
nextion::NextionQueue *nextion_queue = allocator.allocate(1); nextion::NextionQueue *nextion_queue = allocator.allocate(1);
if (nextion_queue == nullptr) { if (nextion_queue == nullptr) {
ESP_LOGW(TAG, "Queue alloc failed"); ESP_LOGW(TAG, "Queue alloc failed");
@@ -1176,7 +1223,7 @@ void Nextion::add_to_get_queue(NextionComponentBase *component) {
new (nextion_queue) nextion::NextionQueue(); new (nextion_queue) nextion::NextionQueue();
nextion_queue->component = component; nextion_queue->component = component;
nextion_queue->queue_time = millis(); nextion_queue->queue_time = App.get_loop_component_start_time();
ESP_LOGN(TAG, "Queue %s: %s", component->get_queue_type_string().c_str(), component->get_variable_name().c_str()); ESP_LOGN(TAG, "Queue %s: %s", component->get_queue_type_string().c_str(), component->get_variable_name().c_str());
@@ -1199,7 +1246,7 @@ void Nextion::add_addt_command_to_queue(NextionComponentBase *component) {
if ((!this->is_setup() && !this->ignore_is_setup_) || this->is_sleeping()) if ((!this->is_setup() && !this->ignore_is_setup_) || this->is_sleeping())
return; return;
ExternalRAMAllocator<nextion::NextionQueue> allocator(ExternalRAMAllocator<nextion::NextionQueue>::ALLOW_FAILURE); RAMAllocator<nextion::NextionQueue> allocator;
nextion::NextionQueue *nextion_queue = allocator.allocate(1); nextion::NextionQueue *nextion_queue = allocator.allocate(1);
if (nextion_queue == nullptr) { if (nextion_queue == nullptr) {
ESP_LOGW(TAG, "Queue alloc failed"); ESP_LOGW(TAG, "Queue alloc failed");
@@ -1208,7 +1255,7 @@ void Nextion::add_addt_command_to_queue(NextionComponentBase *component) {
new (nextion_queue) nextion::NextionQueue(); new (nextion_queue) nextion::NextionQueue();
nextion_queue->component = component; nextion_queue->component = component;
nextion_queue->queue_time = millis(); nextion_queue->queue_time = App.get_loop_component_start_time();
this->waveform_queue_.push_back(nextion_queue); this->waveform_queue_.push_back(nextion_queue);
if (this->waveform_queue_.size() == 1) if (this->waveform_queue_.size() == 1)

View File

@@ -1309,9 +1309,23 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
#ifdef USE_NEXTION_MAX_QUEUE_SIZE #ifdef USE_NEXTION_MAX_QUEUE_SIZE
size_t max_queue_size_{0}; size_t max_queue_size_{0};
#endif // USE_NEXTION_MAX_QUEUE_SIZE #endif // USE_NEXTION_MAX_QUEUE_SIZE
#ifdef USE_NEXTION_COMMAND_SPACING #ifdef USE_NEXTION_COMMAND_SPACING
NextionCommandPacer command_pacer_{0}; NextionCommandPacer command_pacer_{0};
/**
* @brief Process any commands in the queue that are pending due to command spacing
*
* This method checks if the first item in the nextion_queue_ has a pending command
* that was previously blocked by command spacing. If spacing now allows and a
* pending command exists, it attempts to send the command. Once successfully sent,
* the pending command is cleared and the queue item continues normal processing.
*
* Called from loop() to retry sending commands that were delayed by spacing.
*/
void process_pending_in_queue_();
#endif // USE_NEXTION_COMMAND_SPACING #endif // USE_NEXTION_COMMAND_SPACING
std::deque<NextionQueue *> nextion_queue_; std::deque<NextionQueue *> nextion_queue_;
std::deque<NextionQueue *> waveform_queue_; std::deque<NextionQueue *> waveform_queue_;
uint16_t recv_ret_string_(std::string &response, uint32_t timeout, bool recv_flag); uint16_t recv_ret_string_(std::string &response, uint32_t timeout, bool recv_flag);
@@ -1348,6 +1362,23 @@ class Nextion : public NextionBase, public PollingComponent, public uart::UARTDe
__attribute__((format(printf, 3, 4))); __attribute__((format(printf, 3, 4)));
void add_no_result_to_queue_with_command_(const std::string &variable_name, const std::string &command); void add_no_result_to_queue_with_command_(const std::string &variable_name, const std::string &command);
#ifdef USE_NEXTION_COMMAND_SPACING
/**
* @brief Add a command to the Nextion queue with a pending command for retry
*
* This method creates a queue entry for a command that was blocked by command spacing.
* The command string is stored in the queue item's pending_command field so it can
* be retried later when spacing allows. This ensures commands are not lost when
* sent too quickly.
*
* If the max_queue_size limit is configured and reached, the command will be dropped.
*
* @param variable_name Name of the variable or component associated with the command
* @param command The actual command string to be sent when spacing allows
*/
void add_no_result_to_queue_with_pending_command_(const std::string &variable_name, const std::string &command);
#endif // USE_NEXTION_COMMAND_SPACING
bool add_no_result_to_queue_with_printf_(const std::string &variable_name, const char *format, ...) bool add_no_result_to_queue_with_printf_(const std::string &variable_name, const char *format, ...)
__attribute__((format(printf, 3, 4))); __attribute__((format(printf, 3, 4)));

View File

@@ -25,6 +25,9 @@ class NextionQueue {
virtual ~NextionQueue() = default; virtual ~NextionQueue() = default;
NextionComponentBase *component; NextionComponentBase *component;
uint32_t queue_time = 0; uint32_t queue_time = 0;
// Store command for retry if spacing blocked it
std::string pending_command; // Empty if command was sent successfully
}; };
class NextionComponentBase { class NextionComponentBase {

View File

@@ -0,0 +1,36 @@
#include "nextion.h"
#ifdef USE_NEXTION_TFT_UPLOAD
#include "esphome/core/application.h"
namespace esphome {
namespace nextion {
static const char *const TAG = "nextion.upload";
bool Nextion::upload_end_(bool successful) {
if (successful) {
ESP_LOGD(TAG, "Upload successful");
delay(1500); // NOLINT
App.safe_reboot();
} else {
ESP_LOGE(TAG, "Upload failed");
this->is_updating_ = false;
this->ignore_is_setup_ = false;
uint32_t baud_rate = this->parent_->get_baud_rate();
if (baud_rate != this->original_baud_rate_) {
ESP_LOGD(TAG, "Baud: %" PRIu32 "->%" PRIu32, baud_rate, this->original_baud_rate_);
this->parent_->set_baud_rate(this->original_baud_rate_);
this->parent_->load_settings();
}
}
return successful;
}
} // namespace nextion
} // namespace esphome
#endif // USE_NEXTION_TFT_UPLOAD

View File

@@ -3,12 +3,12 @@
#ifdef USE_NEXTION_TFT_UPLOAD #ifdef USE_NEXTION_TFT_UPLOAD
#ifdef USE_ARDUINO #ifdef USE_ARDUINO
#include <cinttypes>
#include "esphome/components/network/util.h"
#include "esphome/core/application.h" #include "esphome/core/application.h"
#include "esphome/core/defines.h" #include "esphome/core/defines.h"
#include "esphome/core/util.h"
#include "esphome/core/log.h" #include "esphome/core/log.h"
#include "esphome/components/network/util.h" #include "esphome/core/util.h"
#include <cinttypes>
#ifdef USE_ESP32 #ifdef USE_ESP32
#include <esp_heap_caps.h> #include <esp_heap_caps.h>
@@ -52,7 +52,7 @@ int Nextion::upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start) {
} }
// Allocate the buffer dynamically // Allocate the buffer dynamically
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE); RAMAllocator<uint8_t> allocator;
uint8_t *buffer = allocator.allocate(4096); uint8_t *buffer = allocator.allocate(4096);
if (!buffer) { if (!buffer) {
ESP_LOGE(TAG, "Buffer alloc failed"); ESP_LOGE(TAG, "Buffer alloc failed");
@@ -67,8 +67,8 @@ int Nextion::upload_by_chunks_(HTTPClient &http_client, uint32_t &range_start) {
ESP_LOGV(TAG, "Fetch %" PRIu16 " bytes", buffer_size); ESP_LOGV(TAG, "Fetch %" PRIu16 " bytes", buffer_size);
uint16_t read_len = 0; uint16_t read_len = 0;
int partial_read_len = 0; int partial_read_len = 0;
const uint32_t start_time = millis(); const uint32_t start_time = App.get_loop_component_start_time();
while (read_len < buffer_size && millis() - start_time < 5000) { while (read_len < buffer_size && App.get_loop_component_start_time() - start_time < 5000) {
if (http_client.getStreamPtr()->available() > 0) { if (http_client.getStreamPtr()->available() > 0) {
partial_read_len = partial_read_len =
http_client.getStreamPtr()->readBytes(reinterpret_cast<char *>(buffer) + read_len, buffer_size - read_len); http_client.getStreamPtr()->readBytes(reinterpret_cast<char *>(buffer) + read_len, buffer_size - read_len);
@@ -335,31 +335,6 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) {
return upload_end_(true); return upload_end_(true);
} }
bool Nextion::upload_end_(bool successful) {
ESP_LOGD(TAG, "TFT upload done: %s", YESNO(successful));
if (successful) {
ESP_LOGD(TAG, "Restart");
delay(1500); // NOLINT
App.safe_reboot();
delay(1500); // NOLINT
} else {
ESP_LOGE(TAG, "TFT upload failed");
this->is_updating_ = false;
this->ignore_is_setup_ = false;
uint32_t baud_rate = this->parent_->get_baud_rate();
if (baud_rate != this->original_baud_rate_) {
ESP_LOGD(TAG, "Baud back: %" PRIu32 "->%" PRIu32, baud_rate, this->original_baud_rate_);
this->parent_->set_baud_rate(this->original_baud_rate_);
this->parent_->load_settings();
}
}
return successful;
}
#ifdef USE_ESP8266 #ifdef USE_ESP8266
WiFiClient *Nextion::get_wifi_client_() { WiFiClient *Nextion::get_wifi_client_() {
if (this->tft_url_.compare(0, 6, "https:") == 0) { if (this->tft_url_.compare(0, 6, "https:") == 0) {

View File

@@ -3,14 +3,14 @@
#ifdef USE_NEXTION_TFT_UPLOAD #ifdef USE_NEXTION_TFT_UPLOAD
#ifdef USE_ESP_IDF #ifdef USE_ESP_IDF
#include "esphome/core/application.h"
#include "esphome/core/defines.h"
#include "esphome/core/util.h"
#include "esphome/core/log.h"
#include "esphome/components/network/util.h"
#include <cinttypes>
#include <esp_heap_caps.h> #include <esp_heap_caps.h>
#include <esp_http_client.h> #include <esp_http_client.h>
#include <cinttypes>
#include "esphome/components/network/util.h"
#include "esphome/core/application.h"
#include "esphome/core/defines.h"
#include "esphome/core/log.h"
#include "esphome/core/util.h"
namespace esphome { namespace esphome {
namespace nextion { namespace nextion {
@@ -51,7 +51,7 @@ int Nextion::upload_by_chunks_(esp_http_client_handle_t http_client, uint32_t &r
} }
// Allocate the buffer dynamically // Allocate the buffer dynamically
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE); RAMAllocator<uint8_t> allocator;
uint8_t *buffer = allocator.allocate(4096); uint8_t *buffer = allocator.allocate(4096);
if (!buffer) { if (!buffer) {
ESP_LOGE(TAG, "Buffer alloc failed"); ESP_LOGE(TAG, "Buffer alloc failed");
@@ -335,30 +335,6 @@ bool Nextion::upload_tft(uint32_t baud_rate, bool exit_reparse) {
return this->upload_end_(true); return this->upload_end_(true);
} }
bool Nextion::upload_end_(bool successful) {
ESP_LOGD(TAG, "TFT upload done: %s", YESNO(successful));
if (successful) {
ESP_LOGD(TAG, "Restart");
delay(1500); // NOLINT
App.safe_reboot();
} else {
ESP_LOGE(TAG, "TFT upload failed");
this->is_updating_ = false;
this->ignore_is_setup_ = false;
uint32_t baud_rate = this->parent_->get_baud_rate();
if (baud_rate != this->original_baud_rate_) {
ESP_LOGD(TAG, "Baud back: %" PRIu32 "->%" PRIu32, baud_rate, this->original_baud_rate_);
this->parent_->set_baud_rate(this->original_baud_rate_);
this->parent_->load_settings();
}
}
return successful;
}
} // namespace nextion } // namespace nextion
} // namespace esphome } // namespace esphome

View File

@@ -76,8 +76,8 @@ from esphome.const import (
DEVICE_CLASS_WIND_SPEED, DEVICE_CLASS_WIND_SPEED,
) )
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
from esphome.cpp_generator import MockObjClass from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@esphome/core"] CODEOWNERS = ["@esphome/core"]
DEVICE_CLASSES = [ DEVICE_CLASSES = [
@@ -207,6 +207,9 @@ _NUMBER_SCHEMA = (
) )
_NUMBER_SCHEMA.add_extra(entity_duplicate_validator("number"))
def number_schema( def number_schema(
class_: MockObjClass, class_: MockObjClass,
*, *,
@@ -237,7 +240,7 @@ NUMBER_SCHEMA.add_extra(cv.deprecated_schema_constant("number"))
async def setup_number_core_( async def setup_number_core_(
var, config, *, min_value: float, max_value: float, step: float var, config, *, min_value: float, max_value: float, step: float
): ):
await setup_entity(var, config) await setup_entity(var, config, "number")
cg.add(var.traits.set_min_value(min_value)) cg.add(var.traits.set_min_value(min_value))
cg.add(var.traits.set_max_value(max_value)) cg.add(var.traits.set_max_value(max_value))

View File

@@ -34,6 +34,7 @@ MULTI_CONF = True
CONF_ON_DOWNLOAD_FINISHED = "on_download_finished" CONF_ON_DOWNLOAD_FINISHED = "on_download_finished"
CONF_PLACEHOLDER = "placeholder" CONF_PLACEHOLDER = "placeholder"
CONF_UPDATE = "update"
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@@ -167,6 +168,7 @@ SET_URL_SCHEMA = cv.Schema(
{ {
cv.GenerateID(): cv.use_id(OnlineImage), cv.GenerateID(): cv.use_id(OnlineImage),
cv.Required(CONF_URL): cv.templatable(cv.url), cv.Required(CONF_URL): cv.templatable(cv.url),
cv.Optional(CONF_UPDATE, default=True): cv.templatable(bool),
} }
) )
@@ -188,6 +190,9 @@ async def online_image_action_to_code(config, action_id, template_arg, args):
if CONF_URL in config: if CONF_URL in config:
template_ = await cg.templatable(config[CONF_URL], args, cg.std_string) template_ = await cg.templatable(config[CONF_URL], args, cg.std_string)
cg.add(var.set_url(template_)) cg.add(var.set_url(template_))
if CONF_UPDATE in config:
template_ = await cg.templatable(config[CONF_UPDATE], args, bool)
cg.add(var.set_update(template_))
return var return var

View File

@@ -201,9 +201,12 @@ template<typename... Ts> class OnlineImageSetUrlAction : public Action<Ts...> {
public: public:
OnlineImageSetUrlAction(OnlineImage *parent) : parent_(parent) {} OnlineImageSetUrlAction(OnlineImage *parent) : parent_(parent) {}
TEMPLATABLE_VALUE(std::string, url) TEMPLATABLE_VALUE(std::string, url)
TEMPLATABLE_VALUE(bool, update)
void play(Ts... x) override { void play(Ts... x) override {
this->parent_->set_url(this->url_.value(x...)); this->parent_->set_url(this->url_.value(x...));
this->parent_->update(); if (this->update_.value(x...)) {
this->parent_->update();
}
} }
protected: protected:

View File

@@ -46,7 +46,7 @@ def set_sdkconfig_options(config):
add_idf_sdkconfig_option("CONFIG_OPENTHREAD_NETWORK_PANID", config[CONF_PAN_ID]) add_idf_sdkconfig_option("CONFIG_OPENTHREAD_NETWORK_PANID", config[CONF_PAN_ID])
add_idf_sdkconfig_option("CONFIG_OPENTHREAD_NETWORK_CHANNEL", config[CONF_CHANNEL]) add_idf_sdkconfig_option("CONFIG_OPENTHREAD_NETWORK_CHANNEL", config[CONF_CHANNEL])
add_idf_sdkconfig_option( add_idf_sdkconfig_option(
"CONFIG_OPENTHREAD_NETWORK_MASTERKEY", f"{config[CONF_NETWORK_KEY]:X}" "CONFIG_OPENTHREAD_NETWORK_MASTERKEY", f"{config[CONF_NETWORK_KEY]:X}".lower()
) )
if network_name := config.get(CONF_NETWORK_NAME): if network_name := config.get(CONF_NETWORK_NAME):
@@ -54,14 +54,14 @@ def set_sdkconfig_options(config):
if (ext_pan_id := config.get(CONF_EXT_PAN_ID)) is not None: if (ext_pan_id := config.get(CONF_EXT_PAN_ID)) is not None:
add_idf_sdkconfig_option( add_idf_sdkconfig_option(
"CONFIG_OPENTHREAD_NETWORK_EXTPANID", f"{ext_pan_id:X}" "CONFIG_OPENTHREAD_NETWORK_EXTPANID", f"{ext_pan_id:X}".lower()
) )
if (mesh_local_prefix := config.get(CONF_MESH_LOCAL_PREFIX)) is not None: if (mesh_local_prefix := config.get(CONF_MESH_LOCAL_PREFIX)) is not None:
add_idf_sdkconfig_option( add_idf_sdkconfig_option(
"CONFIG_OPENTHREAD_MESH_LOCAL_PREFIX", f"{mesh_local_prefix:X}" "CONFIG_OPENTHREAD_MESH_LOCAL_PREFIX", f"{mesh_local_prefix}".lower()
) )
if (pskc := config.get(CONF_PSKC)) is not None: if (pskc := config.get(CONF_PSKC)) is not None:
add_idf_sdkconfig_option("CONFIG_OPENTHREAD_NETWORK_PSKC", f"{pskc:X}") add_idf_sdkconfig_option("CONFIG_OPENTHREAD_NETWORK_PSKC", f"{pskc:X}".lower())
if CONF_FORCE_DATASET in config: if CONF_FORCE_DATASET in config:
if config[CONF_FORCE_DATASET]: if config[CONF_FORCE_DATASET]:
@@ -98,7 +98,7 @@ _CONNECTION_SCHEMA = cv.Schema(
cv.Optional(CONF_EXT_PAN_ID): cv.hex_int, cv.Optional(CONF_EXT_PAN_ID): cv.hex_int,
cv.Optional(CONF_NETWORK_NAME): cv.string_strict, cv.Optional(CONF_NETWORK_NAME): cv.string_strict,
cv.Optional(CONF_PSKC): cv.hex_int, cv.Optional(CONF_PSKC): cv.hex_int,
cv.Optional(CONF_MESH_LOCAL_PREFIX): cv.hex_int, cv.Optional(CONF_MESH_LOCAL_PREFIX): cv.ipv6network,
} }
) )

View File

@@ -137,7 +137,7 @@ void OpenThreadSrpComponent::setup() {
// Copy the mdns services to our local instance so that the c_str pointers remain valid for the lifetime of this // Copy the mdns services to our local instance so that the c_str pointers remain valid for the lifetime of this
// component // component
this->mdns_services_ = this->mdns_->get_services(); this->mdns_services_ = this->mdns_->get_services();
ESP_LOGW(TAG, "Setting up SRP services. count = %d\n", this->mdns_services_.size()); ESP_LOGD(TAG, "Setting up SRP services. count = %d\n", this->mdns_services_.size());
for (const auto &service : this->mdns_services_) { for (const auto &service : this->mdns_services_) {
otSrpClientBuffersServiceEntry *entry = otSrpClientBuffersAllocateService(instance); otSrpClientBuffersServiceEntry *entry = otSrpClientBuffersAllocateService(instance);
if (!entry) { if (!entry) {
@@ -185,11 +185,11 @@ void OpenThreadSrpComponent::setup() {
if (error != OT_ERROR_NONE) { if (error != OT_ERROR_NONE) {
ESP_LOGW(TAG, "Failed to add service: %s", otThreadErrorToString(error)); ESP_LOGW(TAG, "Failed to add service: %s", otThreadErrorToString(error));
} }
ESP_LOGW(TAG, "Added service: %s", full_service.c_str()); ESP_LOGD(TAG, "Added service: %s", full_service.c_str());
} }
otSrpClientEnableAutoStartMode(instance, srp_start_callback, nullptr); otSrpClientEnableAutoStartMode(instance, srp_start_callback, nullptr);
ESP_LOGW(TAG, "Finished SRP setup"); ESP_LOGD(TAG, "Finished SRP setup");
} }
void *OpenThreadSrpComponent::pool_alloc_(size_t size) { void *OpenThreadSrpComponent::pool_alloc_(size_t size) {

View File

@@ -1,5 +1,6 @@
# Sourced from https://gist.github.com/agners/0338576e0003318b63ec1ea75adc90f9 # Sourced from https://gist.github.com/agners/0338576e0003318b63ec1ea75adc90f9
import binascii import binascii
import ipaddress
from esphome.const import CONF_CHANNEL from esphome.const import CONF_CHANNEL
@@ -37,6 +38,12 @@ def parse_tlv(tlv) -> dict:
if tag in TLV_TYPES: if tag in TLV_TYPES:
if tag == 3: if tag == 3:
output[TLV_TYPES[tag]] = val.decode("utf-8") output[TLV_TYPES[tag]] = val.decode("utf-8")
elif tag == 7:
mesh_local_prefix = binascii.hexlify(val).decode("utf-8")
mesh_local_prefix_str = f"{mesh_local_prefix}0000000000000000"
ipv6_bytes = bytes.fromhex(mesh_local_prefix_str)
ipv6_address = ipaddress.IPv6Address(ipv6_bytes)
output[TLV_TYPES[tag]] = f"{ipv6_address}/64"
else: else:
output[TLV_TYPES[tag]] = int.from_bytes(val) output[TLV_TYPES[tag]] = int.from_bytes(val)
return output return output

View File

View File

@@ -0,0 +1,122 @@
#include "opt3001.h"
#include "esphome/core/log.h"
namespace esphome {
namespace opt3001 {
static const char *const TAG = "opt3001.sensor";
static const uint8_t OPT3001_REG_RESULT = 0x00;
static const uint8_t OPT3001_REG_CONFIGURATION = 0x01;
// See datasheet for full description of each bit.
static const uint16_t OPT3001_CONFIGURATION_RANGE_FULL = 0b1100000000000000;
static const uint16_t OPT3001_CONFIGURATION_CONVERSION_TIME_800 = 0b100000000000;
static const uint16_t OPT3001_CONFIGURATION_CONVERSION_MODE_MASK = 0b11000000000;
static const uint16_t OPT3001_CONFIGURATION_CONVERSION_MODE_SINGLE_SHOT = 0b01000000000;
static const uint16_t OPT3001_CONFIGURATION_CONVERSION_MODE_SHUTDOWN = 0b00000000000;
// tl;dr: Configure an automatic-ranged, 800ms single shot reading,
// with INT processing disabled
static const uint16_t OPT3001_CONFIGURATION_FULL_RANGE_ONE_SHOT = OPT3001_CONFIGURATION_RANGE_FULL |
OPT3001_CONFIGURATION_CONVERSION_TIME_800 |
OPT3001_CONFIGURATION_CONVERSION_MODE_SINGLE_SHOT;
static const uint16_t OPT3001_CONVERSION_TIME_800 = 825; // give it 25 extra ms; it seems to not be ready quite often
/*
opt3001 properties:
- e (exponent) = high 4 bits of result register
- m (mantissa) = low 12 bits of result register
- formula: (0.01 * 2^e) * m lx
*/
void OPT3001Sensor::read_result_(const std::function<void(float)> &f) {
// ensure the single shot flag is clear, indicating it's done
uint16_t raw_value;
if (this->read(reinterpret_cast<uint8_t *>(&raw_value), 2) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Reading configuration register failed");
f(NAN);
return;
}
raw_value = i2c::i2ctohs(raw_value);
if ((raw_value & OPT3001_CONFIGURATION_CONVERSION_MODE_MASK) != OPT3001_CONFIGURATION_CONVERSION_MODE_SHUTDOWN) {
// not ready; wait 10ms and try again
ESP_LOGW(TAG, "Data not ready; waiting 10ms");
this->set_timeout("opt3001_wait", 10, [this, f]() { read_result_(f); });
return;
}
if (this->read_register(OPT3001_REG_RESULT, reinterpret_cast<uint8_t *>(&raw_value), 2) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Reading result register failed");
f(NAN);
return;
}
raw_value = i2c::i2ctohs(raw_value);
uint8_t exponent = raw_value >> 12;
uint16_t mantissa = raw_value & 0b111111111111;
double lx = 0.01 * pow(2.0, double(exponent)) * double(mantissa);
f(float(lx));
}
void OPT3001Sensor::read_lx_(const std::function<void(float)> &f) {
// turn on (after one-shot sensor automatically powers down)
uint16_t start_measurement = i2c::htoi2cs(OPT3001_CONFIGURATION_FULL_RANGE_ONE_SHOT);
if (this->write_register(OPT3001_REG_CONFIGURATION, reinterpret_cast<uint8_t *>(&start_measurement), 2) !=
i2c::ERROR_OK) {
ESP_LOGW(TAG, "Triggering one shot measurement failed");
f(NAN);
return;
}
this->set_timeout("read", OPT3001_CONVERSION_TIME_800, [this, f]() {
if (this->write(&OPT3001_REG_CONFIGURATION, 1, true) != i2c::ERROR_OK) {
ESP_LOGW(TAG, "Starting configuration register read failed");
f(NAN);
return;
}
this->read_result_(f);
});
}
void OPT3001Sensor::dump_config() {
LOG_SENSOR("", "OPT3001", this);
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
}
LOG_UPDATE_INTERVAL(this);
}
void OPT3001Sensor::update() {
// Set a flag and skip just in case the sensor isn't responding,
// and we just keep waiting for it in read_result_.
// This way we don't end up with potentially boundless "threads"
// using up memory and eventually crashing the device
if (this->updating_) {
return;
}
this->updating_ = true;
this->read_lx_([this](float val) {
this->updating_ = false;
if (std::isnan(val)) {
this->status_set_warning();
this->publish_state(NAN);
return;
}
ESP_LOGD(TAG, "'%s': Illuminance=%.1flx", this->get_name().c_str(), val);
this->status_clear_warning();
this->publish_state(val);
});
}
float OPT3001Sensor::get_setup_priority() const { return setup_priority::DATA; }
} // namespace opt3001
} // namespace esphome

View File

@@ -0,0 +1,27 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h"
namespace esphome {
namespace opt3001 {
/// This class implements support for the i2c-based OPT3001 ambient light sensor.
class OPT3001Sensor : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice {
public:
void dump_config() override;
void update() override;
float get_setup_priority() const override;
protected:
// checks if one-shot is complete before reading the result and returning it
void read_result_(const std::function<void(float)> &f);
// begins a one-shot measurement
void read_lx_(const std::function<void(float)> &f);
bool updating_{false};
};
} // namespace opt3001
} // namespace esphome

View File

@@ -0,0 +1,35 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor
from esphome.const import (
DEVICE_CLASS_ILLUMINANCE,
STATE_CLASS_MEASUREMENT,
UNIT_LUX,
)
DEPENDENCIES = ["i2c"]
CODEOWNERS = ["@ccutrer"]
opt3001_ns = cg.esphome_ns.namespace("opt3001")
OPT3001Sensor = opt3001_ns.class_(
"OPT3001Sensor", sensor.Sensor, cg.PollingComponent, i2c.I2CDevice
)
CONFIG_SCHEMA = (
sensor.sensor_schema(
OPT3001Sensor,
unit_of_measurement=UNIT_LUX,
accuracy_decimals=1,
device_class=DEVICE_CLASS_ILLUMINANCE,
state_class=STATE_CLASS_MEASUREMENT,
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x44))
)
async def to_code(config):
var = await sensor.new_sensor(config)
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)

View File

@@ -9,8 +9,8 @@
#include <hardware/dma.h> #include <hardware/dma.h>
#include <hardware/irq.h> #include <hardware/irq.h>
#include <hardware/pio.h> #include <hardware/pio.h>
#include <pico/stdlib.h>
#include <pico/sem.h> #include <pico/sem.h>
#include <pico/stdlib.h>
namespace esphome { namespace esphome {
namespace rp2040_pio_led_strip { namespace rp2040_pio_led_strip {
@@ -44,7 +44,7 @@ void RP2040PIOLEDStripLightOutput::setup() {
size_t buffer_size = this->get_buffer_size_(); size_t buffer_size = this->get_buffer_size_();
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE); RAMAllocator<uint8_t> allocator;
this->buf_ = allocator.allocate(buffer_size); this->buf_ = allocator.allocate(buffer_size);
if (this->buf_ == nullptr) { if (this->buf_ == nullptr) {
ESP_LOGE(TAG, "Failed to allocate buffer of size %u", buffer_size); ESP_LOGE(TAG, "Failed to allocate buffer of size %u", buffer_size);

View File

@@ -17,8 +17,8 @@ from esphome.const import (
CONF_WEB_SERVER, CONF_WEB_SERVER,
) )
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
from esphome.cpp_generator import MockObjClass from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@esphome/core"] CODEOWNERS = ["@esphome/core"]
IS_PLATFORM_COMPONENT = True IS_PLATFORM_COMPONENT = True
@@ -65,6 +65,9 @@ _SELECT_SCHEMA = (
) )
_SELECT_SCHEMA.add_extra(entity_duplicate_validator("select"))
def select_schema( def select_schema(
class_: MockObjClass, class_: MockObjClass,
*, *,
@@ -89,7 +92,7 @@ SELECT_SCHEMA.add_extra(cv.deprecated_schema_constant("select"))
async def setup_select_core_(var, config, *, options: list[str]): async def setup_select_core_(var, config, *, options: list[str]):
await setup_entity(var, config) await setup_entity(var, config, "select")
cg.add(var.traits.set_options(options)) cg.add(var.traits.set_options(options))

View File

@@ -101,8 +101,8 @@ from esphome.const import (
ENTITY_CATEGORY_CONFIG, ENTITY_CATEGORY_CONFIG,
) )
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
from esphome.cpp_generator import MockObjClass from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
from esphome.util import Registry from esphome.util import Registry
CODEOWNERS = ["@esphome/core"] CODEOWNERS = ["@esphome/core"]
@@ -318,6 +318,8 @@ _SENSOR_SCHEMA = (
) )
) )
_SENSOR_SCHEMA.add_extra(entity_duplicate_validator("sensor"))
def sensor_schema( def sensor_schema(
class_: MockObjClass = cv.UNDEFINED, class_: MockObjClass = cv.UNDEFINED,
@@ -787,7 +789,7 @@ async def build_filters(config):
async def setup_sensor_core_(var, config): async def setup_sensor_core_(var, config):
await setup_entity(var, config) await setup_entity(var, config, "sensor")
if (device_class := config.get(CONF_DEVICE_CLASS)) is not None: if (device_class := config.get(CONF_DEVICE_CLASS)) is not None:
cg.add(var.set_device_class(device_class)) cg.add(var.set_device_class(device_class))

View File

@@ -79,6 +79,7 @@ CONF_SPI_MODE = "spi_mode"
CONF_FORCE_SW = "force_sw" CONF_FORCE_SW = "force_sw"
CONF_INTERFACE = "interface" CONF_INTERFACE = "interface"
CONF_INTERFACE_INDEX = "interface_index" CONF_INTERFACE_INDEX = "interface_index"
CONF_RELEASE_DEVICE = "release_device"
TYPE_SINGLE = "single" TYPE_SINGLE = "single"
TYPE_QUAD = "quad" TYPE_QUAD = "quad"
TYPE_OCTAL = "octal" TYPE_OCTAL = "octal"
@@ -378,6 +379,7 @@ def spi_device_schema(
cv.Optional(CONF_SPI_MODE, default=default_mode): cv.enum( cv.Optional(CONF_SPI_MODE, default=default_mode): cv.enum(
SPI_MODE_OPTIONS, upper=True SPI_MODE_OPTIONS, upper=True
), ),
cv.Optional(CONF_RELEASE_DEVICE): cv.All(cv.boolean, cv.only_with_esp_idf),
} }
if cs_pin_required: if cs_pin_required:
schema[cv.Required(CONF_CS_PIN)] = pins.gpio_output_pin_schema schema[cv.Required(CONF_CS_PIN)] = pins.gpio_output_pin_schema
@@ -389,13 +391,15 @@ def spi_device_schema(
async def register_spi_device(var, config): async def register_spi_device(var, config):
parent = await cg.get_variable(config[CONF_SPI_ID]) parent = await cg.get_variable(config[CONF_SPI_ID])
cg.add(var.set_spi_parent(parent)) cg.add(var.set_spi_parent(parent))
if CONF_CS_PIN in config: if cs_pin := config.get(CONF_CS_PIN):
pin = await cg.gpio_pin_expression(config[CONF_CS_PIN]) pin = await cg.gpio_pin_expression(cs_pin)
cg.add(var.set_cs_pin(pin)) cg.add(var.set_cs_pin(pin))
if CONF_DATA_RATE in config: if data_rate := config.get(CONF_DATA_RATE):
cg.add(var.set_data_rate(config[CONF_DATA_RATE])) cg.add(var.set_data_rate(data_rate))
if CONF_SPI_MODE in config: if spi_mode := config.get(CONF_SPI_MODE):
cg.add(var.set_mode(config[CONF_SPI_MODE])) cg.add(var.set_mode(spi_mode))
if release_device := config.get(CONF_RELEASE_DEVICE):
cg.add(var.set_release_device(release_device))
def final_validate_device_schema(name: str, *, require_mosi: bool, require_miso: bool): def final_validate_device_schema(name: str, *, require_mosi: bool, require_miso: bool):

View File

@@ -16,12 +16,13 @@ bool SPIDelegate::is_ready() { return true; }
GPIOPin *const NullPin::NULL_PIN = new NullPin(); // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) GPIOPin *const NullPin::NULL_PIN = new NullPin(); // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
SPIDelegate *SPIComponent::register_device(SPIClient *device, SPIMode mode, SPIBitOrder bit_order, uint32_t data_rate, SPIDelegate *SPIComponent::register_device(SPIClient *device, SPIMode mode, SPIBitOrder bit_order, uint32_t data_rate,
GPIOPin *cs_pin) { GPIOPin *cs_pin, bool release_device, bool write_only) {
if (this->devices_.count(device) != 0) { if (this->devices_.count(device) != 0) {
ESP_LOGE(TAG, "Device already registered"); ESP_LOGE(TAG, "Device already registered");
return this->devices_[device]; return this->devices_[device];
} }
SPIDelegate *delegate = this->spi_bus_->get_delegate(data_rate, bit_order, mode, cs_pin); // NOLINT SPIDelegate *delegate =
this->spi_bus_->get_delegate(data_rate, bit_order, mode, cs_pin, release_device, write_only); // NOLINT
this->devices_[device] = delegate; this->devices_[device] = delegate;
return delegate; return delegate;
} }

View File

@@ -317,7 +317,8 @@ class SPIBus {
SPIBus(GPIOPin *clk, GPIOPin *sdo, GPIOPin *sdi) : clk_pin_(clk), sdo_pin_(sdo), sdi_pin_(sdi) {} SPIBus(GPIOPin *clk, GPIOPin *sdo, GPIOPin *sdi) : clk_pin_(clk), sdo_pin_(sdo), sdi_pin_(sdi) {}
virtual SPIDelegate *get_delegate(uint32_t data_rate, SPIBitOrder bit_order, SPIMode mode, GPIOPin *cs_pin) { virtual SPIDelegate *get_delegate(uint32_t data_rate, SPIBitOrder bit_order, SPIMode mode, GPIOPin *cs_pin,
bool release_device, bool write_only) {
return new SPIDelegateBitBash(data_rate, bit_order, mode, cs_pin, this->clk_pin_, this->sdo_pin_, this->sdi_pin_); return new SPIDelegateBitBash(data_rate, bit_order, mode, cs_pin, this->clk_pin_, this->sdo_pin_, this->sdi_pin_);
} }
@@ -334,7 +335,7 @@ class SPIClient;
class SPIComponent : public Component { class SPIComponent : public Component {
public: public:
SPIDelegate *register_device(SPIClient *device, SPIMode mode, SPIBitOrder bit_order, uint32_t data_rate, SPIDelegate *register_device(SPIClient *device, SPIMode mode, SPIBitOrder bit_order, uint32_t data_rate,
GPIOPin *cs_pin); GPIOPin *cs_pin, bool release_device, bool write_only);
void unregister_device(SPIClient *device); void unregister_device(SPIClient *device);
void set_clk(GPIOPin *clk) { this->clk_pin_ = clk; } void set_clk(GPIOPin *clk) { this->clk_pin_ = clk; }
@@ -390,7 +391,8 @@ class SPIClient {
virtual void spi_setup() { virtual void spi_setup() {
esph_log_d("spi_device", "mode %u, data_rate %ukHz", (unsigned) this->mode_, (unsigned) (this->data_rate_ / 1000)); esph_log_d("spi_device", "mode %u, data_rate %ukHz", (unsigned) this->mode_, (unsigned) (this->data_rate_ / 1000));
this->delegate_ = this->parent_->register_device(this, this->mode_, this->bit_order_, this->data_rate_, this->cs_); this->delegate_ = this->parent_->register_device(this, this->mode_, this->bit_order_, this->data_rate_, this->cs_,
this->release_device_, this->write_only_);
} }
virtual void spi_teardown() { virtual void spi_teardown() {
@@ -399,6 +401,8 @@ class SPIClient {
} }
bool spi_is_ready() { return this->delegate_->is_ready(); } bool spi_is_ready() { return this->delegate_->is_ready(); }
void set_release_device(bool release) { this->release_device_ = release; }
void set_write_only(bool write_only) { this->write_only_ = write_only; }
protected: protected:
SPIBitOrder bit_order_{BIT_ORDER_MSB_FIRST}; SPIBitOrder bit_order_{BIT_ORDER_MSB_FIRST};
@@ -406,6 +410,8 @@ class SPIClient {
uint32_t data_rate_{1000000}; uint32_t data_rate_{1000000};
SPIComponent *parent_{nullptr}; SPIComponent *parent_{nullptr};
GPIOPin *cs_{nullptr}; GPIOPin *cs_{nullptr};
bool release_device_{false};
bool write_only_{false};
SPIDelegate *delegate_{SPIDelegate::NULL_DELEGATE}; SPIDelegate *delegate_{SPIDelegate::NULL_DELEGATE};
}; };

View File

@@ -43,10 +43,7 @@ class SPIDelegateHw : public SPIDelegate {
return; return;
} }
#ifdef USE_RP2040 #ifdef USE_RP2040
// avoid overwriting the supplied buffer. Use vector for automatic deallocation this->channel_->transfer(ptr, nullptr, length);
auto rxbuf = std::vector<uint8_t>(length);
memcpy(rxbuf.data(), ptr, length);
this->channel_->transfer((void *) rxbuf.data(), length);
#elif defined(USE_ESP8266) #elif defined(USE_ESP8266)
// ESP8266 SPI library requires the pointer to be word aligned, but the data may not be // ESP8266 SPI library requires the pointer to be word aligned, but the data may not be
// so we need to copy the data to a temporary buffer // so we need to copy the data to a temporary buffer
@@ -89,7 +86,8 @@ class SPIBusHw : public SPIBus {
#endif #endif
} }
SPIDelegate *get_delegate(uint32_t data_rate, SPIBitOrder bit_order, SPIMode mode, GPIOPin *cs_pin) override { SPIDelegate *get_delegate(uint32_t data_rate, SPIBitOrder bit_order, SPIMode mode, GPIOPin *cs_pin,
bool release_device, bool write_only) override {
return new SPIDelegateHw(this->channel_, data_rate, bit_order, mode, cs_pin); return new SPIDelegateHw(this->channel_, data_rate, bit_order, mode, cs_pin);
} }

View File

@@ -11,34 +11,26 @@ static const size_t MAX_TRANSFER_SIZE = 4092; // dictated by ESP-IDF API.
class SPIDelegateHw : public SPIDelegate { class SPIDelegateHw : public SPIDelegate {
public: public:
SPIDelegateHw(SPIInterface channel, uint32_t data_rate, SPIBitOrder bit_order, SPIMode mode, GPIOPin *cs_pin, SPIDelegateHw(SPIInterface channel, uint32_t data_rate, SPIBitOrder bit_order, SPIMode mode, GPIOPin *cs_pin,
bool write_only) bool release_device, bool write_only)
: SPIDelegate(data_rate, bit_order, mode, cs_pin), channel_(channel), write_only_(write_only) { : SPIDelegate(data_rate, bit_order, mode, cs_pin),
spi_device_interface_config_t config = {}; channel_(channel),
config.mode = static_cast<uint8_t>(mode); release_device_(release_device),
config.clock_speed_hz = static_cast<int>(data_rate); write_only_(write_only) {
config.spics_io_num = -1; if (!this->release_device_)
config.flags = 0; add_device_();
config.queue_size = 1;
config.pre_cb = nullptr;
config.post_cb = nullptr;
if (bit_order == BIT_ORDER_LSB_FIRST)
config.flags |= SPI_DEVICE_BIT_LSBFIRST;
if (write_only)
config.flags |= SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_NO_DUMMY;
esp_err_t const err = spi_bus_add_device(channel, &config, &this->handle_);
if (err != ESP_OK)
ESP_LOGE(TAG, "Add device failed - err %X", err);
} }
bool is_ready() override { return this->handle_ != nullptr; } bool is_ready() override { return this->handle_ != nullptr; }
void begin_transaction() override { void begin_transaction() override {
if (this->release_device_)
this->add_device_();
if (this->is_ready()) { if (this->is_ready()) {
if (spi_device_acquire_bus(this->handle_, portMAX_DELAY) != ESP_OK) if (spi_device_acquire_bus(this->handle_, portMAX_DELAY) != ESP_OK)
ESP_LOGE(TAG, "Failed to acquire SPI bus"); ESP_LOGE(TAG, "Failed to acquire SPI bus");
SPIDelegate::begin_transaction(); SPIDelegate::begin_transaction();
} else { } else {
ESP_LOGW(TAG, "spi_setup called before initialisation"); ESP_LOGW(TAG, "SPI device not ready, cannot begin transaction");
} }
} }
@@ -46,6 +38,10 @@ class SPIDelegateHw : public SPIDelegate {
if (this->is_ready()) { if (this->is_ready()) {
SPIDelegate::end_transaction(); SPIDelegate::end_transaction();
spi_device_release_bus(this->handle_); spi_device_release_bus(this->handle_);
if (this->release_device_) {
spi_bus_remove_device(this->handle_);
this->handle_ = nullptr; // reset handle to indicate no device is registered
}
} }
} }
@@ -189,8 +185,30 @@ class SPIDelegateHw : public SPIDelegate {
void read_array(uint8_t *ptr, size_t length) override { this->transfer(nullptr, ptr, length); } void read_array(uint8_t *ptr, size_t length) override { this->transfer(nullptr, ptr, length); }
protected: protected:
bool add_device_() {
spi_device_interface_config_t config = {};
config.mode = static_cast<uint8_t>(this->mode_);
config.clock_speed_hz = static_cast<int>(this->data_rate_);
config.spics_io_num = -1;
config.flags = 0;
config.queue_size = 1;
config.pre_cb = nullptr;
config.post_cb = nullptr;
if (this->bit_order_ == BIT_ORDER_LSB_FIRST)
config.flags |= SPI_DEVICE_BIT_LSBFIRST;
if (this->write_only_)
config.flags |= SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_NO_DUMMY;
esp_err_t const err = spi_bus_add_device(this->channel_, &config, &this->handle_);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Add device failed - err %X", err);
return false;
}
return true;
}
SPIInterface channel_{}; SPIInterface channel_{};
spi_device_handle_t handle_{}; spi_device_handle_t handle_{};
bool release_device_{false};
bool write_only_{false}; bool write_only_{false};
}; };
@@ -231,9 +249,10 @@ class SPIBusHw : public SPIBus {
ESP_LOGE(TAG, "Bus init failed - err %X", err); ESP_LOGE(TAG, "Bus init failed - err %X", err);
} }
SPIDelegate *get_delegate(uint32_t data_rate, SPIBitOrder bit_order, SPIMode mode, GPIOPin *cs_pin) override { SPIDelegate *get_delegate(uint32_t data_rate, SPIBitOrder bit_order, SPIMode mode, GPIOPin *cs_pin,
return new SPIDelegateHw(this->channel_, data_rate, bit_order, mode, cs_pin, bool release_device, bool write_only) override {
Utility::get_pin_no(this->sdi_pin_) == -1); return new SPIDelegateHw(this->channel_, data_rate, bit_order, mode, cs_pin, release_device,
write_only || Utility::get_pin_no(this->sdi_pin_) == -1);
} }
protected: protected:

View File

@@ -5,7 +5,7 @@ namespace spi_led_strip {
SpiLedStrip::SpiLedStrip(uint16_t num_leds) { SpiLedStrip::SpiLedStrip(uint16_t num_leds) {
this->num_leds_ = num_leds; this->num_leds_ = num_leds;
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE); RAMAllocator<uint8_t> allocator;
this->buffer_size_ = num_leds * 4 + 8; this->buffer_size_ = num_leds * 4 + 8;
this->buf_ = allocator.allocate(this->buffer_size_); this->buf_ = allocator.allocate(this->buffer_size_);
if (this->buf_ == nullptr) { if (this->buf_ == nullptr) {

View File

@@ -20,8 +20,8 @@ from esphome.const import (
DEVICE_CLASS_SWITCH, DEVICE_CLASS_SWITCH,
) )
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
from esphome.cpp_generator import MockObjClass from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@esphome/core"] CODEOWNERS = ["@esphome/core"]
IS_PLATFORM_COMPONENT = True IS_PLATFORM_COMPONENT = True
@@ -91,6 +91,9 @@ _SWITCH_SCHEMA = (
) )
_SWITCH_SCHEMA.add_extra(entity_duplicate_validator("switch"))
def switch_schema( def switch_schema(
class_: MockObjClass, class_: MockObjClass,
*, *,
@@ -131,7 +134,7 @@ SWITCH_SCHEMA.add_extra(cv.deprecated_schema_constant("switch"))
async def setup_switch_core_(var, config): async def setup_switch_core_(var, config):
await setup_entity(var, config) await setup_entity(var, config, "switch")
if (inverted := config.get(CONF_INVERTED)) is not None: if (inverted := config.get(CONF_INVERTED)) is not None:
cg.add(var.set_inverted(inverted)) cg.add(var.set_inverted(inverted))

View File

@@ -14,8 +14,8 @@ from esphome.const import (
CONF_WEB_SERVER, CONF_WEB_SERVER,
) )
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
from esphome.cpp_generator import MockObjClass from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@mauritskorse"] CODEOWNERS = ["@mauritskorse"]
IS_PLATFORM_COMPONENT = True IS_PLATFORM_COMPONENT = True
@@ -58,6 +58,9 @@ _TEXT_SCHEMA = (
) )
_TEXT_SCHEMA.add_extra(entity_duplicate_validator("text"))
def text_schema( def text_schema(
class_: MockObjClass = cv.UNDEFINED, class_: MockObjClass = cv.UNDEFINED,
*, *,
@@ -94,7 +97,7 @@ async def setup_text_core_(
max_length: int | None, max_length: int | None,
pattern: str | None, pattern: str | None,
): ):
await setup_entity(var, config) await setup_entity(var, config, "text")
cg.add(var.traits.set_min_length(min_length)) cg.add(var.traits.set_min_length(min_length))
cg.add(var.traits.set_max_length(max_length)) cg.add(var.traits.set_max_length(max_length))

View File

@@ -21,8 +21,8 @@ from esphome.const import (
DEVICE_CLASS_TIMESTAMP, DEVICE_CLASS_TIMESTAMP,
) )
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
from esphome.cpp_generator import MockObjClass from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
from esphome.util import Registry from esphome.util import Registry
DEVICE_CLASSES = [ DEVICE_CLASSES = [
@@ -153,6 +153,9 @@ _TEXT_SENSOR_SCHEMA = (
) )
_TEXT_SENSOR_SCHEMA.add_extra(entity_duplicate_validator("text_sensor"))
def text_sensor_schema( def text_sensor_schema(
class_: MockObjClass = cv.UNDEFINED, class_: MockObjClass = cv.UNDEFINED,
*, *,
@@ -186,7 +189,7 @@ async def build_filters(config):
async def setup_text_sensor_core_(var, config): async def setup_text_sensor_core_(var, config):
await setup_entity(var, config) await setup_entity(var, config, "text_sensor")
if (device_class := config.get(CONF_DEVICE_CLASS)) is not None: if (device_class := config.get(CONF_DEVICE_CLASS)) is not None:
cg.add(var.set_device_class(device_class)) cg.add(var.set_device_class(device_class))

View File

@@ -15,8 +15,8 @@ from esphome.const import (
ENTITY_CATEGORY_CONFIG, ENTITY_CATEGORY_CONFIG,
) )
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
from esphome.cpp_generator import MockObjClass from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@jesserockz"] CODEOWNERS = ["@jesserockz"]
IS_PLATFORM_COMPONENT = True IS_PLATFORM_COMPONENT = True
@@ -58,6 +58,9 @@ _UPDATE_SCHEMA = (
) )
_UPDATE_SCHEMA.add_extra(entity_duplicate_validator("update"))
def update_schema( def update_schema(
class_: MockObjClass = cv.UNDEFINED, class_: MockObjClass = cv.UNDEFINED,
*, *,
@@ -87,7 +90,7 @@ UPDATE_SCHEMA.add_extra(cv.deprecated_schema_constant("update"))
async def setup_update_core_(var, config): async def setup_update_core_(var, config):
await setup_entity(var, config) await setup_entity(var, config, "update")
if device_class_config := config.get(CONF_DEVICE_CLASS): if device_class_config := config.get(CONF_DEVICE_CLASS):
cg.add(var.set_device_class(device_class_config)) cg.add(var.set_device_class(device_class_config))

View File

@@ -6,7 +6,7 @@ from esphome.components.esp32 import (
only_on_variant, only_on_variant,
) )
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import CONF_ID from esphome.const import CONF_DEVICES, CONF_ID
from esphome.cpp_types import Component from esphome.cpp_types import Component
AUTO_LOAD = ["bytebuffer"] AUTO_LOAD = ["bytebuffer"]
@@ -16,9 +16,9 @@ usb_host_ns = cg.esphome_ns.namespace("usb_host")
USBHost = usb_host_ns.class_("USBHost", Component) USBHost = usb_host_ns.class_("USBHost", Component)
USBClient = usb_host_ns.class_("USBClient", Component) USBClient = usb_host_ns.class_("USBClient", Component)
CONF_DEVICES = "devices"
CONF_VID = "vid" CONF_VID = "vid"
CONF_PID = "pid" CONF_PID = "pid"
CONF_ENABLE_HUBS = "enable_hubs"
def usb_device_schema(cls=USBClient, vid: int = None, pid: [int] = None) -> cv.Schema: def usb_device_schema(cls=USBClient, vid: int = None, pid: [int] = None) -> cv.Schema:
@@ -42,6 +42,7 @@ CONFIG_SCHEMA = cv.All(
cv.COMPONENT_SCHEMA.extend( cv.COMPONENT_SCHEMA.extend(
{ {
cv.GenerateID(): cv.declare_id(USBHost), cv.GenerateID(): cv.declare_id(USBHost),
cv.Optional(CONF_ENABLE_HUBS, default=False): cv.boolean,
cv.Optional(CONF_DEVICES): cv.ensure_list(usb_device_schema()), cv.Optional(CONF_DEVICES): cv.ensure_list(usb_device_schema()),
} }
), ),
@@ -58,6 +59,8 @@ async def register_usb_client(config):
async def to_code(config): async def to_code(config):
add_idf_sdkconfig_option("CONFIG_USB_HOST_CONTROL_TRANSFER_MAX_SIZE", 1024) add_idf_sdkconfig_option("CONFIG_USB_HOST_CONTROL_TRANSFER_MAX_SIZE", 1024)
if config.get(CONF_ENABLE_HUBS):
add_idf_sdkconfig_option("CONFIG_USB_HOST_HUBS_SUPPORTED", True)
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config) await cg.register_component(var, config)
for device in config.get(CONF_DEVICES) or (): for device in config.get(CONF_DEVICES) or ():

View File

@@ -22,8 +22,8 @@ from esphome.const import (
DEVICE_CLASS_WATER, DEVICE_CLASS_WATER,
) )
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
from esphome.cpp_generator import MockObjClass from esphome.cpp_generator import MockObjClass
from esphome.cpp_helpers import setup_entity
IS_PLATFORM_COMPONENT = True IS_PLATFORM_COMPONENT = True
@@ -103,6 +103,9 @@ _VALVE_SCHEMA = (
) )
_VALVE_SCHEMA.add_extra(entity_duplicate_validator("valve"))
def valve_schema( def valve_schema(
class_: MockObjClass = cv.UNDEFINED, class_: MockObjClass = cv.UNDEFINED,
*, *,
@@ -132,7 +135,7 @@ VALVE_SCHEMA.add_extra(cv.deprecated_schema_constant("valve"))
async def _setup_valve_core(var, config): async def _setup_valve_core(var, config):
await setup_entity(var, config) await setup_entity(var, config, "valve")
if device_class_config := config.get(CONF_DEVICE_CLASS): if device_class_config := config.get(CONF_DEVICE_CLASS):
cg.add(var.set_device_class(device_class_config)) cg.add(var.set_device_class(device_class_config))

Some files were not shown because too many files have changed in this diff Show More